Merge "Adjust SET_DISPLAY_BRIGHTNESS check" into sc-dev
diff --git a/.clang-format b/.clang-format
index 03af56d..6725a1f 100644
--- a/.clang-format
+++ b/.clang-format
@@ -11,3 +11,4 @@
 IndentWidth: 4
 PenaltyBreakBeforeFirstCallParameter: 100000
 SpacesBeforeTrailingComments: 1
+IncludeBlocks: Preserve
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 922367f..8bcb1e5 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -5,6 +5,7 @@
 # Only turn on clang-format check for the following subfolders.
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
                cmds/idlcli/
+               cmds/servicemanager/
                include/input/
                include/powermanager/
                libs/binder/fuzzer/
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index f48f1fb..aff32c3 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -99,6 +99,7 @@
         "libhidlbase",
         "liblog",
         "libutils",
+        "libbinderdebug",
     ],
     srcs: [
         "DumpstateService.cpp",
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 7ab2a8d..336c5a2 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -793,6 +793,9 @@
     if (module_metadata_version != 0) {
         printf("Module Metadata version: %" PRId64 "\n", module_metadata_version);
     }
+    printf("SDK extension versions [r=%s s=%s]\n",
+           android::base::GetProperty("build.version.extensions.r", "-").c_str(),
+           android::base::GetProperty("build.version.extensions.s", "-").c_str());
 
     printf("Kernel: ");
     DumpFileToFd(STDOUT_FILENO, "", "/proc/version");
@@ -2175,14 +2178,13 @@
     }
 
     /*
-     * mount debugfs for non-user builds which launch with S and unmount it
-     * after invoking dumpstateBoard_* methods. This is to enable debug builds
-     * to not have debugfs mounted during runtime. It will also ensure that
-     * debugfs is only accessed by the dumpstate HAL.
+     * mount debugfs for non-user builds with ro.product.debugfs_restrictions.enabled
+     * set to true and unmount it after invoking dumpstateBoard_* methods.
+     * This is to enable debug builds to not have debugfs mounted during runtime.
+     * It will also ensure that debugfs is only accessed by the dumpstate HAL.
      */
-    auto api_level = android::base::GetIntProperty("ro.product.first_api_level", 0);
-    bool mount_debugfs = !PropertiesHelper::IsUserBuild() && api_level >= 31;
-
+    auto mount_debugfs =
+        android::base::GetBoolProperty("ro.product.debugfs_restrictions.enabled", false);
     if (mount_debugfs) {
         RunCommand("mount debugfs", {"mount", "-t", "debugfs", "debugfs", "/sys/kernel/debug"},
                    AS_ROOT_20);
@@ -2290,7 +2292,10 @@
     }
 
     if (mount_debugfs) {
-        RunCommand("unmount debugfs", {"umount", "/sys/kernel/debug"}, AS_ROOT_20);
+        auto keep_debugfs_mounted =
+            android::base::GetProperty("persist.dbg.keep_debugfs_mounted", "");
+        if (keep_debugfs_mounted.empty())
+            RunCommand("unmount debugfs", {"umount", "/sys/kernel/debug"}, AS_ROOT_20);
     }
 
     auto file_sizes = std::make_unique<ssize_t[]>(paths.size());
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 2c573e4..db508b5 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -1032,12 +1032,12 @@
     ZipArchiveHandle handle_;
 };
 
-// Generate a quick wifi report redirected to a file, open it and verify entry exist.
-TEST_F(ZippedBugReportStreamTest, StreamWifiReport) {
-    std::string out_path = kTestDataPath + "out.zip";
+// Generate a quick LimitedOnly report redirected to a file, open it and verify entry exist.
+TEST_F(ZippedBugReportStreamTest, StreamLimitedOnlyReport) {
+    std::string out_path = kTestDataPath + "StreamLimitedOnlyReportOut.zip";
     android::base::unique_fd out_fd;
     CreateFd(out_path, &out_fd);
-    ds_.options_->wifi_only = true;
+    ds_.options_->limited_only = true;
     ds_.options_->stream_to_socket = true;
     RedirectOutputToFd(out_fd);
 
@@ -1051,7 +1051,7 @@
     ExtractToMemory(handle_, &entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()),
                     entry.uncompressed_length);
     EXPECT_THAT(bugreport_txt_name,
-                testing::ContainsRegex("(bugreport-.+-wifi(-[[:digit:]]+){6}\\.txt)"));
+                testing::ContainsRegex("(bugreport-.+(-[[:digit:]]+){6}\\.txt)"));
     VerifyEntry(handle_, bugreport_txt_name, &entry);
 }
 
diff --git a/cmds/dumpsys/Android.bp b/cmds/dumpsys/Android.bp
index 91aa018..6ab6b7f 100644
--- a/cmds/dumpsys/Android.bp
+++ b/cmds/dumpsys/Android.bp
@@ -32,6 +32,7 @@
         "libutils",
         "liblog",
         "libbinder",
+        "libbinderdebug",
     ],
 
     static_libs: [
diff --git a/cmds/dumpsys/OWNERS b/cmds/dumpsys/OWNERS
index 2a9b681..4f6a89e 100644
--- a/cmds/dumpsys/OWNERS
+++ b/cmds/dumpsys/OWNERS
@@ -2,3 +2,6 @@
 
 nandana@google.com
 jsharkey@android.com
+
+# for ServiceManager mock
+per-file dumpsys_test.cpp=smoreland@google.com
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index a017246..ba1c449 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -25,6 +25,7 @@
 #include <binder/Parcel.h>
 #include <binder/ProcessState.h>
 #include <binder/TextOutput.h>
+#include <binderdebug/BinderDebug.h>
 #include <serviceutils/PriorityDumper.h>
 #include <utils/Log.h>
 #include <utils/Vector.h>
@@ -60,13 +61,15 @@
             "usage: dumpsys\n"
             "         To dump all services.\n"
             "or:\n"
-            "       dumpsys [-t TIMEOUT] [--priority LEVEL] [--pid] [--help | -l | --skip SERVICES "
+            "       dumpsys [-t TIMEOUT] [--priority LEVEL] [--pid] [--thread] [--help | -l | "
+            "--skip SERVICES "
             "| SERVICE [ARGS]]\n"
             "         --help: shows this help\n"
             "         -l: only list services, do not dump them\n"
             "         -t TIMEOUT_SEC: TIMEOUT to use in seconds instead of default 10 seconds\n"
             "         -T TIMEOUT_MS: TIMEOUT to use in milliseconds instead of default 10 seconds\n"
             "         --pid: dump PID instead of usual dump\n"
+            "         --thread: dump thread usage instead of usual dump\n"
             "         --proto: filter services that support dumping data in proto format. Dumps\n"
             "               will be in proto format.\n"
             "         --priority LEVEL: filter services based on specified priority\n"
@@ -125,7 +128,8 @@
     Type type = Type::DUMP;
     int timeoutArgMs = 10000;
     int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
-    static struct option longOptions[] = {{"pid", no_argument, 0, 0},
+    static struct option longOptions[] = {{"thread", no_argument, 0, 0},
+                                          {"pid", no_argument, 0, 0},
                                           {"priority", required_argument, 0, 0},
                                           {"proto", no_argument, 0, 0},
                                           {"skip", no_argument, 0, 0},
@@ -163,6 +167,8 @@
                 }
             } else if (!strcmp(longOptions[optionIndex].name, "pid")) {
                 type = Type::PID;
+            } else if (!strcmp(longOptions[optionIndex].name, "thread")) {
+                type = Type::THREAD;
             }
             break;
 
@@ -329,6 +335,23 @@
      return OK;
 }
 
+static status_t dumpThreadsToFd(const sp<IBinder>& service, const unique_fd& fd) {
+    pid_t pid;
+    status_t status = service->getDebugPid(&pid);
+    if (status != OK) {
+        return status;
+    }
+    BinderPidInfo pidInfo;
+    status = getBinderPidInfo(BinderDebugContext::BINDER, pid, &pidInfo);
+    if (status != OK) {
+        return status;
+    }
+    WriteStringToFd("Threads in use: " + std::to_string(pidInfo.threadUsage) + "/" +
+                        std::to_string(pidInfo.threadCount) + "\n",
+                    fd.get());
+    return OK;
+}
+
 status_t Dumpsys::startDumpThread(Type type, const String16& serviceName,
                                   const Vector<String16>& args) {
     sp<IBinder> service = sm_->checkService(serviceName);
@@ -359,6 +382,9 @@
         case Type::PID:
             err = dumpPidToFd(service, remote_end);
             break;
+        case Type::THREAD:
+            err = dumpThreadsToFd(service, remote_end);
+            break;
         default:
             std::cerr << "Unknown dump type" << static_cast<int>(type) << std::endl;
             return;
diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h
index 929c55c..349947c 100644
--- a/cmds/dumpsys/dumpsys.h
+++ b/cmds/dumpsys/dumpsys.h
@@ -52,13 +52,14 @@
     static void setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags);
 
     enum class Type {
-        DUMP,  // dump using `dump` function
-        PID,   // dump pid of server only
+        DUMP,    // dump using `dump` function
+        PID,     // dump pid of server only
+        THREAD,  // dump thread usage of server only
     };
 
     /**
      * Starts a thread to connect to a service and get its dump output. The thread redirects
-     * the output to a pipe. Thread must be stopped by a subsequent callto {@code
+     * the output to a pipe. Thread must be stopped by a subsequent call to {@code
      * stopDumpThread}.
      * @param serviceName
      * @param args list of arguments to pass to service dump method.
diff --git a/cmds/dumpsys/tests/Android.bp b/cmds/dumpsys/tests/Android.bp
index 6854c75..58fec30 100644
--- a/cmds/dumpsys/tests/Android.bp
+++ b/cmds/dumpsys/tests/Android.bp
@@ -19,6 +19,7 @@
         "libbase",
         "libbinder",
         "libutils",
+        "libbinderdebug",
     ],
 
     static_libs: [
@@ -26,6 +27,4 @@
         "libgmock",
         "libserviceutils",
     ],
-
-    clang: true,
 }
diff --git a/cmds/dumpsys/tests/AndroidTest.xml b/cmds/dumpsys/tests/AndroidTest.xml
index 1a8c67f..c2351d9 100644
--- a/cmds/dumpsys/tests/AndroidTest.xml
+++ b/cmds/dumpsys/tests/AndroidTest.xml
@@ -23,4 +23,4 @@
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="dumpsys_test" />
     </test>
-</configuration>
\ No newline at end of file
+</configuration>
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index 67a77f6..c9d2dbb 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -16,12 +16,15 @@
 
 #include "../dumpsys.h"
 
+#include <regex>
 #include <vector>
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include <android-base/file.h>
+#include <binder/Binder.h>
+#include <binder/ProcessState.h>
 #include <serviceutils/PriorityDumper.h>
 #include <utils/String16.h>
 #include <utils/String8.h>
@@ -56,6 +59,7 @@
     MOCK_METHOD1(waitForService, sp<IBinder>(const String16&));
     MOCK_METHOD1(isDeclared, bool(const String16&));
     MOCK_METHOD1(getDeclaredInstances, Vector<String16>(const String16&));
+    MOCK_METHOD1(updatableViaApex, std::optional<String16>(const String16&));
   protected:
     MOCK_METHOD0(onAsBinder, IBinder*());
 };
@@ -222,6 +226,10 @@
         EXPECT_THAT(stdout_, HasSubstr(expected));
     }
 
+    void AssertOutputFormat(const std::string format) {
+        EXPECT_THAT(stdout_, testing::MatchesRegex(format));
+    }
+
     void AssertDumped(const std::string& service, const std::string& dump) {
         EXPECT_THAT(stdout_, HasSubstr("DUMP OF SERVICE " + service + ":\n" + dump));
         EXPECT_THAT(stdout_, HasSubstr("was the duration of dumpsys " + service + ", ending at: "));
@@ -574,6 +582,30 @@
     AssertOutput(std::to_string(getpid()) + "\n");
 }
 
+// Tests 'dumpsys --thread'
+TEST_F(DumpsysTest, ListAllServicesWithThread) {
+    ExpectListServices({"Locksmith", "Valet"});
+    ExpectCheckService("Locksmith");
+    ExpectCheckService("Valet");
+
+    CallMain({"--thread"});
+
+    AssertRunningServices({"Locksmith", "Valet"});
+
+    const std::string format("(.|\n)*((Threads in use: [0-9]+/[0-9]+)?\n-(.|\n)*){2}");
+    AssertOutputFormat(format);
+}
+
+// Tests 'dumpsys --thread service_name'
+TEST_F(DumpsysTest, ListServiceWithThread) {
+    ExpectCheckService("Locksmith");
+
+    CallMain({"--thread", "Locksmith"});
+    // returns an empty string without root enabled
+    const std::string format("(^$|Threads in use: [0-9]/[0-9]+\n)");
+    AssertOutputFormat(format);
+}
+
 TEST_F(DumpsysTest, GetBytesWritten) {
     const char* serviceName = "service2";
     const char* dumpContents = "dump1";
@@ -599,3 +631,13 @@
                         /* as_proto = */ false, elapsedDuration, bytesWritten);
     EXPECT_THAT(status, Eq(INVALID_OPERATION));
 }
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    // start a binder thread pool for testing --thread option
+    android::ProcessState::self()->setThreadPoolMaxThreadCount(8);
+    ProcessState::self()->startThreadPool();
+
+    return RUN_ALL_TESTS();
+}
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index c04b558..83f01de 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -27,6 +27,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
+#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 #include <libdm/dm.h>
@@ -72,6 +73,15 @@
     }
 }
 
+static void DeactivateApexPackages() {
+    std::vector<std::string> apexd_cmd{"/system/bin/apexd", "--unmount-all"};
+    std::string apexd_error_msg;
+    bool exec_result = Exec(apexd_cmd, &apexd_error_msg);
+    if (!exec_result) {
+        PLOG(ERROR) << "Running /system/bin/apexd --unmount-all failed: " << apexd_error_msg;
+    }
+}
+
 static void TryExtraMount(const char* name, const char* slot, const char* target) {
     std::string partition_name = StringPrintf("%s%s", name, slot);
 
@@ -169,6 +179,11 @@
     // want it for product APKs. Same notes as vendor above.
     TryExtraMount("product", arg[2], "/postinstall/product");
 
+    // Try to mount the system_ext partition. update_engine doesn't do this for
+    // us, but we want it for system_ext APKs. Same notes as vendor and product
+    // above.
+    TryExtraMount("system_ext", arg[2], "/postinstall/system_ext");
+
     constexpr const char* kPostInstallLinkerconfig = "/postinstall/linkerconfig";
     // Try to mount /postinstall/linkerconfig. we will set it up after performing the chroot
     if (mount("tmpfs", kPostInstallLinkerconfig, "tmpfs", 0, nullptr) != 0) {
@@ -231,10 +246,30 @@
         exit(205);
     }
 
+    // Call apexd --unmount-all to free up loop and dm block devices, so that we can re-use
+    // them during the next invocation. Since otapreopt_chroot calls exit in case something goes
+    // wrong we need to register our own atexit handler.
+    // We want to register this handler before actually activating apex packages. This is mostly
+    // due to the fact that if fail to unmount apexes, then on the next run of otapreopt_chroot
+    // we will ask for new loop devices instead of re-using existing ones, and we really don't want
+    // to do that. :)
+    if (atexit(DeactivateApexPackages) != 0) {
+        LOG(ERROR) << "Failed to register atexit hander";
+        exit(206);
+    }
+
     // Try to mount APEX packages in "/apex" in the chroot dir. We need at least
     // the ART APEX, as it is required by otapreopt to run dex2oat.
     ActivateApexPackages();
 
+    auto cleanup = android::base::make_scope_guard([](){
+        std::vector<std::string> apexd_cmd{"/system/bin/apexd", "--unmount-all"};
+        std::string apexd_error_msg;
+        bool exec_result = Exec(apexd_cmd, &apexd_error_msg);
+        if (!exec_result) {
+            PLOG(ERROR) << "Running /system/bin/apexd --unmount-all failed: " << apexd_error_msg;
+        }
+    });
     // Check that an ART APEX has been activated; clean up and exit
     // early otherwise.
     static constexpr const std::string_view kRequiredApexs[] = {
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index 17ea903..a27fd10 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -86,7 +86,7 @@
                             bool generate_compact_dex,
                             bool use_jitzygote_image,
                             const char* compilation_reason) {
-    PrepareBootImageAndBootClasspathFlags(use_jitzygote_image);
+    PrepareBootImageFlags(use_jitzygote_image);
 
     PrepareInputFileFlags(output_oat, output_vdex, output_image, input_dex, input_vdex,
                           dex_metadata, profile, swap_fd, class_loader_context,
@@ -112,7 +112,7 @@
 
 RunDex2Oat::~RunDex2Oat() {}
 
-void RunDex2Oat::PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image) {
+void RunDex2Oat::PrepareBootImageFlags(bool use_jitzygote_image) {
     std::string boot_image;
     if (use_jitzygote_image) {
         boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
@@ -120,23 +120,6 @@
         boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
     }
     AddArg(boot_image);
-
-    // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query
-    // BOOTCLASSPATH.
-    char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH");
-    if (dex2oat_bootclasspath != nullptr) {
-        AddRuntimeArg(StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath));
-    }
-
-    std::string updatable_bcp_packages =
-            MapPropertyToArg("dalvik.vm.dex2oat-updatable-bcp-packages-file",
-                             "--updatable-bcp-packages-file=%s");
-    if (updatable_bcp_packages.empty()) {
-        // Make dex2oat fail by providing non-existent file name.
-        updatable_bcp_packages =
-                "--updatable-bcp-packages-file=/nonx/updatable-bcp-packages.txt";
-    }
-    AddArg(updatable_bcp_packages);
 }
 
 void RunDex2Oat::PrepareInputFileFlags(const UniqueFile& output_oat,
diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h
index 325a3a2..475e124 100644
--- a/cmds/installd/run_dex2oat.h
+++ b/cmds/installd/run_dex2oat.h
@@ -56,7 +56,7 @@
     void Exec(int exit_code);
 
   protected:
-    void PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image);
+    void PrepareBootImageFlags(bool use_jitzygote_image);
     void PrepareInputFileFlags(const UniqueFile& output_oat,
                                const UniqueFile& output_vdex,
                                const UniqueFile& output_image,
diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp
index 3813cf7..0a638cd 100644
--- a/cmds/installd/run_dex2oat_test.cpp
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -175,8 +175,6 @@
         default_expected_flags_["--swap-fd"] = FLAG_UNUSED;
         default_expected_flags_["--class-loader-context"] = FLAG_UNUSED;
         default_expected_flags_["--class-loader-context-fds"] = FLAG_UNUSED;
-        default_expected_flags_["--updatable-bcp-packages-file"] =
-                "=/nonx/updatable-bcp-packages.txt";
 
         // Arch
         default_expected_flags_["--instruction-set"] = "=arm64";
@@ -320,28 +318,6 @@
     VerifyExpectedFlags();
 }
 
-TEST_F(RunDex2OatTest, DEX2OATBOOTCLASSPATH) {
-    ASSERT_EQ(nullptr, getenv("DEX2OATBOOTCLASSPATH"));
-    ASSERT_EQ(0, setenv("DEX2OATBOOTCLASSPATH", "foobar", /*override=*/ false))
-        << "Failed to setenv: " << strerror(errno);
-
-    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
-
-    SetExpectedFlagUsed("-Xbootclasspath", ":foobar");
-    VerifyExpectedFlags();
-
-    ASSERT_EQ(0, unsetenv("DEX2OATBOOTCLASSPATH"))
-        << "Failed to setenv: " << strerror(errno);
-}
-
-TEST_F(RunDex2OatTest, UpdatableBootClassPath) {
-    setSystemProperty("dalvik.vm.dex2oat-updatable-bcp-packages-file", "/path/to/file");
-    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
-
-    SetExpectedFlagUsed("--updatable-bcp-packages-file", "=/path/to/file");
-    VerifyExpectedFlags();
-}
-
 TEST_F(RunDex2OatTest, DoNotGenerateCompactDex) {
     auto args = RunDex2OatArgs::MakeDefaultTestArgs();
     args->generate_compact_dex = false;
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index fbf1e0c..e272025 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -351,7 +351,7 @@
             uid = kTestAppUid;
         }
         if (class_loader_context == nullptr) {
-            class_loader_context = "&";
+            class_loader_context = "PCL[]";
         }
         int32_t dexopt_needed = 0;  // does not matter;
         std::optional<std::string> out_path; // does not matter
@@ -478,7 +478,7 @@
                            bool should_binder_call_succeed,
                            /*out */ binder::Status* binder_result) {
         std::optional<std::string> out_path = oat_dir ? std::make_optional<std::string>(oat_dir) : std::nullopt;
-        std::string class_loader_context = "&";
+        std::string class_loader_context = "PCL[]";
         int32_t target_sdk_version = 0;  // default
         std::string profile_name = "primary.prof";
         std::optional<std::string> dm_path_opt = dm_path ? std::make_optional<std::string>(dm_path) : std::nullopt;
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index c47df52..c4ecd07 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -1062,6 +1062,8 @@
 
 static const char* kProcFilesystems = "/proc/filesystems";
 bool supports_sdcardfs() {
+    if (!property_get_bool("external_storage.sdcardfs.enabled", true))
+        return false;
     std::string supported;
     if (!android::base::ReadFileToString(kProcFilesystems, &supported)) {
         PLOG(ERROR) << "Failed to read supported filesystems";
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index d5110f6..2722e21 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -417,7 +417,7 @@
         }
     }
     out << "-->" << std::endl;
-    out << vintf::gHalManifestConverter(manifest, vintf::SerializeFlags::HALS_ONLY);
+    out << vintf::toXml(manifest, vintf::SerializeFlags::HALS_ONLY);
 }
 
 std::string ListCommand::INIT_VINTF_NOTES{
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index b6ff28d..6f08f74 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -47,8 +47,6 @@
 using ::android::hardware::Void;
 using android::vintf::Arch;
 using android::vintf::CompatibilityMatrix;
-using android::vintf::gCompatibilityMatrixConverter;
-using android::vintf::gHalManifestConverter;
 using android::vintf::HalManifest;
 using android::vintf::Transport;
 using android::vintf::VintfObject;
@@ -508,10 +506,10 @@
     EXPECT_THAT(output, HasSubstr("a.h.foo6@6.0::IFoo/6"));
     EXPECT_EQ("", err.str());
 
+    std::string error;
     vintf::HalManifest m;
-    EXPECT_EQ(true, vintf::gHalManifestConverter(&m, out.str()))
-        << "--init-vintf does not emit valid HAL manifest: "
-        << vintf::gHalManifestConverter.lastError();
+    EXPECT_EQ(true, vintf::fromXml(&m, out.str(), &error))
+        << "--init-vintf does not emit valid HAL manifest: " << error;
 }
 
 // test default columns
@@ -775,10 +773,10 @@
     auto deviceMatrix = std::make_shared<CompatibilityMatrix>();
     auto frameworkMatrix = std::make_shared<CompatibilityMatrix>();
 
-    ASSERT_TRUE(gHalManifestConverter(deviceManifest.get(), deviceManifestXml));
-    ASSERT_TRUE(gHalManifestConverter(frameworkManifest.get(), frameworkManifestXml));
-    ASSERT_TRUE(gCompatibilityMatrixConverter(deviceMatrix.get(), deviceMatrixXml));
-    ASSERT_TRUE(gCompatibilityMatrixConverter(frameworkMatrix.get(), frameworkMatrixXml));
+    ASSERT_TRUE(fromXml(deviceManifest.get(), deviceManifestXml));
+    ASSERT_TRUE(fromXml(frameworkManifest.get(), frameworkManifestXml));
+    ASSERT_TRUE(fromXml(deviceMatrix.get(), deviceMatrixXml));
+    ASSERT_TRUE(fromXml(frameworkMatrix.get(), frameworkMatrixXml));
 
     ON_CALL(*mockList, getDeviceManifest()).WillByDefault(Return(deviceManifest));
     ON_CALL(*mockList, getDeviceMatrix()).WillByDefault(Return(deviceMatrix));
@@ -964,7 +962,7 @@
                 "    </hal>\n"
                 "</manifest>";
         auto manifest = std::make_shared<HalManifest>();
-        EXPECT_TRUE(gHalManifestConverter(manifest.get(), mockManifestXml));
+        EXPECT_TRUE(fromXml(manifest.get(), mockManifestXml));
         EXPECT_CALL(*mockList, getDeviceManifest())
             .Times(AnyNumber())
             .WillRepeatedly(Return(manifest));
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 9de344a..3ebdeee 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -14,6 +14,7 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
     ],
 
     srcs: [
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 0dbab4e..b429fb3 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -58,22 +58,34 @@
     return false;
 }
 
-static bool isVintfDeclared(const std::string& name) {
-    size_t firstSlash = name.find('/');
-    size_t lastDot = name.rfind('.', firstSlash);
-    if (firstSlash == std::string::npos || lastDot == std::string::npos) {
-        LOG(ERROR) << "VINTF HALs require names in the format type/instance (e.g. "
-                   << "some.package.foo.IFoo/default) but got: " << name;
-        return false;
-    }
-    const std::string package = name.substr(0, lastDot);
-    const std::string iface = name.substr(lastDot+1, firstSlash-lastDot-1);
-    const std::string instance = name.substr(firstSlash+1);
+struct AidlName {
+    std::string package;
+    std::string iface;
+    std::string instance;
 
-    bool found = forEachManifest([&] (const ManifestWithDescription& mwd) {
-        if (mwd.manifest->hasAidlInstance(package, iface, instance)) {
+    static bool fill(const std::string& name, AidlName* aname) {
+        size_t firstSlash = name.find('/');
+        size_t lastDot = name.rfind('.', firstSlash);
+        if (firstSlash == std::string::npos || lastDot == std::string::npos) {
+            LOG(ERROR) << "VINTF HALs require names in the format type/instance (e.g. "
+                       << "some.package.foo.IFoo/default) but got: " << name;
+            return false;
+        }
+        aname->package = name.substr(0, lastDot);
+        aname->iface = name.substr(lastDot + 1, firstSlash - lastDot - 1);
+        aname->instance = name.substr(firstSlash + 1);
+        return true;
+    }
+};
+
+static bool isVintfDeclared(const std::string& name) {
+    AidlName aname;
+    if (!AidlName::fill(name, &aname)) return false;
+
+    bool found = forEachManifest([&](const ManifestWithDescription& mwd) {
+        if (mwd.manifest->hasAidlInstance(aname.package, aname.iface, aname.instance)) {
             LOG(INFO) << "Found " << name << " in " << mwd.description << " VINTF manifest.";
-            return true;
+            return true; // break
         }
         return false;  // continue
     });
@@ -81,13 +93,34 @@
     if (!found) {
         // Although it is tested, explicitly rebuilding qualified name, in case it
         // becomes something unexpected.
-        LOG(ERROR) << "Could not find " << package << "." << iface << "/" << instance
-                   << " in the VINTF manifest.";
+        LOG(ERROR) << "Could not find " << aname.package << "." << aname.iface << "/"
+                   << aname.instance << " in the VINTF manifest.";
     }
 
     return found;
 }
 
+static std::optional<std::string> getVintfUpdatableApex(const std::string& name) {
+    AidlName aname;
+    if (!AidlName::fill(name, &aname)) return std::nullopt;
+
+    std::optional<std::string> updatableViaApex;
+
+    forEachManifest([&](const ManifestWithDescription& mwd) {
+        mwd.manifest->forEachInstance([&](const auto& manifestInstance) {
+            if (manifestInstance.format() != vintf::HalFormat::AIDL) return true;
+            if (manifestInstance.package() != aname.package) return true;
+            if (manifestInstance.interface() != aname.iface) return true;
+            if (manifestInstance.instance() != aname.instance) return true;
+            updatableViaApex = manifestInstance.updatableViaApex();
+            return false; // break (libvintf uses opposite convention)
+        });
+        return false; // continue
+    });
+
+    return updatableViaApex;
+}
+
 static std::vector<std::string> getVintfInstances(const std::string& interface) {
     size_t lastDot = interface.rfind('.');
     if (lastDot == std::string::npos) {
@@ -239,7 +272,8 @@
 #endif  // !VENDORSERVICEMANAGER
 
     // implicitly unlinked when the binder is removed
-    if (binder->remoteBinder() != nullptr && binder->linkToDeath(this) != OK) {
+    if (binder->remoteBinder() != nullptr &&
+        binder->linkToDeath(sp<ServiceManager>::fromExisting(this)) != OK) {
         LOG(ERROR) << "Could not linkToDeath when adding " << name;
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
@@ -307,7 +341,9 @@
         return Status::fromExceptionCode(Status::EX_NULL_POINTER);
     }
 
-    if (OK != IInterface::asBinder(callback)->linkToDeath(this)) {
+    if (OK !=
+        IInterface::asBinder(callback)->linkToDeath(
+                sp<ServiceManager>::fromExisting(this))) {
         LOG(ERROR) << "Could not linkToDeath when adding " << name;
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
@@ -385,6 +421,22 @@
     return Status::ok();
 }
 
+Status ServiceManager::updatableViaApex(const std::string& name,
+                                        std::optional<std::string>* outReturn) {
+    auto ctx = mAccess->getCallingContext();
+
+    if (!mAccess->canFind(ctx, name)) {
+        return Status::fromExceptionCode(Status::EX_SECURITY);
+    }
+
+    *outReturn = std::nullopt;
+
+#ifndef VENDORSERVICEMANAGER
+    *outReturn = getVintfUpdatableApex(name);
+#endif
+    return Status::ok();
+}
+
 void ServiceManager::removeRegistrationCallback(const wp<IBinder>& who,
                                     ServiceCallbackMap::iterator* it,
                                     bool* found) {
@@ -461,7 +513,8 @@
         return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
     }
 
-    if (OK != IInterface::asBinder(cb)->linkToDeath(this)) {
+    if (OK !=
+        IInterface::asBinder(cb)->linkToDeath(sp<ServiceManager>::fromExisting(this))) {
         LOG(ERROR) << "Could not linkToDeath when adding client callback for " << name;
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
@@ -491,7 +544,7 @@
 }
 
 ssize_t ServiceManager::Service::getNodeStrongRefCount() {
-    sp<BpBinder> bpBinder = binder->remoteBinder();
+    sp<BpBinder> bpBinder = sp<BpBinder>::fromExisting(binder->remoteBinder());
     if (bpBinder == nullptr) return -1;
 
     return ProcessState::self()->getStrongRefCountForNode(bpBinder);
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index c089115..4f23c21 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -46,6 +46,8 @@
 
     binder::Status isDeclared(const std::string& name, bool* outReturn) override;
     binder::Status getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) override;
+    binder::Status updatableViaApex(const std::string& name,
+                                    std::optional<std::string>* outReturn) override;
     binder::Status registerClientCallback(const std::string& name, const sp<IBinder>& service,
                                           const sp<IClientCallback>& cb) override;
     binder::Status tryUnregisterService(const std::string& name, const sp<IBinder>& binder) override;
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 627dfe6..8c1beac 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -39,7 +39,7 @@
 class BinderCallback : public LooperCallback {
 public:
     static sp<BinderCallback> setupTo(const sp<Looper>& looper) {
-        sp<BinderCallback> cb = new BinderCallback;
+        sp<BinderCallback> cb = sp<BinderCallback>::make();
 
         int binder_fd = -1;
         IPCThreadState::self()->setupPolling(&binder_fd);
@@ -65,7 +65,7 @@
 class ClientCallbackCallback : public LooperCallback {
 public:
     static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) {
-        sp<ClientCallbackCallback> cb = new ClientCallbackCallback(manager);
+        sp<ClientCallbackCallback> cb = sp<ClientCallbackCallback>::make(manager);
 
         int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/);
         LOG_ALWAYS_FATAL_IF(fdTimer < 0, "Failed to timerfd_create: fd: %d err: %d", fdTimer, errno);
@@ -105,6 +105,7 @@
         return 1;  // Continue receiving callbacks.
     }
 private:
+    friend sp<ClientCallbackCallback>;
     ClientCallbackCallback(const sp<ServiceManager>& manager) : mManager(manager) {}
     sp<ServiceManager> mManager;
 };
@@ -120,7 +121,7 @@
     ps->setThreadPoolMaxThreadCount(0);
     ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
 
-    sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>());
+    sp<ServiceManager> manager = sp<ServiceManager>::make(std::make_unique<Access>());
     if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
         LOG(ERROR) << "Could not self register servicemanager";
     }
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index fb9f9df..5d5a75e 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -46,7 +46,7 @@
         }
     };
 
-    return new LinkableBinder;
+    return sp<LinkableBinder>::make();
 }
 
 class MockAccess : public Access {
@@ -71,7 +71,7 @@
     ON_CALL(*access, canFind(_, _)).WillByDefault(Return(true));
     ON_CALL(*access, canList(_)).WillByDefault(Return(true));
 
-    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+    sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
     return sm;
 }
 
@@ -119,7 +119,7 @@
             .uid = uid,
         }));
         EXPECT_CALL(*access, canAdd(_, _)).Times(0);
-        sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+        sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
 
         EXPECT_FALSE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
             IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -161,7 +161,7 @@
     EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
     EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(false));
 
-    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+    sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
 
     EXPECT_FALSE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -194,7 +194,7 @@
     EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
     EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(false));
 
-    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+    sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
 
     EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -218,7 +218,7 @@
     EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
     EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
 
-    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+    sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
 
     sp<IBinder> service = getBinder();
     EXPECT_TRUE(sm->addService("foo", service, true /*allowIsolated*/,
@@ -244,7 +244,7 @@
     // TODO(b/136023468): when security check is first, this should be called first
     // EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
 
-    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+    sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
 
     EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
         IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -261,7 +261,7 @@
     EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
     EXPECT_CALL(*access, canList(_)).WillOnce(Return(false));
 
-    sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
+    sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
 
     std::vector<std::string> out;
     EXPECT_FALSE(sm->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL, &out).isOk());
@@ -329,9 +329,9 @@
     EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
     EXPECT_CALL(*access, canFind(_,_)).WillOnce(Return(false));
 
-    sp<ServiceManager> sm = new ServiceManager(std::move(access));
+    sp<ServiceManager> sm = sp<ServiceManager>::make(std::move(access));
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     EXPECT_EQ(sm->registerForNotifications("foofoo", cb).exceptionCode(),
         Status::EX_SECURITY);
@@ -343,9 +343,9 @@
     EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
     EXPECT_CALL(*access, canFind(_,_)).WillOnce(Return(false));
 
-    sp<ServiceManager> sm = new ServiceManager(std::move(access));
+    sp<ServiceManager> sm = sp<ServiceManager>::make(std::move(access));
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     // should always hit security error first
     EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(),
@@ -355,7 +355,7 @@
 TEST(ServiceNotifications, InvalidName) {
     auto sm = getPermissiveServiceManager();
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     EXPECT_EQ(sm->registerForNotifications("foo@foo", cb).exceptionCode(),
         Status::EX_ILLEGAL_ARGUMENT);
@@ -371,7 +371,7 @@
 TEST(ServiceNotifications, Unregister) {
     auto sm = getPermissiveServiceManager();
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     EXPECT_TRUE(sm->registerForNotifications("foofoo", cb).isOk());
     EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(), 0);
@@ -380,7 +380,7 @@
 TEST(ServiceNotifications, UnregisterWhenNoRegistrationExists) {
     auto sm = getPermissiveServiceManager();
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(),
         Status::EX_ILLEGAL_STATE);
@@ -389,7 +389,7 @@
 TEST(ServiceNotifications, NoNotification) {
     auto sm = getPermissiveServiceManager();
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     EXPECT_TRUE(sm->registerForNotifications("foofoo", cb).isOk());
     EXPECT_TRUE(sm->addService("otherservice", getBinder(),
@@ -402,7 +402,7 @@
 TEST(ServiceNotifications, GetNotification) {
     auto sm = getPermissiveServiceManager();
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     sp<IBinder> service = getBinder();
 
@@ -417,7 +417,7 @@
 TEST(ServiceNotifications, GetNotificationForAlreadyRegisteredService) {
     auto sm = getPermissiveServiceManager();
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     sp<IBinder> service = getBinder();
 
@@ -433,7 +433,7 @@
 TEST(ServiceNotifications, GetMultipleNotification) {
     auto sm = getPermissiveServiceManager();
 
-    sp<CallbackHistorian> cb = new CallbackHistorian;
+    sp<CallbackHistorian> cb = sp<CallbackHistorian>::make();
 
     sp<IBinder> binder1 = getBinder();
     sp<IBinder> binder2 = getBinder();
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index 79aab82..3798ba7 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -46,11 +46,9 @@
         HiddenFlagChange            hidden_flag             = 12;
         OpaqueFlagChange            opaque_flag             = 13;
         SecureFlagChange            secure_flag             = 14;
-        DeferredTransactionChange   deferred_transaction    = 15;
         CornerRadiusChange          corner_radius           = 16;
         ReparentChange              reparent                = 17;
         RelativeParentChange        relative_parent         = 18;
-        ReparentChildrenChange      reparent_children       = 19;
         BackgroundBlurRadiusChange  background_blur_radius  = 20;
         ShadowRadiusChange          shadow_radius           = 21;
         BlurRegionsChange           blur_regions            = 22;
@@ -114,11 +112,6 @@
     required bool secure_flag = 1;
 }
 
-message DeferredTransactionChange {
-    required int32  layer_id     = 1;
-    required uint64 frame_number = 2;
-}
-
 message DisplayChange {
     required int32 id = 1;
 
@@ -190,10 +183,6 @@
     required int32 parent_id = 1;
 }
 
-message ReparentChildrenChange {
-    required int32 parent_id = 1;
-}
-
 message RelativeParentChange {
     required int32 relative_parent_id = 1;
     required int32 z = 2;
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index bbbe6f7..cfd42fe 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -402,17 +402,9 @@
             case SurfaceChange::SurfaceChangeCase::kSecureFlag:
                 setSecureFlag(transaction, change.id(), change.secure_flag());
                 break;
-            case SurfaceChange::SurfaceChangeCase::kDeferredTransaction:
-                waitUntilDeferredTransactionLayerExists(change.deferred_transaction(), lock);
-                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;
@@ -563,19 +555,6 @@
     t.setFlags(mLayers[id], flag, layer_state_t::eLayerSecure);
 }
 
-void Replayer::setDeferredTransaction(SurfaceComposerClient::Transaction& t,
-        layer_id id, const DeferredTransactionChange& dtc) {
-    ALOGV("Layer %d: Setting Deferred Transaction -- layer_id=%d, "
-          "frame_number=%llu",
-            id, dtc.layer_id(), dtc.frame_number());
-    if (mLayers.count(dtc.layer_id()) == 0 || mLayers[dtc.layer_id()] == nullptr) {
-        ALOGE("Layer %d not found in Deferred Transaction", dtc.layer_id());
-        return;
-    }
-
-    t.deferTransactionUntil_legacy(mLayers[id], mLayers[dtc.layer_id()], dtc.frame_number());
-}
-
 void Replayer::setDisplaySurface(SurfaceComposerClient::Transaction& t,
         display_id id, const DispSurfaceChange& /*dsc*/) {
     sp<IGraphicBufferProducer> outProducer;
@@ -679,13 +658,6 @@
     std::this_thread::sleep_for(std::chrono::nanoseconds(timestamp - mCurrentTime));
 }
 
-void Replayer::waitUntilDeferredTransactionLayerExists(
-        const DeferredTransactionChange& dtc, std::unique_lock<std::mutex>& lock) {
-    if (mLayers.count(dtc.layer_id()) == 0 || mLayers[dtc.layer_id()] == nullptr) {
-        mLayerCond.wait(lock, [&] { return (mLayers[dtc.layer_id()] != nullptr); });
-    }
-}
-
 status_t Replayer::loadSurfaceComposerClient() {
     mComposerClient = new SurfaceComposerClient;
     return mComposerClient->initCheck();
@@ -709,15 +681,6 @@
     t.setRelativeLayer(mLayers[id], mLayers[c.relative_parent_id()], c.z());
 }
 
-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()]);
-}
-
 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 324d591..d62522a 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -110,14 +110,10 @@
             layer_id id, const OpaqueFlagChange& ofc);
     void setSecureFlag(SurfaceComposerClient::Transaction& t,
             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 setReparentChildrenChange(SurfaceComposerClient::Transaction& t,
-            layer_id id, const ReparentChildrenChange& c);
     void setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
             layer_id id, const ShadowRadiusChange& c);
     void setBlurRegionsChange(SurfaceComposerClient::Transaction& t,
@@ -133,8 +129,6 @@
             display_id id, const ProjectionChange& pc);
 
     void waitUntilTimestamp(int64_t timestamp);
-    void waitUntilDeferredTransactionLayerExists(
-            const DeferredTransactionChange& dtc, std::unique_lock<std::mutex>& lock);
     status_t loadSurfaceComposerClient();
 
     Trace mTrace;
diff --git a/data/etc/android.software.translation.xml b/data/etc/android.software.translation.xml
deleted file mode 100644
index 3b361e5..0000000
--- a/data/etc/android.software.translation.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<permissions>
-    <feature name="android.software.translation" />
-</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index c3c3a7f..41f1514 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -54,7 +54,6 @@
     <feature name="android.software.autofill" />
     <feature name="android.software.cant_save_state" />
     <feature name="android.software.secure_lock_screen" />
-    <feature name="android.software.translation" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
     <feature name="android.software.device_admin" />
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index 4b565fd..8c369de 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -54,7 +54,6 @@
     <feature name="android.software.autofill" />
     <feature name="android.software.cant_save_state" />
     <feature name="android.software.secure_lock_screen" />
-    <feature name="android.software.translation" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
     <feature name="android.software.device_admin" />
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index a70dffd..6704a1d 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -241,6 +241,7 @@
  *
  *  Available since API level 30.
  *
+ *  @param env Handle to the JNI environment pointer.
  *  @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
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index cc5420e..b743f49 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -32,6 +32,11 @@
 __BEGIN_DECLS
 
 struct AChoreographer;
+/**
+ * Opaque type that provides access to an AChoreographer object.
+ *
+ * A pointer can be obtained using {@link AChoreographer_getInstance()}.
+ */
 typedef struct AChoreographer AChoreographer;
 
 /**
diff --git a/include/android/font.h b/include/android/font.h
index a172618..8a3a474 100644
--- a/include/android/font.h
+++ b/include/android/font.h
@@ -189,7 +189,7 @@
  * Available since API level 29.
  *
  * \param font a font object. Passing NULL is not allowed.
- * \return a positive integer less than or equal to {@link ASYSTEM_FONT_MAX_WEIGHT} is returned.
+ * \return a positive integer less than or equal to {@link AFONT_WEIGHT_MAX} is returned.
  */
 uint16_t AFont_getWeight(const AFont* _Nonnull font) __INTRODUCED_IN(29);
 
@@ -241,7 +241,7 @@
  * In this case, AFont_getAxisCount returns 2 and AFont_getAxisTag
  * and AFont_getAxisValue will return following values.
  * \code{.cpp}
- *     AFont* font = AFontIterator_next(ite);
+ *     AFont* font = ASystemFontIterator_next(ite);
  *
  *     // Returns the number of axes
  *     AFont_getAxisCount(font);  // Returns 2
@@ -289,7 +289,7 @@
  *
  * \param font a font object. Passing NULL is not allowed.
  * \param axisIndex an index to the font variation settings. Passing value larger than or
- *         equal to {@link ASYstemFont_getAxisCount} is not allwed.
+ *         equal to {@link AFont_getAxisCount} is not allowed.
  * \return a float value for the given font variation setting.
  */
 float AFont_getAxisValue(const AFont* _Nonnull font, uint32_t axisIndex)
diff --git a/include/android/font_matcher.h b/include/android/font_matcher.h
index 49e478c..4417422 100644
--- a/include/android/font_matcher.h
+++ b/include/android/font_matcher.h
@@ -36,7 +36,7 @@
  *  // Simple font query for the ASCII character.
  *  std::vector<uint16_t> text = { 'A' };
  *  AFontMatcher* matcher = AFontMatcher_create("sans-serif");
- *  ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ *  AFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
  *  // runLength will be 1 and the font will points a valid font file.
  *  AFontMatcher_destroy(matcher);
  *
@@ -44,17 +44,17 @@
  *  std::vector<uint16_t> text = { 0x9AA8 };
  *  AFontMatcher* matcher = AFontMatcher_create("sans-serif");
  *  AFontMatcher_setLocales(matcher, "zh-CN,ja-JP");
- *  ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ *  AFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
  *  // runLength will be 1 and the font will points a Simplified Chinese font.
  *  AFontMatcher_setLocales(matcher, "ja-JP,zh-CN");
- *  ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ *  AFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
  *  // runLength will be 1 and the font will points a Japanese font.
  *  AFontMatcher_destroy(matcher);
  *
  *  // Querying font for text/color emoji
  *  std::vector<uint16_t> text = { 0xD83D, 0xDC68, 0x200D, 0x2764, 0xFE0F, 0x200D, 0xD83D, 0xDC68 };
  *  AFontMatcher* matcher = AFontMatcher_create("sans-serif");
- *  ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ *  AFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
  *  // runLength will be 8 and the font will points a color emoji font.
  *  AFontMatcher_destroy(matcher);
  *
@@ -62,7 +62,7 @@
  *  // 0x05D0 is a Hebrew character and 0x0E01 is a Thai character.
  *  std::vector<uint16_t> text = { 0x05D0, 0x0E01 };
  *  AFontMatcher* matcher = AFontMatcher_create("sans-serif");
- *  ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
+ *  AFont* font = AFontMatcher_match(text.data(), text.length(), &runLength);
  *  // runLength will be 1 and the font will points a Hebrew font.
  *  AFontMatcher_destroy(matcher);
  * \endcode
@@ -146,7 +146,7 @@
 /**
  * Set font style to matcher.
  *
- * If this function is not called, the matcher performs with {@link ASYSTEM_FONT_WEIGHT_NORMAL}
+ * If this function is not called, the matcher performs with {@link AFONT_WEIGHT_NORMAL}
  * with non-italic style.
  *
  * Available since API level 29.
@@ -206,7 +206,7 @@
  * \param textLength a length of the given text buffer. This must not be zero.
  * \param runLengthOut if not null, the font run length will be filled.
  * \return a font to be used for given text and params. You need to release the returned font by
- *         ASystemFont_close when it is no longer needed.
+ *         AFont_close when it is no longer needed.
  */
 AFont* _Nonnull AFontMatcher_match(
         const AFontMatcher* _Nonnull matcher,
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index ee1eee2..8d1bf99 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -15,7 +15,7 @@
  */
 
 /**
- * @defgroup ImageDecoder
+ * @defgroup ImageDecoder Android Image Decoder
  *
  * Functions for converting encoded images into RGBA pixels.
  *
@@ -261,7 +261,8 @@
 
 /**
  * Delete the AImageDecoder.
- *
+ * @param decoder {@link AImageDecoder} object created with one of AImageDecoder_createFrom...
+ *        functions.
  * Available since API level 30.
  */
 void AImageDecoder_delete(AImageDecoder* _Nullable decoder) __INTRODUCED_IN(30);
@@ -276,6 +277,7 @@
  * Available since API level 30.
  *
  * @param format {@link AndroidBitmapFormat} to use for the output.
+ * @param decoder an {@link AImageDecoder} object.
  * @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
@@ -307,6 +309,7 @@
  *
  * Available since API level 30.
  *
+ * @param decoder an {@link AImageDecoder} object.
  * @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.
@@ -335,6 +338,7 @@
  *
  * Available since API level 30.
  *
+ * @param decoder an {@link AImageDecoder} object.
  * @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
@@ -373,6 +377,7 @@
  *
  * Available since API level 30.
  *
+ * @param decoder an {@link AImageDecoder} object.
  * @param width Width of the output (prior to cropping).
  *              This will affect future calls to
  *              {@link AImageDecoder_getMinimumStride}, which will now return
@@ -404,6 +409,7 @@
  *
  * Available since API level 30.
  *
+ * @param decoder an {@link AImageDecoder} object.
  * @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
@@ -437,6 +443,7 @@
  *
  * Available since API level 30.
  *
+ * @param decoder an {@link AImageDecoder} object.
  * @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
@@ -475,6 +482,8 @@
  * This is owned by the {@link AImageDecoder} and will be destroyed when the
  * AImageDecoder is destroyed via {@link AImageDecoder_delete}.
  *
+ * @param decoder an {@link AImageDecoder} object.
+ *
  * Available since API level 30.
  */
 const AImageDecoderHeaderInfo* _Nonnull  AImageDecoder_getHeaderInfo(
@@ -562,7 +571,7 @@
 
 /**
  * Return the minimum stride that can be used in
- * {@link AImageDecoder_decodeImage).
+ * {@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}
@@ -571,6 +580,8 @@
  * If the output is scaled (via {@link AImageDecoder_setTargetSize}) and/or
  * cropped (via {@link AImageDecoder_setCrop}), this takes those into account.
  *
+ * @param decoder an {@link AImageDecoder} object.
+ *
  * Available since API level 30.
  */
 size_t AImageDecoder_getMinimumStride(AImageDecoder* _Nonnull decoder) __INTRODUCED_IN(30);
@@ -664,6 +675,8 @@
  * A single frame GIF is considered to *not* be animated. This may require
  * seeking past the first frame to verify whether there is a following frame.
  *
+ * @param decoder an {@link AImageDecoder} object.
+ *
  * Errors:
  * - returns false if |decoder| is null.
  */
@@ -671,7 +684,7 @@
         __INTRODUCED_IN(31);
 
 enum {
-    /*
+    /**
      * Reported by {@link AImageDecoder_getRepeatCount} if the
      * animation should repeat forever.
      *
@@ -696,6 +709,7 @@
  * an image with only one frame (i.e. {@link AImageDecoder_isAnimated} returns
  * false) if the encoded image contains a repeat count.
  *
+ * @param decoder an {@link AImageDecoder} object.
  * @return Number of times to repeat on success or a value
  *         indicating the reason for the failure.
  *
@@ -725,6 +739,7 @@
  * skipping frames in an image with such frames may not produce the correct
  * results.
  *
+ * @param decoder an {@link AImageDecoder} object.
  * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
  *         indicating the reason for the failure.
  *
@@ -755,6 +770,7 @@
  * the end of the animation or an error or in the middle of the
  * animation.
  *
+ * @param decoder an {@link AImageDecoder} object.
  * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
  *         indicating the reason for the failure.
  *
@@ -847,7 +863,7 @@
 
 /**
  * The rectangle of the image (within 0, 0,
- * {@link AImageDecoder_getWidth}, {@link AImageDecoder_getHeight})
+ * {@link AImageDecoderHeaderInfo_getWidth}, {@link AImageDecoderHeaderInfo_getHeight})
  * updated by this frame.
  *
  * Introduced in API 31.
@@ -905,15 +921,15 @@
  * sequential client does not need this.
  */
 enum {
-    // No disposal. The following frame will be drawn directly
-    // on top of this one.
+    /// No disposal. The following frame will be drawn directly
+    /// on top of this one.
     ANDROID_IMAGE_DECODER_DISPOSE_OP_NONE = 1,
-    // The frame’s rectangle is cleared to transparent (by AImageDecoder)
-    // before decoding the next frame.
+    /// The frame’s rectangle is cleared to transparent (by AImageDecoder)
+    /// before decoding the next frame.
     ANDROID_IMAGE_DECODER_DISPOSE_OP_BACKGROUND = 2,
-    // The frame’s rectangle is reverted to the prior frame before decoding
-    // the next frame. This is handled by AImageDecoder, unless
-    // {@link AImageDecoder_setInternallyHandleDisposePrevious} is set to false.
+    /// The frame’s rectangle is reverted to the prior frame before decoding
+    /// the next frame. This is handled by AImageDecoder, unless
+    /// {@link AImageDecoder_setInternallyHandleDisposePrevious} is set to false.
     ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS = 3,
 };
 
@@ -948,10 +964,10 @@
  * sequential client does not need this.
  */
 enum {
-    // This frame replaces existing content. This corresponds
-    // to webp’s “do not blend”.
+    /// This frame replaces existing content. This corresponds
+    /// to webp’s “do not blend”.
     ANDROID_IMAGE_DECODER_BLEND_OP_SRC = 1,
-    // This frame blends with the previous frame.
+    /// This frame blends with the previous frame.
     ANDROID_IMAGE_DECODER_BLEND_OP_SRC_OVER = 2,
 };
 
@@ -1002,6 +1018,7 @@
  * When asked to decode frame i+1, AImageDecoder will now assume that
  * the client provided i-1 in |pixels|.
  *
+ * @param decoder an {@link AImageDecoder} object.
  * @param handleInternally Whether AImageDecoder will internally
  *               handle ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS
  *               frames.
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 92b79c7..9dc6983 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -228,8 +228,8 @@
      *
      * If a device supports the sensor additional information feature, it will
      * report additional information events via {@link ASensorEvent} and will
-     * have {@link ASensorEvent#type} set to
-     * {@link ASENSOR_TYPE_ADDITIONAL_INFO} and {@link ASensorEvent#sensor} set
+     * have the type of {@link ASensorEvent} set to
+     * {@link ASENSOR_TYPE_ADDITIONAL_INFO} and the sensor of {@link ASensorEvent} set
      * to the handle of the reporting sensor.
      *
      * Additional information reports consist of multiple frames ordered by
@@ -428,6 +428,10 @@
 } ADynamicSensorEvent;
 
 typedef struct AAdditionalInfoEvent {
+    /**
+     * Event type, such as ASENSOR_ADDITIONAL_INFO_BEGIN, ASENSOR_ADDITIONAL_INFO_END and others.
+     * Refer to {@link ASENSOR_TYPE_ADDITIONAL_INFO} for the expected reporting behavior.
+     */
     int32_t type;
     int32_t serial;
     union {
@@ -436,12 +440,22 @@
     };
 } AAdditionalInfoEvent;
 
+/**
+ * Information that describes a sensor event, refer to
+ * <a href="/reference/android/hardware/SensorEvent">SensorEvent</a> for additional
+ * documentation.
+ */
 /* NOTE: changes to this struct has to be backward compatible */
 typedef struct ASensorEvent {
     int32_t version; /* sizeof(struct ASensorEvent) */
-    int32_t sensor;
-    int32_t type;
-    int32_t reserved0;
+    int32_t sensor;  /** The sensor that generates this event */
+    int32_t type;    /** Sensor type for the event, such as {@link ASENSOR_TYPE_ACCELEROMETER} */
+    int32_t reserved0; /** do not use */
+    /**
+     * The time in nanoseconds at which the event happened, and its behavior
+     * is identical to <a href="/reference/android/hardware/SensorEvent#timestamp">
+     * SensorEvent::timestamp</a> in Java API.
+     */
     int64_t timestamp;
     union {
         union {
@@ -653,9 +667,10 @@
 /**
  * Destroy a direct channel
  *
- * Destroy a direct channel previously created using {@link ASensorManager_createDirectChannel}.
- * The buffer used for creating direct channel does not get destroyed with
- * {@link ASensorManager_destroy} and has to be close or released separately.
+ * Destroy a direct channel previously created by using one of
+ * ASensorManager_create*DirectChannel() derivative functions.
+ * Note that the buffer used for creating the direct channel does not get destroyed with
+ * ASensorManager_destroyDirectChannel and has to be closed or released separately.
  *
  * Available since API level 26.
  *
@@ -701,7 +716,7 @@
  * \param channelId channel id (a positive integer) returned from
  *                  {@link ASensorManager_createSharedMemoryDirectChannel} or
  *                  {@link ASensorManager_createHardwareBufferDirectChannel}.
- *
+ * \param rate      one of predefined ASENSOR_DIRECT_RATE_... that is supported by the sensor.
  * \return positive token for success or negative error code.
  */
 int ASensorManager_configureDirectReport(ASensorManager* manager,
@@ -718,7 +733,7 @@
  * \param queue {@link ASensorEventQueue} for sensor event to be report to.
  * \param sensor {@link ASensor} to be enabled.
  * \param samplingPeriodUs sampling period of sensor in microseconds.
- * \param maxBatchReportLatencyus maximum time interval between two batch of sensor events are
+ * \param maxBatchReportLatencyUs maximum time interval between two batches of sensor events are
  *                                delievered in microseconds. For sensor streaming, set to 0.
  * \return 0 on success or a negative error code on failure.
  */
@@ -778,7 +793,7 @@
  * Retrieve next available events from the queue to a specified event array.
  *
  * \param queue {@link ASensorEventQueue} to get events from
- * \param events pointer to an array of {@link ASensorEvents}.
+ * \param events pointer to an array of {@link ASensorEvent}.
  * \param count max number of event that can be filled into array event.
  * \return number of events returned on success; negative error code when
  *         no events are pending or an error has occurred.
@@ -798,7 +813,7 @@
  * Request that {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to be delivered on
  * the given {@link ASensorEventQueue}.
  *
- * Sensor data events are always delivered to the {@ASensorEventQueue}.
+ * Sensor data events are always delivered to the {@link ASensorEventQueue}.
  *
  * The {@link ASENSOR_TYPE_ADDITIONAL_INFO} events will be returned through
  * {@link ASensorEventQueue_getEvents}. The client is responsible for checking
@@ -890,7 +905,7 @@
  *
  * \param sensor  a {@link ASensor} to denote the sensor to be checked.
  * \param channelType  Channel type constant, either
- *                     {@ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY}
+ *                     {@link ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY}
  *                     or {@link ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER}.
  * \returns true if sensor supports the specified direct channel type.
  */
@@ -913,15 +928,15 @@
  * Returns the sensor's handle.
  *
  * The handle identifies the sensor within the system and is included in the
- * {@link ASensorEvent#sensor} field of sensor events, including those sent with type
+ * sensor field of {@link ASensorEvent}, including those sent with type
  * {@link ASENSOR_TYPE_ADDITIONAL_INFO}.
  *
  * A sensor's handle is able to be used to map {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to the
  * sensor that generated the event.
  *
  * It is important to note that the value returned by {@link ASensor_getHandle} is not the same as
- * the value returned by the Java API {@link android.hardware.Sensor#getId} and no mapping exists
- * between the values.
+ * the value returned by the Java API <a href="/reference/android/hardware/Sensor#getId()">
+ * android.hardware.Sensor's getId()</a> and no mapping exists between the values.
  *
  * Available since API level 29.
  */
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h
index 7994aa9..e0a8045 100644
--- a/include/android/sharedmem.h
+++ b/include/android/sharedmem.h
@@ -59,9 +59,10 @@
  *
  * Use close() to release the shared memory region.
  *
- * Use {@link android.os.ParcelFileDescriptor} to pass the file descriptor to
- * another process. File descriptors may also be sent to other processes over a Unix domain
- * socket with sendmsg and SCM_RIGHTS. See sendmsg(3) and cmsg(3) man pages for more information.
+ * Use <a href="/reference/android/os/ParcelFileDescriptor">android.os.ParcelFileDescriptor</a>
+ * to pass the file descriptor to another process. File descriptors may also be sent to other
+ * processes over a Unix domain socket with sendmsg and SCM_RIGHTS. See sendmsg(3) and
+ * cmsg(3) man pages for more information.
  *
  * If you intend to share this file descriptor with a child process after
  * calling exec(3), note that you will need to use fcntl(2) with FD_SETFD
@@ -71,7 +72,8 @@
  *
  * \param name an optional name.
  * \param size size of the shared memory region
- * \return file descriptor that denotes the shared memory; -1 and sets errno on failure, or -EINVAL if the error is that size was 0.
+ * \return file descriptor that denotes the shared memory;
+ *         -1 and sets errno on failure, or -EINVAL if the error is that size was 0.
  */
 int ASharedMemory_create(const char *name, size_t size) __INTRODUCED_IN(26);
 
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index eb9c528..b7eafcd 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -147,6 +147,28 @@
 typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactionStats* stats)
                                                __INTRODUCED_IN(29);
 
+
+/**
+ * The ASurfaceTransaction_OnCommit callback is invoked when transaction is applied and the updates
+ * are ready to be presented. This callback will be invoked before the
+ * ASurfaceTransaction_OnComplete callback.
+ *
+ * \param context Optional context provided by the client that is passed into the callback.
+ *
+ * \param stats Opaque handle that can be passed to ASurfaceTransactionStats functions to query
+ * information about the transaction. The handle is only valid during the callback.
+ * Present and release fences are not available for this callback. Querying them using
+ * ASurfaceTransactionStats_getPresentFenceFd and ASurfaceTransactionStats_getPreviousReleaseFenceFd
+ * will result in failure.
+ *
+ * THREADING
+ * The transaction committed callback can be invoked on any thread.
+ *
+ * Available since API level 31.
+ */
+typedef void (*ASurfaceTransaction_OnCommit)(void* context, ASurfaceTransactionStats* stats)
+                                               __INTRODUCED_IN(31);
+
 /**
  * Returns the timestamp of when the frame was latched by the framework. Once a frame is
  * latched by the framework, it is presented at the following hardware vsync.
@@ -161,6 +183,8 @@
  * The recipient of the callback takes ownership of the fence and is responsible for closing
  * it. If a device does not support present fences, a -1 will be returned.
  *
+ * This query is not valid for ASurfaceTransaction_OnCommit callback.
+ *
  * Available since API level 29.
  */
 int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats)
@@ -218,6 +242,8 @@
  * The client must ensure that all pending refs on a buffer are released before attempting to reuse
  * this buffer, otherwise synchronization errors may occur.
  *
+ * This query is not valid for ASurfaceTransaction_OnCommit callback.
+ *
  * Available since API level 29.
  */
 int ASurfaceTransactionStats_getPreviousReleaseFenceFd(
@@ -236,6 +262,16 @@
                                        ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29);
 
 /**
+ * Sets the callback that will be invoked when the updates from this transaction are applied and are
+ * ready to be presented. This callback will be invoked before the ASurfaceTransaction_OnComplete
+ * callback.
+ *
+ * Available since API level 31.
+ */
+void ASurfaceTransaction_setOnCommit(ASurfaceTransaction* transaction, void* context,
+                                    ASurfaceTransaction_OnCommit func) __INTRODUCED_IN(31);
+
+/**
  * Reparents the \a surface_control from its old parent to the \a new_parent surface control.
  * Any children of the reparented \a surface_control will remain children of the \a surface_control.
  *
@@ -286,6 +322,9 @@
  * The frameworks takes ownership of the \a acquire_fence_fd passed and is responsible
  * for closing it.
  *
+ * Note that the buffer must be allocated with AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE
+ * as the surface control might be composited using the GPU.
+ *
  * Available since API level 29.
  */
 void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction,
@@ -326,39 +365,53 @@
                                      __INTRODUCED_IN(29);
 
 /**
- * \param source The sub-rect within the buffer's content to be rendered inside the surface's area
- * The surface's source rect is clipped by the bounds of its current buffer. The source rect's width
- * and height must be > 0.
+ * Bounds the surface and its children to the bounds specified. The crop and buffer size will be
+ * used to determine the bounds of the surface. If no crop is specified and the surface has no
+ * buffer, the surface bounds is only constrained by the size of its parent bounds.
+ *
+ * \param crop The bounds of the crop to apply.
  *
  * Available since API level 31.
  */
-void ASurfaceTransaction_setSourceRect(ASurfaceTransaction* transaction,
-                                       ASurfaceControl* surface_control, const ARect& source)
+void ASurfaceTransaction_setCrop(ASurfaceTransaction* transaction,
+                                       ASurfaceControl* surface_control, const ARect& crop)
                                        __INTRODUCED_IN(31);
 
 /**
- * \param destination Specifies the rect in the parent's space where this surface will be drawn. The
- * post source rect bounds are scaled to fit the destination rect. The surface's destination rect is
- * clipped by the bounds of its parent. The destination rect's width and height must be > 0.
+ * Specifies the position in the parent's space where the surface will be drawn.
+ *
+ * \param x The x position to render the surface.
+ * \param y The y position to render the surface.
  *
  * Available since API level 31.
  */
 void ASurfaceTransaction_setPosition(ASurfaceTransaction* transaction,
-                                     ASurfaceControl* surface_control, const ARect& destination)
+                                     ASurfaceControl* surface_control, int32_t x, int32_t y)
                                      __INTRODUCED_IN(31);
 
 /**
  * \param transform The transform applied after the source rect is applied to the buffer. This
- * parameter should be set to 0 for no transform. To specify a transfrom use the
+ * parameter should be set to 0 for no transform. To specify a transform use the
  * NATIVE_WINDOW_TRANSFORM_* enum.
  *
  * Available since API level 31.
  */
-void ASurfaceTransaction_setTransform(ASurfaceTransaction* transaction,
+void ASurfaceTransaction_setBufferTransform(ASurfaceTransaction* transaction,
                                       ASurfaceControl* surface_control, int32_t transform)
                                       __INTRODUCED_IN(31);
 
 /**
+ * Sets an x and y scale of a surface with (0, 0) as the centerpoint of the scale.
+ *
+ * \param xScale The scale in the x direction. Must be greater than 0.
+ * \param yScale The scale in the y direction. Must be greater than 0.
+ *
+ * Available since API level 31.
+ */
+void ASurfaceTransaction_setScale(ASurfaceTransaction* transaction,
+                                      ASurfaceControl* surface_control, float xScale, float yScale)
+                                      __INTRODUCED_IN(31);
+/**
  * Parameter for ASurfaceTransaction_setBufferTransparency().
  */
 enum {
@@ -452,10 +505,10 @@
                                                  __INTRODUCED_IN(29);
 
 /**
- * Same as ASurfaceTransaction_setFrameRateWithSeamlessness(transaction, surface_control,
- * frameRate, compatibility, true).
+ * Same as ASurfaceTransaction_setFrameRateWithChangeStrategy(transaction, surface_control,
+ * frameRate, compatibility, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS).
  *
- * See ASurfaceTransaction_setFrameRateWithSeamlessness().
+ * See ASurfaceTransaction_setFrameRateWithChangeStrategy().
  *
  * Available since API level 30.
  */
@@ -483,19 +536,46 @@
  * influence the system's choice of display frame rate. To specify a compatibility use the
  * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum.
  *
- * \param shouldBeSeamless Whether display refresh rate transitions should be seamless. A
- * seamless transition is one that doesn't have any visual interruptions, such as a black
- * screen for a second or two. True indicates that any frame rate changes caused by this
- * request should be seamless. False indicates that non-seamless refresh rates are also
- * acceptable.
+ * \param changeFrameRateStrategy Whether display refresh rate transitions should be seamless.
+ * A seamless transition is one that doesn't have any visual interruptions, such as a black
+ * screen for a second or two. See the ANATIVEWINDOW_CHANGE_FRAME_RATE_* values.
  *
  * Available since API level 31.
  */
-void ASurfaceTransaction_setFrameRateWithSeamlessness(ASurfaceTransaction* transaction,
+void ASurfaceTransaction_setFrameRateWithChangeStrategy(ASurfaceTransaction* transaction,
                                       ASurfaceControl* surface_control, float frameRate,
-                                      int8_t compatibility, bool shouldBeSeamless)
+                                      int8_t compatibility, int8_t changeFrameRateStrategy)
                                       __INTRODUCED_IN(31);
 
+/**
+ * Indicate whether to enable backpressure for buffer submission to a given SurfaceControl.
+ *
+ * By default backpressure is disabled, which means submitting a buffer prior to receiving
+ * a callback for the previous buffer could lead to that buffer being "dropped". In cases
+ * where you are selecting for latency, this may be a desirable behavior! We had a new buffer
+ * ready, why shouldn't we show it?
+ *
+ * When back pressure is enabled, each buffer will be required to be presented
+ * before it is released and the callback delivered
+ * (absent the whole SurfaceControl being removed).
+ *
+ * Most apps are likely to have some sort of backpressure internally, e.g. you are
+ * waiting on the callback from frame N-2 before starting frame N. In high refresh
+ * rate scenarios there may not be much time between SurfaceFlinger completing frame
+ * N-1 (and therefore releasing buffer N-2) and beginning frame N. This means
+ * your app may not have enough time to respond in the callback. Using this flag
+ * and pushing buffers earlier for server side queuing will be advantageous
+ * in such cases.
+ *
+ * \param transaction The transaction in which to make the change.
+ * \param surface_control The ASurfaceControl on which to control buffer backpressure behavior.
+ * \param enableBackPressure Whether to enable back pressure.
+ */
+void ASurfaceTransaction_setEnableBackPressure(ASurfaceTransaction* transaction,
+                                               ASurfaceControl* surface_control,
+                                               bool enableBackPressure)
+                                               __INTRODUCED_IN(31);
+
 __END_DECLS
 
 #endif // ANDROID_SURFACE_CONTROL_H
diff --git a/include/android/surface_texture.h b/include/android/surface_texture.h
index 4757254..9ae211e 100644
--- a/include/android/surface_texture.h
+++ b/include/android/surface_texture.h
@@ -86,8 +86,8 @@
 /**
  * Attach the SurfaceTexture to the OpenGL ES context that is current on the calling thread.  A
  * new OpenGL ES texture object is created and populated with the SurfaceTexture image frame
- * that was current at the time of the last call to {@link #detachFromGLContext}.  This new
- * texture is bound to the GL_TEXTURE_EXTERNAL_OES texture target.
+ * that was current at the time of the last call to {@link ASurfaceTexture_detachFromGLContext}.
+ * This new texture is bound to the GL_TEXTURE_EXTERNAL_OES texture target.
  *
  * This can be used to access the SurfaceTexture image contents from multiple OpenGL ES
  * contexts.  Note, however, that the image contents are only accessible from one OpenGL ES
@@ -106,8 +106,8 @@
  * Detach the SurfaceTexture from the OpenGL ES context that owns the OpenGL ES texture object.
  * This call must be made with the OpenGL ES context current on the calling thread.  The OpenGL
  * ES texture object will be deleted as a result of this call.  After calling this method all
- * calls to {@link #updateTexImage} will fail until a successful call to {@link #attachToGLContext}
- * is made.
+ * calls to {@link ASurfaceTexture_updateTexImage} will fail until a successful call to
+ * {@link ASurfaceTexture_attachToGLContext} is made.
  *
  * This can be used to access the SurfaceTexture image contents from multiple OpenGL ES
  * contexts.  Note, however, that the image contents are only accessible from one OpenGL ES
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 0ea13d3..32580ba 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -60,6 +60,10 @@
 extern "C" {
 #endif
 
+/**
+ * Thermal status used in function {@link AThermal_getCurrentThermalStatus} and
+ * {@link AThermal_StatusCallback}.
+ */
 enum AThermalStatus {
     /** Error in thermal status. */
     ATHERMAL_STATUS_ERROR = -1,
@@ -194,10 +198,10 @@
  *
  * The value returned is a non-negative float that represents how much of the thermal envelope
  * is in use (or is forecasted to be in use). A value of 1.0 indicates that the device is
- * (or will be) throttled at {@link #THERMAL_STATUS_SEVERE}. Such throttling can affect the
+ * (or will be) throttled at {@link #ATHERMAL_STATUS_SEVERE}. Such throttling can affect the
  * CPU, GPU, and other subsystems. Values may exceed 1.0, but there is no implied mapping
  * to specific thermal levels beyond that point. This means that values greater than 1.0
- * may correspond to {@link #THERMAL_STATUS_SEVERE}, but may also represent heavier throttling.
+ * may correspond to {@link #ATHERMAL_STATUS_SEVERE}, but may also represent heavier throttling.
  *
  * A value of 0.0 corresponds to a fixed distance from 1.0, but does not correspond to any
  * particular thermal status or temperature. Values on (0.0, 1.0] may be expected to scale
diff --git a/include/android/trace.h b/include/android/trace.h
index dcefffb..d11158b 100644
--- a/include/android/trace.h
+++ b/include/android/trace.h
@@ -91,7 +91,7 @@
  *
  * Available since API level 29.
  *
- * \param methodName The method name to appear in the trace.
+ * \param sectionName The method name to appear in the trace.
  * \param cookie Unique identifier for distinguishing simultaneous events
  */
 void ATrace_endAsyncSection(const char* sectionName, int32_t cookie) __INTRODUCED_IN(29);
diff --git a/include/android/window.h b/include/android/window.h
index 436bf3a..c144864 100644
--- a/include/android/window.h
+++ b/include/android/window.h
@@ -103,8 +103,9 @@
      * bar) while this window is displayed.  This allows the window to
      * use the entire display space for itself -- the status bar will
      * be hidden when an app window with this flag set is on the top
-     * layer. A fullscreen window will ignore a value of {@link
-     * AWINDOW_SOFT_INPUT_ADJUST_RESIZE}; the window will stay
+     * layer. A fullscreen window will ignore a value of
+     * <a href="/reference/android/view/WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE">
+     * SOFT_INPUT_ADJUST_RESIZE</a>; the window will stay
      * fullscreen and will not resize.
      */
     AWINDOW_FLAG_FULLSCREEN                 = 0x00000400,
diff --git a/include/input/Input.h b/include/input/Input.h
index f9fe6b9..bb5ca0e 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -266,6 +266,20 @@
 const char* motionClassificationToString(MotionClassification classification);
 
 /**
+ * Portion of FrameMetrics timeline of interest to input code.
+ */
+enum GraphicsTimeline : size_t {
+    /** Time when the app sent the buffer to SurfaceFlinger. */
+    GPU_COMPLETED_TIME = 0,
+
+    /** Time when the frame was presented on the display */
+    PRESENT_TIME = 1,
+
+    /** Total size of the 'GraphicsTimeline' array. Must always be last. */
+    SIZE = 2
+};
+
+/**
  * Generator of unique numbers used to identify input events.
  *
  * Layout of ID:
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 2deb99d..712adfa 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -177,6 +177,14 @@
     int32_t ordinal;
 };
 
+struct InputDeviceBatteryInfo {
+    explicit InputDeviceBatteryInfo(std::string name, int32_t id) : name(name), id(id) {}
+    // Name string of the battery.
+    std::string name;
+    // Battery id
+    int32_t id;
+};
+
 /*
  * Describes the characteristics and capabilities of an input device.
  */
@@ -219,6 +227,7 @@
             float min, float max, float flat, float fuzz, float resolution);
     void addMotionRange(const MotionRange& range);
     void addSensorInfo(const InputDeviceSensorInfo& info);
+    void addBatteryInfo(const InputDeviceBatteryInfo& info);
     void addLightInfo(const InputDeviceLightInfo& info);
 
     inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
@@ -276,6 +285,8 @@
     std::unordered_map<InputDeviceSensorType, InputDeviceSensorInfo> mSensors;
     /* Map from light ID to light info */
     std::unordered_map<int32_t, InputDeviceLightInfo> mLights;
+    /* Map from battery ID to battery info */
+    std::unordered_map<int32_t, InputDeviceBatteryInfo> mBatteries;
 };
 
 /* Types of input device configuration files. */
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 3e5674e..898d1a9 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -71,6 +71,7 @@
         FOCUS,
         CAPTURE,
         DRAG,
+        TIMELINE,
     };
 
     struct Header {
@@ -195,6 +196,14 @@
 
             inline size_t size() const { return sizeof(Drag); }
         } drag;
+
+        struct Timeline {
+            int32_t eventId;
+            uint32_t empty;
+            std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+
+            inline size_t size() const { return sizeof(Timeline); }
+        } timeline;
     } __attribute__((aligned(8))) body;
 
     bool isValid(size_t actualSize) const;
@@ -381,10 +390,25 @@
         nsecs_t consumeTime;
     };
 
-    /* Receives the finished signal from the consumer in reply to the original dispatch signal.
-     * If a signal was received, returns a Finished object.
+    struct Timeline {
+        int32_t inputEventId;
+        std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+    };
+
+    typedef std::variant<Finished, Timeline> ConsumerResponse;
+    /* Receive a signal from the consumer in reply to the original dispatch signal.
+     * If a signal was received, returns a Finished or a Timeline object.
+     * The InputConsumer should return a Finished object for every InputMessage that it is sent
+     * to confirm that it has been processed and that the InputConsumer is responsive.
+     * If several InputMessages are sent to InputConsumer, it's possible to receive Finished
+     * events out of order for those messages.
      *
-     * The returned sequence number is never 0 unless the operation failed.
+     * The Timeline object is returned whenever the receiving end has processed a graphical frame
+     * and is returning the timeline of the frame. Not all input events will cause a Timeline
+     * object to be returned, and there is not guarantee about when it will arrive.
+     *
+     * If an object of Finished is returned, the returned sequence number is never 0 unless the
+     * operation failed.
      *
      * Returned error codes:
      *         OK on success.
@@ -392,7 +416,7 @@
      *         DEAD_OBJECT if the channel's peer has been closed.
      *         Other errors probably indicate that the channel is broken.
      */
-    android::base::Result<Finished> receiveFinishedSignal();
+    android::base::Result<ConsumerResponse> receiveConsumerResponse();
 
 private:
     std::shared_ptr<InputChannel> mChannel;
@@ -448,6 +472,9 @@
      */
     status_t sendFinishedSignal(uint32_t seq, bool handled);
 
+    status_t sendTimeline(int32_t inputEventId,
+                          std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
+
     /* Returns true if there is a deferred event waiting.
      *
      * Should be called after calling consume() to determine whether the consumer
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
index dd34c0a..71a36d0 100644
--- a/include/powermanager/PowerHalController.h
+++ b/include/powermanager/PowerHalController.h
@@ -20,6 +20,7 @@
 #include <android-base/thread_annotations.h>
 #include <android/hardware/power/Boost.h>
 #include <android/hardware/power/IPower.h>
+#include <android/hardware/power/IPowerHintSession.h>
 #include <android/hardware/power/Mode.h>
 #include <powermanager/PowerHalWrapper.h>
 
@@ -54,8 +55,12 @@
 
     void init();
 
-    virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
-    virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+    virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+    virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
+    virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+            int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+            int64_t durationNanos) override;
+    virtual HalResult<int64_t> getHintSessionPreferredRate() override;
 
 private:
     std::mutex mConnectedHalMutex;
@@ -67,7 +72,8 @@
     const std::shared_ptr<HalWrapper> mDefaultHal = std::make_shared<EmptyHalWrapper>();
 
     std::shared_ptr<HalWrapper> initHal();
-    HalResult processHalResult(HalResult result, const char* functionName);
+    template <typename T>
+    HalResult<T> processHalResult(HalResult<T> result, const char* functionName);
 };
 
 // -------------------------------------------------------------------------------------------------
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
index c3e7601..2c6eacb 100644
--- a/include/powermanager/PowerHalWrapper.h
+++ b/include/powermanager/PowerHalWrapper.h
@@ -21,6 +21,7 @@
 #include <android/hardware/power/1.1/IPower.h>
 #include <android/hardware/power/Boost.h>
 #include <android/hardware/power/IPower.h>
+#include <android/hardware/power/IPowerHintSession.h>
 #include <android/hardware/power/Mode.h>
 
 namespace android {
@@ -34,11 +35,81 @@
     OFF = 2,
 };
 
-// State of the Power HAL api call result.
-enum class HalResult {
-    SUCCESSFUL = 0,
-    FAILED = 1,
-    UNSUPPORTED = 2,
+// Result of a call to the Power HAL wrapper, holding data if successful.
+template <typename T>
+class HalResult {
+public:
+    static HalResult<T> ok(T value) { return HalResult(value); }
+    static HalResult<T> failed(std::string msg) {
+        return HalResult(std::move(msg), /* unsupported= */ false);
+    }
+    static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
+
+    static HalResult<T> fromStatus(binder::Status status, T data) {
+        if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+            return HalResult<T>::unsupported();
+        }
+        if (status.isOk()) {
+            return HalResult<T>::ok(data);
+        }
+        return HalResult<T>::failed(std::string(status.toString8().c_str()));
+    }
+    static HalResult<T> fromStatus(hardware::power::V1_0::Status status, T data);
+
+    template <typename R>
+    static HalResult<T> fromReturn(hardware::Return<R>& ret, T data);
+
+    template <typename R>
+    static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
+                                   T data);
+
+    // This will throw std::bad_optional_access if this result is not ok.
+    const T& value() const { return mValue.value(); }
+    bool isOk() const { return !mUnsupported && mValue.has_value(); }
+    bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
+    bool isUnsupported() const { return mUnsupported; }
+    const char* errorMessage() const { return mErrorMessage.c_str(); }
+
+private:
+    std::optional<T> mValue;
+    std::string mErrorMessage;
+    bool mUnsupported;
+
+    explicit HalResult(T value)
+          : mValue(std::make_optional(value)), mErrorMessage(), mUnsupported(false) {}
+    explicit HalResult(std::string errorMessage, bool unsupported)
+          : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {}
+};
+
+// Empty result of a call to the Power HAL wrapper.
+template <>
+class HalResult<void> {
+public:
+    static HalResult<void> ok() { return HalResult(); }
+    static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
+    static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }
+
+    static HalResult<void> fromStatus(status_t status);
+    static HalResult<void> fromStatus(binder::Status status);
+    static HalResult<void> fromStatus(hardware::power::V1_0::Status status);
+
+    template <typename R>
+    static HalResult<void> fromReturn(hardware::Return<R>& ret);
+
+    bool isOk() const { return !mUnsupported && !mFailed; }
+    bool isFailed() const { return !mUnsupported && mFailed; }
+    bool isUnsupported() const { return mUnsupported; }
+    const char* errorMessage() const { return mErrorMessage.c_str(); }
+
+private:
+    std::string mErrorMessage;
+    bool mFailed;
+    bool mUnsupported;
+
+    explicit HalResult(bool unsupported = false)
+          : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {}
+    explicit HalResult(std::string errorMessage)
+          : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {}
 };
 
 // Wrapper for Power HAL handlers.
@@ -46,8 +117,12 @@
 public:
     virtual ~HalWrapper() = default;
 
-    virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) = 0;
-    virtual HalResult setMode(hardware::power::Mode mode, bool enabled) = 0;
+    virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) = 0;
+    virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) = 0;
+    virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+            int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+            int64_t durationNanos) = 0;
+    virtual HalResult<int64_t> getHintSessionPreferredRate() = 0;
 };
 
 // Empty Power HAL wrapper that ignores all api calls.
@@ -56,8 +131,12 @@
     EmptyHalWrapper() = default;
     ~EmptyHalWrapper() = default;
 
-    virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
-    virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+    virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+    virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
+    virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+            int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+            int64_t durationNanos) override;
+    virtual HalResult<int64_t> getHintSessionPreferredRate() override;
 };
 
 // Wrapper for the HIDL Power HAL v1.0.
@@ -67,16 +146,20 @@
           : mHandleV1_0(std::move(Hal)) {}
     virtual ~HidlHalWrapperV1_0() = default;
 
-    virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
-    virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+    virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+    virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
+    virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+            int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+            int64_t durationNanos) override;
+    virtual HalResult<int64_t> getHintSessionPreferredRate() override;
 
 protected:
-    virtual HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId, uint32_t data);
+    virtual HalResult<void> sendPowerHint(hardware::power::V1_0::PowerHint hintId, uint32_t data);
 
 private:
     sp<hardware::power::V1_0::IPower> mHandleV1_0;
-    HalResult setInteractive(bool enabled);
-    HalResult setFeature(hardware::power::V1_0::Feature feature, bool enabled);
+    HalResult<void> setInteractive(bool enabled);
+    HalResult<void> setFeature(hardware::power::V1_0::Feature feature, bool enabled);
 };
 
 // Wrapper for the HIDL Power HAL v1.1.
@@ -88,8 +171,8 @@
     virtual ~HidlHalWrapperV1_1() = default;
 
 protected:
-    virtual HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId,
-                                    uint32_t data) override;
+    virtual HalResult<void> sendPowerHint(hardware::power::V1_0::PowerHint hintId,
+                                          uint32_t data) override;
 
 private:
     sp<hardware::power::V1_1::IPower> mHandleV1_1;
@@ -101,8 +184,12 @@
     explicit AidlHalWrapper(sp<hardware::power::IPower> handle) : mHandle(std::move(handle)) {}
     virtual ~AidlHalWrapper() = default;
 
-    virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override;
-    virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override;
+    virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+    virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
+    virtual HalResult<sp<hardware::power::IPowerHintSession>> createHintSession(
+            int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+            int64_t durationNanos) override;
+    virtual HalResult<int64_t> getHintSessionPreferredRate() override;
 
 private:
     // Control access to the boost and mode supported arrays.
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 274c3ae..1800481 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -58,21 +58,11 @@
 // transport itself and should be moved to AIDL or in domain-specific libs.
 //
 // Currently, these are only on system android (not vendor, not host)
+// TODO(b/183654927) - move these into separate libraries
 libbinder_device_interface_sources = [
-    "ActivityManager.cpp",
-    "AppOpsManager.cpp",
-    "IActivityManager.cpp",
-    "IAppOpsCallback.cpp",
-    "IAppOpsService.cpp",
-    "IBatteryStats.cpp",
-    "IMediaResourceMonitor.cpp",
     "IPermissionController.cpp",
-    "IProcessInfoService.cpp",
-    "IUidObserver.cpp",
     "PermissionCache.cpp",
     "PermissionController.cpp",
-    "ProcessInfoService.cpp",
-    "IpPrefix.cpp",
 ]
 
 cc_library {
@@ -132,7 +122,6 @@
         "Utils.cpp",
         ":packagemanager_aidl",
         ":libbinder_aidl",
-        ":activity_manager_procstate_aidl",
     ],
 
     target: {
@@ -167,6 +156,7 @@
         "-Werror",
         "-Wzero-as-null-pointer-constant",
         "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
+        "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
     ],
     product_variables: {
         binder32bit: {
@@ -263,3 +253,56 @@
         },
     },
 }
+
+// libbinder historically contained additional interfaces that provided specific
+// functionality in the platform but have nothing to do with binder itself. These
+// are moved out of libbinder in order to avoid the overhead of their vtables.
+// If you are working on or own one of these interfaces, the responsible things
+// to would be:
+// - give them a new home
+// - convert them to AIDL instead of having manually written parceling code
+
+cc_library {
+    name: "libbatterystats_aidl",
+    srcs: [
+        "IBatteryStats.cpp",
+    ],
+    export_include_dirs: ["include_batterystats"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+}
+
+cc_library {
+    name: "libprocessinfoservice_aidl",
+    srcs: [
+        "IProcessInfoService.cpp",
+        "ProcessInfoService.cpp",
+    ],
+    export_include_dirs: ["include_processinfo"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+        "liblog",
+    ],
+}
+
+cc_library {
+    name: "libactivitymanager_aidl",
+    srcs: [
+        "ActivityManager.cpp",
+        "IActivityManager.cpp",
+        "IUidObserver.cpp",
+        ":activity_manager_procstate_aidl",
+    ],
+    export_include_dirs: ["include_activitymanager"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+        "liblog",
+    ],
+    aidl: {
+        export_aidl_headers: true,
+    },
+}
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 825a821..fdcf94a 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -107,8 +107,7 @@
 
 // ---------------------------------------------------------------------------
 
-
-BpBinder* BpBinder::create(int32_t handle) {
+sp<BpBinder> BpBinder::create(int32_t handle) {
     int32_t trackedUid = -1;
     if (sCountByUidEnabled) {
         trackedUid = IPCThreadState::self()->getCallingUid();
@@ -134,10 +133,10 @@
         }
         sTrackingMap[trackedUid]++;
     }
-    return new BpBinder(BinderHandle{handle}, trackedUid);
+    return sp<BpBinder>::make(BinderHandle{handle}, trackedUid);
 }
 
-BpBinder* BpBinder::create(const sp<RpcConnection>& connection, const RpcAddress& address) {
+sp<BpBinder> BpBinder::create(const sp<RpcConnection>& connection, const RpcAddress& address) {
     LOG_ALWAYS_FATAL_IF(connection == nullptr, "BpBinder::create null connection");
 
     // These are not currently tracked, since there is no UID or other
@@ -145,7 +144,7 @@
     // needed, connection objects keep track of all BpBinder objects on a
     // per-connection basis.
 
-    return new BpBinder(SocketHandle{connection, address});
+    return sp<BpBinder>::make(SocketHandle{connection, address});
 }
 
 BpBinder::BpBinder(Handle&& handle)
@@ -194,10 +193,13 @@
 const String16& BpBinder::getInterfaceDescriptor() const
 {
     if (isDescriptorCached() == false) {
-        Parcel send, reply;
+        sp<BpBinder> thiz = sp<BpBinder>::fromExisting(const_cast<BpBinder*>(this));
+
+        Parcel data;
+        data.markForBinder(thiz);
+        Parcel reply;
         // do the IPC without a lock held.
-        status_t err = const_cast<BpBinder*>(this)->transact(
-                INTERFACE_TRANSACTION, send, &reply);
+        status_t err = thiz->transact(INTERFACE_TRANSACTION, data, &reply);
         if (err == NO_ERROR) {
             String16 res(reply.readString16());
             Mutex::Autolock _l(mLock);
@@ -223,7 +225,7 @@
 status_t BpBinder::pingBinder()
 {
     Parcel data;
-    data.markForBinder(this);
+    data.markForBinder(sp<BpBinder>::fromExisting(this));
     Parcel reply;
     return transact(PING_TRANSACTION, data, &reply);
 }
@@ -400,7 +402,7 @@
     ALOGV("Reporting death to recipient: %p\n", recipient.get());
     if (recipient == nullptr) return;
 
-    recipient->binderDied(this);
+    recipient->binderDied(wp<BpBinder>::fromExisting(this));
 }
 
 
diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp
index 349658e..a90bfd2 100644
--- a/libs/binder/BufferedTextOutput.cpp
+++ b/libs/binder/BufferedTextOutput.cpp
@@ -254,7 +254,7 @@
         BufferState* bs = ts.states[mIndex].get();
         if (bs != nullptr && bs->seq == mSeq) return bs;
 
-        ts.states.editItemAt(mIndex) = new BufferState(mIndex);
+        ts.states.editItemAt(mIndex) = sp<BufferState>::make(mIndex);
         bs = ts.states[mIndex].get();
         if (bs != nullptr) return bs;
     }
diff --git a/libs/binder/IBatteryStats.cpp b/libs/binder/IBatteryStats.cpp
index d0085df..0de804c 100644
--- a/libs/binder/IBatteryStats.cpp
+++ b/libs/binder/IBatteryStats.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <binder/IBatteryStats.h>
+#include <batterystats/IBatteryStats.h>
 
 #include <utils/Log.h>
 #include <binder/Parcel.h>
diff --git a/libs/binder/IInterface.cpp b/libs/binder/IInterface.cpp
index b19004d..2780bd4 100644
--- a/libs/binder/IInterface.cpp
+++ b/libs/binder/IInterface.cpp
@@ -33,14 +33,14 @@
 sp<IBinder> IInterface::asBinder(const IInterface* iface)
 {
     if (iface == nullptr) return nullptr;
-    return const_cast<IInterface*>(iface)->onAsBinder();
+    return sp<IBinder>::fromExisting(const_cast<IInterface*>(iface)->onAsBinder());
 }
 
 // static
 sp<IBinder> IInterface::asBinder(const sp<IInterface>& iface)
 {
     if (iface == nullptr) return nullptr;
-    return iface->onAsBinder();
+    return sp<IBinder>::fromExisting(iface->onAsBinder());
 }
 
 
diff --git a/libs/binder/IMediaResourceMonitor.cpp b/libs/binder/IMediaResourceMonitor.cpp
deleted file mode 100644
index f5fa817..0000000
--- a/libs/binder/IMediaResourceMonitor.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <binder/IMediaResourceMonitor.h>
-#include <binder/Parcel.h>
-#include <utils/Errors.h>
-#include <sys/types.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class BpMediaResourceMonitor : public BpInterface<IMediaResourceMonitor> {
-public:
-    explicit BpMediaResourceMonitor(const sp<IBinder>& impl)
-        : BpInterface<IMediaResourceMonitor>(impl) {}
-
-    virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IMediaResourceMonitor::getInterfaceDescriptor());
-        data.writeInt32(pid);
-        data.writeInt32(type);
-        remote()->transact(NOTIFY_RESOURCE_GRANTED, data, &reply, IBinder::FLAG_ONEWAY);
-    }
-};
-
-IMPLEMENT_META_INTERFACE(MediaResourceMonitor, "android.media.IMediaResourceMonitor")
-
-// ----------------------------------------------------------------------
-
-// NOLINTNEXTLINE(google-default-arguments)
-status_t BnMediaResourceMonitor::onTransact( uint32_t code, const Parcel& data, Parcel* reply,
-        uint32_t flags) {
-    switch(code) {
-        case NOTIFY_RESOURCE_GRANTED: {
-            CHECK_INTERFACE(IMediaResourceMonitor, data, reply);
-            int32_t pid = data.readInt32();
-            const int32_t type = data.readInt32();
-            notifyResourceGranted(/*in*/ pid, /*in*/ type);
-            return NO_ERROR;
-        } break;
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
-    }
-}
-
-// ----------------------------------------------------------------------
-
-} // namespace android
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index cca8f81..bd974b0 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -68,7 +68,7 @@
     // TODO: Reimplemement based on standard C++ container?
 };
 
-static sp<HeapCache> gHeapCache = new HeapCache();
+static sp<HeapCache> gHeapCache = sp<HeapCache>::make();
 
 /******************************************************************************/
 
@@ -288,7 +288,7 @@
     int32_t heapId = mHeapId.load(memory_order_acquire);
     if (heapId == -1) {
         sp<IBinder> binder(IInterface::asBinder(const_cast<BpMemoryHeap*>(this)));
-        sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));
+        sp<BpMemoryHeap> heap = sp<BpMemoryHeap>::cast(find_heap(binder));
         heap->assertReallyMapped();
         if (heap->mBase != MAP_FAILED) {
             Mutex::Autolock _l(mLock);
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 406bd54..6fb1227 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -90,6 +90,8 @@
     "BR_DEAD_BINDER",
     "BR_CLEAR_DEATH_NOTIFICATION_DONE",
     "BR_FAILED_REPLY",
+    "BR_FROZEN_REPLY",
+    "BR_ONEWAY_SPAM_SUSPECT",
     "BR_TRANSACTION_SEC_CTX",
 };
 
@@ -894,6 +896,11 @@
         }
 
         switch (cmd) {
+        case BR_ONEWAY_SPAM_SUSPECT:
+            ALOGE("Process seems to be sending too many oneway calls.");
+            CallStack::logStack("oneway spamming", CallStack::getCurrent().get(),
+                    ANDROID_LOG_ERROR);
+            [[fallthrough]];
         case BR_TRANSACTION_COMPLETE:
             if (!reply && !acquireResult) goto finish;
             break;
diff --git a/libs/binder/IProcessInfoService.cpp b/libs/binder/IProcessInfoService.cpp
index 570edb9..d26754e 100644
--- a/libs/binder/IProcessInfoService.cpp
+++ b/libs/binder/IProcessInfoService.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <binder/IProcessInfoService.h>
+#include <processinfo/IProcessInfoService.h>
 #include <binder/Parcel.h>
 #include <utils/Errors.h>
 #include <sys/types.h>
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index ca067e2..f684cf6 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -75,6 +75,7 @@
     sp<IBinder> waitForService(const String16& name16) override;
     bool isDeclared(const String16& name) override;
     Vector<String16> getDeclaredInstances(const String16& interface) override;
+    std::optional<String16> updatableViaApex(const String16& name) override;
 
     // for legacy ABI
     const String16& getInterfaceDescriptor() const override {
@@ -102,7 +103,7 @@
             }
         }
 
-        gDefaultServiceManager = new ServiceManagerShim(sm);
+        gDefaultServiceManager = sp<ServiceManagerShim>::make(sm);
     });
 
     return gDefaultServiceManager;
@@ -324,7 +325,7 @@
     }
     if (out != nullptr) return out;
 
-    sp<Waiter> waiter = new Waiter;
+    sp<Waiter> waiter = sp<Waiter>::make();
     if (!mTheRealServiceManager->registerForNotifications(
             name, waiter).isOk()) {
         return nullptr;
@@ -388,4 +389,12 @@
     return res;
 }
 
+std::optional<String16> ServiceManagerShim::updatableViaApex(const String16& name) {
+    std::optional<std::string> declared;
+    if (!mTheRealServiceManager->updatableViaApex(String8(name).c_str(), &declared).isOk()) {
+        return std::nullopt;
+    }
+    return declared ? std::optional<String16>(String16(declared.value().c_str())) : std::nullopt;
+}
+
 } // namespace android
diff --git a/libs/binder/IpPrefix.cpp b/libs/binder/IpPrefix.cpp
deleted file mode 100644
index 4edc493..0000000
--- a/libs/binder/IpPrefix.cpp
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * 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 "IpPrefix"
-
-#include <binder/IpPrefix.h>
-#include <vector>
-
-#include <binder/IBinder.h>
-#include <binder/Parcel.h>
-#include <log/log.h>
-#include <utils/Errors.h>
-
-using android::BAD_VALUE;
-using android::NO_ERROR;
-using android::Parcel;
-using android::status_t;
-
-namespace android {
-
-namespace net {
-
-#define RETURN_IF_FAILED(calledOnce)                                     \
-    {                                                                    \
-        status_t returnStatus = calledOnce;                              \
-        if (returnStatus) {                                              \
-            ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
-            return returnStatus;                                         \
-         }                                                               \
-    }
-
-status_t IpPrefix::writeToParcel(Parcel* parcel) const {
-    /*
-     * Keep implementation in sync with writeToParcel() in
-     * frameworks/base/core/java/android/net/IpPrefix.java.
-     */
-    std::vector<uint8_t> byte_vector;
-
-    if (mIsIpv6) {
-        const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mIn6Addr);
-        byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr));
-    } else {
-        const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&mUnion.mInAddr);
-        byte_vector.insert(byte_vector.end(), bytes, bytes+sizeof(mUnion.mIn6Addr));
-    }
-
-    RETURN_IF_FAILED(parcel->writeByteVector(byte_vector));
-    RETURN_IF_FAILED(parcel->writeInt32(static_cast<int32_t>(mPrefixLength)));
-
-    return NO_ERROR;
-}
-
-status_t IpPrefix::readFromParcel(const Parcel* parcel) {
-    /*
-     * Keep implementation in sync with readFromParcel() in
-     * frameworks/base/core/java/android/net/IpPrefix.java.
-     */
-    std::vector<uint8_t> byte_vector;
-
-    RETURN_IF_FAILED(parcel->readByteVector(&byte_vector));
-    RETURN_IF_FAILED(parcel->readInt32(&mPrefixLength));
-
-    if (byte_vector.size() == 16) {
-        mIsIpv6 = true;
-        memcpy((void*)&mUnion.mIn6Addr, &byte_vector[0], sizeof(mUnion.mIn6Addr));
-
-    } else if (byte_vector.size() == 4) {
-        mIsIpv6 = false;
-        memcpy((void*)&mUnion.mInAddr, &byte_vector[0], sizeof(mUnion.mInAddr));
-
-    } else {
-        ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
-        return BAD_VALUE;
-    }
-
-    return NO_ERROR;
-}
-
-const struct in6_addr& IpPrefix::getAddressAsIn6Addr() const
-{
-    return mUnion.mIn6Addr;
-}
-
-const struct in_addr& IpPrefix::getAddressAsInAddr() const
-{
-    return mUnion.mInAddr;
-}
-
-bool IpPrefix::getAddressAsIn6Addr(struct in6_addr* addr) const
-{
-    if (isIpv6()) {
-        *addr = mUnion.mIn6Addr;
-        return true;
-    }
-    return false;
-}
-
-bool IpPrefix::getAddressAsInAddr(struct in_addr* addr) const
-{
-    if (isIpv4()) {
-        *addr = mUnion.mInAddr;
-        return true;
-    }
-    return false;
-}
-
-bool IpPrefix::isIpv6() const
-{
-    return mIsIpv6;
-}
-
-bool IpPrefix::isIpv4() const
-{
-    return !mIsIpv6;
-}
-
-int32_t IpPrefix::getPrefixLength() const
-{
-    return mPrefixLength;
-}
-
-void IpPrefix::setAddress(const struct in6_addr& addr)
-{
-    mUnion.mIn6Addr = addr;
-    mIsIpv6 = true;
-}
-
-void IpPrefix::setAddress(const struct in_addr& addr)
-{
-    mUnion.mInAddr = addr;
-    mIsIpv6 = false;
-}
-
-void IpPrefix::setPrefixLength(int32_t prefix)
-{
-    mPrefixLength = prefix;
-}
-
-bool operator==(const IpPrefix& lhs, const IpPrefix& rhs)
-{
-    if (lhs.mIsIpv6 != rhs.mIsIpv6) {
-        return false;
-    }
-
-    if (lhs.mPrefixLength != rhs.mPrefixLength) {
-        return false;
-    }
-
-    if (lhs.mIsIpv6) {
-        return 0 == memcmp(lhs.mUnion.mIn6Addr.s6_addr, rhs.mUnion.mIn6Addr.s6_addr, sizeof(struct in6_addr));
-    }
-
-    return 0 == memcmp(&lhs.mUnion.mInAddr, &rhs.mUnion.mInAddr, sizeof(struct in_addr));
-}
-
-}  // namespace net
-
-}  // namespace android
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index f96b6bb..b503beb 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -129,7 +129,9 @@
     }
 
     if (!reRegister) {
-        if(!manager->registerClientCallback(name, service, this).isOk()) {
+        if (!manager->registerClientCallback(name, service,
+                                             sp<android::os::IClientCallback>::fromExisting(this))
+                     .isOk()) {
             ALOGE("Failed to add client callback for service %s", name.c_str());
             return false;
         }
diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp
index b46b3e8..c4475c7 100644
--- a/libs/binder/MemoryDealer.cpp
+++ b/libs/binder/MemoryDealer.cpp
@@ -228,10 +228,8 @@
 // ----------------------------------------------------------------------------
 
 MemoryDealer::MemoryDealer(size_t size, const char* name, uint32_t flags)
-    : mHeap(new MemoryHeapBase(size, flags, name)),
-    mAllocator(new SimpleBestFitAllocator(size))
-{    
-}
+      : mHeap(sp<MemoryHeapBase>::make(size, flags, name)),
+        mAllocator(new SimpleBestFitAllocator(size)) {}
 
 MemoryDealer::~MemoryDealer()
 {
@@ -243,7 +241,7 @@
     sp<IMemory> memory;
     const ssize_t offset = allocator()->allocate(size);
     if (offset >= 0) {
-        memory = new Allocation(this, heap(), offset, size);
+        memory = sp<Allocation>::make(sp<MemoryDealer>::fromExisting(this), heap(), offset, size);
     }
     return memory;
 }
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 7fedba2..a735309 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -293,7 +293,8 @@
     if (flat) {
         switch (flat->hdr.type) {
             case BINDER_TYPE_BINDER: {
-                sp<IBinder> binder = reinterpret_cast<IBinder*>(flat->cookie);
+                sp<IBinder> binder =
+                        sp<IBinder>::fromExisting(reinterpret_cast<IBinder*>(flat->cookie));
                 return finishUnflattenBinder(binder, out);
             }
             case BINDER_TYPE_HANDLE: {
@@ -418,6 +419,11 @@
 
 status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len)
 {
+    if (parcel->isForRpc() != isForRpc()) {
+        ALOGE("Cannot append Parcel of one format to another.");
+        return BAD_TYPE;
+    }
+
     status_t err;
     const uint8_t *data = parcel->mData;
     const binder_size_t *objects = parcel->mObjects;
@@ -555,12 +561,17 @@
 }
 
 void Parcel::markForBinder(const sp<IBinder>& binder) {
+    LOG_ALWAYS_FATAL_IF(mData != nullptr, "format must be set before data is written");
+
     if (binder && binder->remoteBinder() && binder->remoteBinder()->isRpcBinder()) {
         markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcConnection());
     }
 }
 
 void Parcel::markForRpc(const sp<RpcConnection>& connection) {
+    LOG_ALWAYS_FATAL_IF(mData != nullptr && mOwner == nullptr,
+                        "format must be set before data is written OR on IPC data");
+
     LOG_ALWAYS_FATAL_IF(connection == nullptr, "markForRpc requires connection");
     mConnection = connection;
 }
@@ -2100,6 +2111,9 @@
 void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
     const binder_size_t* objects, size_t objectsCount, release_func relFunc)
 {
+    // this code uses 'mOwner == nullptr' to understand whether it owns memory
+    LOG_ALWAYS_FATAL_IF(relFunc == nullptr, "must provide cleanup function");
+
     freeData();
 
     mData = const_cast<uint8_t*>(data);
diff --git a/libs/binder/ProcessInfoService.cpp b/libs/binder/ProcessInfoService.cpp
index f75141e..0fb954a 100644
--- a/libs/binder/ProcessInfoService.cpp
+++ b/libs/binder/ProcessInfoService.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <binder/ProcessInfoService.h>
+#include <processinfo/ProcessInfoService.h>
 #include <binder/IServiceManager.h>
 
 #include <utils/Log.h>
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index abb792e..0414e76 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -43,6 +43,7 @@
 
 #define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
 #define DEFAULT_MAX_BINDER_THREADS 15
+#define DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION 1
 
 #ifdef __ANDROID_VNDK__
 const char* kDefaultDriver = "/dev/vndbinder";
@@ -105,7 +106,7 @@
         }
 
         std::lock_guard<std::mutex> l(gProcessMutex);
-        gProcess = new ProcessState(driver);
+        gProcess = sp<ProcessState>::make(driver);
     });
 
     if (requireDefault) {
@@ -299,8 +300,8 @@
                    return nullptr;
             }
 
-            b = BpBinder::create(handle);
-            e->binder = b;
+            sp<BpBinder> b = BpBinder::create(handle);
+            e->binder = b.get();
             if (b) e->refs = b->getWeakRefs();
             result = b;
         } else {
@@ -340,7 +341,7 @@
     if (mThreadPoolStarted) {
         String8 name = makeBinderThreadName();
         ALOGV("Spawning new pooled thread, name=%s\n", name.string());
-        sp<Thread> t = new PoolThread(isMain);
+        sp<Thread> t = sp<PoolThread>::make(isMain);
         t->run(name.string());
     }
 }
@@ -358,6 +359,15 @@
     return result;
 }
 
+status_t ProcessState::enableOnewaySpamDetection(bool enable) {
+    uint32_t enableDetection = enable ? 1 : 0;
+    if (ioctl(mDriverFD, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enableDetection) == -1) {
+        ALOGE("Binder ioctl to enable oneway spam detection failed: %s", strerror(errno));
+        return -errno;
+    }
+    return NO_ERROR;
+}
+
 void ProcessState::giveThreadPoolName() {
     androidSetThreadName( makeBinderThreadName().string() );
 }
@@ -388,6 +398,11 @@
         if (result == -1) {
             ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
         }
+        uint32_t enable = DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION;
+        result = ioctl(fd, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable);
+        if (result == -1) {
+            ALOGE("Binder ioctl to enable oneway spam detection failed: %s", strerror(errno));
+        }
     } else {
         ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
     }
diff --git a/libs/binder/RpcConnection.cpp b/libs/binder/RpcConnection.cpp
index 83a1618..1388a80 100644
--- a/libs/binder/RpcConnection.cpp
+++ b/libs/binder/RpcConnection.cpp
@@ -18,24 +18,37 @@
 
 #include <binder/RpcConnection.h>
 
-#include <binder/Parcel.h>
-#include <binder/Stability.h>
-
-#include "RpcState.h"
-#include "RpcWireFormat.h"
-
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/un.h>
 #include <unistd.h>
 
-#if defined(__GLIBC__)
+#include <string_view>
+
+#include <binder/Parcel.h>
+#include <binder/Stability.h>
+#include <utils/String8.h>
+
+#include "RpcState.h"
+#include "RpcWireFormat.h"
+
+#ifdef __GLIBC__
 extern "C" pid_t gettid();
 #endif
 
+#ifdef __BIONIC__
+#include <linux/vm_sockets.h>
+#endif
+
 namespace android {
 
 using base::unique_fd;
+using AddrInfo = std::unique_ptr<addrinfo, decltype(&freeaddrinfo)>;
+
+RpcConnection::SocketAddress::~SocketAddress() {}
 
 RpcConnection::RpcConnection() {
     LOG_RPC_DETAIL("RpcConnection created %p", this);
@@ -44,122 +57,204 @@
 }
 RpcConnection::~RpcConnection() {
     LOG_RPC_DETAIL("RpcConnection destroyed %p", this);
+
+    std::lock_guard<std::mutex> _l(mSocketMutex);
+    LOG_ALWAYS_FATAL_IF(mServers.size() != 0,
+                        "Should not be able to destroy a connection with servers in use.");
 }
 
 sp<RpcConnection> RpcConnection::make() {
-    return new RpcConnection;
+    return sp<RpcConnection>::make();
 }
 
+class UnixSocketAddress : public RpcConnection::SocketAddress {
+public:
+    explicit UnixSocketAddress(const char* path) : mAddr({.sun_family = AF_UNIX}) {
+        unsigned int pathLen = strlen(path) + 1;
+        LOG_ALWAYS_FATAL_IF(pathLen > sizeof(mAddr.sun_path), "Socket path is too long: %u %s",
+                            pathLen, path);
+        memcpy(mAddr.sun_path, path, pathLen);
+    }
+    virtual ~UnixSocketAddress() {}
+    std::string toString() const override {
+        return String8::format("path '%.*s'", static_cast<int>(sizeof(mAddr.sun_path)),
+                               mAddr.sun_path)
+                .c_str();
+    }
+    const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
+    size_t addrSize() const override { return sizeof(mAddr); }
+
+private:
+    sockaddr_un mAddr;
+};
+
 bool RpcConnection::setupUnixDomainServer(const char* path) {
-    LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Only supports one server now");
-
-    unique_fd serverFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
-    if (serverFd == -1) {
-        ALOGE("Could not create socket at %s: %s", path, strerror(errno));
-        return false;
-    }
-
-    struct sockaddr_un addr = {
-            .sun_family = AF_UNIX,
-    };
-
-    unsigned int pathLen = strlen(path) + 1;
-    LOG_ALWAYS_FATAL_IF(pathLen > sizeof(addr.sun_path), "%u", pathLen);
-    memcpy(addr.sun_path, path, pathLen);
-
-    if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), (struct sockaddr*)&addr, sizeof(addr)))) {
-        ALOGE("Could not bind socket at %s: %s", path, strerror(errno));
-        return false;
-    }
-
-    if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) {
-        ALOGE("Could not listen socket at %s: %s", path, strerror(errno));
-        return false;
-    }
-
-    mServer = std::move(serverFd);
-    return true;
+    return setupSocketServer(UnixSocketAddress(path));
 }
 
 bool RpcConnection::addUnixDomainClient(const char* path) {
-    LOG_RPC_DETAIL("Connecting on path: %s", path);
+    return addSocketClient(UnixSocketAddress(path));
+}
 
-    unique_fd serverFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
-    if (serverFd == -1) {
-        ALOGE("Could not create socket at %s: %s", path, strerror(errno));
-        return false;
+#ifdef __BIONIC__
+
+class VsockSocketAddress : public RpcConnection::SocketAddress {
+public:
+    VsockSocketAddress(unsigned int cid, unsigned int port)
+          : mAddr({
+                    .svm_family = AF_VSOCK,
+                    .svm_port = port,
+                    .svm_cid = cid,
+            }) {}
+    virtual ~VsockSocketAddress() {}
+    std::string toString() const override {
+        return String8::format("cid %u port %u", mAddr.svm_cid, mAddr.svm_port).c_str();
+    }
+    const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
+    size_t addrSize() const override { return sizeof(mAddr); }
+
+private:
+    sockaddr_vm mAddr;
+};
+
+bool RpcConnection::setupVsockServer(unsigned int port) {
+    // realizing value w/ this type at compile time to avoid ubsan abort
+    constexpr unsigned int kAnyCid = VMADDR_CID_ANY;
+
+    return setupSocketServer(VsockSocketAddress(kAnyCid, port));
+}
+
+bool RpcConnection::addVsockClient(unsigned int cid, unsigned int port) {
+    return addSocketClient(VsockSocketAddress(cid, port));
+}
+
+#endif // __BIONIC__
+
+class SocketAddressImpl : public RpcConnection::SocketAddress {
+public:
+    SocketAddressImpl(const sockaddr* addr, size_t size, const String8& desc)
+          : mAddr(addr), mSize(size), mDesc(desc) {}
+    [[nodiscard]] std::string toString() const override {
+        return std::string(mDesc.c_str(), mDesc.size());
+    }
+    [[nodiscard]] const sockaddr* addr() const override { return mAddr; }
+    [[nodiscard]] size_t addrSize() const override { return mSize; }
+    void set(const sockaddr* addr, size_t size) {
+        mAddr = addr;
+        mSize = size;
     }
 
-    struct sockaddr_un addr = {
-            .sun_family = AF_UNIX,
+private:
+    const sockaddr* mAddr = nullptr;
+    size_t mSize = 0;
+    String8 mDesc;
+};
+
+AddrInfo GetAddrInfo(const char* addr, unsigned int port) {
+    addrinfo hint{
+            .ai_flags = 0,
+            .ai_family = AF_UNSPEC,
+            .ai_socktype = SOCK_STREAM,
+            .ai_protocol = 0,
     };
+    addrinfo* aiStart = nullptr;
+    if (int rc = getaddrinfo(addr, std::to_string(port).data(), &hint, &aiStart); 0 != rc) {
+        ALOGE("Unable to resolve %s:%u: %s", addr, port, gai_strerror(rc));
+        return AddrInfo(nullptr, nullptr);
+    }
+    if (aiStart == nullptr) {
+        ALOGE("Unable to resolve %s:%u: getaddrinfo returns null", addr, port);
+        return AddrInfo(nullptr, nullptr);
+    }
+    return AddrInfo(aiStart, &freeaddrinfo);
+}
 
-    unsigned int pathLen = strlen(path) + 1;
-    LOG_ALWAYS_FATAL_IF(pathLen > sizeof(addr.sun_path), "%u", pathLen);
-    memcpy(addr.sun_path, path, pathLen);
+bool RpcConnection::setupInetServer(unsigned int port) {
+    auto aiStart = GetAddrInfo("127.0.0.1", port);
+    if (aiStart == nullptr) return false;
+    SocketAddressImpl socketAddress(nullptr, 0, String8::format("127.0.0.1:%u", port));
+    for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
+        socketAddress.set(ai->ai_addr, ai->ai_addrlen);
+        if (setupSocketServer(socketAddress)) return true;
+    }
+    ALOGE("None of the socket address resolved for 127.0.0.1:%u can be set up as inet server.",
+          port);
+    return false;
+}
 
-    if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), (struct sockaddr*)&addr, sizeof(addr)))) {
-        ALOGE("Could not connect socket at %s: %s", path, strerror(errno));
+bool RpcConnection::addInetClient(const char* addr, unsigned int port) {
+    auto aiStart = GetAddrInfo(addr, port);
+    if (aiStart == nullptr) return false;
+    SocketAddressImpl socketAddress(nullptr, 0, String8::format("%s:%u", addr, port));
+    for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
+        socketAddress.set(ai->ai_addr, ai->ai_addrlen);
+        if (addSocketClient(socketAddress)) return true;
+    }
+    ALOGE("None of the socket address resolved for %s:%u can be added as inet client.", addr, port);
+    return false;
+}
+
+bool RpcConnection::addNullDebuggingClient() {
+    unique_fd serverFd(TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC)));
+
+    if (serverFd == -1) {
+        ALOGE("Could not connect to /dev/null: %s", strerror(errno));
         return false;
     }
 
-    LOG_RPC_DETAIL("Unix domain client with fd %d", serverFd.get());
-
     addClient(std::move(serverFd));
     return true;
 }
 
 sp<IBinder> RpcConnection::getRootObject() {
-    ExclusiveSocket socket(this, SocketUse::CLIENT);
-    return state()->getRootObject(socket.fd(), this);
+    ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::CLIENT);
+    return state()->getRootObject(socket.fd(), sp<RpcConnection>::fromExisting(this));
 }
 
 status_t RpcConnection::transact(const RpcAddress& address, uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags) {
-    ExclusiveSocket socket(this,
+    ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this),
                            (flags & IBinder::FLAG_ONEWAY) ? SocketUse::CLIENT_ASYNC
                                                           : SocketUse::CLIENT);
-    return state()->transact(socket.fd(), address, code, data, this, reply, flags);
+    return state()->transact(socket.fd(), address, code, data,
+                             sp<RpcConnection>::fromExisting(this), reply, flags);
 }
 
 status_t RpcConnection::sendDecStrong(const RpcAddress& address) {
-    ExclusiveSocket socket(this, SocketUse::CLIENT_REFCOUNT);
+    ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::CLIENT_REFCOUNT);
     return state()->sendDecStrong(socket.fd(), address);
 }
 
 void RpcConnection::join() {
-    // establish a connection
-    {
-        struct sockaddr_un clientSa;
-        socklen_t clientSaLen = sizeof(clientSa);
-
-        unique_fd clientFd(TEMP_FAILURE_RETRY(
-                accept4(mServer.get(), (struct sockaddr*)&clientSa, &clientSaLen, SOCK_CLOEXEC)));
-        if (clientFd < 0) {
-            // If this log becomes confusing, should save more state from setupUnixDomainServer
-            // in order to output here.
-            ALOGE("Could not accept4 socket: %s", strerror(errno));
-            return;
-        }
-
-        LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
-
-        addServer(std::move(clientFd));
+    // TODO(b/185167543): do this dynamically, instead of from a static number
+    // of threads
+    unique_fd clientFd(
+            TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, 0 /*length*/, SOCK_CLOEXEC)));
+    if (clientFd < 0) {
+        // If this log becomes confusing, should save more state from setupUnixDomainServer
+        // in order to output here.
+        ALOGE("Could not accept4 socket: %s", strerror(errno));
+        return;
     }
 
-    // We may not use the connection we just established (two threads might
-    // establish connections for each other), but for now, just use one
-    // server/socket connection.
-    ExclusiveSocket socket(this, SocketUse::SERVER);
+    LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
+
+    // must be registered to allow arbitrary client code executing commands to
+    // be able to do nested calls (we can't only read from it)
+    sp<ConnectionSocket> socket = assignServerToThisThread(std::move(clientFd));
 
     while (true) {
-        status_t error = state()->getAndExecuteCommand(socket.fd(), this);
+        status_t error =
+                state()->getAndExecuteCommand(socket->fd, sp<RpcConnection>::fromExisting(this));
 
         if (error != OK) {
             ALOGI("Binder socket thread closing w/ status %s", statusToString(error).c_str());
-            return;
+            break;
         }
     }
+
+    LOG_ALWAYS_FATAL_IF(!removeServerSocket(socket),
+                        "bad state: socket object guaranteed to be in list");
 }
 
 void RpcConnection::setForServer(const wp<RpcServer>& server) {
@@ -170,18 +265,77 @@
     return mForServer;
 }
 
-void RpcConnection::addClient(base::unique_fd&& fd) {
+bool RpcConnection::setupSocketServer(const SocketAddress& addr) {
+    LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Each RpcConnection can only have one server.");
+
+    unique_fd serverFd(
+            TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+    if (serverFd == -1) {
+        ALOGE("Could not create socket: %s", strerror(errno));
+        return false;
+    }
+
+    if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), addr.addr(), addr.addrSize()))) {
+        int savedErrno = errno;
+        ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+        return false;
+    }
+
+    if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) {
+        int savedErrno = errno;
+        ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+        return false;
+    }
+
+    mServer = std::move(serverFd);
+    return true;
+}
+
+bool RpcConnection::addSocketClient(const SocketAddress& addr) {
+    unique_fd serverFd(
+            TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+    if (serverFd == -1) {
+        int savedErrno = errno;
+        ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+        return false;
+    }
+
+    if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
+        int savedErrno = errno;
+        ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+        return false;
+    }
+
+    LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
+
+    addClient(std::move(serverFd));
+    return true;
+}
+
+void RpcConnection::addClient(unique_fd&& fd) {
     std::lock_guard<std::mutex> _l(mSocketMutex);
-    sp<ConnectionSocket> connection = new ConnectionSocket();
+    sp<ConnectionSocket> connection = sp<ConnectionSocket>::make();
     connection->fd = std::move(fd);
     mClients.push_back(connection);
 }
 
-void RpcConnection::addServer(base::unique_fd&& fd) {
+sp<RpcConnection::ConnectionSocket> RpcConnection::assignServerToThisThread(unique_fd&& fd) {
     std::lock_guard<std::mutex> _l(mSocketMutex);
-    sp<ConnectionSocket> connection = new ConnectionSocket();
+    sp<ConnectionSocket> connection = sp<ConnectionSocket>::make();
     connection->fd = std::move(fd);
+    connection->exclusiveTid = gettid();
     mServers.push_back(connection);
+
+    return connection;
+}
+
+bool RpcConnection::removeServerSocket(const sp<ConnectionSocket>& socket) {
+    std::lock_guard<std::mutex> _l(mSocketMutex);
+    if (auto it = std::find(mServers.begin(), mServers.end(), socket); it != mServers.end()) {
+        mServers.erase(it);
+        return true;
+    }
+    return false;
 }
 
 RpcConnection::ExclusiveSocket::ExclusiveSocket(const sp<RpcConnection>& connection, SocketUse use)
@@ -196,37 +350,31 @@
 
         // CHECK FOR DEDICATED CLIENT SOCKET
         //
-        // A server/looper should always use a dedicated connection.
-        if (use != SocketUse::SERVER) {
-            findSocket(tid, &exclusive, &available, mConnection->mClients,
-                       mConnection->mClientsOffset);
+        // A server/looper should always use a dedicated connection if available
+        findSocket(tid, &exclusive, &available, mConnection->mClients, mConnection->mClientsOffset);
 
-            // WARNING: this assumes a server cannot request its client to send
-            // a transaction, as mServers is excluded below.
-            //
-            // Imagine we have more than one thread in play, and a single thread
-            // sends a synchronous, then an asynchronous command. Imagine the
-            // asynchronous command is sent on the first client socket. Then, if
-            // we naively send a synchronous command to that same socket, the
-            // thread on the far side might be busy processing the asynchronous
-            // command. So, we move to considering the second available thread
-            // for subsequent calls.
-            if (use == SocketUse::CLIENT_ASYNC && (exclusive != nullptr || available != nullptr)) {
-                mConnection->mClientsOffset =
-                        (mConnection->mClientsOffset + 1) % mConnection->mClients.size();
-            }
+        // WARNING: this assumes a server cannot request its client to send
+        // a transaction, as mServers is excluded below.
+        //
+        // Imagine we have more than one thread in play, and a single thread
+        // sends a synchronous, then an asynchronous command. Imagine the
+        // asynchronous command is sent on the first client socket. Then, if
+        // we naively send a synchronous command to that same socket, the
+        // thread on the far side might be busy processing the asynchronous
+        // command. So, we move to considering the second available thread
+        // for subsequent calls.
+        if (use == SocketUse::CLIENT_ASYNC && (exclusive != nullptr || available != nullptr)) {
+            mConnection->mClientsOffset =
+                    (mConnection->mClientsOffset + 1) % mConnection->mClients.size();
         }
 
-        // USE SERVING SOCKET (to start serving or for nested transaction)
+        // USE SERVING SOCKET (for nested transaction)
         //
         // asynchronous calls cannot be nested
         if (use != SocketUse::CLIENT_ASYNC) {
-            // servers should start serving on an available thread only
-            // otherwise, this should only be a nested call
-            bool useAvailable = use == SocketUse::SERVER;
-
-            findSocket(tid, &exclusive, (useAvailable ? &available : nullptr),
-                       mConnection->mServers, 0 /* index hint */);
+            // server sockets are always assigned to a thread
+            findSocket(tid, &exclusive, nullptr /*available*/, mConnection->mServers,
+                       0 /* index hint */);
         }
 
         // if our thread is already using a connection, prioritize using that
@@ -240,8 +388,6 @@
             break;
         }
 
-        LOG_ALWAYS_FATAL_IF(use == SocketUse::SERVER, "Must create connection to join one.");
-
         // in regular binder, this would usually be a deadlock :)
         LOG_ALWAYS_FATAL_IF(mConnection->mClients.size() == 0,
                             "Not a client of any connection. You must create a connection to an "
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index df07916..1fa37ba 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -36,7 +36,7 @@
 RpcServer::~RpcServer() {}
 
 sp<RpcServer> RpcServer::make() {
-    return new RpcServer;
+    return sp<RpcServer>::make();
 }
 
 void RpcServer::iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction() {
@@ -47,7 +47,7 @@
     LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
 
     auto connection = RpcConnection::make();
-    connection->setForServer(this);
+    connection->setForServer(sp<RpcServer>::fromExisting(this));
     mConnections.push_back(connection);
     return connection;
 }
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 64e842e..d934136 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -40,7 +40,7 @@
         // We need to be able to send instructions over the socket for how to
         // connect to a different server, and we also need to let the host
         // process know that this is happening.
-        ALOGE("Canot send binder from unrelated binder RPC connection.");
+        ALOGE("Cannot send binder from unrelated binder RPC connection.");
         return INVALID_OPERATION;
     }
 
@@ -192,7 +192,7 @@
         return false;
     }
 
-    ssize_t sent = TEMP_FAILURE_RETRY(send(fd.get(), data, size, 0));
+    ssize_t sent = TEMP_FAILURE_RETRY(send(fd.get(), data, size, MSG_NOSIGNAL));
 
     if (sent < 0 || sent != static_cast<ssize_t>(size)) {
         ALOGE("Failed to send %s (sent %zd of %zu bytes) on fd %d, error: %s", what, sent, size,
@@ -212,7 +212,7 @@
         return false;
     }
 
-    ssize_t recd = TEMP_FAILURE_RETRY(recv(fd.get(), data, size, MSG_WAITALL));
+    ssize_t recd = TEMP_FAILURE_RETRY(recv(fd.get(), data, size, MSG_WAITALL | MSG_NOSIGNAL));
 
     if (recd < 0 || recd != static_cast<ssize_t>(size)) {
         terminate();
@@ -312,8 +312,8 @@
     return waitForReply(fd, connection, reply);
 }
 
-static void cleanup_data(Parcel* p, const uint8_t* data, size_t dataSize,
-                         const binder_size_t* objects, size_t objectsCount) {
+static void cleanup_reply_data(Parcel* p, const uint8_t* data, size_t dataSize,
+                               const binder_size_t* objects, size_t objectsCount) {
     (void)p;
     delete[] const_cast<uint8_t*>(data - offsetof(RpcWireReply, data));
     (void)dataSize;
@@ -351,7 +351,7 @@
     if (rpcReply->status != OK) return rpcReply->status;
 
     reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data),
-                               nullptr, 0, cleanup_data);
+                               nullptr, 0, cleanup_reply_data);
 
     reply->markForRpc(connection);
 
@@ -427,6 +427,15 @@
     return processTransactInternal(fd, connection, std::move(transactionData));
 }
 
+static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t dataSize,
+                                        const binder_size_t* objects, size_t objectsCount) {
+    (void)p;
+    (void)data;
+    (void)dataSize;
+    (void)objects;
+    (void)objectsCount;
+}
+
 status_t RpcState::processTransactInternal(const base::unique_fd& fd,
                                            const sp<RpcConnection>& connection,
                                            std::vector<uint8_t>&& transactionData) {
@@ -489,14 +498,20 @@
         }
     }
 
-    Parcel data;
-    data.setData(transaction->data, transactionData.size() - offsetof(RpcWireTransaction, data));
-    data.markForRpc(connection);
-
     Parcel reply;
     reply.markForRpc(connection);
 
     if (replyStatus == OK) {
+        Parcel data;
+        // transaction->data is owned by this function. Parcel borrows this data and
+        // only holds onto it for the duration of this function call. Parcel will be
+        // deleted before the 'transactionData' object.
+        data.ipcSetDataReference(transaction->data,
+                                 transactionData.size() - offsetof(RpcWireTransaction, data),
+                                 nullptr /*object*/, 0 /*objectCount*/,
+                                 do_nothing_to_transact_data);
+        data.markForRpc(connection);
+
         if (target) {
             replyStatus = target->transact(transaction->code, data, &reply, transaction->flags);
         } else {
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index c3f1ba7..f12ef4e 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -38,18 +38,30 @@
     };
 }
 
-void Stability::forceDowngradeCompilationUnit(const sp<IBinder>& binder) {
+void Stability::forceDowngradeToStability(const sp<IBinder>& binder, Level level) {
     // Downgrading a remote binder would require also copying the version from
     // the binder sent here. In practice though, we don't need to downgrade the
     // stability of a remote binder, since this would as an effect only restrict
     // what we can do to it.
     LOG_ALWAYS_FATAL_IF(!binder || !binder->localBinder(), "Can only downgrade local binder");
 
-    auto stability = Category::currentFromLevel(getLocalLevel());
+    auto stability = Category::currentFromLevel(level);
     status_t result = setRepr(binder.get(), stability.repr(), REPR_LOG | REPR_ALLOW_DOWNGRADE);
     LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object.");
 }
 
+void Stability::forceDowngradeToLocalStability(const sp<IBinder>& binder) {
+    forceDowngradeToStability(binder, getLocalLevel());
+}
+
+void Stability::forceDowngradeToSystemStability(const sp<IBinder>& binder) {
+    forceDowngradeToStability(binder, Level::SYSTEM);
+}
+
+void Stability::forceDowngradeToVendorStability(const sp<IBinder>& binder) {
+    forceDowngradeToStability(binder, Level::VENDOR);
+}
+
 std::string Stability::Category::debugString() {
     return levelString(level) + " wire protocol version "
         + std::to_string(version);
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index 2fabf94..75c4092 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -108,6 +108,11 @@
     @utf8InCpp String[] getDeclaredInstances(@utf8InCpp String iface);
 
     /**
+     * If updatable-via-apex, returns the APEX via which this is updated.
+     */
+    @nullable @utf8InCpp String updatableViaApex(@utf8InCpp String name);
+
+    /**
      * Request a callback when the number of clients of the service changes.
      * Used by LazyServiceRegistrar to dynamically stop services that have no clients.
      */
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 7079544..7e9be41 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -131,8 +131,8 @@
     virtual void            onLastStrongRef(const void* id);
     virtual bool            onIncStrongAttempted(uint32_t flags, const void* id);
 
-    inline  IBinder*        remote()                { return mRemote; }
-    inline  IBinder*        remote() const          { return mRemote; }
+    inline IBinder* remote() const { return mRemote; }
+    inline sp<IBinder> remoteStrong() const { return sp<IBinder>::fromExisting(mRemote); }
 
 private:
                             BpRefBase(const BpRefBase& o);
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 8ab7893..ad618f9 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -40,8 +40,8 @@
 class BpBinder : public IBinder
 {
 public:
-    static BpBinder*    create(int32_t handle);
-    static BpBinder* create(const sp<RpcConnection>& connection, const RpcAddress& address);
+    static sp<BpBinder> create(int32_t handle);
+    static sp<BpBinder> create(const sp<RpcConnection>& connection, const RpcAddress& address);
 
     /**
      * Return value:
@@ -143,6 +143,7 @@
 
 private:
     friend PrivateAccessorForId;
+    friend class sp<BpBinder>;
 
     struct BinderHandle {
         int32_t handle;
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index f930d29..ff90b30 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -143,11 +143,10 @@
     {                                                                   \
         ::android::sp<I##INTERFACE> intr;                               \
         if (obj != nullptr) {                                           \
-            intr = static_cast<I##INTERFACE*>(                          \
-                obj->queryLocalInterface(                               \
-                        I##INTERFACE::descriptor).get());               \
+            intr = ::android::sp<I##INTERFACE>::cast(                   \
+                obj->queryLocalInterface(I##INTERFACE::descriptor));    \
             if (intr == nullptr) {                                      \
-                intr = new Bp##INTERFACE(obj);                          \
+                intr = ::android::sp<Bp##INTERFACE>::make(obj);         \
             }                                                           \
         }                                                               \
         return intr;                                                    \
@@ -186,7 +185,7 @@
 inline sp<IInterface> BnInterface<INTERFACE>::queryLocalInterface(
         const String16& _descriptor)
 {
-    if (_descriptor == INTERFACE::descriptor) return this;
+    if (_descriptor == INTERFACE::descriptor) return sp<IInterface>::fromExisting(this);
     return nullptr;
 }
 
diff --git a/libs/binder/include/binder/IMediaResourceMonitor.h b/libs/binder/include/binder/IMediaResourceMonitor.h
deleted file mode 100644
index f92d557..0000000
--- a/libs/binder/include/binder/IMediaResourceMonitor.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#ifndef __ANDROID_VNDK__
-
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IMediaResourceMonitor : public IInterface {
-public:
-    DECLARE_META_INTERFACE(MediaResourceMonitor)
-
-    // Values should be in sync with Intent.EXTRA_MEDIA_RESOURCE_TYPE_XXX.
-    enum {
-        TYPE_VIDEO_CODEC = 0,
-        TYPE_AUDIO_CODEC = 1,
-    };
-
-    virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type) = 0;
-
-    enum {
-        NOTIFY_RESOURCE_GRANTED = IBinder::FIRST_CALL_TRANSACTION,
-    };
-};
-
-// ----------------------------------------------------------------------
-
-class BnMediaResourceMonitor : public BnInterface<IMediaResourceMonitor> {
-public:
-    // NOLINTNEXTLINE(google-default-arguments)
-    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-            uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-} // namespace android
-
-#else // __ANDROID_VNDK__
-#error "This header is not visible to vendors"
-#endif // __ANDROID_VNDK__
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 5f0d056..3dbe2c4 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -20,6 +20,8 @@
 #include <utils/Vector.h>
 #include <utils/String16.h>
 
+#include <optional>
+
 namespace android {
 
 // ----------------------------------------------------------------------
@@ -99,6 +101,12 @@
      * Get all instances of a service as declared in the VINTF manifest
      */
     virtual Vector<String16> getDeclaredInstances(const String16& interface) = 0;
+
+    /**
+     * If this instance is updatable via an APEX, returns the APEX with which
+     * this can be updated.
+     */
+    virtual std::optional<String16> updatableViaApex(const String16& name) = 0;
 };
 
 sp<IServiceManager> defaultServiceManager();
diff --git a/libs/binder/include/binder/IpPrefix.h b/libs/binder/include/binder/IpPrefix.h
deleted file mode 100644
index a8faa3f..0000000
--- a/libs/binder/include/binder/IpPrefix.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#ifndef __ANDROID_VNDK__
-
-#include <netinet/in.h>
-
-#include <binder/Parcelable.h>
-#include <utils/String16.h>
-#include <utils/StrongPointer.h>
-
-namespace android {
-
-namespace net {
-
-/*
- * C++ implementation of the Java class android.net.IpPrefix
- */
-class IpPrefix : public Parcelable {
-public:
-    IpPrefix() = default;
-    virtual ~IpPrefix() = default;
-    IpPrefix(const IpPrefix& prefix) = default;
-
-    IpPrefix(const struct in6_addr& addr, int32_t plen):
-        mUnion(addr), mPrefixLength(plen), mIsIpv6(true) { }
-
-    IpPrefix(const struct in_addr& addr, int32_t plen):
-        mUnion(addr), mPrefixLength(plen), mIsIpv6(false) { }
-
-    bool getAddressAsIn6Addr(struct in6_addr* addr) const;
-    bool getAddressAsInAddr(struct in_addr* addr) const;
-
-    const struct in6_addr& getAddressAsIn6Addr() const;
-    const struct in_addr& getAddressAsInAddr() const;
-
-    bool isIpv6() const;
-    bool isIpv4() const;
-
-    int32_t getPrefixLength() const;
-
-    void setAddress(const struct in6_addr& addr);
-    void setAddress(const struct in_addr& addr);
-
-    void setPrefixLength(int32_t prefix);
-
-    friend bool operator==(const IpPrefix& lhs, const IpPrefix& rhs);
-
-    friend bool operator!=(const IpPrefix& lhs, const IpPrefix& rhs) {
-        return !(lhs == rhs);
-    }
-
-public:
-    // Overrides
-    status_t writeToParcel(Parcel* parcel) const override;
-    status_t readFromParcel(const Parcel* parcel) override;
-
-private:
-    union InternalUnion {
-        InternalUnion() = default;
-        explicit InternalUnion(const struct in6_addr &addr):mIn6Addr(addr) { }
-        explicit InternalUnion(const struct in_addr &addr):mInAddr(addr) { }
-        struct in6_addr mIn6Addr;
-        struct in_addr mInAddr;
-    } mUnion;
-    int32_t mPrefixLength;
-    bool mIsIpv6;
-};
-
-}  // namespace net
-
-}  // namespace android
-
-#else // __ANDROID_VNDK__
-#error "This header is not visible to vendors"
-#endif // __ANDROID_VNDK__
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 2405ab6..b9db5d7 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -58,6 +58,7 @@
             void                spawnPooledThread(bool isMain);
             
             status_t            setThreadPoolMaxThreadCount(size_t maxThreads);
+            status_t            enableOnewaySpamDetection(bool enable);
             void                giveThreadPoolName();
 
             String8             getDriverName();
@@ -88,7 +89,8 @@
     static  sp<ProcessState>    init(const char *defaultDriver, bool requireDefault);
 
     friend class IPCThreadState;
-    
+    friend class sp<ProcessState>;
+
             explicit            ProcessState(const char* driver);
                                 ~ProcessState();
 
@@ -124,7 +126,6 @@
 
             Vector<handle_entry>mHandleToObject;
 
-            String8             mRootDir;
             bool                mThreadPoolStarted;
     volatile int32_t            mThreadPoolSeq;
 
diff --git a/libs/binder/include/binder/RpcConnection.h b/libs/binder/include/binder/RpcConnection.h
index 65c5232..2395e78 100644
--- a/libs/binder/include/binder/RpcConnection.h
+++ b/libs/binder/include/binder/RpcConnection.h
@@ -61,6 +61,37 @@
      */
     [[nodiscard]] bool addUnixDomainClient(const char* path);
 
+#ifdef __BIONIC__
+    /**
+     * Creates an RPC server at the current port.
+     */
+    [[nodiscard]] bool setupVsockServer(unsigned int port);
+
+    /**
+     * Connects to an RPC server at the CVD & port.
+     */
+    [[nodiscard]] bool addVsockClient(unsigned int cvd, unsigned int port);
+#endif // __BIONIC__
+
+    /**
+     * Creates an RPC server at the current port.
+     */
+    [[nodiscard]] bool setupInetServer(unsigned int port);
+
+    /**
+     * Connects to an RPC server at the given address and port.
+     */
+    [[nodiscard]] bool addInetClient(const char* addr, unsigned int port);
+
+    /**
+     * For debugging!
+     *
+     * Sets up an empty socket. All queries to this socket which require a
+     * response will never be satisfied. All data sent here will be
+     * unceremoniously cast down the bottomless pit, /dev/null.
+     */
+    [[nodiscard]] bool addNullDebuggingClient();
+
     /**
      * Query the other side of the connection for the root object hosted by that
      * process's RpcServer (if one exists)
@@ -85,11 +116,17 @@
     // internal only
     const std::unique_ptr<RpcState>& state() { return mState; }
 
-private:
-    RpcConnection();
+    class SocketAddress {
+    public:
+        virtual ~SocketAddress();
+        virtual std::string toString() const = 0;
+        virtual const sockaddr* addr() const = 0;
+        virtual size_t addrSize() const = 0;
+    };
 
-    void addServer(base::unique_fd&& fd);
-    void addClient(base::unique_fd&& fd);
+private:
+    friend sp<RpcConnection>;
+    RpcConnection();
 
     struct ConnectionSocket : public RefBase {
         base::unique_fd fd;
@@ -99,11 +136,16 @@
         std::optional<pid_t> exclusiveTid;
     };
 
+    bool setupSocketServer(const SocketAddress& address);
+    bool addSocketClient(const SocketAddress& address);
+    void addClient(base::unique_fd&& fd);
+    sp<ConnectionSocket> assignServerToThisThread(base::unique_fd&& fd);
+    bool removeServerSocket(const sp<ConnectionSocket>& socket);
+
     enum class SocketUse {
         CLIENT,
         CLIENT_ASYNC,
         CLIENT_REFCOUNT,
-        SERVER,
     };
 
     // RAII object for connection socket
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index a2c2aee..d29b651 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -72,6 +72,7 @@
     ~RpcServer();
 
 private:
+    friend sp<RpcServer>;
     RpcServer();
 
     bool mAgreedExperimental = false;
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index a09e587..f4bfac8 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -54,12 +54,37 @@
     // Given a binder interface at a certain stability, there may be some
     // requirements associated with that higher stability level. For instance, a
     // VINTF stability binder is required to be in the VINTF manifest. This API
-    // can be called to use that same interface within a partition.
-    static void forceDowngradeCompilationUnit(const sp<IBinder>& binder);
+    // can be called to use that same interface within the local partition.
+    static void forceDowngradeToLocalStability(const sp<IBinder>& binder);
 
     // WARNING: Below APIs are only ever expected to be called by auto-generated code.
     //     Instead of calling them, you should set the stability of a .aidl interface
 
+    // WARNING: The only client of
+    //      - forceDowngradeToSystemStability() and;
+    //      - korceDowngradeToVendorStability()
+    //  should be AIBinder_forceDowngradeToLocalStability().
+    //
+    // getLocalLevel() in libbinder returns Level::SYSTEM when called
+    // from libbinder_ndk (even on vendor partition). So we explicitly provide
+    // these methods for use by the NDK API:
+    //      AIBinder_forceDowngradeToLocalStability().
+    //
+    // This allows correctly downgrading the binder's stability to either system/vendor,
+    // depending on the partition.
+
+    // Given a binder interface at a certain stability, there may be some
+    // requirements associated with that higher stability level. For instance, a
+    // VINTF stability binder is required to be in the VINTF manifest. This API
+    // can be called to use that same interface within the vendor partition.
+    static void forceDowngradeToVendorStability(const sp<IBinder>& binder);
+
+    // Given a binder interface at a certain stability, there may be some
+    // requirements associated with that higher stability level. For instance, a
+    // VINTF stability binder is required to be in the VINTF manifest. This API
+    // can be called to use that same interface within the system partition.
+    static void forceDowngradeToSystemStability(const sp<IBinder>& binder);
+
     // WARNING: This is only ever expected to be called by auto-generated code. You likely want to
     // change or modify the stability class of the interface you are using.
     // This must be called as soon as the binder in question is constructed. No thread safety
@@ -146,6 +171,9 @@
     // returns the stability according to how this was built
     static Level getLocalLevel();
 
+    // Downgrades binder stability to the specified level.
+    static void forceDowngradeToStability(const sp<IBinder>& binder, Level level);
+
     enum {
       REPR_NONE = 0,
       REPR_LOG = 1,
diff --git a/libs/binder/include/binder/Status.h b/libs/binder/include/binder/Status.h
index c30ae01..aaafa36 100644
--- a/libs/binder/include/binder/Status.h
+++ b/libs/binder/include/binder/Status.h
@@ -91,6 +91,9 @@
     static Status fromExceptionCode(int32_t exceptionCode,
                                     const char* message);
 
+    // warning: this is still considered an error if it is constructed with a
+    // zero value error code. Please use Status::ok() instead and avoid zero
+    // error codes
     static Status fromServiceSpecificError(int32_t serviceSpecificErrorCode);
     static Status fromServiceSpecificError(int32_t serviceSpecificErrorCode,
                                            const String8& message);
diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/include/private/binder/binder_module.h
index 1579199..151235c 100644
--- a/libs/binder/include/private/binder/binder_module.h
+++ b/libs/binder/include/private/binder/binder_module.h
@@ -32,10 +32,6 @@
 #include <sys/ioctl.h>
 #include <linux/android/binder.h>
 
-#ifdef __cplusplus
-namespace android {
-#endif
-
 #ifndef BR_FROZEN_REPLY
 // Temporary definition of BR_FROZEN_REPLY. For production
 // this will come from UAPI binder.h
@@ -88,8 +84,18 @@
 };
 #endif //BINDER_GET_FROZEN_INFO
 
-#ifdef __cplusplus
-}   // namespace android
-#endif
+#ifndef BR_ONEWAY_SPAM_SUSPECT
+// Temporary definition of BR_ONEWAY_SPAM_SUSPECT. For production
+// this will come from UAPI binder.h
+#define BR_ONEWAY_SPAM_SUSPECT _IO('r', 19)
+#endif //BR_ONEWAY_SPAM_SUSPECT
+
+#ifndef BINDER_ENABLE_ONEWAY_SPAM_DETECTION
+/*
+ * Temporary definitions for oneway spam detection support. For the final version
+ * these will be defined in the UAPI binder.h file from upstream kernel.
+ */
+#define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32)
+#endif //BINDER_ENABLE_ONEWAY_SPAM_DETECTION
 
 #endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include_activitymanager/binder/ActivityManager.h
similarity index 93%
rename from libs/binder/include/binder/ActivityManager.h
rename to libs/binder/include_activitymanager/binder/ActivityManager.h
index 830971b..b772b80 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include_activitymanager/binder/ActivityManager.h
@@ -41,7 +41,11 @@
         // Flag for registerUidObserver: report uid has become idle
         UID_OBSERVER_IDLE = 1<<2,
         // Flag for registerUidObserver: report uid has become active
-        UID_OBSERVER_ACTIVE = 1<<3
+        UID_OBSERVER_ACTIVE = 1<<3,
+        // Flag for registerUidObserver: report uid cached state has changed
+        UID_OBSERVER_CACHED = 1<<4,
+        // Flag for registerUidObserver: report uid capability has changed
+        UID_OBSERVER_CAPABILITY = 1<<5,
     };
 
     // PROCESS_STATE_* must come from frameworks/base/core/java/android/app/ProcessStateEnum.aidl.
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include_activitymanager/binder/IActivityManager.h
similarity index 99%
rename from libs/binder/include/binder/IActivityManager.h
rename to libs/binder/include_activitymanager/binder/IActivityManager.h
index 2d58c46..4632b2e 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include_activitymanager/binder/IActivityManager.h
@@ -18,8 +18,8 @@
 
 #ifndef __ANDROID_VNDK__
 
-#include <binder/IInterface.h>
 #include <binder/IUidObserver.h>
+#include <binder/IInterface.h>
 
 namespace android {
 
diff --git a/libs/binder/include/binder/IUidObserver.h b/libs/binder/include_activitymanager/binder/IUidObserver.h
similarity index 100%
rename from libs/binder/include/binder/IUidObserver.h
rename to libs/binder/include_activitymanager/binder/IUidObserver.h
diff --git a/libs/binder/include/binder/IBatteryStats.h b/libs/binder/include_batterystats/batterystats/IBatteryStats.h
similarity index 100%
rename from libs/binder/include/binder/IBatteryStats.h
rename to libs/binder/include_batterystats/batterystats/IBatteryStats.h
diff --git a/libs/binder/include/binder/IProcessInfoService.h b/libs/binder/include_processinfo/processinfo/IProcessInfoService.h
similarity index 100%
rename from libs/binder/include/binder/IProcessInfoService.h
rename to libs/binder/include_processinfo/processinfo/IProcessInfoService.h
diff --git a/libs/binder/include/binder/ProcessInfoService.h b/libs/binder/include_processinfo/processinfo/ProcessInfoService.h
similarity index 98%
rename from libs/binder/include/binder/ProcessInfoService.h
rename to libs/binder/include_processinfo/processinfo/ProcessInfoService.h
index 6b3b5ce..978856d 100644
--- a/libs/binder/include/binder/ProcessInfoService.h
+++ b/libs/binder/include_processinfo/processinfo/ProcessInfoService.h
@@ -18,7 +18,7 @@
 
 #ifndef __ANDROID_VNDK__
 
-#include <binder/IProcessInfoService.h>
+#include <processinfo/IProcessInfoService.h>
 #include <utils/Errors.h>
 #include <utils/Singleton.h>
 #include <sys/types.h>
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 3c90681..1dcb41b 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -363,7 +363,8 @@
 }
 
 void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) {
-    CHECK(who == mWho);
+    CHECK(who == mWho) << who.unsafe_get() << "(" << who.get_refs() << ") vs " << mWho.unsafe_get()
+                       << " (" << mWho.get_refs() << ")";
 
     mOnDied(mCookie);
 
@@ -598,6 +599,8 @@
     }
 
     *in = new AParcel(binder);
+    (*in)->get()->markForBinder(binder->getBinder());
+
     status_t status = (*in)->get()->writeInterfaceToken(clazz->getInterfaceDescriptor());
     binder_status_t ret = PruneStatusT(status);
 
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 8941e49..9e2050b 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -36,6 +36,9 @@
 
 __BEGIN_DECLS
 
+/**
+ * Flags for AIBinder_transact.
+ */
 typedef uint32_t binder_flags_t;
 enum {
     /**
@@ -47,7 +50,10 @@
     FLAG_ONEWAY = 0x01,
 };
 
-// Also see IBinder.h in libbinder
+/**
+ * Codes for AIBinder_transact. This defines the range of codes available for
+ * usage. Other codes are used or reserved by the Android system.
+ */
 typedef uint32_t transaction_code_t;
 enum {
     /**
@@ -167,7 +173,7 @@
  * Available since API level 29.
  *
  * \param interfaceDescriptor this is a unique identifier for the class. This is used internally for
- * sanity checks on transactions.
+ * validity checks on transactions. This should be utf-8.
  * \param onCreate see AIBinder_Class_onCreate.
  * \param onDestroy see AIBinder_Class_onDestroy.
  * \param onTransact see AIBinder_Class_onTransact.
@@ -202,7 +208,8 @@
  *
  * Available since API level 29.
  *
- * \param dump function to call when an instance of this binder class is being dumped.
+ * \param clazz class which should use this dump function
+ * \param onDump function to call when an instance of this binder class is being dumped.
  */
 void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __INTRODUCED_IN(29);
 
@@ -638,7 +645,9 @@
  *
  * \return the class descriptor string. This pointer will never be null; a
  * descriptor is required to define a class. The pointer is owned by the class
- * and will remain valid as long as the class does.
+ * and will remain valid as long as the class does. For a local class, this will
+ * be the same value (not necessarily pointer equal) as is passed into
+ * AIBinder_Class_define. Format is utf-8.
  */
 const char* AIBinder_Class_getDescriptor(const AIBinder_Class* clazz) __INTRODUCED_IN(31);
 
@@ -662,7 +671,7 @@
  *
  * \return whether "lhs < rhs" is true
  */
-bool AIBinder_lt(const AIBinder* lhs, const AIBinder* rhs);
+bool AIBinder_lt(const AIBinder* lhs, const AIBinder* rhs) __INTRODUCED_IN(31);
 
 /**
  * Clone an AIBinder_Weak. Useful because even if a weak binder promotes to a
@@ -676,7 +685,7 @@
  * \return clone of the input parameter. This must be deleted with
  * AIBinder_Weak_delete. Null if weak input parameter is also null.
  */
-AIBinder_Weak* AIBinder_Weak_clone(const AIBinder_Weak* weak);
+AIBinder_Weak* AIBinder_Weak_clone(const AIBinder_Weak* weak) __INTRODUCED_IN(31);
 
 /**
  * Whether AIBinder_Weak is less than another.
@@ -711,7 +720,7 @@
  *
  * \return whether "lhs < rhs" is true
  */
-bool AIBinder_Weak_lt(const AIBinder_Weak* lhs, const AIBinder_Weak* rhs);
+bool AIBinder_Weak_lt(const AIBinder_Weak* lhs, const AIBinder_Weak* rhs) __INTRODUCED_IN(31);
 
 __END_DECLS
 
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index b4dc08a..6f1fdfc 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -189,7 +189,7 @@
  *
  * Available since API level 29.
  *
- * \param a low-level error to associate with this status object.
+ * \param status a low-level error to associate with this status object.
  *
  * \return a newly constructed status object that the caller owns.
  */
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 0668472..a90b4aa 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -26,7 +26,7 @@
  * This registers the service with the default service manager under this instance name. This does
  * not take ownership of binder.
  *
- * WARNING: when using this API across an APEX boundary, it should only be used with stable
+ * WARNING: when using this API across an APEX boundary, do not use with unstable
  * AIDL services. TODO(b/139325195)
  *
  * \param binder object to register globally with the service manager.
@@ -42,7 +42,7 @@
  * service is not available This also implicitly calls AIBinder_incStrong (so the caller of this
  * function is responsible for calling AIBinder_decStrong).
  *
- * WARNING: when using this API across an APEX boundary, it should only be used with stable
+ * WARNING: when using this API across an APEX boundary, do not use with unstable
  * AIDL services. TODO(b/139325195)
  *
  * \param instance identifier of the service used to lookup the service.
@@ -54,7 +54,7 @@
  * it. This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible
  * for calling AIBinder_decStrong).
  *
- * WARNING: when using this API across an APEX boundary, it should only be used with stable
+ * WARNING: when using this API across an APEX boundary, do not use with unstable
  * AIDL services. TODO(b/139325195)
  *
  * \param instance identifier of the service used to lookup the service.
@@ -87,7 +87,7 @@
  * This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible
  * for calling AIBinder_decStrong).
  *
- * WARNING: when using this API across an APEX boundary, it should only be used with stable
+ * WARNING: when using this API across an APEX boundary, do not use with unstable
  * AIDL services. TODO(b/139325195)
  *
  * \param instance identifier of the service used to lookup the service.
@@ -124,6 +124,15 @@
         __INTRODUCED_IN(31);
 
 /**
+ * Check if a service is updatable via an APEX module.
+ *
+ * \param instance identifier of the service
+ *
+ * \return whether the interface is updatable via APEX
+ */
+bool AServiceManager_isUpdatableViaApex(const char* instance) __INTRODUCED_IN(31);
+
+/**
  * Prevent lazy services without client from shutting down their process
  *
  * \param persist 'true' if the process should not exit.
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index 44ed48f..f113ba8 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -30,9 +30,7 @@
     FLAG_PRIVATE_VENDOR = 0x10000000,
 };
 
-// TODO(b/180646847): __ANDROID_APEX__ here is what allows product partition to
-// talk to system.
-#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+#if defined(__ANDROID_VENDOR__)
 
 enum {
     FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_VENDOR,
@@ -47,7 +45,19 @@
     AIBinder_markVendorStability(binder);
 }
 
-#else  // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+/**
+ * Given a binder interface at a certain stability, there may be some
+ * requirements associated with that higher stability level. For instance, a
+ * VINTF stability binder is required to be in the VINTF manifest. This API
+ * can be called to use that same interface within the vendor partition.
+ */
+void AIBinder_forceDowngradeToVendorStability(AIBinder* binder);
+
+static inline void AIBinder_forceDowngradeToLocalStability(AIBinder* binder) {
+    AIBinder_forceDowngradeToVendorStability(binder);
+}
+
+#else  // defined(__ANDROID_VENDOR__)
 
 enum {
     FLAG_PRIVATE_LOCAL = 0,
@@ -64,9 +74,27 @@
     AIBinder_markSystemStability(binder);
 }
 
-#endif  // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+/**
+ * Given a binder interface at a certain stability, there may be some
+ * requirements associated with that higher stability level. For instance, a
+ * VINTF stability binder is required to be in the VINTF manifest. This API
+ * can be called to use that same interface within the system partition.
+ */
+void AIBinder_forceDowngradeToSystemStability(AIBinder* binder);
+
+static inline void AIBinder_forceDowngradeToLocalStability(AIBinder* binder) {
+    AIBinder_forceDowngradeToSystemStability(binder);
+}
+
+#endif  // defined(__ANDROID_VENDOR__)
 
 /**
+ * WARNING: this is not expected to be used manually. When the build system has
+ * versioned checks in place for an interface that prevent it being changed year
+ * over year (specifically like those for @VintfStability stable AIDL
+ * interfaces), this could be called. Calling this without this or equivalent
+ * infrastructure will lead to de facto frozen APIs or GSI test failures.
+ *
  * This interface has system<->vendor stability
  */
 void AIBinder_markVintfStability(AIBinder* binder);
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index f1db653..7d4b82e 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -118,14 +118,18 @@
     AIBinder_getCallingSid; # apex
     AIBinder_setRequestingSid; # apex
     AParcel_markSensitive; # llndk
-    AServiceManager_isDeclared; # apex llndk
     AServiceManager_forEachDeclaredInstance; # apex llndk
-    AServiceManager_registerLazyService; # llndk
-    AServiceManager_waitForService; # apex llndk
     AServiceManager_forceLazyServicesPersist; # llndk
+    AServiceManager_isDeclared; # apex llndk
+    AServiceManager_isUpdatableViaApex; # apex
+    AServiceManager_reRegister; # llndk
+    AServiceManager_registerLazyService; # llndk
     AServiceManager_setActiveServicesCallback; # llndk
     AServiceManager_tryUnregister; # llndk
-    AServiceManager_reRegister; # llndk
+    AServiceManager_waitForService; # apex llndk
+
+    AIBinder_forceDowngradeToSystemStability; # apex
+    AIBinder_forceDowngradeToVendorStability; # llndk
 
     AIBinder_Class_getDescriptor;
     AIBinder_Weak_clone;
diff --git a/libs/binder/ndk/parcel_internal.h b/libs/binder/ndk/parcel_internal.h
index 6b7295e..b4f6358 100644
--- a/libs/binder/ndk/parcel_internal.h
+++ b/libs/binder/ndk/parcel_internal.h
@@ -27,9 +27,8 @@
     const ::android::Parcel* get() const { return mParcel; }
     ::android::Parcel* get() { return mParcel; }
 
-    explicit AParcel(const AIBinder* binder)
-        : AParcel(binder, new ::android::Parcel, true /*owns*/) {}
-    AParcel(const AIBinder* binder, ::android::Parcel* parcel, bool owns)
+    explicit AParcel(AIBinder* binder) : AParcel(binder, new ::android::Parcel, true /*owns*/) {}
+    AParcel(AIBinder* binder, ::android::Parcel* parcel, bool owns)
         : mBinder(binder), mParcel(parcel), mOwns(owns) {}
 
     ~AParcel() {
@@ -38,7 +37,7 @@
         }
     }
 
-    static const AParcel readOnly(const AIBinder* binder, const ::android::Parcel* parcel) {
+    static const AParcel readOnly(AIBinder* binder, const ::android::Parcel* parcel) {
         return AParcel(binder, const_cast<::android::Parcel*>(parcel), false);
     }
 
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index 1ccd0d2..7649a26 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -105,6 +105,14 @@
         callback(String8(instance).c_str(), context);
     }
 }
+bool AServiceManager_isUpdatableViaApex(const char* instance) {
+    if (instance == nullptr) {
+        return false;
+    }
+
+    sp<IServiceManager> sm = defaultServiceManager();
+    return sm->updatableViaApex(String16(instance)) != std::nullopt;
+}
 void AServiceManager_forceLazyServicesPersist(bool persist) {
     auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance();
     serviceRegistrar.forcePersist(persist);
diff --git a/libs/binder/ndk/stability.cpp b/libs/binder/ndk/stability.cpp
index a5b3ece..7eafb9c 100644
--- a/libs/binder/ndk/stability.cpp
+++ b/libs/binder/ndk/stability.cpp
@@ -31,7 +31,7 @@
 #error libbinder_ndk should only be built in a system context
 #endif
 
-// explicit extern because symbol is only declared in header when __ANDROID_VNDK__
+// explicit extern because symbol is only declared in header when __ANDROID_VENDOR__
 extern "C" void AIBinder_markVendorStability(AIBinder* binder) {
     Stability::markVndk(binder->getBinder().get());
 }
@@ -43,3 +43,12 @@
 void AIBinder_markVintfStability(AIBinder* binder) {
     Stability::markVintf(binder->getBinder().get());
 }
+
+// explicit extern because symbol is only declared in header when __ANDROID_VENDOR__
+extern "C" void AIBinder_forceDowngradeToVendorStability(AIBinder* binder) {
+    Stability::forceDowngradeToVendorStability(binder->getBinder());
+}
+
+void AIBinder_forceDowngradeToSystemStability(AIBinder* binder) {
+    Stability::forceDowngradeToSystemStability(binder->getBinder());
+}
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 6a88401..62db3cf 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -300,6 +300,11 @@
     EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
 }
 
+TEST(NdkBinder, IsUpdatable) {
+    bool isUpdatable = AServiceManager_isUpdatableViaApex("android.hardware.light.ILights/default");
+    EXPECT_EQ(isUpdatable, false);
+}
+
 // This is too slow
 TEST(NdkBinder, CheckLazyServiceShutDown) {
     ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService));
diff --git a/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
index b92a6a9..749bf21 100644
--- a/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
+++ b/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
@@ -20,5 +20,12 @@
 #include <fuzzer/FuzzedDataProvider.h>
 
 namespace android {
+/**
+ * Fill parcel data, including some random binder objects and FDs
+ */
 void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider);
+/**
+ * Fill parcel data, but don't fill any objects.
+ */
+void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider);
 } // namespace android
diff --git a/libs/binder/parcel_fuzzer/main.cpp b/libs/binder/parcel_fuzzer/main.cpp
index 78606cc..332e2ad 100644
--- a/libs/binder/parcel_fuzzer/main.cpp
+++ b/libs/binder/parcel_fuzzer/main.cpp
@@ -23,6 +23,7 @@
 #include <iostream>
 
 #include <android-base/logging.h>
+#include <binder/RpcConnection.h>
 #include <fuzzbinder/random_parcel.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
@@ -32,6 +33,8 @@
 #include <sys/time.h>
 
 using android::fillRandomParcel;
+using android::RpcConnection;
+using android::sp;
 
 void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider) {
     // TODO: functionality to create random parcels for libhwbinder parcels
@@ -56,7 +59,18 @@
             provider.ConsumeIntegralInRange<size_t>(0, maxInstructions));
 
     P p;
-    fillRandomParcel(&p, std::move(provider));
+    if constexpr (std::is_same_v<P, android::Parcel>) {
+        if (provider.ConsumeBool()) {
+            auto connection = sp<RpcConnection>::make();
+            CHECK(connection->addNullDebuggingClient());
+            p.markForRpc(connection);
+            fillRandomParcelData(&p, std::move(provider));
+        } else {
+            fillRandomParcel(&p, std::move(provider));
+        }
+    } else {
+        fillRandomParcel(&p, std::move(provider));
+    }
 
     // since we are only using a byte to index
     CHECK(reads.size() <= 255) << reads.size();
diff --git a/libs/binder/parcel_fuzzer/random_parcel.cpp b/libs/binder/parcel_fuzzer/random_parcel.cpp
index 9ca4c8a..b045a22 100644
--- a/libs/binder/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/parcel_fuzzer/random_parcel.cpp
@@ -75,4 +75,9 @@
     }
 }
 
+void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) {
+    std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(provider.remaining_bytes());
+    CHECK(OK == p->write(data.data(), data.size()));
+}
+
 } // namespace android
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index e12a429..57c9013 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -65,15 +65,15 @@
         // rustified
         "--constified-enum", "android::c_interface::consts::.*",
 
-        "--whitelist-type", "android::c_interface::.*",
-        "--whitelist-type", "AStatus",
-        "--whitelist-type", "AIBinder_Class",
-        "--whitelist-type", "AIBinder",
-        "--whitelist-type", "AIBinder_Weak",
-        "--whitelist-type", "AIBinder_DeathRecipient",
-        "--whitelist-type", "AParcel",
-        "--whitelist-type", "binder_status_t",
-        "--whitelist-function", ".*",
+        "--allowlist-type", "android::c_interface::.*",
+        "--allowlist-type", "AStatus",
+        "--allowlist-type", "AIBinder_Class",
+        "--allowlist-type", "AIBinder",
+        "--allowlist-type", "AIBinder_Weak",
+        "--allowlist-type", "AIBinder_DeathRecipient",
+        "--allowlist-type", "AParcel",
+        "--allowlist-type", "binder_status_t",
+        "--allowlist-function", ".*",
     ],
     shared_libs: [
         "libbinder_ndk",
diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp
index 0bf76c6..607860f 100644
--- a/libs/binder/rust/tests/Android.bp
+++ b/libs/binder/rust/tests/Android.bp
@@ -114,8 +114,8 @@
     source_stem: "bindings",
     cpp_std: "gnu++17",
     bindgen_flags: [
-        "--whitelist-type", "Transaction",
-        "--whitelist-var", "TESTDATA_.*",
+        "--allowlist-type", "Transaction",
+        "--allowlist-var", "TESTDATA_.*",
     ],
 
     shared_libs: [
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index a44cddf..f303b7c 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -104,26 +104,74 @@
     require_root: true,
 }
 
-cc_test {
-    name: "binderRpcTest",
-    defaults: ["binder_test_defaults"],
-
+aidl_interface {
+    name: "binderRpcTestIface",
+    host_supported: true,
+    unstable: true,
     srcs: [
         "IBinderRpcSession.aidl",
         "IBinderRpcTest.aidl",
+    ],
+    backend: {
+        java: {
+            enabled: false,
+        },
+    },
+}
+
+cc_test {
+    name: "binderRpcTest",
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+    defaults: [
+        "binder_test_defaults",
+        "libbinder_ndk_host_user",
+    ],
+
+    srcs: [
         "binderRpcTest.cpp",
     ],
     shared_libs: [
         "libbinder",
+        "libbinder_ndk",
         "libbase",
         "libutils",
         "libcutils",
         "liblog",
     ],
+    static_libs: [
+        "binderRpcTestIface-cpp",
+        "binderRpcTestIface-ndk_platform",
+    ],
     test_suites: ["general-tests"],
     require_root: true,
 }
 
+cc_benchmark {
+    name: "binderRpcBenchmark",
+    defaults: ["binder_test_defaults"],
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+    srcs: [
+        "binderRpcBenchmark.cpp",
+        "IBinderRpcBenchmark.aidl",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "liblog",
+        "libutils",
+    ],
+}
+
 cc_test {
     name: "binderThroughputTest",
     defaults: ["binder_test_defaults"],
@@ -210,6 +258,11 @@
     srcs: [
         "IBinderStabilityTest.aidl",
     ],
+    backend: {
+        java: {
+            enabled: false,
+        },
+    },
 }
 
 cc_test {
diff --git a/libs/binder/tests/IBinderRpcBenchmark.aidl b/libs/binder/tests/IBinderRpcBenchmark.aidl
new file mode 100644
index 0000000..1457422
--- /dev/null
+++ b/libs/binder/tests/IBinderRpcBenchmark.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface IBinderRpcBenchmark {
+    @utf8InCpp String repeatString(@utf8InCpp String str);
+    IBinder repeatBinder(IBinder binder);
+}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index e2193fa..dc8c0f1 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -88,6 +88,7 @@
     BINDER_LIB_TEST_GETPID,
     BINDER_LIB_TEST_ECHO_VECTOR,
     BINDER_LIB_TEST_REJECT_BUF,
+    BINDER_LIB_TEST_CAN_GET_SID,
 };
 
 pid_t start_server_process(int arg2, bool usePoll = false)
@@ -1192,6 +1193,14 @@
     EXPECT_NE(NO_ERROR, ret);
 }
 
+TEST_F(BinderLibTest, GotSid) {
+    sp<IBinder> server = addServer();
+
+    Parcel data;
+    status_t ret = server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr);
+    EXPECT_EQ(OK, ret);
+}
+
 class BinderLibTestService : public BBinder
 {
     public:
@@ -1494,6 +1503,9 @@
             case BINDER_LIB_TEST_REJECT_BUF: {
                 return data.objectsCount() == 0 ? BAD_VALUE : NO_ERROR;
             }
+            case BINDER_LIB_TEST_CAN_GET_SID: {
+                return IPCThreadState::self()->getCallingSid() == nullptr ? BAD_VALUE : NO_ERROR;
+            }
             default:
                 return UNKNOWN_TRANSACTION;
             };
diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp
new file mode 100644
index 0000000..7c82226
--- /dev/null
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <BnBinderRpcBenchmark.h>
+#include <android-base/logging.h>
+#include <benchmark/benchmark.h>
+#include <binder/Binder.h>
+#include <binder/RpcConnection.h>
+#include <binder/RpcServer.h>
+
+#include <thread>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+using android::BBinder;
+using android::IBinder;
+using android::interface_cast;
+using android::OK;
+using android::RpcConnection;
+using android::RpcServer;
+using android::sp;
+using android::binder::Status;
+
+class MyBinderRpcBenchmark : public BnBinderRpcBenchmark {
+    Status repeatString(const std::string& str, std::string* out) override {
+        *out = str;
+        return Status::ok();
+    }
+    Status repeatBinder(const sp<IBinder>& str, sp<IBinder>* out) override {
+        *out = str;
+        return Status::ok();
+    }
+};
+
+static sp<RpcConnection> gConnection = RpcConnection::make();
+
+void BM_getRootObject(benchmark::State& state) {
+    while (state.KeepRunning()) {
+        CHECK(gConnection->getRootObject() != nullptr);
+    }
+}
+BENCHMARK(BM_getRootObject);
+
+void BM_pingTransaction(benchmark::State& state) {
+    sp<IBinder> binder = gConnection->getRootObject();
+    CHECK(binder != nullptr);
+
+    while (state.KeepRunning()) {
+        CHECK_EQ(OK, binder->pingBinder());
+    }
+}
+BENCHMARK(BM_pingTransaction);
+
+void BM_repeatString(benchmark::State& state) {
+    sp<IBinder> binder = gConnection->getRootObject();
+    CHECK(binder != nullptr);
+    sp<IBinderRpcBenchmark> iface = interface_cast<IBinderRpcBenchmark>(binder);
+    CHECK(iface != nullptr);
+
+    // Googlers might see go/another-look-at-aidl-hidl-perf
+    //
+    // When I checked in July 2019, 99.5% of AIDL transactions and 99.99% of HIDL
+    // transactions were less than one page in size (system wide during a test
+    // involving media and camera). This is why this diverges from
+    // binderThroughputTest and hwbinderThroughputTest. Future consideration - get
+    // this data on continuous integration. Here we are testing sending a
+    // transaction of twice this size. In other cases, we should focus on
+    // benchmarks of particular usecases. If individual binder transactions like
+    // the ones tested here are fast, then Android performance will be dominated
+    // by how many binder calls work together (and by factors like the scheduler,
+    // thermal throttling, core choice, etc..).
+    std::string str = std::string(getpagesize() * 2, 'a');
+    CHECK_EQ(str.size(), getpagesize() * 2);
+
+    while (state.KeepRunning()) {
+        std::string out;
+        Status ret = iface->repeatString(str, &out);
+        CHECK(ret.isOk()) << ret;
+    }
+}
+BENCHMARK(BM_repeatString);
+
+void BM_repeatBinder(benchmark::State& state) {
+    sp<IBinder> binder = gConnection->getRootObject();
+    CHECK(binder != nullptr);
+    sp<IBinderRpcBenchmark> iface = interface_cast<IBinderRpcBenchmark>(binder);
+    CHECK(iface != nullptr);
+
+    while (state.KeepRunning()) {
+        // force creation of a new address
+        sp<IBinder> binder = sp<BBinder>::make();
+
+        sp<IBinder> out;
+        Status ret = iface->repeatBinder(binder, &out);
+        CHECK(ret.isOk()) << ret;
+    }
+}
+BENCHMARK(BM_repeatBinder);
+
+int main(int argc, char** argv) {
+    ::benchmark::Initialize(&argc, argv);
+    if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1;
+
+    std::string addr = std::string(getenv("TMPDIR") ?: "/tmp") + "/binderRpcBenchmark";
+    (void)unlink(addr.c_str());
+
+    std::thread([addr]() {
+        sp<RpcServer> server = RpcServer::make();
+        server->setRootObject(sp<MyBinderRpcBenchmark>::make());
+
+        server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+
+        sp<RpcConnection> connection = server->addClientConnection();
+        CHECK(connection->setupUnixDomainServer(addr.c_str()));
+
+        connection->join();
+    }).detach();
+
+    for (size_t tries = 0; tries < 5; tries++) {
+        usleep(10000);
+        if (gConnection->addUnixDomainClient(addr.c_str())) goto success;
+    }
+    LOG(FATAL) << "Could not connect.";
+success:
+
+    ::benchmark::RunSpecifiedBenchmarks();
+}
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 6fa5333..dd68fdb 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -14,17 +14,12 @@
  * limitations under the License.
  */
 
-#include <sys/prctl.h>
-#include <unistd.h>
-
-#include <chrono>
-#include <cstdlib>
-#include <iostream>
-#include <thread>
-
 #include <BnBinderRpcSession.h>
 #include <BnBinderRpcTest.h>
+#include <aidl/IBinderRpcTest.h>
 #include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_libbinder.h>
 #include <binder/Binder.h>
 #include <binder/BpBinder.h>
 #include <binder/IServiceManager.h>
@@ -33,10 +28,29 @@
 #include <binder/RpcServer.h>
 #include <gtest/gtest.h>
 
+#include <chrono>
+#include <cstdlib>
+#include <iostream>
+#include <thread>
+
+#ifdef __BIONIC__
+#include <linux/vm_sockets.h>
+#endif //__BIONIC__
+
+#include <sys/prctl.h>
+#include <unistd.h>
+
 #include "../RpcState.h" // for debugging
 
 namespace android {
 
+TEST(BinderRpcParcel, EntireParcelFormatted) {
+    Parcel p;
+    p.writeInt32(3);
+
+    EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "");
+}
+
 using android::binder::Status;
 
 #define EXPECT_OK(status)                 \
@@ -66,11 +80,10 @@
     sp<RpcConnection> connection;
 
     Status sendString(const std::string& str) override {
-        std::cout << "Child received string: " << str << std::endl;
+        (void)str;
         return Status::ok();
     }
     Status doubleString(const std::string& str, std::string* strstr) override {
-        std::cout << "Child received string to double: " << str << std::endl;
         *strstr = str + str;
         return Status::ok();
     }
@@ -185,8 +198,8 @@
 
 static std::string allocateSocketAddress() {
     static size_t id = 0;
-
-    return "/dev/binderRpcTest_" + std::to_string(id++);
+    std::string temp = getenv("TMPDIR") ?: "/tmp";
+    return temp + "/binderRpcTest_" + std::to_string(id++);
 };
 
 struct ProcessConnection {
@@ -214,58 +227,6 @@
     }
 };
 
-// This creates a new process serving an interface on a certain number of
-// threads.
-ProcessConnection createRpcTestSocketServerProcess(
-        size_t numThreads,
-        const std::function<void(const sp<RpcServer>&, const sp<RpcConnection>&)>& configure) {
-    CHECK_GT(numThreads, 0);
-
-    std::string addr = allocateSocketAddress();
-    unlink(addr.c_str());
-
-    auto ret = ProcessConnection{
-            .host = Process([&] {
-                sp<RpcServer> server = RpcServer::make();
-
-                server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
-
-                // server supporting one client on one socket
-                sp<RpcConnection> connection = server->addClientConnection();
-                CHECK(connection->setupUnixDomainServer(addr.c_str())) << addr;
-
-                configure(server, connection);
-
-                // accept 'numThreads' connections
-                std::vector<std::thread> pool;
-                for (size_t i = 0; i + 1 < numThreads; i++) {
-                    pool.push_back(std::thread([=] { connection->join(); }));
-                }
-                connection->join();
-                for (auto& t : pool) t.join();
-            }),
-            .connection = RpcConnection::make(),
-    };
-
-    // wait up to 1s for sockets to be created
-    constexpr useconds_t kMaxWaitUs = 1000000;
-    constexpr useconds_t kWaitDivision = 100;
-    for (size_t i = 0; i < kWaitDivision && 0 != access(addr.c_str(), F_OK); i++) {
-        usleep(kMaxWaitUs / kWaitDivision);
-    }
-
-    // create remainder of connections
-    for (size_t i = 0; i < numThreads; i++) {
-        // Connection refused sometimes after file created but before listening.
-        CHECK(ret.connection->addUnixDomainClient(addr.c_str()) ||
-              (usleep(10000), ret.connection->addUnixDomainClient(addr.c_str())))
-                << i;
-    }
-
-    ret.rootBinder = ret.connection->getRootObject();
-    return ret;
-}
-
 // Process connection where the process hosts IBinderRpcTest, the server used
 // for most testing here
 struct BinderRpcTestProcessConnection {
@@ -290,26 +251,131 @@
     }
 };
 
-BinderRpcTestProcessConnection createRpcTestSocketServerProcess(size_t numThreads) {
-    BinderRpcTestProcessConnection ret{
-            .proc = createRpcTestSocketServerProcess(numThreads,
-                                                     [&](const sp<RpcServer>& server,
-                                                         const sp<RpcConnection>& connection) {
-                                                         sp<MyBinderRpcTest> service =
-                                                                 new MyBinderRpcTest;
-                                                         server->setRootObject(service);
-                                                         service->connection =
-                                                                 connection; // for testing only
-                                                     }),
-    };
-
-    ret.rootBinder = ret.proc.rootBinder;
-    ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
-
-    return ret;
+enum class SocketType {
+    UNIX,
+#ifdef __BIONIC__
+    VSOCK,
+#endif // __BIONIC__
+    INET,
+};
+static inline std::string PrintSocketType(const testing::TestParamInfo<SocketType>& info) {
+    switch (info.param) {
+        case SocketType::UNIX:
+            return "unix_domain_socket";
+#ifdef __BIONIC__
+        case SocketType::VSOCK:
+            return "vm_socket";
+#endif // __BIONIC__
+        case SocketType::INET:
+            return "inet_socket";
+        default:
+            LOG_ALWAYS_FATAL("Unknown socket type");
+            return "";
+    }
 }
+class BinderRpc : public ::testing::TestWithParam<SocketType> {
+public:
+    // This creates a new process serving an interface on a certain number of
+    // threads.
+    ProcessConnection createRpcTestSocketServerProcess(
+            size_t numThreads,
+            const std::function<void(const sp<RpcServer>&, const sp<RpcConnection>&)>& configure) {
+        CHECK_GT(numThreads, 0);
 
-TEST(BinderRpc, RootObjectIsNull) {
+        SocketType socketType = GetParam();
+
+        std::string addr = allocateSocketAddress();
+        unlink(addr.c_str());
+        static unsigned int port = 3456;
+        port++;
+
+        auto ret = ProcessConnection{
+                .host = Process([&] {
+                    sp<RpcServer> server = RpcServer::make();
+
+                    server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+
+                    // server supporting one client on one socket
+                    sp<RpcConnection> connection = server->addClientConnection();
+
+                    switch (socketType) {
+                        case SocketType::UNIX:
+                            CHECK(connection->setupUnixDomainServer(addr.c_str())) << addr;
+                            break;
+#ifdef __BIONIC__
+                        case SocketType::VSOCK:
+                            CHECK(connection->setupVsockServer(port));
+                            break;
+#endif // __BIONIC__
+                        case SocketType::INET:
+                            CHECK(connection->setupInetServer(port));
+                            break;
+                        default:
+                            LOG_ALWAYS_FATAL("Unknown socket type");
+                    }
+
+                    configure(server, connection);
+
+                    // accept 'numThreads' connections
+                    std::vector<std::thread> pool;
+                    for (size_t i = 0; i + 1 < numThreads; i++) {
+                        pool.push_back(std::thread([=] { connection->join(); }));
+                    }
+                    connection->join();
+                    for (auto& t : pool) t.join();
+                }),
+                .connection = RpcConnection::make(),
+        };
+
+        // create remainder of connections
+        for (size_t i = 0; i < numThreads; i++) {
+            for (size_t tries = 0; tries < 5; tries++) {
+                usleep(10000);
+                switch (socketType) {
+                    case SocketType::UNIX:
+                        if (ret.connection->addUnixDomainClient(addr.c_str())) goto success;
+                        break;
+#ifdef __BIONIC__
+                    case SocketType::VSOCK:
+                        if (ret.connection->addVsockClient(VMADDR_CID_LOCAL, port)) goto success;
+                        break;
+#endif // __BIONIC__
+                    case SocketType::INET:
+                        if (ret.connection->addInetClient("127.0.0.1", port)) goto success;
+                        break;
+                    default:
+                        LOG_ALWAYS_FATAL("Unknown socket type");
+                }
+            }
+            LOG_ALWAYS_FATAL("Could not connect");
+        success:;
+        }
+
+        ret.rootBinder = ret.connection->getRootObject();
+        return ret;
+    }
+
+    BinderRpcTestProcessConnection createRpcTestSocketServerProcess(size_t numThreads) {
+        BinderRpcTestProcessConnection ret{
+                .proc = createRpcTestSocketServerProcess(numThreads,
+                                                         [&](const sp<RpcServer>& server,
+                                                             const sp<RpcConnection>& connection) {
+                                                             sp<MyBinderRpcTest> service =
+                                                                     new MyBinderRpcTest;
+                                                             server->setRootObject(service);
+                                                             service->connection =
+                                                                     connection; // for testing only
+                                                         }),
+        };
+
+        ret.rootBinder = ret.proc.rootBinder;
+        ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
+
+        return ret;
+    }
+};
+
+TEST_P(BinderRpc, RootObjectIsNull) {
     auto proc = createRpcTestSocketServerProcess(1,
                                                  [](const sp<RpcServer>& server,
                                                     const sp<RpcConnection>&) {
@@ -324,20 +390,39 @@
     EXPECT_EQ(nullptr, proc.connection->getRootObject());
 }
 
-TEST(BinderRpc, Ping) {
+TEST_P(BinderRpc, Ping) {
     auto proc = createRpcTestSocketServerProcess(1);
     ASSERT_NE(proc.rootBinder, nullptr);
     EXPECT_EQ(OK, proc.rootBinder->pingBinder());
 }
 
-TEST(BinderRpc, TransactionsMustBeMarkedRpc) {
+TEST_P(BinderRpc, GetInterfaceDescriptor) {
+    auto proc = createRpcTestSocketServerProcess(1);
+    ASSERT_NE(proc.rootBinder, nullptr);
+    EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
+}
+
+TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) {
     auto proc = createRpcTestSocketServerProcess(1);
     Parcel data;
     Parcel reply;
     EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0));
 }
 
-TEST(BinderRpc, UnknownTransaction) {
+TEST_P(BinderRpc, AppendSeparateFormats) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    Parcel p1;
+    p1.markForBinder(proc.rootBinder);
+    p1.writeInt32(3);
+
+    Parcel p2;
+
+    EXPECT_EQ(BAD_TYPE, p1.appendFrom(&p2, 0, p2.dataSize()));
+    EXPECT_EQ(BAD_TYPE, p2.appendFrom(&p1, 0, p1.dataSize()));
+}
+
+TEST_P(BinderRpc, UnknownTransaction) {
     auto proc = createRpcTestSocketServerProcess(1);
     Parcel data;
     data.markForBinder(proc.rootBinder);
@@ -345,19 +430,19 @@
     EXPECT_EQ(UNKNOWN_TRANSACTION, proc.rootBinder->transact(1337, data, &reply, 0));
 }
 
-TEST(BinderRpc, SendSomethingOneway) {
+TEST_P(BinderRpc, SendSomethingOneway) {
     auto proc = createRpcTestSocketServerProcess(1);
     EXPECT_OK(proc.rootIface->sendString("asdf"));
 }
 
-TEST(BinderRpc, SendAndGetResultBack) {
+TEST_P(BinderRpc, SendAndGetResultBack) {
     auto proc = createRpcTestSocketServerProcess(1);
     std::string doubled;
     EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled));
     EXPECT_EQ("cool cool ", doubled);
 }
 
-TEST(BinderRpc, SendAndGetResultBackBig) {
+TEST_P(BinderRpc, SendAndGetResultBackBig) {
     auto proc = createRpcTestSocketServerProcess(1);
     std::string single = std::string(1024, 'a');
     std::string doubled;
@@ -365,7 +450,7 @@
     EXPECT_EQ(single + single, doubled);
 }
 
-TEST(BinderRpc, CallMeBack) {
+TEST_P(BinderRpc, CallMeBack) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     int32_t pingResult;
@@ -375,7 +460,7 @@
     EXPECT_EQ(0, MyBinderRpcSession::gNum);
 }
 
-TEST(BinderRpc, RepeatBinder) {
+TEST_P(BinderRpc, RepeatBinder) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     sp<IBinder> inBinder = new MyBinderRpcSession("foo");
@@ -397,7 +482,7 @@
     EXPECT_EQ(0, MyBinderRpcSession::gNum);
 }
 
-TEST(BinderRpc, RepeatTheirBinder) {
+TEST_P(BinderRpc, RepeatTheirBinder) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     sp<IBinderRpcSession> session;
@@ -421,7 +506,7 @@
     EXPECT_EQ(nullptr, weak.promote());
 }
 
-TEST(BinderRpc, RepeatBinderNull) {
+TEST_P(BinderRpc, RepeatBinderNull) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     sp<IBinder> outBinder;
@@ -429,7 +514,7 @@
     EXPECT_EQ(nullptr, outBinder);
 }
 
-TEST(BinderRpc, HoldBinder) {
+TEST_P(BinderRpc, HoldBinder) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     IBinder* ptr = nullptr;
@@ -455,7 +540,7 @@
 // These are behavioral differences form regular binder, where certain usecases
 // aren't supported.
 
-TEST(BinderRpc, CannotMixBindersBetweenUnrelatedSocketConnections) {
+TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketConnections) {
     auto proc1 = createRpcTestSocketServerProcess(1);
     auto proc2 = createRpcTestSocketServerProcess(1);
 
@@ -464,7 +549,7 @@
               proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
 }
 
-TEST(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
+TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager());
@@ -473,7 +558,7 @@
               proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError());
 }
 
-TEST(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
+TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     // for historical reasons, IServiceManager interface only returns the
@@ -484,7 +569,7 @@
 
 // END TESTS FOR LIMITATIONS OF SOCKET BINDER
 
-TEST(BinderRpc, RepeatRootObject) {
+TEST_P(BinderRpc, RepeatRootObject) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     sp<IBinder> outBinder;
@@ -492,7 +577,7 @@
     EXPECT_EQ(proc.rootBinder, outBinder);
 }
 
-TEST(BinderRpc, NestedTransactions) {
+TEST_P(BinderRpc, NestedTransactions) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     auto nastyNester = sp<MyBinderRpcTest>::make();
@@ -503,7 +588,7 @@
     EXPECT_EQ(nullptr, weak.promote());
 }
 
-TEST(BinderRpc, SameBinderEquality) {
+TEST_P(BinderRpc, SameBinderEquality) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     sp<IBinder> a;
@@ -515,7 +600,7 @@
     EXPECT_EQ(a, b);
 }
 
-TEST(BinderRpc, SameBinderEqualityWeak) {
+TEST_P(BinderRpc, SameBinderEqualityWeak) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     sp<IBinder> a;
@@ -547,7 +632,7 @@
         EXPECT_EQ(expected, session);                     \
     } while (false)
 
-TEST(BinderRpc, SingleSession) {
+TEST_P(BinderRpc, SingleSession) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     sp<IBinderRpcSession> session;
@@ -561,7 +646,7 @@
     expectSessions(0, proc.rootIface);
 }
 
-TEST(BinderRpc, ManySessions) {
+TEST_P(BinderRpc, ManySessions) {
     auto proc = createRpcTestSocketServerProcess(1);
 
     std::vector<sp<IBinderRpcSession>> sessions;
@@ -595,7 +680,7 @@
     return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
 }
 
-TEST(BinderRpc, ThreadPoolGreaterThanEqualRequested) {
+TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) {
     constexpr size_t kNumThreads = 10;
 
     auto proc = createRpcTestSocketServerProcess(kNumThreads);
@@ -627,7 +712,7 @@
     for (auto& t : ts) t.join();
 }
 
-TEST(BinderRpc, ThreadPoolOverSaturated) {
+TEST_P(BinderRpc, ThreadPoolOverSaturated) {
     constexpr size_t kNumThreads = 10;
     constexpr size_t kNumCalls = kNumThreads + 3;
     constexpr size_t kSleepMs = 500;
@@ -651,7 +736,7 @@
     EXPECT_LE(epochMsAfter, epochMsBefore + 3 * kSleepMs);
 }
 
-TEST(BinderRpc, ThreadingStressTest) {
+TEST_P(BinderRpc, ThreadingStressTest) {
     constexpr size_t kNumClientThreads = 10;
     constexpr size_t kNumServerThreads = 10;
     constexpr size_t kNumCalls = 100;
@@ -663,7 +748,7 @@
         threads.push_back(std::thread([&] {
             for (size_t j = 0; j < kNumCalls; j++) {
                 sp<IBinder> out;
-                proc.rootIface->repeatBinder(proc.rootBinder, &out);
+                EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &out));
                 EXPECT_EQ(proc.rootBinder, out);
             }
         }));
@@ -672,7 +757,29 @@
     for (auto& t : threads) t.join();
 }
 
-TEST(BinderRpc, OnewayCallDoesNotWait) {
+TEST_P(BinderRpc, OnewayStressTest) {
+    constexpr size_t kNumClientThreads = 10;
+    constexpr size_t kNumServerThreads = 10;
+    constexpr size_t kNumCalls = 100;
+
+    auto proc = createRpcTestSocketServerProcess(kNumServerThreads);
+
+    std::vector<std::thread> threads;
+    for (size_t i = 0; i < kNumClientThreads; i++) {
+        threads.push_back(std::thread([&] {
+            for (size_t j = 0; j < kNumCalls; j++) {
+                EXPECT_OK(proc.rootIface->sendString("a"));
+            }
+
+            // check threads are not stuck
+            EXPECT_OK(proc.rootIface->sleepMs(250));
+        }));
+    }
+
+    for (auto& t : threads) t.join();
+}
+
+TEST_P(BinderRpc, OnewayCallDoesNotWait) {
     constexpr size_t kReallyLongTimeMs = 100;
     constexpr size_t kSleepMs = kReallyLongTimeMs * 5;
 
@@ -687,7 +794,7 @@
     EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs);
 }
 
-TEST(BinderRpc, OnewayCallQueueing) {
+TEST_P(BinderRpc, OnewayCallQueueing) {
     constexpr size_t kNumSleeps = 10;
     constexpr size_t kNumExtraServerThreads = 4;
     constexpr size_t kSleepMs = 50;
@@ -711,10 +818,7 @@
     EXPECT_GT(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps);
 }
 
-TEST(BinderRpc, Die) {
-    // TODO(b/183141167): handle this in library
-    signal(SIGPIPE, SIG_IGN);
-
+TEST_P(BinderRpc, Die) {
     for (bool doDeathCleanup : {true, false}) {
         auto proc = createRpcTestSocketServerProcess(1);
 
@@ -733,6 +837,30 @@
     }
 }
 
+TEST_P(BinderRpc, WorksWithLibbinderNdkPing) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder));
+    ASSERT_NE(binder, nullptr);
+
+    ASSERT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
+}
+
+TEST_P(BinderRpc, WorksWithLibbinderNdkUserTransaction) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder));
+    ASSERT_NE(binder, nullptr);
+
+    auto ndkBinder = aidl::IBinderRpcTest::fromBinder(binder);
+    ASSERT_NE(ndkBinder, nullptr);
+
+    std::string out;
+    ndk::ScopedAStatus status = ndkBinder->doubleString("aoeu", &out);
+    ASSERT_TRUE(status.isOk()) << status.getDescription();
+    ASSERT_EQ("aoeuaoeu", out);
+}
+
 ssize_t countFds() {
     DIR* dir = opendir("/proc/self/fd/");
     if (dir == nullptr) return -1;
@@ -743,7 +871,7 @@
     return ret;
 }
 
-TEST(BinderRpc, Fds) {
+TEST_P(BinderRpc, Fds) {
     ssize_t beforeFds = countFds();
     ASSERT_GE(beforeFds, 0);
     {
@@ -753,10 +881,20 @@
     ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?");
 }
 
-extern "C" int main(int argc, char** argv) {
+INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc,
+                        ::testing::ValuesIn({
+                                SocketType::UNIX,
+#ifdef __BIONIC__
+                                SocketType::VSOCK,
+#endif // __BIONIC__
+                                SocketType::INET,
+                        }),
+                        PrintSocketType);
+
+} // namespace android
+
+int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
     android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
     return RUN_ALL_TESTS();
 }
-
-} // namespace android
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
index ffb3ef2..c857d62 100644
--- a/libs/binder/tests/binderSafeInterfaceTest.cpp
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -226,7 +226,7 @@
         IncrementNativeHandle,
         IncrementNoCopyNoMove,
         IncrementParcelableVector,
-        ToUpper,
+        DoubleString,
         CallMeBack,
         IncrementInt32,
         IncrementUint32,
@@ -256,7 +256,7 @@
     virtual status_t increment(const NoCopyNoMove& a, NoCopyNoMove* aPlusOne) const = 0;
     virtual status_t increment(const std::vector<TestParcelable>& a,
                                std::vector<TestParcelable>* aPlusOne) const = 0;
-    virtual status_t toUpper(const String8& str, String8* upperStr) const = 0;
+    virtual status_t doubleString(const String8& str, String8* doubleStr) const = 0;
     // As mentioned above, sp<IBinder> is already tested by setDeathToken
     virtual void callMeBack(const sp<ICallback>& callback, int32_t a) const = 0;
     virtual status_t increment(int32_t a, int32_t* aPlusOne) const = 0;
@@ -329,9 +329,10 @@
                                                            std::vector<TestParcelable>*);
         return callRemote<Signature>(Tag::IncrementParcelableVector, a, aPlusOne);
     }
-    status_t toUpper(const String8& str, String8* upperStr) const override {
+    status_t doubleString(const String8& str, String8* doubleStr) const override {
         ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
-        return callRemote<decltype(&ISafeInterfaceTest::toUpper)>(Tag::ToUpper, str, upperStr);
+        return callRemote<decltype(&ISafeInterfaceTest::doubleString)>(Tag::DoubleString, str,
+                                                                       doubleStr);
     }
     void callMeBack(const sp<ICallback>& callback, int32_t a) const override {
         ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
@@ -454,10 +455,9 @@
         }
         return NO_ERROR;
     }
-    status_t toUpper(const String8& str, String8* upperStr) const override {
+    status_t doubleString(const String8& str, String8* doubleStr) const override {
         ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
-        *upperStr = str;
-        upperStr->toUpper();
+        *doubleStr = str + str;
         return NO_ERROR;
     }
     void callMeBack(const sp<ICallback>& callback, int32_t a) const override {
@@ -548,8 +548,8 @@
                                                          std::vector<TestParcelable>*) const;
                 return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment);
             }
-            case ISafeInterfaceTest::Tag::ToUpper: {
-                return callLocal(data, reply, &ISafeInterfaceTest::toUpper);
+            case ISafeInterfaceTest::Tag::DoubleString: {
+                return callLocal(data, reply, &ISafeInterfaceTest::doubleString);
             }
             case ISafeInterfaceTest::Tag::CallMeBack: {
                 return callLocalAsync(data, reply, &ISafeInterfaceTest::callMeBack);
@@ -726,12 +726,12 @@
     }
 }
 
-TEST_F(SafeInterfaceTest, TestToUpper) {
-    const String8 str{"Hello, world!"};
-    String8 upperStr;
-    status_t result = mSafeInterfaceTest->toUpper(str, &upperStr);
+TEST_F(SafeInterfaceTest, TestDoubleString) {
+    const String8 str{"asdf"};
+    String8 doubleStr;
+    status_t result = mSafeInterfaceTest->doubleString(str, &doubleStr);
     ASSERT_EQ(NO_ERROR, result);
-    ASSERT_TRUE(upperStr == String8{"HELLO, WORLD!"});
+    ASSERT_TRUE(doubleStr == String8{"asdfasdf"});
 }
 
 TEST_F(SafeInterfaceTest, TestCallMeBack) {
diff --git a/libs/binder/tests/binderStabilityTest.cpp b/libs/binder/tests/binderStabilityTest.cpp
index 4270540..2ce13df 100644
--- a/libs/binder/tests/binderStabilityTest.cpp
+++ b/libs/binder/tests/binderStabilityTest.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <android/binder_libbinder.h>
 #include <android/binder_manager.h>
 #include <android/binder_stability.h>
 #include <binder/Binder.h>
@@ -131,17 +132,55 @@
     EXPECT_TRUE(Stability::requiresVintfDeclaration(BadStableBinder::vintf()));
 }
 
-TEST(BinderStability, ForceDowngradeStability) {
+TEST(BinderStability, ForceDowngradeToLocalStability) {
     sp<IBinder> someBinder = BadStableBinder::vintf();
 
     EXPECT_TRUE(Stability::requiresVintfDeclaration(someBinder));
 
     // silly to do this after already using the binder, but it's for the test
-    Stability::forceDowngradeCompilationUnit(someBinder);
+    Stability::forceDowngradeToLocalStability(someBinder);
 
     EXPECT_FALSE(Stability::requiresVintfDeclaration(someBinder));
 }
 
+TEST(BinderStability, NdkForceDowngradeToLocalStability) {
+    sp<IBinder> someBinder = BadStableBinder::vintf();
+
+    EXPECT_TRUE(Stability::requiresVintfDeclaration(someBinder));
+
+    // silly to do this after already using the binder, but it's for the test
+    AIBinder_forceDowngradeToLocalStability(AIBinder_fromPlatformBinder(someBinder));
+
+    EXPECT_FALSE(Stability::requiresVintfDeclaration(someBinder));
+}
+
+TEST(BinderStability, ForceDowngradeToVendorStability) {
+    sp<IBinder> serverBinder = android::defaultServiceManager()->getService(kSystemStabilityServer);
+    auto server = interface_cast<IBinderStabilityTest>(serverBinder);
+
+    ASSERT_NE(nullptr, server.get());
+    ASSERT_NE(nullptr, IInterface::asBinder(server)->remoteBinder());
+
+    {
+        sp<BadStableBinder> binder = BadStableBinder::vintf();
+
+        EXPECT_TRUE(Stability::requiresVintfDeclaration(binder));
+        EXPECT_TRUE(server->sendAndCallBinder(binder).isOk());
+        EXPECT_TRUE(binder->gotUserTransaction);
+    }
+    {
+        sp<BadStableBinder> binder = BadStableBinder::vintf();
+
+        // This method should never be called directly. This is done only for the test.
+        Stability::forceDowngradeToVendorStability(binder);
+
+        // Binder downgraded to vendor stability, cannot be called from system context
+        EXPECT_FALSE(Stability::requiresVintfDeclaration(binder));
+        EXPECT_EQ(BAD_TYPE, server->sendAndCallBinder(binder).exceptionCode());
+        EXPECT_FALSE(binder->gotUserTransaction);
+    }
+}
+
 TEST(BinderStability, VintfStabilityServerMustBeDeclaredInManifest) {
     sp<IBinder> vintfServer = BadStableBinder::vintf();
 
@@ -153,6 +192,8 @@
         EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT,
             android::defaultServiceManager()->addService(String16("."), vintfServer)) << instance8;
         EXPECT_FALSE(android::defaultServiceManager()->isDeclared(instance)) << instance8;
+        EXPECT_EQ(std::nullopt, android::defaultServiceManager()->updatableViaApex(instance))
+                << instance8;
     }
 }
 
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp
index 4ecbe53..761e45c 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/ServiceManager.cpp
@@ -73,4 +73,9 @@
     return out;
 }
 
+std::optional<String16> ServiceManager::updatableViaApex(const String16& name) {
+    (void)name;
+    return std::nullopt;
+}
+
 }  // namespace android
diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/ServiceManager.h
index 4ef47fb..e26c21b 100644
--- a/libs/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/ServiceManager.h
@@ -19,6 +19,7 @@
 #include <binder/IServiceManager.h>
 
 #include <map>
+#include <optional>
 
 namespace android {
 
@@ -48,6 +49,8 @@
 
     Vector<String16> getDeclaredInstances(const String16& iface) override;
 
+    std::optional<String16> updatableViaApex(const String16& name) override;
+
 private:
     std::map<String16, sp<IBinder>> mNameToService;
 };
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index ff059d7..64203f7 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -39,12 +39,50 @@
     min_sdk_version: "29",
 }
 
+cc_library_headers {
+    name: "libgui_aidl_headers",
+    vendor_available: true,
+    static_libs: [
+        "libgui_aidl_static",
+    ],
+
+    export_static_lib_headers: [
+        "libgui_aidl_static",
+    ],
+}
+
 filegroup {
     name: "libgui_aidl",
     srcs: ["aidl/**/*.aidl"],
     path: "aidl/",
 }
 
+cc_library_static {
+    name: "libgui_aidl_static",
+    vendor_available: true,
+    srcs: [
+        ":libgui_aidl",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "libui",
+    ],
+
+    local_include_dirs: [
+        "include",
+    ],
+
+    export_shared_lib_headers: [
+        "libbinder",
+    ],
+
+    aidl: {
+        export_aidl_headers: true
+    }
+}
+
+
 cc_library_shared {
     name: "libgui",
     vendor_available: true,
@@ -56,10 +94,16 @@
 
     defaults: ["libgui_bufferqueue-defaults"],
 
+    static_libs: [
+        "libgui_aidl_static",
+    ],
+    export_static_lib_headers: [
+        "libgui_aidl_static",
+    ],
+
     srcs: [
         ":framework_native_aidl",
         ":inputconstants_aidl",
-        ":libgui_aidl",
         ":libgui_bufferqueue_sources",
 
         "BitTube.cpp",
@@ -115,6 +159,10 @@
         "libinput",
     ],
 
+    export_header_lib_headers: [
+        "libgui_aidl_headers",
+    ],
+
     // bufferhub is not used when building libgui for vendors
     target: {
         vendor: {
@@ -136,15 +184,16 @@
         },
     },
 
-    header_libs: [
-        "libdvr_headers",
-        "libpdx_headers",
-    ],
-
     aidl: {
         export_aidl_headers: true,
     },
 
+    header_libs: [
+        "libdvr_headers",
+        "libgui_aidl_headers",
+        "libpdx_headers",
+    ],
+
     pgo: {
         sampling: true,
         profile_file: "libgui/libgui.profdata",
@@ -175,8 +224,8 @@
 
     srcs: [
         ":inputconstants_aidl",
-        ":libgui_aidl",
         ":libgui_bufferqueue_sources",
+        ":libgui_aidl",
     ],
 }
 
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 1976b49..3d854c2 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -161,9 +161,9 @@
     mTransformHint = mSurfaceControl->getTransformHint();
     mBufferItemConsumer->setTransformHint(mTransformHint);
     SurfaceComposerClient::Transaction()
-            .setFlags(surface, layer_state_t::eEnableBackpressure,
-                      layer_state_t::eEnableBackpressure)
-            .apply();
+          .setFlags(surface, layer_state_t::eEnableBackpressure,
+                    layer_state_t::eEnableBackpressure)
+          .apply();
 
     mNumAcquired = 0;
     mNumFrameAvailable = 0;
@@ -186,6 +186,7 @@
 void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
                               int32_t format) {
     std::unique_lock _lock{mMutex};
+    BQA_LOGV("update width=%d height=%d format=%d", width, height, format);
     if (mFormat != format) {
         mFormat = format;
         mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format));
@@ -204,13 +205,16 @@
     if (mRequestedSize != newSize) {
         mRequestedSize.set(newSize);
         mBufferItemConsumer->setDefaultBufferSize(mRequestedSize.width, mRequestedSize.height);
-        if (mLastBufferScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) {
+        if (mLastBufferInfo.scalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) {
             // If the buffer supports scaling, update the frame immediately since the client may
             // want to scale the existing buffer to the new size.
             mSize = mRequestedSize;
-            t.setFrame(mSurfaceControl,
-                       {0, 0, static_cast<int32_t>(mSize.width),
-                        static_cast<int32_t>(mSize.height)});
+            // We only need to update the scale if we've received at least one buffer. The reason
+            // for this is the scale is calculated based on the requested size and buffer size.
+            // If there's no buffer, the scale will always be 1.
+            if (mLastBufferInfo.hasBuffer) {
+                setMatrix(&t, mLastBufferInfo);
+            }
             applyTransaction = true;
         }
     }
@@ -239,29 +243,49 @@
         ATRACE_CALL();
         BQA_LOGV("transactionCallback");
 
-        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);
-            currFrameNumber = stats[0].frameEventStats.frameNumber;
-
-            if (mTransactionCompleteCallback &&
-                currFrameNumber >= mTransactionCompleteFrameNumber) {
-                if (currFrameNumber > mTransactionCompleteFrameNumber) {
-                    BQA_LOGE("transactionCallback received for a newer framenumber=%" PRIu64
-                             " than expected=%" PRIu64,
-                             currFrameNumber, mTransactionCompleteFrameNumber);
+        if (!mSurfaceControlsWithPendingCallback.empty()) {
+            sp<SurfaceControl> pendingSC = mSurfaceControlsWithPendingCallback.front();
+            mSurfaceControlsWithPendingCallback.pop();
+            bool found = false;
+            for (auto stat : stats) {
+                if (!SurfaceControl::isSameSurface(pendingSC, stat.surfaceControl)) {
+                    continue;
                 }
-                transactionCompleteCallback = std::move(mTransactionCompleteCallback);
-                mTransactionCompleteFrameNumber = 0;
+
+                mTransformHint = stat.transformHint;
+                mBufferItemConsumer->setTransformHint(mTransformHint);
+                mBufferItemConsumer
+                        ->updateFrameTimestamps(stat.frameEventStats.frameNumber,
+                                                stat.frameEventStats.refreshStartTime,
+                                                stat.frameEventStats.gpuCompositionDoneFence,
+                                                stat.presentFence, stat.previousReleaseFence,
+                                                stat.frameEventStats.compositorTiming,
+                                                stat.latchTime,
+                                                stat.frameEventStats.dequeueReadyTime);
+
+                currFrameNumber = stat.frameEventStats.frameNumber;
+
+                if (mTransactionCompleteCallback &&
+                    currFrameNumber >= mTransactionCompleteFrameNumber) {
+                    if (currFrameNumber > mTransactionCompleteFrameNumber) {
+                        BQA_LOGE("transactionCallback received for a newer framenumber=%" PRIu64
+                                 " than expected=%" PRIu64,
+                                 currFrameNumber, mTransactionCompleteFrameNumber);
+                    }
+                    transactionCompleteCallback = std::move(mTransactionCompleteCallback);
+                    mTransactionCompleteFrameNumber = 0;
+                }
+
+                found = true;
+                break;
             }
+
+            if (!found) {
+                BQA_LOGE("Failed to find matching SurfaceControl in transaction callback");
+            }
+        } else {
+            BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was "
+                     "empty.");
         }
 
         decStrong((void*)transactionCallbackThunk);
@@ -374,8 +398,11 @@
     // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
     incStrong((void*)transactionCallbackThunk);
 
-    mLastBufferScalingMode = bufferItem.mScalingMode;
+    Rect crop = computeCrop(bufferItem);
     mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
+    mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(),
+                           bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
+                           bufferItem.mScalingMode, crop);
 
     auto releaseBufferCallback =
             std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
@@ -387,10 +414,10 @@
     t->setAcquireFence(mSurfaceControl,
                        bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
     t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
+    mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
 
-    t->setFrame(mSurfaceControl,
-                {0, 0, static_cast<int32_t>(mSize.width), static_cast<int32_t>(mSize.height)});
-    t->setCrop(mSurfaceControl, computeCrop(bufferItem));
+    setMatrix(t, mLastBufferInfo);
+    t->setCrop(mSurfaceControl, crop);
     t->setTransform(mSurfaceControl, bufferItem.mTransform);
     t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
     if (!bufferItem.mIsAutoTimestamp) {
@@ -494,6 +521,7 @@
 
 bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) {
     if (item.mScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) {
+        mSize = mRequestedSize;
         // Only reject buffers if scaling mode is freeze.
         return false;
     }
@@ -515,6 +543,19 @@
     return mSize != bufferSize;
 }
 
+void BLASTBufferQueue::setMatrix(SurfaceComposerClient::Transaction* t,
+                                 const BufferInfo& bufferInfo) {
+    uint32_t bufWidth = bufferInfo.crop.getWidth();
+    uint32_t bufHeight = bufferInfo.crop.getHeight();
+
+    float sx = mSize.width / static_cast<float>(bufWidth);
+    float sy = mSize.height / static_cast<float>(bufHeight);
+
+    t->setMatrix(mSurfaceControl, sx, 0, 0, sy);
+    // Update position based on crop.
+    t->setPosition(mSurfaceControl, bufferInfo.crop.left * sx * -1, bufferInfo.crop.top * sy * -1);
+}
+
 void BLASTBufferQueue::setTransactionCompleteCallback(
         uint64_t frameNumber, std::function<void(int64_t)>&& transactionCompleteCallback) {
     std::lock_guard _lock{mMutex};
@@ -555,11 +596,13 @@
         }).detach();
     }
 
-    status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless) override {
-        if (!ValidateFrameRate(frameRate, compatibility, "BBQSurface::setFrameRate")) {
+    status_t setFrameRate(float frameRate, int8_t compatibility,
+                          int8_t changeFrameRateStrategy) override {
+        if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+                               "BBQSurface::setFrameRate")) {
             return BAD_VALUE;
         }
-        return mBbq->setFrameRate(frameRate, compatibility, shouldBeSeamless);
+        return mBbq->setFrameRate(frameRate, compatibility, changeFrameRateStrategy);
     }
 
     status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override {
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 9cd3f63..e1b1efc 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -152,6 +152,7 @@
                     *outCount = ev.vsync.count;
                     outVsyncEventData->id = ev.vsync.vsyncId;
                     outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp;
+                    outVsyncEventData->frameInterval = ev.vsync.frameInterval;
                     break;
                 case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
                     dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 177f339..53721cf 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -99,7 +99,7 @@
         SAFE_PARCEL(data.writeVectorSize, listenerCallbacks);
         for (const auto& [listener, callbackIds] : listenerCallbacks) {
             SAFE_PARCEL(data.writeStrongBinder, listener);
-            SAFE_PARCEL(data.writeInt64Vector, callbackIds);
+            SAFE_PARCEL(data.writeParcelableVector, callbackIds);
         }
 
         SAFE_PARCEL(data.writeUint64, transactionId);
@@ -497,6 +497,28 @@
         return result;
     }
 
+    status_t onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) {
+        Parcel data, reply;
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(data.writeInt32, atomId);
+
+        status_t err = remote()->transact(BnSurfaceComposer::ON_PULL_ATOM, data, &reply);
+        if (err != NO_ERROR) {
+            ALOGE("onPullAtom failed to transact: %d", err);
+            return err;
+        }
+
+        int32_t size = 0;
+        SAFE_PARCEL(reply.readInt32, &size);
+        const void* dataPtr = reply.readInplace(size);
+        if (dataPtr == nullptr) {
+            return UNEXPECTED_NULL;
+        }
+        pulledData->assign((const char*)dataPtr, size);
+        SAFE_PARCEL(reply.readBool, success);
+        return NO_ERROR;
+    }
+
     status_t enableVSyncInjections(bool enable) override {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -1061,39 +1083,15 @@
     }
 
     status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
-                          int8_t compatibility, bool shouldBeSeamless) override {
+                          int8_t compatibility, int8_t changeFrameRateStrategy) override {
         Parcel data, reply;
-        status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        if (err != NO_ERROR) {
-            ALOGE("setFrameRate: failed writing interface token: %s (%d)", strerror(-err), -err);
-            return err;
-        }
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(surface));
+        SAFE_PARCEL(data.writeFloat, frameRate);
+        SAFE_PARCEL(data.writeByte, compatibility);
+        SAFE_PARCEL(data.writeByte, changeFrameRateStrategy);
 
-        err = data.writeStrongBinder(IInterface::asBinder(surface));
-        if (err != NO_ERROR) {
-            ALOGE("setFrameRate: failed writing strong binder: %s (%d)", strerror(-err), -err);
-            return err;
-        }
-
-        err = data.writeFloat(frameRate);
-        if (err != NO_ERROR) {
-            ALOGE("setFrameRate: failed writing float: %s (%d)", strerror(-err), -err);
-            return err;
-        }
-
-        err = data.writeByte(compatibility);
-        if (err != NO_ERROR) {
-            ALOGE("setFrameRate: failed writing byte: %s (%d)", strerror(-err), -err);
-            return err;
-        }
-
-        err = data.writeBool(shouldBeSeamless);
-        if (err != NO_ERROR) {
-            ALOGE("setFrameRate: failed writing bool: %s (%d)", strerror(-err), -err);
-            return err;
-        }
-
-        err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply);
+        status_t err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply);
         if (err != NO_ERROR) {
             ALOGE("setFrameRate: failed to transact: %s (%d)", strerror(-err), err);
             return err;
@@ -1270,7 +1268,7 @@
             for (int32_t i = 0; i < listenersSize; i++) {
                 SAFE_PARCEL(data.readStrongBinder, &tmpBinder);
                 std::vector<CallbackId> callbackIds;
-                SAFE_PARCEL(data.readInt64Vector, &callbackIds);
+                SAFE_PARCEL(data.readParcelableVector, &callbackIds);
                 listenerCallbacks.emplace_back(tmpBinder, callbackIds);
             }
 
@@ -1954,36 +1952,24 @@
         case SET_FRAME_RATE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> binder;
-            status_t err = data.readStrongBinder(&binder);
-            if (err != NO_ERROR) {
-                ALOGE("setFrameRate: failed to read strong binder: %s (%d)", strerror(-err), -err);
-                return err;
-            }
+            SAFE_PARCEL(data.readStrongBinder, &binder);
+
             sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>(binder);
             if (!surface) {
-                ALOGE("setFrameRate: failed to cast to IGraphicBufferProducer: %s (%d)",
-                      strerror(-err), -err);
-                return err;
+                ALOGE("setFrameRate: failed to cast to IGraphicBufferProducer");
+                return BAD_VALUE;
             }
             float frameRate;
-            err = data.readFloat(&frameRate);
-            if (err != NO_ERROR) {
-                ALOGE("setFrameRate: failed to read float: %s (%d)", strerror(-err), -err);
-                return err;
-            }
+            SAFE_PARCEL(data.readFloat, &frameRate);
+
             int8_t compatibility;
-            err = data.readByte(&compatibility);
-            if (err != NO_ERROR) {
-                ALOGE("setFrameRate: failed to read byte: %s (%d)", strerror(-err), -err);
-                return err;
-            }
-            bool shouldBeSeamless;
-            err = data.readBool(&shouldBeSeamless);
-            if (err != NO_ERROR) {
-                ALOGE("setFrameRate: failed to read bool: %s (%d)", strerror(-err), -err);
-                return err;
-            }
-            status_t result = setFrameRate(surface, frameRate, compatibility, shouldBeSeamless);
+            SAFE_PARCEL(data.readByte, &compatibility);
+
+            int8_t changeFrameRateStrategy;
+            SAFE_PARCEL(data.readByte, &changeFrameRateStrategy);
+
+            status_t result =
+                    setFrameRate(surface, frameRate, compatibility, changeFrameRateStrategy);
             reply->writeInt32(result);
             return NO_ERROR;
         }
@@ -2057,6 +2043,19 @@
             }
             return overrideHdrTypes(display, hdrTypesVector);
         }
+        case ON_PULL_ATOM: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            int32_t atomId = 0;
+            SAFE_PARCEL(data.readInt32, &atomId);
+
+            std::string pulledData;
+            bool success;
+            status_t err = onPullAtom(atomId, &pulledData, &success);
+            SAFE_PARCEL(reply->writeByteArray, pulledData.size(),
+                        reinterpret_cast<const uint8_t*>(pulledData.data()));
+            SAFE_PARCEL(reply->writeBool, success);
+            return err;
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index b42793b..f74f91e 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -152,7 +152,7 @@
 }
 
 status_t TransactionStats::writeToParcel(Parcel* output) const {
-    status_t err = output->writeInt64Vector(callbackIds);
+    status_t err = output->writeParcelableVector(callbackIds);
     if (err != NO_ERROR) {
         return err;
     }
@@ -176,7 +176,7 @@
 }
 
 status_t TransactionStats::readFromParcel(const Parcel* input) {
-    status_t err = input->readInt64Vector(&callbackIds);
+    status_t err = input->readParcelableVector(&callbackIds);
     if (err != NO_ERROR) {
         return err;
     }
@@ -227,8 +227,9 @@
     return NO_ERROR;
 }
 
-ListenerStats ListenerStats::createEmpty(const sp<IBinder>& listener,
-                                         const std::unordered_set<CallbackId>& callbackIds) {
+ListenerStats ListenerStats::createEmpty(
+        const sp<IBinder>& listener,
+        const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds) {
     ListenerStats listenerStats;
     listenerStats.listener = listener;
     listenerStats.transactionStats.emplace_back(callbackIds);
@@ -278,4 +279,28 @@
     }
 }
 
+ListenerCallbacks ListenerCallbacks::filter(CallbackId::Type type) const {
+    std::vector<CallbackId> filteredCallbackIds;
+    for (const auto& callbackId : callbackIds) {
+        if (callbackId.type == type) {
+            filteredCallbackIds.push_back(callbackId);
+        }
+    }
+    return ListenerCallbacks(transactionCompletedListener, filteredCallbackIds);
+}
+
+status_t CallbackId::writeToParcel(Parcel* output) const {
+    SAFE_PARCEL(output->writeInt64, id);
+    SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(type));
+    return NO_ERROR;
+}
+
+status_t CallbackId::readFromParcel(const Parcel* input) {
+    SAFE_PARCEL(input->readInt64, &id);
+    int32_t typeAsInt;
+    SAFE_PARCEL(input->readInt32, &typeAsInt);
+    type = static_cast<CallbackId::Type>(typeAsInt);
+    return NO_ERROR;
+}
+
 }; // namespace android
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index d653ae7..517b49e 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -19,6 +19,7 @@
 #include <apex/window.h>
 #include <inttypes.h>
 
+#include <android/native_window.h>
 #include <binder/Parcel.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/ISurfaceComposerClient.h>
@@ -44,7 +45,6 @@
         reserved(0),
         cornerRadius(0.0f),
         backgroundBlurRadius(0),
-        barrierFrameNumber(0),
         transform(0),
         transformToDisplayInverse(false),
         crop(Rect::INVALID_RECT),
@@ -60,7 +60,7 @@
         frameRateSelectionPriority(-1),
         frameRate(0.0f),
         frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
-        shouldBeSeamless(true),
+        changeFrameRateStrategy(ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS),
         fixedTransformHint(ui::Transform::ROT_INVALID),
         frameNumber(0),
         autoRefresh(false),
@@ -86,9 +86,7 @@
     SAFE_PARCEL(output.writeUint32, mask);
     SAFE_PARCEL(matrix.write, output);
     SAFE_PARCEL(output.write, crop);
-    SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, barrierSurfaceControl_legacy);
     SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, reparentSurfaceControl);
-    SAFE_PARCEL(output.writeUint64, barrierFrameNumber);
     SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, relativeLayerSurfaceControl);
     SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, parentSurfaceControlForChild);
     SAFE_PARCEL(output.writeFloat, color.r);
@@ -142,13 +140,13 @@
 
     for (auto listener : listeners) {
         SAFE_PARCEL(output.writeStrongBinder, listener.transactionCompletedListener);
-        SAFE_PARCEL(output.writeInt64Vector, listener.callbackIds);
+        SAFE_PARCEL(output.writeParcelableVector, listener.callbackIds);
     }
     SAFE_PARCEL(output.writeFloat, shadowRadius);
     SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority);
     SAFE_PARCEL(output.writeFloat, frameRate);
     SAFE_PARCEL(output.writeByte, frameRateCompatibility);
-    SAFE_PARCEL(output.writeBool, shouldBeSeamless);
+    SAFE_PARCEL(output.writeByte, changeFrameRateStrategy);
     SAFE_PARCEL(output.writeUint32, fixedTransformHint);
     SAFE_PARCEL(output.writeUint64, frameNumber);
     SAFE_PARCEL(output.writeBool, autoRefresh);
@@ -192,9 +190,7 @@
 
     SAFE_PARCEL(matrix.read, input);
     SAFE_PARCEL(input.read, crop);
-    SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &barrierSurfaceControl_legacy);
     SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &reparentSurfaceControl);
-    SAFE_PARCEL(input.readUint64, &barrierFrameNumber);
 
     SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl);
     SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild);
@@ -262,14 +258,14 @@
         sp<IBinder> listener;
         std::vector<CallbackId> callbackIds;
         SAFE_PARCEL(input.readNullableStrongBinder, &listener);
-        SAFE_PARCEL(input.readInt64Vector, &callbackIds);
+        SAFE_PARCEL(input.readParcelableVector, &callbackIds);
         listeners.emplace_back(listener, callbackIds);
     }
     SAFE_PARCEL(input.readFloat, &shadowRadius);
     SAFE_PARCEL(input.readInt32, &frameRateSelectionPriority);
     SAFE_PARCEL(input.readFloat, &frameRate);
     SAFE_PARCEL(input.readByte, &frameRateCompatibility);
-    SAFE_PARCEL(input.readBool, &shouldBeSeamless);
+    SAFE_PARCEL(input.readByte, &changeFrameRateStrategy);
     SAFE_PARCEL(input.readUint32, &tmpUint32);
     fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
     SAFE_PARCEL(input.readUint64, &frameNumber);
@@ -424,15 +420,6 @@
         what |= eBlurRegionsChanged;
         blurRegions = other.blurRegions;
     }
-    if (other.what & eDeferTransaction_legacy) {
-        what |= eDeferTransaction_legacy;
-        barrierSurfaceControl_legacy = other.barrierSurfaceControl_legacy;
-        barrierFrameNumber = other.barrierFrameNumber;
-    }
-    if (other.what & eReparentChildren) {
-        what |= eReparentChildren;
-        reparentSurfaceControl = other.reparentSurfaceControl;
-    }
     if (other.what & eRelativeLayerChanged) {
         what |= eRelativeLayerChanged;
         what &= ~eLayerChanged;
@@ -458,10 +445,6 @@
         what |= eCropChanged;
         crop = other.crop;
     }
-    if (other.what & eFrameChanged) {
-        what |= eFrameChanged;
-        orientedDisplaySpaceRect = other.orientedDisplaySpaceRect;
-    }
     if (other.what & eBufferChanged) {
         what |= eBufferChanged;
         buffer = other.buffer;
@@ -531,7 +514,7 @@
         what |= eFrameRateChanged;
         frameRate = other.frameRate;
         frameRateCompatibility = other.frameRateCompatibility;
-        shouldBeSeamless = other.shouldBeSeamless;
+        changeFrameRateStrategy = other.changeFrameRateStrategy;
     }
     if (other.what & eFixedTransformHintChanged) {
         what |= eFixedTransformHintChanged;
@@ -563,6 +546,10 @@
     }
 }
 
+bool layer_state_t::hasBufferChanges() const {
+    return (what & layer_state_t::eBufferChanged) || (what & layer_state_t::eCachedBufferChanged);
+}
+
 status_t layer_state_t::matrix22_t::write(Parcel& output) const {
     SAFE_PARCEL(output.writeFloat, dsdx);
     SAFE_PARCEL(output.writeFloat, dtdx);
@@ -624,8 +611,8 @@
     return NO_ERROR;
 }
 
-bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* inFunctionName,
-                       bool privileged) {
+bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy,
+                       const char* inFunctionName, bool privileged) {
     const char* functionName = inFunctionName != nullptr ? inFunctionName : "call";
     int floatClassification = std::fpclassify(frameRate);
     if (frameRate < 0 || floatClassification == FP_INFINITE || floatClassification == FP_NAN) {
@@ -641,6 +628,12 @@
         return false;
     }
 
+    if (changeFrameRateStrategy != ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS &&
+        changeFrameRateStrategy != ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS) {
+        ALOGE("%s failed - invalid change frame rate strategy value %d", functionName,
+              changeFrameRateStrategy);
+    }
+
     return true;
 }
 
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 6de3e97..2fc9d47 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1729,8 +1729,8 @@
 int Surface::dispatchSetFrameRate(va_list args) {
     float frameRate = static_cast<float>(va_arg(args, double));
     int8_t compatibility = static_cast<int8_t>(va_arg(args, int));
-    bool shouldBeSeamless = static_cast<bool>(va_arg(args, int));
-    return setFrameRate(frameRate, compatibility, shouldBeSeamless);
+    int8_t changeFrameRateStrategy = static_cast<int8_t>(va_arg(args, int));
+    return setFrameRate(frameRate, compatibility, changeFrameRateStrategy);
 }
 
 int Surface::dispatchAddCancelInterceptor(va_list args) {
@@ -2575,16 +2575,18 @@
     mSurfaceListener->onBuffersDiscarded(discardedBufs);
 }
 
-status_t Surface::setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless) {
+status_t Surface::setFrameRate(float frameRate, int8_t compatibility,
+                               int8_t changeFrameRateStrategy) {
     ATRACE_CALL();
     ALOGV("Surface::setFrameRate");
 
-    if (!ValidateFrameRate(frameRate, compatibility, "Surface::setFrameRate")) {
+    if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+                           "Surface::setFrameRate")) {
         return BAD_VALUE;
     }
 
     return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility,
-                                           shouldBeSeamless);
+                                           changeFrameRateStrategy);
 }
 
 status_t Surface::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 07618a4..5db0eae 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -139,7 +139,7 @@
 // 0 is an invalid callback id
 TransactionCompletedListener::TransactionCompletedListener() : mCallbackIdCounter(1) {}
 
-CallbackId TransactionCompletedListener::getNextIdLocked() {
+int64_t TransactionCompletedListener::getNextIdLocked() {
     return mCallbackIdCounter++;
 }
 
@@ -163,13 +163,13 @@
 CallbackId TransactionCompletedListener::addCallbackFunction(
         const TransactionCompletedCallback& callbackFunction,
         const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
-                surfaceControls) {
+                surfaceControls,
+        CallbackId::Type callbackType) {
     std::lock_guard<std::mutex> lock(mMutex);
     startListeningLocked();
 
-    CallbackId callbackId = getNextIdLocked();
+    CallbackId callbackId(getNextIdLocked(), callbackType);
     mCallbacks[callbackId].callbackFunction = callbackFunction;
-
     auto& callbackSurfaceControls = mCallbacks[callbackId].surfaceControls;
 
     for (const auto& surfaceControl : surfaceControls) {
@@ -228,7 +228,7 @@
 
 void TransactionCompletedListener::addSurfaceControlToCallbacks(
         const sp<SurfaceControl>& surfaceControl,
-        const std::unordered_set<CallbackId>& callbackIds) {
+        const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     for (auto callbackId : callbackIds) {
@@ -240,7 +240,7 @@
 }
 
 void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
-    std::unordered_map<CallbackId, CallbackTranslation> callbacksMap;
+    std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
     std::multimap<sp<IBinder>, sp<JankDataListener>> jankListenersMap;
     std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry> surfaceListeners;
     {
@@ -267,7 +267,36 @@
         }
     }
     for (const auto& transactionStats : listenerStats.transactionStats) {
+        // handle on commit callbacks
         for (auto callbackId : transactionStats.callbackIds) {
+            if (callbackId.type != CallbackId::Type::ON_COMMIT) {
+                continue;
+            }
+            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(callbacksMap[callbackId]
+                                              .surfaceControls[surfaceStats.surfaceControl],
+                                      transactionStats.latchTime, surfaceStats.acquireTime,
+                                      transactionStats.presentFence,
+                                      surfaceStats.previousReleaseFence, surfaceStats.transformHint,
+                                      surfaceStats.eventStats);
+            }
+
+            callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
+                             surfaceControlStats);
+        }
+
+        // handle on complete callbacks
+        for (auto callbackId : transactionStats.callbackIds) {
+            if (callbackId.type != CallbackId::Type::ON_COMPLETE) {
+                continue;
+            }
             auto& [callbackFunction, callbackSurfaceControls] = callbacksMap[callbackId];
             if (!callbackFunction) {
                 ALOGE("cannot call null callback function, skipping");
@@ -542,7 +571,9 @@
             return BAD_VALUE;
         }
         for (size_t j = 0; j < numCallbackIds; j++) {
-            listenerCallbacks[listener].callbackIds.insert(parcel->readInt64());
+            CallbackId id;
+            parcel->readParcelable(&id);
+            listenerCallbacks[listener].callbackIds.insert(id);
         }
         size_t numSurfaces = parcel->readUint32();
         if (numSurfaces > parcel->dataSize()) {
@@ -628,7 +659,7 @@
         parcel->writeStrongBinder(ITransactionCompletedListener::asBinder(listener));
         parcel->writeUint32(static_cast<uint32_t>(callbackInfo.callbackIds.size()));
         for (auto callbackId : callbackInfo.callbackIds) {
-            parcel->writeInt64(callbackId);
+            parcel->writeParcelable(callbackId);
         }
         parcel->writeUint32(static_cast<uint32_t>(callbackInfo.surfaceControls.size()));
         for (auto surfaceControl : callbackInfo.surfaceControls) {
@@ -896,7 +927,7 @@
 }
 
 layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) {
-    auto handle = sc->getHandle();
+    auto handle = sc->getLayerStateHandle();
 
     if (mComposerStates.count(handle) == 0) {
         // we don't have it, add an initialized layer_state to our list
@@ -1140,37 +1171,6 @@
     return *this;
 }
 
-SurfaceComposerClient::Transaction&
-SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(
-        const sp<SurfaceControl>& sc, const sp<SurfaceControl>& barrierSurfaceControl,
-        uint64_t frameNumber) {
-    layer_state_t* s = getLayerState(sc);
-    if (!s) {
-        mStatus = BAD_INDEX;
-        return *this;
-    }
-    s->what |= layer_state_t::eDeferTransaction_legacy;
-    s->barrierSurfaceControl_legacy = barrierSurfaceControl;
-    s->barrierFrameNumber = frameNumber;
-
-    registerSurfaceControlForCallback(sc);
-    return *this;
-}
-
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparentChildren(
-        const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent) {
-    layer_state_t* s = getLayerState(sc);
-    if (!s) {
-        mStatus = BAD_INDEX;
-        return *this;
-    }
-    s->what |= layer_state_t::eReparentChildren;
-    s->reparentSurfaceControl = newParent;
-
-    registerSurfaceControlForCallback(sc);
-    return *this;
-}
-
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparent(
         const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent) {
     layer_state_t* s = getLayerState(sc);
@@ -1178,8 +1178,11 @@
         mStatus = BAD_INDEX;
         return *this;
     }
+    if (SurfaceControl::isSameSurface(sc, newParent)) {
+        return *this;
+    }
     s->what |= layer_state_t::eReparent;
-    s->parentSurfaceControlForChild = newParent;
+    s->parentSurfaceControlForChild = newParent ? newParent->getParentingLayer() : nullptr;
 
     registerSurfaceControlForCallback(sc);
     return *this;
@@ -1246,20 +1249,6 @@
     return *this;
 }
 
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrame(
-        const sp<SurfaceControl>& sc, const Rect& frame) {
-    layer_state_t* s = getLayerState(sc);
-    if (!s) {
-        mStatus = BAD_INDEX;
-        return *this;
-    }
-    s->what |= layer_state_t::eFrameChanged;
-    s->orientedDisplaySpaceRect = frame;
-
-    registerSurfaceControlForCallback(sc);
-    return *this;
-}
-
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
         const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
         ReleaseBufferCallback callback) {
@@ -1431,9 +1420,9 @@
     return *this;
 }
 
-SurfaceComposerClient::Transaction&
-SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
-        TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::addTransactionCallback(
+        TransactionCompletedCallbackTakesContext callback, void* callbackContext,
+        CallbackId::Type callbackType) {
     auto listener = TransactionCompletedListener::getInstance();
 
     auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1,
@@ -1441,13 +1430,26 @@
     const auto& surfaceControls =
             mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls;
 
-    CallbackId callbackId = listener->addCallbackFunction(callbackWithContext, surfaceControls);
+    CallbackId callbackId =
+            listener->addCallbackFunction(callbackWithContext, surfaceControls, callbackType);
 
     mListenerCallbacks[TransactionCompletedListener::getIInstance()].callbackIds.emplace(
             callbackId);
     return *this;
 }
 
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
+        TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
+    return addTransactionCallback(callback, callbackContext, CallbackId::Type::ON_COMPLETE);
+}
+
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::addTransactionCommittedCallback(
+        TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
+    return addTransactionCallback(callback, callbackContext, CallbackId::Type::ON_COMMIT);
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyProducerDisconnect(
         const sp<SurfaceControl>& sc) {
     layer_state_t* s = getLayerState(sc);
@@ -1583,7 +1585,7 @@
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate(
         const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility,
-        bool shouldBeSeamless) {
+        int8_t changeFrameRateStrategy) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
@@ -1591,7 +1593,8 @@
     }
     // Allow privileged values as well here, those will be ignored by SF if
     // the caller is not privileged
-    if (!ValidateFrameRate(frameRate, compatibility, "Transaction::setFrameRate",
+    if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+                           "Transaction::setFrameRate",
                            /*privileged=*/true)) {
         mStatus = BAD_VALUE;
         return *this;
@@ -1599,7 +1602,7 @@
     s->what |= layer_state_t::eFrameRateChanged;
     s->frameRate = frameRate;
     s->frameRateCompatibility = compatibility;
-    s->shouldBeSeamless = shouldBeSeamless;
+    s->changeFrameRateStrategy = changeFrameRateStrategy;
     return *this;
 }
 
@@ -1837,7 +1840,8 @@
         }
         ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
         if (err == NO_ERROR) {
-            *outSurface = new SurfaceControl(this, handle, gbp, id, transformHint);
+            *outSurface =
+                    new SurfaceControl(this, handle, gbp, id, w, h, format, transformHint, flags);
         }
     }
     return err;
@@ -1990,6 +1994,11 @@
     return ComposerService::getComposerService()->overrideHdrTypes(display, hdrTypes);
 }
 
+status_t SurfaceComposerClient::onPullAtom(const int32_t atomId, std::string* outData,
+                                           bool* success) {
+    return ComposerService::getComposerService()->onPullAtom(atomId, outData, success);
+}
+
 status_t SurfaceComposerClient::getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
                                                                       ui::PixelFormat* outFormat,
                                                                       ui::Dataspace* outDataspace,
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 7e2f8f9..37750fa 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -35,6 +35,7 @@
 #include <ui/StaticDisplayInfo.h>
 
 #include <gui/BufferQueueCore.h>
+#include <gui/BLASTBufferQueue.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
@@ -49,12 +50,17 @@
 
 SurfaceControl::SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
                                const sp<IGraphicBufferProducer>& gbp, int32_t layerId,
-                               uint32_t transform)
+                               uint32_t w, uint32_t h, PixelFormat format, uint32_t transform,
+                               uint32_t flags)
       : mClient(client),
         mHandle(handle),
         mGraphicBufferProducer(gbp),
         mLayerId(layerId),
-        mTransformHint(transform) {}
+        mTransformHint(transform),
+        mWidth(w),
+        mHeight(h),
+        mFormat(format),
+        mCreateFlags(flags) {}
 
 SurfaceControl::SurfaceControl(const sp<SurfaceControl>& other) {
     mClient = other->mClient;
@@ -62,6 +68,9 @@
     mGraphicBufferProducer = other->mGraphicBufferProducer;
     mTransformHint = other->mTransformHint;
     mLayerId = other->mLayerId;
+    mWidth = other->mWidth;
+    mHeight = other->mHeight;
+    mCreateFlags = other->mCreateFlags;
 }
 
 SurfaceControl::~SurfaceControl()
@@ -70,13 +79,13 @@
     // happen without delay, since these resources are quite heavy.
     mClient.clear();
     mHandle.clear();
-    mGraphicBufferProducer.clear();
+    mBbq.clear();
     IPCThreadState::self()->flushCommands();
 }
 
 void SurfaceControl::disconnect() {
-    if (mGraphicBufferProducer != nullptr) {
-        mGraphicBufferProducer->disconnect(
+    if (getIGraphicBufferProducer() != nullptr) {
+        getIGraphicBufferProducer()->disconnect(
                 BufferQueueCore::CURRENTLY_CONNECTED_API);
     }
 }
@@ -118,21 +127,28 @@
 {
     sp<IGraphicBufferProducer> bp;
     if (control != nullptr) {
-        bp = control->mGraphicBufferProducer;
+        bp = control->getIGraphicBufferProducer();
     }
     return parcel->writeStrongBinder(IInterface::asBinder(bp));
 }
 
-sp<Surface> SurfaceControl::generateSurfaceLocked() const
+sp<Surface> SurfaceControl::generateSurfaceLocked()
 {
+    uint32_t ignore;
+    auto flags = mCreateFlags & (ISurfaceComposerClient::eCursorWindow |
+                                 ISurfaceComposerClient::eOpaque);
+    mBbqChild = mClient->createSurface(String8("bbq-wrapper"), 0, 0, mFormat,
+                                       flags, mHandle, {}, &ignore);
+    mBbq = new BLASTBufferQueue("bbq-adapter", mBbqChild, mWidth, mHeight, mFormat);
+
     // This surface is always consumed by SurfaceFlinger, so the
     // producerControlledByApp value doesn't matter; using false.
-    mSurfaceData = new Surface(mGraphicBufferProducer, false);
+    mSurfaceData = mBbq->getSurface(true);
 
     return mSurfaceData;
 }
 
-sp<Surface> SurfaceControl::getSurface() const
+sp<Surface> SurfaceControl::getSurface()
 {
     Mutex::Autolock _l(mLock);
     if (mSurfaceData == nullptr) {
@@ -141,25 +157,42 @@
     return mSurfaceData;
 }
 
-sp<Surface> SurfaceControl::createSurface() const
+sp<Surface> SurfaceControl::createSurface()
 {
-    Mutex::Autolock _l(mLock);
-    return generateSurfaceLocked();
+    return getSurface();
 }
 
-sp<IBinder> SurfaceControl::getHandle() const
+void SurfaceControl::updateDefaultBufferSize(uint32_t width, uint32_t height) {
+    Mutex::Autolock _l(mLock);
+    mWidth = width; mHeight = height;
+    if (mBbq) {
+      mBbq->update(this, width, height, mFormat);
+    }
+
+}
+
+sp<IBinder> SurfaceControl::getLayerStateHandle() const
 {
     return mHandle;
 }
 
+sp<IBinder> SurfaceControl::getHandle() const {
+    if (mBbqChild != nullptr) {
+        return mBbqChild->getHandle();
+    }
+    return getLayerStateHandle();
+}
+
 int32_t SurfaceControl::getLayerId() const {
     return mLayerId;
 }
 
-sp<IGraphicBufferProducer> SurfaceControl::getIGraphicBufferProducer() const
+sp<IGraphicBufferProducer> SurfaceControl::getIGraphicBufferProducer()
 {
+    getSurface();
     Mutex::Autolock _l(mLock);
-    return mGraphicBufferProducer;
+
+    return mBbq->getIGraphicBufferProducer();
 }
 
 sp<SurfaceComposerClient> SurfaceControl::getClient() const
@@ -180,9 +213,11 @@
 status_t SurfaceControl::writeToParcel(Parcel& parcel) {
     SAFE_PARCEL(parcel.writeStrongBinder, ISurfaceComposerClient::asBinder(mClient->getClient()));
     SAFE_PARCEL(parcel.writeStrongBinder, mHandle);
-    SAFE_PARCEL(parcel.writeStrongBinder, IGraphicBufferProducer::asBinder(mGraphicBufferProducer));
     SAFE_PARCEL(parcel.writeInt32, mLayerId);
     SAFE_PARCEL(parcel.writeUint32, mTransformHint);
+    SAFE_PARCEL(parcel.writeUint32, mWidth);
+    SAFE_PARCEL(parcel.writeUint32, mHeight);
+    SAFE_PARCEL(parcel.writeUint32, mFormat);
 
     return NO_ERROR;
 }
@@ -191,21 +226,26 @@
                                         sp<SurfaceControl>* outSurfaceControl) {
     sp<IBinder> client;
     sp<IBinder> handle;
-    sp<IBinder> gbp;
     int32_t layerId;
     uint32_t transformHint;
+    uint32_t width;
+    uint32_t height;
+    uint32_t format;
 
     SAFE_PARCEL(parcel.readStrongBinder, &client);
     SAFE_PARCEL(parcel.readStrongBinder, &handle);
-    SAFE_PARCEL(parcel.readNullableStrongBinder, &gbp);
     SAFE_PARCEL(parcel.readInt32, &layerId);
     SAFE_PARCEL(parcel.readUint32, &transformHint);
+    SAFE_PARCEL(parcel.readUint32, &width);
+    SAFE_PARCEL(parcel.readUint32, &height);
+    SAFE_PARCEL(parcel.readUint32, &format);
 
     // We aren't the original owner of the surface.
     *outSurfaceControl =
             new SurfaceControl(new SurfaceComposerClient(
                                        interface_cast<ISurfaceComposerClient>(client)),
-                               handle.get(), interface_cast<IGraphicBufferProducer>(gbp), layerId,
+                               handle.get(), nullptr, layerId,
+                               width, height, format,
                                transformHint);
 
     return NO_ERROR;
@@ -233,5 +273,12 @@
     return NO_ERROR;
 }
 
+sp<SurfaceControl> SurfaceControl::getParentingLayer() {
+    if (mBbqChild != nullptr) {
+        return mBbqChild;
+    }
+    return this;
+}
+
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index fbd16f4..139dbb7 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -142,6 +142,39 @@
     ui::Size mRequestedSize GUARDED_BY(mMutex);
     int32_t mFormat GUARDED_BY(mMutex);
 
+    struct BufferInfo {
+        bool hasBuffer = false;
+        uint32_t width;
+        uint32_t height;
+        uint32_t transform;
+        // This is used to check if we should update the blast layer size immediately or wait until
+        // we get the next buffer. This will support scenarios where the layer can change sizes
+        // and the buffer will scale to fit the new size.
+        uint32_t scalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+        Rect crop;
+
+        void update(bool hasBuffer, uint32_t width, uint32_t height, uint32_t transform,
+                    uint32_t scalingMode, const Rect& crop) {
+            this->hasBuffer = hasBuffer;
+            this->width = width;
+            this->height = height;
+            this->transform = transform;
+            this->scalingMode = scalingMode;
+            if (!crop.isEmpty()) {
+                this->crop = crop;
+            } else {
+                this->crop = Rect(width, height);
+            }
+        }
+    };
+
+    // Last acquired buffer's info. This is used to calculate the correct scale when size change is
+    // requested. We need to use the old buffer's info to determine what scale we need to apply to
+    // ensure the correct size.
+    BufferInfo mLastBufferInfo GUARDED_BY(mMutex);
+    void setMatrix(SurfaceComposerClient::Transaction* t, const BufferInfo& bufferInfo)
+            REQUIRES(mMutex);
+
     uint32_t mTransformHint GUARDED_BY(mMutex);
 
     sp<IGraphicBufferConsumer> mConsumer;
@@ -159,11 +192,6 @@
 
     std::queue<FrameTimelineInfo> mNextFrameTimelineInfoQueue GUARDED_BY(mMutex);
 
-    // Last acquired buffer's scaling mode. This is used to check if we should update the blast
-    // layer size immediately or wait until we get the next buffer. This will support scenarios
-    // where the layer can change sizes and the buffer will scale to fit the new size.
-    uint32_t mLastBufferScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
-
     // Tracks the last acquired frame number
     uint64_t mLastAcquiredFrameNumber GUARDED_BY(mMutex) = 0;
 
@@ -181,6 +209,10 @@
     // it for debugging purposes.
     std::unordered_map<uint64_t /* bufferId */, nsecs_t> mDequeueTimestamps
             GUARDED_BY(mTimestampMutex);
+
+    // Keep track of SurfaceControls that have submitted a transaction and BBQ is waiting on a
+    // callback for them.
+    std::queue<sp<SurfaceControl>> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex);
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index d74c2ba..4ade240 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -30,6 +30,9 @@
     // The deadline in CLOCK_MONOTONIC that the app needs to complete its
     // frame by (both on the CPU and the GPU)
     int64_t deadlineTimestamp = std::numeric_limits<int64_t>::max();
+
+    // The current frame interval in ns when this frame was scheduled.
+    int64_t frameInterval = 0;
 };
 
 class DisplayEventDispatcher : public LooperCallback {
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 7179a20..0dffbde 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -75,6 +75,7 @@
             uint32_t count;
             nsecs_t expectedVSyncTimestamp __attribute__((aligned(8)));
             nsecs_t deadlineTimestamp __attribute__((aligned(8)));
+            nsecs_t frameInterval __attribute__((aligned(8)));
             int64_t vsyncId;
         };
 
diff --git a/libs/gui/include/gui/FrameTimelineInfo.h b/libs/gui/include/gui/FrameTimelineInfo.h
index 3b4c009..a23c202 100644
--- a/libs/gui/include/gui/FrameTimelineInfo.h
+++ b/libs/gui/include/gui/FrameTimelineInfo.h
@@ -18,7 +18,6 @@
 
 #include <stdint.h>
 
-#include <android/os/IInputConstants.h>
 #include <binder/Parcel.h>
 
 namespace android {
@@ -31,7 +30,11 @@
     int64_t vsyncId = INVALID_VSYNC_ID;
 
     // The id of the input event that caused this buffer
-    int32_t inputEventId = android::os::IInputConstants::INVALID_INPUT_EVENT_ID;
+    // Default is android::os::IInputConstants::INVALID_INPUT_EVENT_ID = 0
+    // We copy the value of the input event ID instead of including the header, because libgui
+    // header libraries containing FrameTimelineInfo must be available to vendors, but libinput is
+    // not directly vendor available.
+    int32_t inputEventId = 0;
 
     status_t write(Parcel& output) const;
     status_t read(const Parcel& input);
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 50198c6..cb04689 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -275,6 +275,12 @@
     virtual status_t overrideHdrTypes(const sp<IBinder>& display,
                                       const std::vector<ui::Hdr>& hdrTypes) = 0;
 
+    /* Pulls surfaceflinger atoms global stats and layer stats to pipe to statsd.
+     *
+     * Requires the calling uid be from system server.
+     */
+    virtual status_t onPullAtom(const int32_t atomId, std::string* outData, bool* success) = 0;
+
     virtual status_t enableVSyncInjections(bool enable) = 0;
 
     virtual status_t injectVSync(nsecs_t when) = 0;
@@ -486,7 +492,7 @@
      * Sets the intended frame rate for a surface. See ANativeWindow_setFrameRate() for more info.
      */
     virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
-                                  int8_t compatibility, bool shouldBeSeamless) = 0;
+                                  int8_t compatibility, int8_t changeFrameRateStrategy) = 0;
 
     /*
      * Acquire a frame rate flexibility token from SurfaceFlinger. While this token is acquired,
@@ -600,6 +606,7 @@
         OVERRIDE_HDR_TYPES,
         ADD_HDR_LAYER_INFO_LISTENER,
         REMOVE_HDR_LAYER_INFO_LISTENER,
+        ON_PULL_ATOM,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 098760e..2d71194 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -36,7 +36,22 @@
 class ITransactionCompletedListener;
 class ListenerCallbacks;
 
-using CallbackId = int64_t;
+class CallbackId : public Parcelable {
+public:
+    int64_t id;
+    enum class Type : int32_t { ON_COMPLETE, ON_COMMIT } type;
+
+    CallbackId() {}
+    CallbackId(int64_t id, Type type) : id(id), type(type) {}
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+
+    bool operator==(const CallbackId& rhs) const { return id == rhs.id && type == rhs.type; }
+};
+
+struct CallbackIdHash {
+    std::size_t operator()(const CallbackId& key) const { return std::hash<int64_t>()(key.id); }
+};
 
 class FrameEventHistoryStats : public Parcelable {
 public:
@@ -112,7 +127,7 @@
 
     TransactionStats() = default;
     TransactionStats(const std::vector<CallbackId>& ids) : callbackIds(ids) {}
-    TransactionStats(const std::unordered_set<CallbackId>& ids)
+    TransactionStats(const std::unordered_set<CallbackId, CallbackIdHash>& ids)
           : callbackIds(ids.begin(), ids.end()) {}
     TransactionStats(const std::vector<CallbackId>& ids, nsecs_t latch, const sp<Fence>& present,
                      const std::vector<SurfaceStats>& surfaces)
@@ -129,8 +144,9 @@
     status_t writeToParcel(Parcel* output) const override;
     status_t readFromParcel(const Parcel* input) override;
 
-    static ListenerStats createEmpty(const sp<IBinder>& listener,
-                                     const std::unordered_set<CallbackId>& callbackIds);
+    static ListenerStats createEmpty(
+            const sp<IBinder>& listener,
+            const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds);
 
     sp<IBinder> listener;
     std::vector<TransactionStats> transactionStats;
@@ -156,7 +172,8 @@
 
 class ListenerCallbacks {
 public:
-    ListenerCallbacks(const sp<IBinder>& listener, const std::unordered_set<CallbackId>& callbacks)
+    ListenerCallbacks(const sp<IBinder>& listener,
+                      const std::unordered_set<CallbackId, CallbackIdHash>& callbacks)
           : transactionCompletedListener(listener),
             callbackIds(callbacks.begin(), callbacks.end()) {}
 
@@ -170,9 +187,12 @@
         if (callbackIds.empty()) {
             return rhs.callbackIds.empty();
         }
-        return callbackIds.front() == rhs.callbackIds.front();
+        return callbackIds.front().id == rhs.callbackIds.front().id;
     }
 
+    // Returns a new ListenerCallbacks filtered by type
+    ListenerCallbacks filter(CallbackId::Type type) const;
+
     sp<IBinder> transactionCompletedListener;
     std::vector<CallbackId> callbackIds;
 };
@@ -191,7 +211,7 @@
     // 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());
+        return std::hash<int64_t>{}((callbackIds.empty()) ? 0 : callbackIds.front().id);
     }
 };
 
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 9274777..b4f62f2 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -82,11 +82,9 @@
         eTransparentRegionChanged = 0x00000020,
         eFlagsChanged = 0x00000040,
         eLayerStackChanged = 0x00000080,
-        /* was eCropChanged_legacy, now available 0x00000100, */
-        eDeferTransaction_legacy = 0x00000200,
         eReleaseBufferListenerChanged = 0x00000400,
         eShadowRadiusChanged = 0x00000800,
-        eReparentChildren = 0x00001000,
+        eLayerCreated = 0x00001000,
         /* was eDetachChildren, now available 0x00002000, */
         eRelativeLayerChanged = 0x00004000,
         eReparent = 0x00008000,
@@ -106,7 +104,7 @@
         eHasListenerCallbacksChanged = 0x20000000,
         eInputInfoChanged = 0x40000000,
         eCornerRadiusChanged = 0x80000000,
-        eFrameChanged = 0x1'00000000,
+        /* was eFrameChanged, now available 0x1'00000000, */
         eCachedBufferChanged = 0x2'00000000,
         eBackgroundColorChanged = 0x4'00000000,
         eMetadataChanged = 0x8'00000000,
@@ -127,6 +125,7 @@
     void merge(const layer_state_t& other);
     status_t write(Parcel& output) const;
     status_t read(const Parcel& input);
+    bool hasBufferChanges() const;
 
     struct matrix22_t {
         float dsdx{0};
@@ -152,9 +151,7 @@
     matrix22_t matrix;
     float cornerRadius;
     uint32_t backgroundBlurRadius;
-    sp<SurfaceControl> barrierSurfaceControl_legacy;
     sp<SurfaceControl> reparentSurfaceControl;
-    uint64_t barrierFrameNumber;
 
     sp<SurfaceControl> relativeLayerSurfaceControl;
 
@@ -207,7 +204,7 @@
     // Layer frame rate and compatibility. See ANativeWindow_setFrameRate().
     float frameRate;
     int8_t frameRateCompatibility;
-    bool shouldBeSeamless;
+    int8_t changeFrameRateStrategy;
 
     // Set by window manager indicating the layer and all its children are
     // in a different orientation than the display. The hint suggests that
@@ -307,10 +304,11 @@
 //
 // @param frameRate the frame rate in Hz
 // @param compatibility a ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_*
+// @param changeFrameRateStrategy a ANATIVEWINDOW_CHANGE_FRAME_RATE_*
 // @param functionName calling function or nullptr. Used for logging
 // @param privileged whether caller has unscoped surfaceflinger access
-bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* functionName,
-                       bool privileged = false);
+bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy,
+                       const char* functionName, bool privileged = false);
 
 struct CaptureArgs {
     const static int32_t UNSET_UID = -1;
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 5881221..d22bdaa 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -187,7 +187,8 @@
     status_t getUniqueId(uint64_t* outId) const;
     status_t getConsumerUsage(uint64_t* outUsage) const;
 
-    virtual status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
+    virtual status_t setFrameRate(float frameRate, int8_t compatibility,
+                                  int8_t changeFrameRateStrategy);
     virtual status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
     virtual status_t getExtraBufferCount(int* extraBuffers) const;
 
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 7bf2e2d..5bbd8e3 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -336,7 +336,7 @@
     struct CallbackInfo {
         // All the callbacks that have been requested for a TransactionCompletedListener in the
         // Transaction
-        std::unordered_set<CallbackId> callbackIds;
+        std::unordered_set<CallbackId, CallbackIdHash> callbackIds;
         // All the SurfaceControls that have been modified in this TransactionCompletedListener's
         // process that require a callback if there is one or more callbackIds set.
         std::unordered_set<sp<SurfaceControl>, SCHash> surfaceControls;
@@ -454,20 +454,8 @@
                                     const std::vector<BlurRegion>& regions);
         Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
         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
-        // what frame number has been reached.
-        Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
-                                                  const sp<SurfaceControl>& barrierSurfaceControl,
-                                                  uint64_t frameNumber);
-        // Reparents all children of this layer to the new parent handle.
-        Transaction& reparentChildren(const sp<SurfaceControl>& sc,
-                                      const sp<SurfaceControl>& newParent);
 
         /// Reparents the current layer to the new parent handle. The new parent must not be null.
-        // This can be used instead of reparentChildren if the caller wants to
-        // only re-parent a specific child.
         Transaction& reparent(const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent);
 
         Transaction& setColor(const sp<SurfaceControl>& sc, const half3& color);
@@ -479,7 +467,6 @@
         Transaction& setTransform(const sp<SurfaceControl>& sc, uint32_t transform);
         Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
                                                   bool transformToDisplayInverse);
-        Transaction& setFrame(const sp<SurfaceControl>& sc, const Rect& frame);
         Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
                                ReleaseBufferCallback callback = nullptr);
         Transaction& setCachedBuffer(const sp<SurfaceControl>& sc, int32_t bufferId);
@@ -497,9 +484,15 @@
         // Sets information about the priority of the frame.
         Transaction& setFrameRateSelectionPriority(const sp<SurfaceControl>& sc, int32_t priority);
 
+        Transaction& addTransactionCallback(TransactionCompletedCallbackTakesContext callback,
+                                            void* callbackContext, CallbackId::Type callbackType);
+
         Transaction& addTransactionCompletedCallback(
                 TransactionCompletedCallbackTakesContext callback, void* callbackContext);
 
+        Transaction& addTransactionCommittedCallback(
+                TransactionCompletedCallbackTakesContext callback, void* callbackContext);
+
         // ONLY FOR BLAST ADAPTER
         Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
         // Set the framenumber generated by the graphics producer to mimic BufferQueue behaviour.
@@ -520,7 +513,7 @@
         Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius);
 
         Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate,
-                                  int8_t compatibility, bool shouldBeSeamless);
+                                  int8_t compatibility, int8_t changeFrameRateStrategy);
 
         // Set by window manager indicating the layer and all its children are
         // in a different orientation than the display. The hint suggests that
@@ -580,6 +573,8 @@
     static status_t overrideHdrTypes(const sp<IBinder>& display,
                                      const std::vector<ui::Hdr>& hdrTypes);
 
+    static status_t onPullAtom(const int32_t atomId, std::string* outData, bool* success);
+
     static void setDisplayProjection(const sp<IBinder>& token, ui::Rotation orientation,
                                      const Rect& layerStackRect, const Rect& displayRect);
 
@@ -632,14 +627,13 @@
 class TransactionCompletedListener : public BnTransactionCompletedListener {
     TransactionCompletedListener();
 
-    CallbackId getNextIdLocked() REQUIRES(mMutex);
+    int64_t getNextIdLocked() REQUIRES(mMutex);
 
     std::mutex mMutex;
 
     bool mListening GUARDED_BY(mMutex) = false;
 
-    CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1;
-
+    int64_t mCallbackIdCounter GUARDED_BY(mMutex) = 1;
     struct CallbackTranslation {
         TransactionCompletedCallback callbackFunction;
         std::unordered_map<sp<IBinder>, sp<SurfaceControl>, SurfaceComposerClient::IBinderHash>
@@ -657,7 +651,8 @@
         SurfaceStatsCallback callback;
     };
 
-    std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex);
+    std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> mCallbacks
+            GUARDED_BY(mMutex);
     std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex);
     std::unordered_map<uint64_t /* graphicsBufferId */, ReleaseBufferCallback>
             mReleaseBufferCallbacks GUARDED_BY(mMutex);
@@ -673,10 +668,12 @@
     CallbackId addCallbackFunction(
             const TransactionCompletedCallback& callbackFunction,
             const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
-                    surfaceControls);
+                    surfaceControls,
+            CallbackId::Type callbackType);
 
-    void addSurfaceControlToCallbacks(const sp<SurfaceControl>& surfaceControl,
-                                      const std::unordered_set<CallbackId>& callbackIds);
+    void addSurfaceControlToCallbacks(
+            const sp<SurfaceControl>& surfaceControl,
+            const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds);
 
     /*
      * Adds a jank listener to be informed about SurfaceFlinger's jank classification for a specific
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index 35bdfc1..9ee4636 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -37,6 +37,7 @@
 class IGraphicBufferProducer;
 class Surface;
 class SurfaceComposerClient;
+class BLASTBufferQueue;
 
 // ---------------------------------------------------------------------------
 
@@ -70,12 +71,13 @@
     static status_t writeSurfaceToParcel(
             const sp<SurfaceControl>& control, Parcel* parcel);
 
-    sp<Surface> getSurface() const;
-    sp<Surface> createSurface() const;
+    sp<Surface> getSurface();
+    sp<Surface> createSurface();
     sp<IBinder> getHandle() const;
+    sp<IBinder> getLayerStateHandle() const;
     int32_t getLayerId() const;
 
-    sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
+    sp<IGraphicBufferProducer> getIGraphicBufferProducer();
 
     status_t clearLayerFrameStats() const;
     status_t getLayerFrameStats(FrameStats* outStats) const;
@@ -85,12 +87,16 @@
     uint32_t getTransformHint() const;
 
     void setTransformHint(uint32_t hint);
+    void updateDefaultBufferSize(uint32_t width, uint32_t height);
 
     explicit SurfaceControl(const sp<SurfaceControl>& other);
 
     SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
                    const sp<IGraphicBufferProducer>& gbp, int32_t layerId,
-                   uint32_t transformHint = 0);
+                   uint32_t width = 0, uint32_t height = 0, PixelFormat format = 0,
+                   uint32_t transformHint = 0, uint32_t flags = 0);
+
+    sp<SurfaceControl> getParentingLayer();
 
 private:
     // can't be copied
@@ -102,7 +108,7 @@
 
     ~SurfaceControl();
 
-    sp<Surface> generateSurfaceLocked() const;
+    sp<Surface> generateSurfaceLocked();
     status_t validate() const;
 
     sp<SurfaceComposerClient>   mClient;
@@ -110,8 +116,14 @@
     sp<IGraphicBufferProducer>  mGraphicBufferProducer;
     mutable Mutex               mLock;
     mutable sp<Surface>         mSurfaceData;
+    mutable sp<BLASTBufferQueue> mBbq;
+    mutable sp<SurfaceControl> mBbqChild;
     int32_t mLayerId;
     uint32_t mTransformHint;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    PixelFormat mFormat;
+    uint32_t mCreateFlags;
 };
 
 }; // namespace android
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index fe48d88..5a5da97 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -140,7 +140,6 @@
                                                  /*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();
@@ -218,13 +217,13 @@
                             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)));
+                    ASSERT_GE(epsilon, abs(r - *(pixel)));
+                    ASSERT_GE(epsilon, abs(g - *(pixel + 1)));
+                    ASSERT_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)));
+                    ASSERT_GE(epsilon, abs(r - *(pixel)));
+                    ASSERT_GE(epsilon, abs(g - *(pixel + 1)));
+                    ASSERT_GE(epsilon, abs(b - *(pixel + 2)));
                 }
                 ASSERT_EQ(false, ::testing::Test::HasFailure());
             }
@@ -466,7 +465,8 @@
     ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
 
     ASSERT_NO_FATAL_FAILURE(
-            checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+            checkScreenCapture(r, g, b,
+                               {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight / 2}));
 }
 
 TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) {
@@ -522,16 +522,148 @@
     adapter.waitForCallbacks();
     // capture screen and verify that it is red
     ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
-
-    ASSERT_NO_FATAL_FAILURE(
-            checkScreenCapture(r, g, b,
-                               {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength}));
+    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b,
+                                               {10, 10, (int32_t)bufferSideLength - 10,
+                                                (int32_t)bufferSideLength - 10}));
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(0, 0, 0,
                                {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength},
                                /*border*/ 0, /*outsideRegion*/ true));
 }
 
+TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToBufferSize) {
+    // add black background
+    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(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+            .setColor(bg, half3{0, 0, 0})
+            .setLayer(bg, 0)
+            .apply();
+
+    Rect windowSize(1000, 1000);
+    Rect bufferSize(windowSize);
+    Rect bufferCrop(200, 200, 700, 700);
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, windowSize.getWidth(), windowSize.getHeight());
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+    int slot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buf;
+    auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufferSize.getWidth(),
+                                          bufferSize.getHeight(), 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));
+    // fill buffer with grey
+    fillBuffer(bufData, bufferSize, buf->getStride(), 127, 127, 127);
+
+    // fill crop area with different colors so we can verify the cropped region has been scaled
+    // correctly.
+    fillBuffer(bufData, Rect(200, 200, 450, 450), buf->getStride(), /* rgb */ 255, 0, 0);
+    fillBuffer(bufData, Rect(200, 451, 450, 700), buf->getStride(), /* rgb */ 0, 255, 0);
+    fillBuffer(bufData, Rect(451, 200, 700, 450), buf->getStride(), /* rgb */ 0, 0, 255);
+    fillBuffer(bufData, Rect(451, 451, 700, 700), buf->getStride(), /* rgb */ 255, 0, 0);
+    buf->unlock();
+
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
+    IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+                                                   HAL_DATASPACE_UNKNOWN,
+                                                   bufferCrop /* Rect::INVALID_RECT */,
+                                                   NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, 0,
+                                                   Fence::NO_FENCE);
+    igbProducer->queueBuffer(slot, input, &qbOutput);
+    ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+    adapter.waitForCallbacks();
+
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+
+    // Verify cropped region is scaled correctly.
+    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {10, 10, 490, 490}));
+    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 255, 0, {10, 510, 490, 990}));
+    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 255, {510, 10, 990, 490}));
+    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {510, 510, 990, 990}));
+    // Verify outside region is black.
+    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 0,
+                                               {0, 0, (int32_t)windowSize.getWidth(),
+                                                (int32_t)windowSize.getHeight()},
+                                               /*border*/ 0, /*outsideRegion*/ true));
+}
+
+TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToWindowSize) {
+    // add black background
+    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(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+            .setColor(bg, half3{0, 0, 0})
+            .setLayer(bg, 0)
+            .apply();
+
+    Rect windowSize(1000, 1000);
+    Rect bufferSize(500, 500);
+    Rect bufferCrop(100, 100, 350, 350);
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, windowSize.getWidth(), windowSize.getHeight());
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+    int slot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buf;
+    auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufferSize.getWidth(),
+                                          bufferSize.getHeight(), 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));
+    // fill buffer with grey
+    fillBuffer(bufData, bufferSize, buf->getStride(), 127, 127, 127);
+
+    // fill crop area with different colors so we can verify the cropped region has been scaled
+    // correctly.
+    fillBuffer(bufData, Rect(100, 100, 225, 225), buf->getStride(), /* rgb */ 255, 0, 0);
+    fillBuffer(bufData, Rect(100, 226, 225, 350), buf->getStride(), /* rgb */ 0, 255, 0);
+    fillBuffer(bufData, Rect(226, 100, 350, 225), buf->getStride(), /* rgb */ 0, 0, 255);
+    fillBuffer(bufData, Rect(226, 226, 350, 350), buf->getStride(), /* rgb */ 255, 0, 0);
+    buf->unlock();
+
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
+    IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+                                                   HAL_DATASPACE_UNKNOWN,
+                                                   bufferCrop /* Rect::INVALID_RECT */,
+                                                   NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, 0,
+                                                   Fence::NO_FENCE);
+    igbProducer->queueBuffer(slot, input, &qbOutput);
+    ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+    adapter.waitForCallbacks();
+
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    // Verify cropped region is scaled correctly.
+    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {10, 10, 490, 490}));
+    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 255, 0, {10, 510, 490, 990}));
+    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 255, {510, 10, 990, 490}));
+    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {510, 510, 990, 990}));
+    // Verify outside region is black.
+    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 0,
+                                               {0, 0, (int32_t)windowSize.getWidth(),
+                                                (int32_t)windowSize.getHeight()},
+                                               /*border*/ 0, /*outsideRegion*/ true));
+}
+
 class TestProducerListener : public BnProducerListener {
 public:
     sp<IGraphicBufferProducer> mIgbp;
@@ -596,7 +728,6 @@
     t.setLayerStack(bgSurface, 0)
             .show(bgSurface)
             .setDataspace(bgSurface, ui::Dataspace::V0_SRGB)
-            .setFrame(bgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight))
             .setLayer(bgSurface, std::numeric_limits<int32_t>::max() - 1)
             .apply();
 
@@ -619,7 +750,8 @@
     ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
 
     ASSERT_NO_FATAL_FAILURE(
-            checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+            checkScreenCapture(r, g, b,
+                               {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight / 2}));
 }
 
 class BLASTBufferQueueTransformTest : public BLASTBufferQueueTest {
@@ -834,26 +966,6 @@
         if (postedTime) *postedTime = systemTime();
         igbProducer->queueBuffer(slot, input, qbOutput);
     }
-
-    void createBufferQueueProducer(sp<IGraphicBufferProducer>* bqIgbp) {
-        mBufferQueueSurfaceControl =
-                mClient->createSurface(String8("BqSurface"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                       ISurfaceComposerClient::eFXSurfaceBufferQueue);
-        ASSERT_NE(nullptr, mBufferQueueSurfaceControl.get());
-        Transaction()
-                .setLayerStack(mBufferQueueSurfaceControl, 0)
-                .show(mBufferQueueSurfaceControl)
-                .setDataspace(mBufferQueueSurfaceControl, ui::Dataspace::V0_SRGB)
-                .setSize(mBufferQueueSurfaceControl, mDisplayWidth, mDisplayHeight)
-                .setLayer(mBufferQueueSurfaceControl, std::numeric_limits<int32_t>::max())
-                .apply();
-
-        sp<Surface> bqSurface = mBufferQueueSurfaceControl->getSurface();
-        ASSERT_NE(nullptr, bqSurface.get());
-
-        *bqIgbp = bqSurface->getIGraphicBufferProducer();
-        setUpProducer(*bqIgbp);
-    }
     sp<SurfaceControl> mBufferQueueSurfaceControl;
 };
 
@@ -909,55 +1021,6 @@
     adapter.waitForCallbacks();
 }
 
-// Runs the same Frame Event History test
-TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic_BufferQueue) {
-    sp<IGraphicBufferProducer> bqIgbp;
-    createBufferQueueProducer(&bqIgbp);
-
-    ProducerFrameEventHistory history;
-    IGraphicBufferProducer::QueueBufferOutput qbOutput;
-    nsecs_t requestedPresentTimeA = 0;
-    nsecs_t postedTimeA = 0;
-    setUpAndQueueBuffer(bqIgbp, &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);
-
-    // wait for buffer to be presented
-    std::this_thread::sleep_for(200ms);
-
-    nsecs_t requestedPresentTimeB = 0;
-    nsecs_t postedTimeB = 0;
-    setUpAndQueueBuffer(bqIgbp, &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_FALSE(events->hasDequeueReadyInfo());
-
-    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);
-}
-
 TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_DroppedFrame) {
     BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
     sp<IGraphicBufferProducer> igbProducer;
@@ -1008,53 +1071,4 @@
     ASSERT_GE(events->postedTime, postedTimeB);
 }
 
-TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_DroppedFrame_BufferQueue) {
-    sp<IGraphicBufferProducer> bqIgbp;
-    createBufferQueueProducer(&bqIgbp);
-
-    ProducerFrameEventHistory history;
-    IGraphicBufferProducer::QueueBufferOutput qbOutput;
-    nsecs_t requestedPresentTimeA = 0;
-    nsecs_t postedTimeA = 0;
-    nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
-    setUpAndQueueBuffer(bqIgbp, &requestedPresentTimeA, &postedTimeA, &qbOutput, true,
-                        presentTimeDelay);
-    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);
-
-    // queue another buffer so the first can be dropped
-    nsecs_t requestedPresentTimeB = 0;
-    nsecs_t postedTimeB = 0;
-    setUpAndQueueBuffer(bqIgbp, &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);
-
-    // a valid latchtime should not be set
-    ASSERT_FALSE(events->hasLatchInfo());
-    ASSERT_FALSE(events->hasDequeueReadyInfo());
-
-    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/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp
index 7dbba31..bcd39db 100644
--- a/libs/gui/tests/DisplayEventStructLayout_test.cpp
+++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp
@@ -34,7 +34,8 @@
     CHECK_OFFSET(DisplayEventReceiver::Event::VSync, count, 0);
     CHECK_OFFSET(DisplayEventReceiver::Event::VSync, expectedVSyncTimestamp, 8);
     CHECK_OFFSET(DisplayEventReceiver::Event::VSync, deadlineTimestamp, 16);
-    CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncId, 24);
+    CHECK_OFFSET(DisplayEventReceiver::Event::VSync, frameInterval, 24);
+    CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncId, 32);
 
     CHECK_OFFSET(DisplayEventReceiver::Event::Hotplug, connected, 0);
 
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 59e5c13..49c44a7 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -47,6 +47,8 @@
 
 using android::os::IInputFlinger;
 
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
 namespace android::test {
 
 using Transaction = SurfaceComposerClient::Transaction;
@@ -95,15 +97,6 @@
         return std::make_unique<InputSurface>(surfaceControl, width, height);
     }
 
-    static std::unique_ptr<InputSurface> makeBlastInputSurface(const sp<SurfaceComposerClient> &scc,
-                                                               int width, int height) {
-        sp<SurfaceControl> surfaceControl =
-                scc->createSurface(String8("Test Buffer Surface"), width, height,
-                                   PIXEL_FORMAT_RGBA_8888,
-                                   ISurfaceComposerClient::eFXSurfaceBufferState);
-        return std::make_unique<InputSurface>(surfaceControl, width, height);
-    }
-
     static std::unique_ptr<InputSurface> makeContainerInputSurface(
             const sp<SurfaceComposerClient> &scc, int width, int height) {
         sp<SurfaceControl> surfaceControl =
@@ -180,16 +173,19 @@
         EXPECT_EQ(flags, mev->getFlags() & flags);
     }
 
-    ~InputSurface() { mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken()); }
+    virtual ~InputSurface() {
+        mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken());
+    }
 
-    void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
-                    const sp<SurfaceControl>&)> transactionBody) {
+    virtual void doTransaction(
+            std::function<void(SurfaceComposerClient::Transaction &, const sp<SurfaceControl> &)>
+                    transactionBody) {
         SurfaceComposerClient::Transaction t;
         transactionBody(t, mSurfaceControl);
         t.apply(true);
     }
 
-    void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) {
+    virtual void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) {
         SurfaceComposerClient::Transaction t;
         t.show(mSurfaceControl);
         t.setInputWindowInfo(mSurfaceControl, mInputInfo);
@@ -259,6 +255,57 @@
     InputConsumer* mInputConsumer;
 };
 
+class BlastInputSurface : public InputSurface {
+public:
+    BlastInputSurface(const sp<SurfaceControl> &sc, const sp<SurfaceControl> &parentSc, int width,
+                      int height)
+          : InputSurface(sc, width, height) {
+        mParentSurfaceControl = parentSc;
+    }
+
+    ~BlastInputSurface() = default;
+
+    static std::unique_ptr<BlastInputSurface> makeBlastInputSurface(
+            const sp<SurfaceComposerClient> &scc, int width, int height) {
+        sp<SurfaceControl> parentSc =
+                scc->createSurface(String8("Test Parent Surface"), 0 /* bufHeight */,
+                                   0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceContainer);
+
+        sp<SurfaceControl> surfaceControl =
+                scc->createSurface(String8("Test Buffer Surface"), width, height,
+                                   PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceBufferState,
+                                   parentSc->getHandle());
+        return std::make_unique<BlastInputSurface>(surfaceControl, parentSc, width, height);
+    }
+
+    void doTransaction(
+            std::function<void(SurfaceComposerClient::Transaction &, const sp<SurfaceControl> &)>
+                    transactionBody) override {
+        SurfaceComposerClient::Transaction t;
+        transactionBody(t, mParentSurfaceControl);
+        t.apply(true);
+    }
+
+    void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) override {
+        SurfaceComposerClient::Transaction t;
+        t.show(mParentSurfaceControl);
+        t.setLayer(mParentSurfaceControl, LAYER_BASE);
+        t.setPosition(mParentSurfaceControl, x, y);
+        t.setCrop(mParentSurfaceControl, crop);
+
+        t.show(mSurfaceControl);
+        t.setInputWindowInfo(mSurfaceControl, mInputInfo);
+        t.setCrop(mSurfaceControl, crop);
+        t.setAlpha(mSurfaceControl, 1);
+        t.apply(true);
+    }
+
+private:
+    sp<SurfaceControl> mParentSurfaceControl;
+};
+
 class InputSurfacesTest : public ::testing::Test {
 public:
     InputSurfacesTest() {
@@ -289,15 +336,12 @@
         return InputSurface::makeColorInputSurface(mComposerClient, width, height);
     }
 
-    void postBuffer(const sp<SurfaceControl> &layer) {
-        // wait for previous transactions (such as setSize) to complete
-        Transaction().apply(true);
-        ANativeWindow_Buffer buffer = {};
-        EXPECT_EQ(NO_ERROR, layer->getSurface()->lock(&buffer, nullptr));
-        ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
-        // Request an empty transaction to get applied synchronously to ensure the buffer is
-        // latched.
-        Transaction().apply(true);
+    void postBuffer(const sp<SurfaceControl> &layer, int32_t w, int32_t h) {
+        int64_t usageFlags = BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE;
+        sp<GraphicBuffer> buffer =
+                new GraphicBuffer(w, h, PIXEL_FORMAT_RGBA_8888, 1, usageFlags, "test");
+        Transaction().setBuffer(layer, buffer).apply(true);
         usleep(mBufferPostDelay);
     }
 
@@ -474,8 +518,8 @@
 // Original bug ref: b/120839715
 TEST_F(InputSurfacesTest, input_ignores_buffer_layer_buffer) {
     std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
-    std::unique_ptr<InputSurface> bufferSurface =
-            InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
+    std::unique_ptr<BlastInputSurface> bufferSurface =
+            BlastInputSurface::makeBlastInputSurface(mComposerClient, 100, 100);
 
     bgSurface->showAt(10, 10);
     bufferSurface->showAt(10, 10);
@@ -483,16 +527,16 @@
     injectTap(11, 11);
     bufferSurface->expectTap(1, 1);
 
-    postBuffer(bufferSurface->mSurfaceControl);
+    postBuffer(bufferSurface->mSurfaceControl, 100, 100);
     injectTap(11, 11);
     bufferSurface->expectTap(1, 1);
 }
 
 TEST_F(InputSurfacesTest, input_ignores_buffer_layer_alpha) {
     std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
-    std::unique_ptr<InputSurface> bufferSurface =
-            InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
-    postBuffer(bufferSurface->mSurfaceControl);
+    std::unique_ptr<BlastInputSurface> bufferSurface =
+            BlastInputSurface::makeBlastInputSurface(mComposerClient, 100, 100);
+    postBuffer(bufferSurface->mSurfaceControl, 100, 100);
 
     bgSurface->showAt(10, 10);
     bufferSurface->showAt(10, 10);
@@ -720,8 +764,8 @@
 TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_blast) {
     std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
 
-    std::unique_ptr<InputSurface> bufferSurface =
-            InputSurface::makeBlastInputSurface(mComposerClient, 0, 0);
+    std::unique_ptr<BlastInputSurface> bufferSurface =
+            BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0);
     bufferSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
     bufferSurface->mInputInfo.ownerUid = 22222;
 
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 9da731a..ea8c295 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -102,14 +102,13 @@
         //   test flakiness.
         mSurfaceControl = mComposerClient->createSurface(
                 String8("Test Surface"), 32, 32, PIXEL_FORMAT_RGBA_8888, 0);
+        SurfaceComposerClient::Transaction().apply(true);
 
         ASSERT_TRUE(mSurfaceControl != nullptr);
         ASSERT_TRUE(mSurfaceControl->isValid());
 
         Transaction t;
-        ASSERT_EQ(NO_ERROR, t.setLayer(mSurfaceControl, 0x7fffffff)
-                .show(mSurfaceControl)
-                .apply());
+        ASSERT_EQ(NO_ERROR, t.setLayer(mSurfaceControl, 0x7fffffff).show(mSurfaceControl).apply());
 
         mSurface = mSurfaceControl->getSurface();
         ASSERT_TRUE(mSurface != nullptr);
@@ -776,6 +775,10 @@
                               const std::vector<ui::Hdr>& /*hdrTypes*/) override {
         return NO_ERROR;
     }
+    status_t onPullAtom(const int32_t /*atomId*/, std::string* /*outData*/,
+                        bool* /*success*/) override {
+        return NO_ERROR;
+    }
     status_t enableVSyncInjections(bool /*enable*/) override {
         return NO_ERROR;
     }
@@ -869,7 +872,7 @@
     }
 
     status_t setFrameRate(const sp<IGraphicBufferProducer>& /*surface*/, float /*frameRate*/,
-                          int8_t /*compatibility*/, bool /*shouldBeSeamless*/) override {
+                          int8_t /*compatibility*/, int8_t /*changeFrameRateStrategy*/) override {
         return NO_ERROR;
     }
 
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index ffcc1cd..31027b6 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -231,6 +231,13 @@
     mSensors.insert_or_assign(info.type, info);
 }
 
+void InputDeviceInfo::addBatteryInfo(const InputDeviceBatteryInfo& info) {
+    if (mBatteries.find(info.id) != mBatteries.end()) {
+        ALOGW("Battery id %d already exists, will be replaced by new battery added.", info.id);
+    }
+    mBatteries.insert_or_assign(info.id, info);
+}
+
 void InputDeviceInfo::addLightInfo(const InputDeviceLightInfo& info) {
     if (mLights.find(info.id) != mLights.end()) {
         ALOGW("Light id %d already exists, will be replaced by new light added.", info.id);
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index c2a3cf1..56a064b 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -110,6 +110,12 @@
                 return true;
             case Type::DRAG:
                 return true;
+            case Type::TIMELINE:
+                const nsecs_t gpuCompletedTime =
+                        body.timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
+                const nsecs_t presentTime =
+                        body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
+                return presentTime > gpuCompletedTime;
         }
     }
     return false;
@@ -129,6 +135,8 @@
             return sizeof(Header) + body.capture.size();
         case Type::DRAG:
             return sizeof(Header) + body.drag.size();
+        case Type::TIMELINE:
+            return sizeof(Header) + body.timeline.size();
     }
     return sizeof(Header);
 }
@@ -260,6 +268,11 @@
             msg->body.drag.isExiting = body.drag.isExiting;
             break;
         }
+        case InputMessage::Type::TIMELINE: {
+            msg->body.timeline.eventId = body.timeline.eventId;
+            msg->body.timeline.graphicsTimeline = body.timeline.graphicsTimeline;
+            break;
+        }
     }
 }
 
@@ -296,7 +309,8 @@
     int sockets[2];
     if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
         status_t result = -errno;
-        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d", name.c_str(), errno);
+        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%s(%d)", name.c_str(),
+              strerror(errno), errno);
         outServerChannel.reset();
         outClientChannel.reset();
         return result;
@@ -629,7 +643,7 @@
     return mChannel->sendMessage(&msg);
 }
 
-android::base::Result<InputPublisher::Finished> InputPublisher::receiveFinishedSignal() {
+android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() {
     if (DEBUG_TRANSPORT_ACTIONS) {
         ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__);
     }
@@ -639,16 +653,24 @@
     if (result) {
         return android::base::Error(result);
     }
-    if (msg.header.type != InputMessage::Type::FINISHED) {
-        ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer",
-              mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str());
-        return android::base::Error(UNKNOWN_ERROR);
+    if (msg.header.type == InputMessage::Type::FINISHED) {
+        return Finished{
+                .seq = msg.header.seq,
+                .handled = msg.body.finished.handled,
+                .consumeTime = msg.body.finished.consumeTime,
+        };
     }
-    return Finished{
-            .seq = msg.header.seq,
-            .handled = msg.body.finished.handled,
-            .consumeTime = msg.body.finished.consumeTime,
-    };
+
+    if (msg.header.type == InputMessage::Type::TIMELINE) {
+        return Timeline{
+                .inputEventId = msg.body.timeline.eventId,
+                .graphicsTimeline = msg.body.timeline.graphicsTimeline,
+        };
+    }
+
+    ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer",
+          mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str());
+    return android::base::Error(UNKNOWN_ERROR);
 }
 
 // --- InputConsumer ---
@@ -785,7 +807,8 @@
                 break;
             }
 
-            case InputMessage::Type::FINISHED: {
+            case InputMessage::Type::FINISHED:
+            case InputMessage::Type::TIMELINE: {
                 LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by "
                                  "InputConsumer!",
                                  NamedEnum::string(mMsg.header.type).c_str());
@@ -1193,6 +1216,24 @@
     return sendUnchainedFinishedSignal(seq, handled);
 }
 
+status_t InputConsumer::sendTimeline(int32_t inputEventId,
+                                     std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) {
+    if (DEBUG_TRANSPORT_ACTIONS) {
+        ALOGD("channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32
+              ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64,
+              mChannel->getName().c_str(), inputEventId,
+              graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME],
+              graphicsTimeline[GraphicsTimeline::PRESENT_TIME]);
+    }
+
+    InputMessage msg;
+    msg.header.type = InputMessage::Type::TIMELINE;
+    msg.header.seq = 0;
+    msg.body.timeline.eventId = inputEventId;
+    msg.body.timeline.graphicsTimeline = std::move(graphicsTimeline);
+    return mChannel->sendMessage(&msg);
+}
+
 nsecs_t InputConsumer::getConsumeTime(uint32_t seq) const {
     auto it = mConsumeTimes.find(seq);
     // Consume time will be missing if either 'finishInputEvent' is called twice, or if it was
@@ -1399,6 +1440,19 @@
                                                        toString(msg.body.drag.isExiting));
                     break;
                 }
+                case InputMessage::Type::TIMELINE: {
+                    const nsecs_t gpuCompletedTime =
+                            msg.body.timeline
+                                    .graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
+                    const nsecs_t presentTime =
+                            msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
+                    out += android::base::StringPrintf("inputEventId=%" PRId32
+                                                       ", gpuCompletedTime=%" PRId64
+                                                       ", presentTime=%" PRId64,
+                                                       msg.body.timeline.eventId, gpuCompletedTime,
+                                                       presentTime);
+                    break;
+                }
             }
             out += "\n";
         }
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index fc31715..088e00b 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -124,13 +124,15 @@
     ASSERT_EQ(OK, status)
             << "consumer sendFinishedSignal should return OK";
 
-    Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
-    ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, result->seq)
-            << "receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_TRUE(result->handled)
-            << "receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(result->consumeTime, publishTime)
+    Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+    ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+    ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+    const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+    ASSERT_EQ(seq, finish.seq)
+            << "receiveConsumerResponse should have returned the original sequence number";
+    ASSERT_TRUE(finish.handled)
+            << "receiveConsumerResponse should have set handled to consumer's reply";
+    ASSERT_GE(finish.consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
@@ -264,13 +266,15 @@
     ASSERT_EQ(OK, status)
             << "consumer sendFinishedSignal should return OK";
 
-    Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
-    ASSERT_TRUE(result.ok()) << "receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, result->seq)
-            << "receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_FALSE(result->handled)
-            << "receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(result->consumeTime, publishTime)
+    Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+    ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+    ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+    const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+    ASSERT_EQ(seq, finish.seq)
+            << "receiveConsumerResponse should have returned the original sequence number";
+    ASSERT_FALSE(finish.handled)
+            << "receiveConsumerResponse should have set handled to consumer's reply";
+    ASSERT_GE(finish.consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
@@ -304,14 +308,16 @@
     status = mConsumer->sendFinishedSignal(seq, true);
     ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
 
-    Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+    Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+    ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+    ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+    const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
 
-    ASSERT_TRUE(result.ok()) << "receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, result->seq)
-            << "receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_TRUE(result->handled)
-            << "receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(result->consumeTime, publishTime)
+    ASSERT_EQ(seq, finish.seq)
+            << "receiveConsumerResponse should have returned the original sequence number";
+    ASSERT_TRUE(finish.handled)
+            << "receiveConsumerResponse should have set handled to consumer's reply";
+    ASSERT_GE(finish.consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
@@ -343,13 +349,15 @@
     status = mConsumer->sendFinishedSignal(seq, true);
     ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
 
-    android::base::Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
-    ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, result->seq)
-            << "receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_TRUE(result->handled)
-            << "receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(result->consumeTime, publishTime)
+    Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+    ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+    ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+    const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+    ASSERT_EQ(seq, finish.seq)
+            << "receiveConsumerResponse should have returned the original sequence number";
+    ASSERT_TRUE(finish.handled)
+            << "receiveConsumerResponse should have set handled to consumer's reply";
+    ASSERT_GE(finish.consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
@@ -385,16 +393,34 @@
     status = mConsumer->sendFinishedSignal(seq, true);
     ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
 
-    android::base::Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
-    ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, result->seq)
-            << "publisher receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_TRUE(result->handled)
-            << "publisher receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(result->consumeTime, publishTime)
+    Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+    ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+    ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+    const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+    ASSERT_EQ(seq, finish.seq)
+            << "receiveConsumerResponse should have returned the original sequence number";
+    ASSERT_TRUE(finish.handled)
+            << "receiveConsumerResponse should have set handled to consumer's reply";
+    ASSERT_GE(finish.consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
+TEST_F(InputPublisherAndConsumerTest, SendTimeline) {
+    const int32_t inputEventId = 20;
+    std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+    graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 30;
+    graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 40;
+    status_t status = mConsumer->sendTimeline(inputEventId, graphicsTimeline);
+    ASSERT_EQ(OK, status);
+
+    Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+    ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+    ASSERT_TRUE(std::holds_alternative<InputPublisher::Timeline>(*result));
+    const InputPublisher::Timeline& timeline = std::get<InputPublisher::Timeline>(*result);
+    ASSERT_EQ(inputEventId, timeline.inputEventId);
+    ASSERT_EQ(graphicsTimeline, timeline.graphicsTimeline);
+}
+
 TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
 }
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 3d80b38..585779e 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -96,6 +96,10 @@
   CHECK_OFFSET(InputMessage::Body::Finished, handled, 0);
   CHECK_OFFSET(InputMessage::Body::Finished, empty, 1);
   CHECK_OFFSET(InputMessage::Body::Finished, consumeTime, 8);
+
+  CHECK_OFFSET(InputMessage::Body::Timeline, eventId, 0);
+  CHECK_OFFSET(InputMessage::Body::Timeline, empty, 4);
+  CHECK_OFFSET(InputMessage::Body::Timeline, graphicsTimeline, 8);
 }
 
 void TestHeaderSize() {
@@ -117,6 +121,9 @@
     static_assert(sizeof(InputMessage::Body::Focus) == 8);
     static_assert(sizeof(InputMessage::Body::Capture) == 8);
     static_assert(sizeof(InputMessage::Body::Drag) == 16);
+    // Timeline
+    static_assert(GraphicsTimeline::SIZE == 2);
+    static_assert(sizeof(InputMessage::Body::Timeline) == 24);
 }
 
 // --- VerifiedInputEvent ---
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index dc2dd29..0edb213 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -130,7 +130,7 @@
     virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
     int64_t getVsyncId() const;
     int64_t getFrameDeadline() const;
-
+    int64_t getFrameInterval() const;
 
 private:
     Choreographer(const Choreographer&) = delete;
@@ -418,6 +418,10 @@
     return mLastVsyncEventData.deadlineTimestamp;
 }
 
+int64_t Choreographer::getFrameInterval() const {
+    return mLastVsyncEventData.frameInterval;
+}
+
 } // namespace android
 using namespace android;
 
@@ -501,6 +505,10 @@
     return AChoreographer_to_Choreographer(choreographer)->getFrameDeadline();
 }
 
+int64_t AChoreographer_getFrameInterval(const AChoreographer* choreographer) {
+    return AChoreographer_to_Choreographer(choreographer)->getFrameInterval();
+}
+
 } // namespace android
 
 /* Glue for the NDK interface */
diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h
index 0f4fd45..7d25ce8 100644
--- a/libs/nativedisplay/include-private/private/android/choreographer.h
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -42,6 +42,11 @@
 // value.
 int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer);
 
+// Returns the current interval in ns between frames.
+// Client are expected to call this function from their frame callback function.
+// Calling this function from anywhere else will return an undefined value.
+int64_t AChoreographer_getFrameInterval(const AChoreographer* choreographer);
+
 // Trampoline functions allowing libandroid.so to define the NDK symbols without including
 // the entirety of libnativedisplay as a whole static lib. As libnativedisplay
 // maintains global state, libnativedisplay can never be directly statically
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index fda6a20..9ed4915 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -31,6 +31,7 @@
       android::AChoreographer_signalRefreshRateCallbacks*;
       android::AChoreographer_getVsyncId*;
       android::AChoreographer_getFrameDeadline*;
+      android::AChoreographer_getFrameInterval*;
       android::ADisplay_acquirePhysicalDisplays*;
       android::ADisplay_release*;
       android::ADisplay_getMaxSupportedFps*;
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index b406a9c..ada689a 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -159,8 +159,8 @@
 }
 
 int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) {
-    return ANativeWindow_setFrameRateWithSeamlessness(window, frameRate, compatibility,
-        /*shouldBeSeamless*/ true);
+    return ANativeWindow_setFrameRateWithChangeStrategy(window, frameRate, compatibility,
+        ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
 }
 
 void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) {
@@ -170,12 +170,12 @@
     window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS);
 }
 
-int32_t ANativeWindow_setFrameRateWithSeamlessness(ANativeWindow* window, float frameRate,
-        int8_t compatibility, bool shouldBeSeamless) {
+int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, float frameRate,
+        int8_t compatibility, int8_t changeFrameRateStrategy) {
     if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
         return -EINVAL;
     }
-    return native_window_set_frame_rate(window, frameRate, compatibility, shouldBeSeamless);
+    return native_window_set_frame_rate(window, frameRate, compatibility, changeFrameRateStrategy);
 }
 
 /**************************************************************************************************
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index 20a1f74..d93a84c 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -164,45 +164,56 @@
  * Buffer usage flags, specifying how the buffer will be accessed.
  */
 enum AHardwareBuffer_UsageFlags {
-    /// The buffer will never be locked for direct CPU reads using the
-    /// AHardwareBuffer_lock() function. Note that reading the buffer
-    /// using OpenGL or Vulkan functions or memory mappings is still
-    /// allowed.
+    /**
+     * The buffer will never be locked for direct CPU reads using the
+     * AHardwareBuffer_lock() function. Note that reading the buffer
+     * using OpenGL or Vulkan functions or memory mappings is still
+     * allowed.
+     */
     AHARDWAREBUFFER_USAGE_CPU_READ_NEVER        = 0UL,
-    /// The buffer will sometimes be locked for direct CPU reads using
-    /// the AHardwareBuffer_lock() function. Note that reading the
-    /// buffer using OpenGL or Vulkan functions or memory mappings
-    /// does not require the presence of this flag.
+    /**
+     * The buffer will sometimes be locked for direct CPU reads using
+     * the AHardwareBuffer_lock() function. Note that reading the
+     * buffer using OpenGL or Vulkan functions or memory mappings
+     * does not require the presence of this flag.
+     */
     AHARDWAREBUFFER_USAGE_CPU_READ_RARELY       = 2UL,
-    /// The buffer will often be locked for direct CPU reads using
-    /// the AHardwareBuffer_lock() function. Note that reading the
-    /// buffer using OpenGL or Vulkan functions or memory mappings
-    /// does not require the presence of this flag.
+    /**
+     * The buffer will often be locked for direct CPU reads using
+     * the AHardwareBuffer_lock() function. Note that reading the
+     * buffer using OpenGL or Vulkan functions or memory mappings
+     * does not require the presence of this flag.
+     */
     AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN        = 3UL,
-    /// CPU read value mask.
+
+    /** CPU read value mask. */
     AHARDWAREBUFFER_USAGE_CPU_READ_MASK         = 0xFUL,
-
-    /// The buffer will never be locked for direct CPU writes using the
-    /// AHardwareBuffer_lock() function. Note that writing the buffer
-    /// using OpenGL or Vulkan functions or memory mappings is still
-    /// allowed.
+    /**
+     * The buffer will never be locked for direct CPU writes using the
+     * AHardwareBuffer_lock() function. Note that writing the buffer
+     * using OpenGL or Vulkan functions or memory mappings is still
+     * allowed.
+     */
     AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER       = 0UL << 4,
-    /// The buffer will sometimes be locked for direct CPU writes using
-    /// the AHardwareBuffer_lock() function. Note that writing the
-    /// buffer using OpenGL or Vulkan functions or memory mappings
-    /// does not require the presence of this flag.
+    /**
+     * The buffer will sometimes be locked for direct CPU writes using
+     * the AHardwareBuffer_lock() function. Note that writing the
+     * buffer using OpenGL or Vulkan functions or memory mappings
+     * does not require the presence of this flag.
+     */
     AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY      = 2UL << 4,
-    /// The buffer will often be locked for direct CPU writes using
-    /// the AHardwareBuffer_lock() function. Note that writing the
-    /// buffer using OpenGL or Vulkan functions or memory mappings
-    /// does not require the presence of this flag.
+    /**
+     * The buffer will often be locked for direct CPU writes using
+     * the AHardwareBuffer_lock() function. Note that writing the
+     * buffer using OpenGL or Vulkan functions or memory mappings
+     * does not require the presence of this flag.
+     */
     AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN       = 3UL << 4,
-    /// CPU write value mask.
+    /** CPU write value mask. */
     AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK        = 0xFUL << 4,
-
-    /// The buffer will be read from by the GPU as a texture.
+    /** The buffer will be read from by the GPU as a texture. */
     AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE     = 1UL << 8,
-    /// The buffer will be written to by the GPU as a framebuffer attachment.
+    /** The buffer will be written to by the GPU as a framebuffer attachment.*/
     AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER       = 1UL << 9,
     /**
      * The buffer will be written to by the GPU as a framebuffer
@@ -237,7 +248,7 @@
      * buffers are expected to behave.
      */
     AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT     = 1UL << 14,
-    /// The buffer will be read by a hardware video encoder.
+    /** The buffer will be read by a hardware video encoder. */
     AHARDWAREBUFFER_USAGE_VIDEO_ENCODE          = 1UL << 16,
     /**
      * The buffer will be used for direct writes from sensors.
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 285f2fb..61b3f94 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -157,6 +157,7 @@
  * For all of these parameters, if 0 is supplied then the window's base
  * value will come back in force.
  *
+ * \param window pointer to an ANativeWindow object.
  * \param width width of the buffers in pixels.
  * \param height height of the buffers in pixels.
  * \param format one of the AHardwareBuffer_Format constants.
@@ -191,6 +192,7 @@
  *
  * Available since API level 26.
  *
+ * \param window pointer to an ANativeWindow object.
  * \param transform combination of {@link ANativeWindowTransform} flags
  * \return 0 for success, or -EINVAL if \p transform is invalid
  */
@@ -208,6 +210,7 @@
  *
  * Available since API level 28.
  *
+ * \param window pointer to an ANativeWindow object.
  * \param dataSpace data space of all buffers queued after this call.
  * \return 0 for success, -EINVAL if window is invalid or the dataspace is not
  * supported.
@@ -247,9 +250,10 @@
 };
 
 /**
- * Same as ANativeWindow_setFrameRateWithSeamlessness(window, frameRate, compatibility, true).
+ * Same as ANativeWindow_setFrameRateWithChangeStrategy(window, frameRate, compatibility,
+ * ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS).
  *
- * See ANativeWindow_setFrameRateWithSeamlessness().
+ * See ANativeWindow_setFrameRateWithChangeStrategy().
  *
  * Available since API level 30.
  */
@@ -267,6 +271,19 @@
  */
 void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) __INTRODUCED_IN(30);
 
+/** Change frame rate strategy value for ANativeWindow_setFrameRate. */
+enum ANativeWindow_ChangeFrameRateStrategy {
+    /**
+     * Change the frame rate only if the transition is going to be seamless.
+     */
+    ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS = 0,
+    /**
+     * Change the frame rate even if the transition is going to be non-seamless,
+     * i.e. with visual interruptions for the user.
+     */
+    ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS = 1
+} __INTRODUCED_IN(31);
+
 /**
  * Sets the intended frame rate for this window.
  *
@@ -292,21 +309,22 @@
  * 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.
  *
+ * \param window pointer to an ANativeWindow object.
+ *
  * \param compatibility The frame rate compatibility of this window. The
  * compatibility value may influence the system's choice of display refresh
  * rate. See the ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* values for more info.
  *
- * \param shouldBeSeamless Whether display refresh rate transitions should be seamless. A
- * seamless transition is one that doesn't have any visual interruptions, such as a black
- * screen for a second or two. True indicates that any frame rate changes caused by this
- * request should be seamless. False indicates that non-seamless refresh rates are also
- * acceptable.
+ * \param changeFrameRateStrategy Whether display refresh rate transitions should be seamless.
+ * A seamless transition is one that doesn't have any visual interruptions, such as a black
+ * screen for a second or two. See the ANATIVEWINDOW_CHANGE_FRAME_RATE_* values.
  *
  * \return 0 for success, -EINVAL if the window, frame rate, or compatibility
  * value are invalid.
  */
-int32_t ANativeWindow_setFrameRateWithSeamlessness(ANativeWindow* window, float frameRate,
-        int8_t compatibility, bool shouldBeSeamless) __INTRODUCED_IN(31);
+int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, float frameRate,
+        int8_t compatibility, int8_t changeFrameRateStrategy)
+        __INTRODUCED_IN(31);
 
 #ifdef __cplusplus
 };
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 7aa2cf4..cc82bb4 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1019,9 +1019,9 @@
 }
 
 static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate,
-                                        int8_t compatibility, bool shouldBeSeamless) {
+                                        int8_t compatibility, int8_t changeFrameRateStrategy) {
     return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate,
-                           (int)compatibility, (int)shouldBeSeamless);
+                           (int)compatibility, (int)changeFrameRateStrategy);
 }
 
 static inline int native_window_set_frame_timeline_info(struct ANativeWindow* window,
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 24d0e3b..988132c 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -47,7 +47,7 @@
     ANativeWindow_setBuffersTransform;
     ANativeWindow_setDequeueTimeout; # apex # introduced=30
     ANativeWindow_setFrameRate; # introduced=30
-    ANativeWindow_setFrameRateWithSeamlessness; # introduced=31
+    ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31
     ANativeWindow_setSharedBufferMode; # llndk
     ANativeWindow_setSwapInterval; # llndk
     ANativeWindow_setUsage; # llndk
diff --git a/libs/permission/Android.bp b/libs/permission/Android.bp
new file mode 100644
index 0000000..a5712b3
--- /dev/null
+++ b/libs/permission/Android.bp
@@ -0,0 +1,23 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_shared {
+    name: "libpermission",
+    srcs: [
+        "AppOpsManager.cpp",
+        "IAppOpsCallback.cpp",
+        "IAppOpsService.cpp",
+    ],
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "libbinder",
+        "liblog",
+        "libutils",
+    ],
+}
diff --git a/libs/binder/AppOpsManager.cpp b/libs/permission/AppOpsManager.cpp
similarity index 98%
rename from libs/binder/AppOpsManager.cpp
rename to libs/permission/AppOpsManager.cpp
index de42f36..baa9d75 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/permission/AppOpsManager.cpp
@@ -37,7 +37,7 @@
 
     pthread_mutex_lock(&gClientIdMutex);
     if (gClientId == nullptr) {
-        gClientId = new BBinder();
+        gClientId = sp<BBinder>::make();
     }
     pthread_mutex_unlock(&gClientIdMutex);
     return gClientId;
diff --git a/libs/binder/IAppOpsCallback.cpp b/libs/permission/IAppOpsCallback.cpp
similarity index 100%
rename from libs/binder/IAppOpsCallback.cpp
rename to libs/permission/IAppOpsCallback.cpp
diff --git a/libs/binder/IAppOpsService.cpp b/libs/permission/IAppOpsService.cpp
similarity index 97%
rename from libs/binder/IAppOpsService.cpp
rename to libs/permission/IAppOpsService.cpp
index 1897969..d59f445 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/permission/IAppOpsService.cpp
@@ -18,8 +18,8 @@
 
 #include <binder/IAppOpsService.h>
 
-#include <utils/Log.h>
 #include <binder/Parcel.h>
+#include <utils/Log.h>
 #include <utils/String8.h>
 
 #include <optional>
@@ -63,6 +63,9 @@
         remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
         // fail on exception
         if (reply.readExceptionCode() != 0) return MODE_ERRORED;
+        // TODO b/184855056: extract to class
+        reply.readInt32();
+        reply.readByte();
         return reply.readInt32();
     }
 
@@ -84,6 +87,9 @@
         remote()->transact(START_OPERATION_TRANSACTION, data, &reply);
         // fail on exception
         if (reply.readExceptionCode() != 0) return MODE_ERRORED;
+        // TODO b/184855056: extract to class
+        reply.readInt32();
+        reply.readByte();
         return reply.readInt32();
     }
 
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/permission/include/binder/AppOpsManager.h
similarity index 97%
rename from libs/binder/include/binder/AppOpsManager.h
rename to libs/permission/include/binder/AppOpsManager.h
index eac1bb2..c048cbe 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/permission/include/binder/AppOpsManager.h
@@ -143,7 +143,10 @@
         OP_COARSE_LOCATION_SOURCE = 109,
         OP_MANAGE_MEDIA = 110,
         OP_BLUETOOTH_CONNECT = 111,
-        _NUM_OP = 112
+        OP_UWB_RANGING = 112,
+        OP_ACTIVITY_RECOGNITION_SOURCE = 113,
+        OP_BLUETOOTH_ADVERTISE = 114,
+        _NUM_OP = 115
     };
 
     AppOpsManager();
diff --git a/libs/binder/include/binder/IAppOpsCallback.h b/libs/permission/include/binder/IAppOpsCallback.h
similarity index 100%
rename from libs/binder/include/binder/IAppOpsCallback.h
rename to libs/permission/include/binder/IAppOpsCallback.h
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/permission/include/binder/IAppOpsService.h
similarity index 100%
rename from libs/binder/include/binder/IAppOpsService.h
rename to libs/permission/include/binder/IAppOpsService.h
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 026b19a..f395ab4 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -48,6 +48,7 @@
     name: "librenderengine_sources",
     srcs: [
         "Description.cpp",
+        "ExternalTexture.cpp",
         "Mesh.cpp",
         "RenderEngine.cpp",
         "Texture.cpp",
@@ -91,6 +92,7 @@
         "skia/debug/CaptureTimer.cpp",
         "skia/debug/CommonPool.cpp",
         "skia/debug/SkiaCapture.cpp",
+        "skia/debug/SkiaMemoryReporter.cpp",
         "skia/filters/BlurFilter.cpp",
         "skia/filters/LinearEffect.cpp",
     ],
diff --git a/libs/renderengine/ExternalTexture.cpp b/libs/renderengine/ExternalTexture.cpp
new file mode 100644
index 0000000..eabff58
--- /dev/null
+++ b/libs/renderengine/ExternalTexture.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/ExternalTexture.h>
+#include <renderengine/RenderEngine.h>
+#include <ui/GraphicBuffer.h>
+
+#include "log/log_main.h"
+
+namespace android::renderengine {
+
+ExternalTexture::ExternalTexture(const sp<GraphicBuffer>& buffer, RenderEngine& renderEngine,
+                                 uint32_t usage)
+      : mBuffer(buffer), mRenderEngine(renderEngine) {
+    LOG_ALWAYS_FATAL_IF(buffer == nullptr,
+                        "Attempted to bind a null buffer to an external texture!");
+    // GLESRenderEngine has a separate texture cache for output buffers,
+    if (usage == Usage::WRITEABLE &&
+        (mRenderEngine.getRenderEngineType() == RenderEngine::RenderEngineType::GLES ||
+         mRenderEngine.getRenderEngineType() == RenderEngine::RenderEngineType::THREADED)) {
+        return;
+    }
+    mRenderEngine.mapExternalTextureBuffer(mBuffer, usage & Usage::WRITEABLE);
+}
+
+ExternalTexture::~ExternalTexture() {
+    mRenderEngine.unmapExternalTextureBuffer(mBuffer);
+}
+
+} // namespace android::renderengine
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index a2963a7..d87315f 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -746,7 +746,8 @@
     return;
 }
 
-void GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+void GLESRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
+                                                bool /*isRenderable*/) {
     ATRACE_CALL();
     mImageManager->cacheAsync(buffer, nullptr);
 }
@@ -797,8 +798,8 @@
     return NO_ERROR;
 }
 
-void GLESRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
-    mImageManager->releaseAsync(bufferId, nullptr);
+void GLESRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+    mImageManager->releaseAsync(buffer->getId(), nullptr);
 }
 
 std::shared_ptr<ImageManager::Barrier> GLESRenderEngine::unbindExternalTextureBufferForTesting(
@@ -1102,7 +1103,7 @@
 
 status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
                                       const std::vector<const LayerSettings*>& layers,
-                                      const sp<GraphicBuffer>& buffer,
+                                      const std::shared_ptr<ExternalTexture>& buffer,
                                       const bool useFramebufferCache, base::unique_fd&& bufferFence,
                                       base::unique_fd* drawFence) {
     ATRACE_CALL();
@@ -1125,7 +1126,7 @@
         return BAD_VALUE;
     }
 
-    validateOutputBufferUsage(buffer);
+    validateOutputBufferUsage(buffer->getBuffer());
 
     std::unique_ptr<BindNativeBufferAsFramebuffer> fbo;
     // Gathering layers that requested blur, we'll need them to decide when to render to an
@@ -1142,11 +1143,13 @@
 
     if (blurLayersSize == 0) {
         fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
-                                                              buffer.get()->getNativeBuffer(),
+                                                              buffer->getBuffer()
+                                                                      .get()
+                                                                      ->getNativeBuffer(),
                                                               useFramebufferCache);
         if (fbo->getStatus() != NO_ERROR) {
             ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
-                  buffer->handle);
+                  buffer->getBuffer()->handle);
             checkErrors();
             return fbo->getStatus();
         }
@@ -1157,7 +1160,7 @@
                 mBlurFilter->setAsDrawTarget(display, blurLayers.front()->backgroundBlurRadius);
         if (status != NO_ERROR) {
             ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
-                  buffer->handle);
+                  buffer->getBuffer()->handle);
             checkErrors();
             return status;
         }
@@ -1194,7 +1197,7 @@
             auto status = mBlurFilter->prepare();
             if (status != NO_ERROR) {
                 ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
-                      buffer->handle);
+                      buffer->getBuffer()->handle);
                 checkErrors("Can't render first blur pass");
                 return status;
             }
@@ -1203,6 +1206,7 @@
                 // Done blurring, time to bind the native FBO and render our blur onto it.
                 fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
                                                                       buffer.get()
+                                                                              ->getBuffer()
                                                                               ->getNativeBuffer(),
                                                                       useFramebufferCache);
                 status = fbo->getStatus();
@@ -1215,7 +1219,7 @@
             }
             if (status != NO_ERROR) {
                 ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
-                      buffer->handle);
+                      buffer->getBuffer()->handle);
                 checkErrors("Can't bind native framebuffer");
                 return status;
             }
@@ -1223,7 +1227,7 @@
             status = mBlurFilter->render(blurLayersSize > 1);
             if (status != NO_ERROR) {
                 ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
-                      buffer->handle);
+                      buffer->getBuffer()->handle);
                 checkErrors("Can't render blur filter");
                 return status;
             }
@@ -1250,7 +1254,7 @@
             disableTexture = false;
             isOpaque = layer->source.buffer.isOpaque;
 
-            sp<GraphicBuffer> gBuf = layer->source.buffer.buffer;
+            sp<GraphicBuffer> gBuf = layer->source.buffer.buffer->getBuffer();
             validateInputBufferUsage(gBuf);
             bindExternalTextureBuffer(layer->source.buffer.textureName, gBuf,
                                       layer->source.buffer.fence);
@@ -1274,7 +1278,7 @@
 
             // Do not cache protected EGLImage, protected memory is limited.
             if (gBuf->getUsage() & GRALLOC_USAGE_PROTECTED) {
-                unbindExternalTextureBuffer(gBuf->getId());
+                unmapExternalTextureBuffer(gBuf);
             }
         }
 
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 06a1722..e7ed9c0 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -60,19 +60,18 @@
     void primeCache() override;
     void genTextures(size_t count, uint32_t* names) override;
     void deleteTextures(size_t count, uint32_t const* names) override;
-    void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
-    void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex);
-
     bool isProtected() const override { return mInProtectedContext; }
     bool supportsProtectedContent() const override;
     bool useProtectedContext(bool useProtectedContext) override;
     status_t drawLayers(const DisplaySettings& display,
                         const std::vector<const LayerSettings*>& layers,
-                        const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
-                        base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
+                        const std::shared_ptr<ExternalTexture>& buffer,
+                        const bool useFramebufferCache, base::unique_fd&& bufferFence,
+                        base::unique_fd* drawFence) override;
     bool cleanupPostRender(CleanupMode mode) override;
     int getContextPriority() override;
     bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
+    void onPrimaryDisplaySizeChanged(ui::Size size) override {}
 
     EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
     // Creates an output image for rendering to
@@ -104,6 +103,9 @@
             EXCLUDES(mFramebufferImageCacheMutex);
     size_t getMaxTextureSize() const override;
     size_t getMaxViewportDims() const override;
+    void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable)
+            EXCLUDES(mRenderingMutex);
+    void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
 
 private:
     friend class BindNativeBufferAsFramebuffer;
diff --git a/libs/renderengine/include/renderengine/ExternalTexture.h b/libs/renderengine/include/renderengine/ExternalTexture.h
new file mode 100644
index 0000000..07f0833
--- /dev/null
+++ b/libs/renderengine/include/renderengine/ExternalTexture.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/macros.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android::renderengine {
+
+class RenderEngine;
+
+/**
+ * Manages GPU image resources on behalf of clients using RenderEngine.
+ *
+ * Clients of RenderEngine are required to wrap their GraphicBuffer objects as an ExternalTexture,
+ * which is then mapped into GPU resources required by RenderEngine. When a client no longer needs
+ * to use the GraphicBuffer as input into RenderEngine::drawLayers, then the client should delete
+ * their ExternalTexture so that resources may be freed.
+ */
+class ExternalTexture {
+public:
+    // Usage specifies the rendering intent for the buffer.
+    enum Usage : uint32_t {
+        // When a buffer is not READABLE but is WRITEABLE, then GLESRenderEngine will use that as a
+        // hint to load the buffer into a separate cache
+        READABLE = 1 << 0,
+
+        // The buffer needs to be mapped as a 2D texture if set, otherwise must be mapped as an
+        // external texture
+        WRITEABLE = 1 << 1,
+    };
+    // Creates an ExternalTexture for the provided buffer and RenderEngine instance, with the given
+    // usage hint of type Usage.
+    ExternalTexture(const sp<GraphicBuffer>& buffer, RenderEngine& renderEngine, uint32_t usage);
+
+    ~ExternalTexture();
+
+    // Retrieves the buffer that is bound to this texture.
+    const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+
+private:
+    sp<GraphicBuffer> mBuffer;
+    RenderEngine& mRenderEngine;
+    DISALLOW_COPY_AND_ASSIGN(ExternalTexture);
+};
+
+} // namespace android::renderengine
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 7661233..c54c5ba 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -16,11 +16,9 @@
 
 #pragma once
 
-#include <iosfwd>
-
 #include <math/mat4.h>
 #include <math/vec3.h>
-#include <renderengine/Texture.h>
+#include <renderengine/ExternalTexture.h>
 #include <ui/BlurRegion.h>
 #include <ui/Fence.h>
 #include <ui/FloatRect.h>
@@ -31,6 +29,8 @@
 #include <ui/StretchEffect.h>
 #include <ui/Transform.h>
 
+#include <iosfwd>
+
 namespace android {
 namespace renderengine {
 
@@ -39,7 +39,7 @@
     // Buffer containing the image that we will render.
     // If buffer == nullptr, then the rest of the fields in this struct will be
     // ignored.
-    sp<GraphicBuffer> buffer = nullptr;
+    std::shared_ptr<ExternalTexture> buffer = nullptr;
 
     // Fence that will fire when the buffer is ready to be bound.
     sp<Fence> fence = nullptr;
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 7c51f1b..ddaa7c7 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -17,19 +17,20 @@
 #ifndef SF_RENDERENGINE_H_
 #define SF_RENDERENGINE_H_
 
-#include <stdint.h>
-#include <sys/types.h>
-#include <memory>
-
 #include <android-base/unique_fd.h>
 #include <math/mat4.h>
 #include <renderengine/DisplaySettings.h>
+#include <renderengine/ExternalTexture.h>
 #include <renderengine/Framebuffer.h>
 #include <renderengine/Image.h>
 #include <renderengine/LayerSettings.h>
+#include <stdint.h>
+#include <sys/types.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Transform.h>
 
+#include <memory>
+
 /**
  * Allows to set RenderEngine backend to GLES (default) or SkiaGL (NOT yet supported).
  */
@@ -51,6 +52,7 @@
 
 namespace renderengine {
 
+class ExternalTexture;
 class Image;
 class Mesh;
 class Texture;
@@ -104,23 +106,6 @@
 
     virtual void genTextures(size_t count, uint32_t* names) = 0;
     virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
-    // Caches Image resources for this buffer, but does not bind the buffer to
-    // a particular texture.
-    // Note that work is deferred to an additional thread, i.e. this call
-    // is made asynchronously, but the caller can expect that cache/unbind calls
-    // are performed in a manner that's conflict serializable, i.e. unbinding
-    // a buffer should never occur before binding the buffer if the caller
-    // called {bind, cache}ExternalTextureBuffer before calling unbind.
-    virtual void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0;
-    // Removes internal resources referenced by the bufferId. This method should be
-    // invoked when the caller will no longer hold a reference to a GraphicBuffer
-    // and needs to clean up its resources.
-    // Note that work is deferred to an additional thread, i.e. this call
-    // is made asynchronously, but the caller can expect that cache/unbind calls
-    // are performed in a manner that's conflict serializable, i.e. unbinding
-    // a buffer should never occur before binding the buffer if the caller
-    // called {bind, cache}ExternalTextureBuffer before calling unbind.
-    virtual void unbindExternalTextureBuffer(uint64_t bufferId) = 0;
 
     enum class CleanupMode {
         CLEAN_OUTPUT_RESOURCES,
@@ -141,7 +126,7 @@
     // do any work.
     virtual bool cleanupPostRender(CleanupMode mode = CleanupMode::CLEAN_OUTPUT_RESOURCES) = 0;
 
-    // queries
+    // queries that are required to be thread safe
     virtual size_t getMaxTextureSize() const = 0;
     virtual size_t getMaxViewportDims() const = 0;
 
@@ -149,10 +134,17 @@
 
     // ----- BEGIN NEW INTERFACE -----
 
+    // queries that are required to be thread safe
     virtual bool isProtected() const = 0;
     virtual bool supportsProtectedContent() const = 0;
+
+    // Attempt to switch RenderEngine into and out of protectedContext mode
     virtual bool useProtectedContext(bool useProtectedContext) = 0;
 
+    // Notify RenderEngine of changes to the dimensions of the primary display
+    // so that it can configure its internal caches accordingly.
+    virtual void onPrimaryDisplaySizeChanged(ui::Size size) = 0;
+
     // Renders layers for a particular display via GPU composition. This method
     // should be called for every display that needs to be rendered via the GPU.
     // @param display The display-wide settings that should be applied prior to
@@ -184,8 +176,9 @@
     // now, this always returns NO_ERROR.
     virtual status_t drawLayers(const DisplaySettings& display,
                                 const std::vector<const LayerSettings*>& layers,
-                                const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
-                                base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0;
+                                const std::shared_ptr<ExternalTexture>& buffer,
+                                const bool useFramebufferCache, base::unique_fd&& bufferFence,
+                                base::unique_fd* drawFence) = 0;
     virtual void cleanFramebufferCache() = 0;
     // Returns the priority this context was actually created with. Note: this may not be
     // the same as specified at context creation time, due to implementation limits on the
@@ -193,7 +186,8 @@
     virtual int getContextPriority() = 0;
 
     // Returns true if blur was requested in the RenderEngineCreationArgs and the implementation
-    // also supports background blur.  If false, no blur will be applied when drawing layers.
+    // also supports background blur.  If false, no blur will be applied when drawing layers. This
+    // query is required to be thread safe.
     virtual bool supportsBackgroundBlur() = 0;
 
     // Returns the current type of RenderEngine instance that was created.
@@ -205,6 +199,31 @@
     static void validateOutputBufferUsage(const sp<GraphicBuffer>&);
 
 protected:
+    // Maps GPU resources for this buffer.
+    // Note that work may be deferred to an additional thread, i.e. this call
+    // is made asynchronously, but the caller can expect that map/unmap calls
+    // are performed in a manner that's conflict serializable, i.e. unmapping
+    // a buffer should never occur before binding the buffer if the caller
+    // called mapExternalTextureBuffer before calling unmap.
+    // Note also that if the buffer contains protected content, then mapping those GPU resources may
+    // be deferred until the buffer is really used for drawing. This is because typical SoCs that
+    // support protected memory only support a limited amount, so optimisitically mapping protected
+    // memory may be too burdensome. If a buffer contains protected content and the RenderEngine
+    // implementation supports protected context, then GPU resources may be mapped into both the
+    // protected and unprotected contexts.
+    // If the buffer may ever be written to by RenderEngine, then isRenderable must be true.
+    virtual void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) = 0;
+    // Unmaps GPU resources used by this buffer. This method should be
+    // invoked when the caller will no longer hold a reference to a GraphicBuffer
+    // and needs to clean up its resources.
+    // Note that if there are multiple callers holding onto the same buffer, then the buffer's
+    // resources may be internally ref-counted to guard against use-after-free errors. Note that
+    // work may be deferred to an additional thread, i.e. this call is expected to be made
+    // asynchronously, but the caller can expect that map/unmap calls are performed in a manner
+    // that's conflict serializable, i.e. unmap a buffer should never occur before binding the
+    // buffer if the caller called mapExternalTextureBuffer before calling unmap.
+    virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0;
+    friend class ExternalTexture;
     friend class threaded::RenderEngineThreaded;
     const RenderEngineType mRenderEngineType;
 };
@@ -289,7 +308,8 @@
     bool precacheToneMapperShaderOnly = false;
     bool supportsBackgroundBlur = false;
     RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM;
-    RenderEngine::RenderEngineType renderEngineType = RenderEngine::RenderEngineType::GLES;
+    RenderEngine::RenderEngineType renderEngineType =
+            RenderEngine::RenderEngineType::SKIA_GL_THREADED;
 };
 
 } // namespace renderengine
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 5f75b81..27dbd1e 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -39,8 +39,6 @@
     MOCK_METHOD1(dump, void(std::string&));
     MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
     MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
-    MOCK_METHOD1(cacheExternalTextureBuffer, void(const sp<GraphicBuffer>&));
-    MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t));
     MOCK_METHOD1(drawMesh, void(const renderengine::Mesh&));
     MOCK_CONST_METHOD0(getMaxTextureSize, size_t());
     MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
@@ -50,11 +48,17 @@
     MOCK_METHOD1(cleanupPostRender, bool(CleanupMode mode));
     MOCK_METHOD6(drawLayers,
                  status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
-                          const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
+                          const std::shared_ptr<ExternalTexture>&, const bool, base::unique_fd&&,
                           base::unique_fd*));
     MOCK_METHOD0(cleanFramebufferCache, void());
     MOCK_METHOD0(getContextPriority, int());
     MOCK_METHOD0(supportsBackgroundBlur, bool());
+    MOCK_METHOD1(onPrimaryDisplaySizeChanged, void(ui::Size));
+
+protected:
+    // mock renderengine still needs to implement these, but callers should never need to call them.
+    void mapExternalTextureBuffer(const sp<GraphicBuffer>&, bool) {}
+    void unmapExternalTextureBuffer(const sp<GraphicBuffer>&) {}
 };
 
 } // namespace mock
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index c535597..9ed759f 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -29,7 +29,8 @@
 namespace skia {
 
 AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer,
-                                       bool isRender) {
+                                       bool isOutputBuffer)
+      : mIsOutputBuffer(isOutputBuffer) {
     ATRACE_CALL();
     AHardwareBuffer_Desc desc;
     AHardwareBuffer_describe(buffer, &desc);
@@ -40,8 +41,12 @@
             GrAHardwareBufferUtils::MakeBackendTexture(context, buffer, desc.width, desc.height,
                                                        &mDeleteProc, &mUpdateProc, &mImageCtx,
                                                        createProtectedImage, backendFormat,
-                                                       isRender);
+                                                       isOutputBuffer);
     mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
+    ALOGE_IF(!mBackendTexture.isValid(),
+             "Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d isWriteable:%d "
+             "format:%d",
+             this, desc.width, desc.height, isOutputBuffer, createProtectedImage, desc.format);
 }
 
 void AutoBackendTexture::unref(bool releaseLocalResources) {
@@ -92,13 +97,16 @@
 
     mImage = image;
     mDataspace = dataspace;
-    LOG_ALWAYS_FATAL_IF(mImage == nullptr, "Unable to generate SkImage from buffer");
+    LOG_ALWAYS_FATAL_IF(mImage == nullptr,
+                        "Unable to generate SkImage. isTextureValid:%d dataspace:%d",
+                        mBackendTexture.isValid(), dataspace);
     return mImage;
 }
 
 sk_sp<SkSurface> AutoBackendTexture::getOrCreateSurface(ui::Dataspace dataspace,
                                                         GrDirectContext* context) {
     ATRACE_CALL();
+    LOG_ALWAYS_FATAL_IF(!mIsOutputBuffer, "You can't generate a SkSurface for a read-only texture");
     if (!mSurface.get() || mDataspace != dataspace) {
         sk_sp<SkSurface> surface =
                 SkSurface::MakeFromBackendTexture(context, mBackendTexture,
@@ -113,7 +121,9 @@
     }
 
     mDataspace = dataspace;
-    LOG_ALWAYS_FATAL_IF(mSurface == nullptr, "Unable to generate SkSurface");
+    LOG_ALWAYS_FATAL_IF(mSurface == nullptr,
+                        "Unable to generate SkSurface. isTextureValid:%d dataspace:%d",
+                        mBackendTexture.isValid(), dataspace);
     return mSurface;
 }
 
diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h
index bb75878..3133de6 100644
--- a/libs/renderengine/skia/AutoBackendTexture.h
+++ b/libs/renderengine/skia/AutoBackendTexture.h
@@ -21,9 +21,9 @@
 #include <SkImage.h>
 #include <SkSurface.h>
 #include <sys/types.h>
+#include <ui/GraphicTypes.h>
 
 #include "android-base/macros.h"
-#include "ui/GraphicTypes.h"
 
 namespace android {
 namespace renderengine {
@@ -41,26 +41,29 @@
     // of shared ownership with Skia objects, so we wrap it here instead.
     class LocalRef {
     public:
-        LocalRef() {}
-
-        ~LocalRef() {
-            // Destroying the texture is the same as setting it to null
-            setTexture(nullptr);
+        LocalRef(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer) {
+            mTexture = new AutoBackendTexture(context, buffer, isOutputBuffer);
+            mTexture->ref();
         }
 
-        // Sets the texture to locally ref-track.
-        void setTexture(AutoBackendTexture* texture) {
+        ~LocalRef() {
             if (mTexture != nullptr) {
                 mTexture->unref(true);
             }
-
-            mTexture = texture;
-            if (mTexture != nullptr) {
-                mTexture->ref();
-            }
         }
 
-        AutoBackendTexture* getTexture() const { return mTexture; }
+        // Makes a new SkImage from the texture content.
+        // As SkImages are immutable but buffer content is not, we create
+        // a new SkImage every time.
+        sk_sp<SkImage> makeImage(ui::Dataspace dataspace, SkAlphaType alphaType,
+                                 GrDirectContext* context) {
+            return mTexture->makeImage(dataspace, alphaType, context);
+        }
+
+        // Makes a new SkSurface from the texture content, if needed.
+        sk_sp<SkSurface> getOrCreateSurface(ui::Dataspace dataspace, GrDirectContext* context) {
+            return mTexture->getOrCreateSurface(dataspace, context);
+        }
 
         DISALLOW_COPY_AND_ASSIGN(LocalRef);
 
@@ -68,8 +71,12 @@
         AutoBackendTexture* mTexture = nullptr;
     };
 
+private:
     // Creates a GrBackendTexture whose contents come from the provided buffer.
-    AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, bool isRender);
+    AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer);
+
+    // The only way to invoke dtor is with unref, when mUsageCount is 0.
+    ~AutoBackendTexture() {}
 
     void ref() { mUsageCount++; }
 
@@ -86,10 +93,6 @@
     // Makes a new SkSurface from the texture content, if needed.
     sk_sp<SkSurface> getOrCreateSurface(ui::Dataspace dataspace, GrDirectContext* context);
 
-private:
-    // The only way to invoke dtor is with unref, when mUsageCount is 0.
-    ~AutoBackendTexture() {}
-
     GrBackendTexture mBackendTexture;
     GrAHardwareBufferUtils::DeleteImageProc mDeleteProc;
     GrAHardwareBufferUtils::UpdateImageProc mUpdateProc;
@@ -100,6 +103,7 @@
 
     int mUsageCount = 0;
 
+    const bool mIsOutputBuffer;
     sk_sp<SkImage> mImage = nullptr;
     sk_sp<SkSurface> mSurface = nullptr;
     ui::Dataspace mDataspace = ui::Dataspace::UNKNOWN;
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 69c6a09..1c2b2fc 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #include "Cache.h"
 #include "AutoBackendTexture.h"
 #include "SkiaRenderEngine.h"
@@ -28,11 +27,26 @@
 
 namespace android::renderengine::skia {
 
+namespace {
 // Warming shader cache, not framebuffer cache.
 constexpr bool kUseFrameBufferCache = false;
 
+// clang-format off
+// Any non-identity matrix will do.
+const auto kScaleAndTranslate = mat4(0.7f,   0.f, 0.f, 0.f,
+                                     0.f,  0.7f, 0.f, 0.f,
+                                     0.f,   0.f, 1.f, 0.f,
+                                   67.3f, 52.2f, 0.f, 1.f);
+// clang-format on
+// When choosing dataspaces below, whether the match the destination or not determined whether
+// a color correction effect is added to the shader. There may be other additional shader details
+// for particular color spaces.
+// TODO(b/184842383) figure out which color related shaders are necessary
+constexpr auto kDestDataSpace = ui::Dataspace::SRGB;
+} // namespace
+
 static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
-                             sp<GraphicBuffer> dstBuffer) {
+                             const std::shared_ptr<ExternalTexture>& dstTexture) {
     // Somewhat arbitrary dimensions, but on screen and slightly shorter, based
     // on actual use.
     FloatRect rect(0, 0, display.physicalDisplay.width(), display.physicalDisplay.height() - 30);
@@ -51,28 +65,35 @@
                             .lightRadius = 2200.0f,
                             .length = 0.955342f,
                     },
+            // important that this matches dest so the general shadow fragment shader doesn't
+            // have color correction added, and important that it be srgb, so the *vertex* shader
+            // doesn't have color correction added.
+            .sourceDataspace = kDestDataSpace,
     };
 
     auto layers = std::vector<const LayerSettings*>{&layer};
-    // The identity matrix will generate the fast shaders, and the second matrix
-    // (based on one seen while going from dialer to the home screen) will
-    // generate the slower (more general case) version. If we also need a
-    // slow version without color correction, we should use this matrix with
-    // display.outputDataspace set to SRGB.
-    bool identity = true;
-    for (const mat4 transform : { mat4(), mat4(0.728872f,   0.f,          0.f, 0.f,
-                                               0.f,         0.727627f,    0.f, 0.f,
-                                               0.f,         0.f,          1.f, 0.f,
-                                               167.355743f, 1852.257812f, 0.f, 1.f) }) {
-        layer.geometry.positionTransform = transform;
-        renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache,
+    // The identity matrix will generate the fast shader
+    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd(),
+                             nullptr);
+    // This matrix, which has different scales for x and y, will
+    // generate the slower (more general case) version, which has variants for translucent
+    // casters and rounded rects.
+    // clang-format off
+    layer.geometry.positionTransform = mat4(0.7f, 0.f,  0.f, 0.f,
+                                            0.f, 0.8f, 0.f, 0.f,
+                                            0.f, 0.f,  1.f, 0.f,
+                                            0.f, 0.f,  0.f, 1.f);
+    // clang-format on
+    for (auto translucent : {false, true}) {
+        layer.shadow.casterIsTranslucent = translucent;
+        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
                                  base::unique_fd(), nullptr);
-        identity = false;
     }
 }
 
 static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
-                            sp<GraphicBuffer> dstBuffer, sp<GraphicBuffer> srcBuffer) {
+                            const std::shared_ptr<ExternalTexture>& dstTexture,
+                            const std::shared_ptr<ExternalTexture>& srcTexture) {
     const Rect& displayRect = display.physicalDisplay;
     FloatRect rect(0, 0, displayRect.width(), displayRect.height());
     LayerSettings layer{
@@ -83,39 +104,31 @@
                     },
             .source = PixelSource{.buffer =
                                           Buffer{
-                                                  .buffer = srcBuffer,
+                                                  .buffer = srcTexture,
                                                   .maxMasteringLuminance = 1000.f,
                                                   .maxContentLuminance = 1000.f,
                                           }},
     };
 
-    // This matrix is based on actual data seen when opening the dialer.
-    //  translate and scale creates new shaders when combined with rounded corners
-    // clang-format off
-    auto scale_and_translate = mat4(.19f,    .0f,  .0f,  .0f,
-                                     .0f,   .19f,  .0f,  .0f,
-                                     .0f,    .0f,  1.f,  .0f,
-                                   169.f, 1527.f,  .0f,  1.f);
-    // clang-format on
+    auto threeCornerRadii = {0.0f, 0.05f, 50.f};
+    auto oneCornerRadius = {50.f};
 
     // Test both drawRect and drawRRect
     auto layers = std::vector<const LayerSettings*>{&layer};
-    for (auto transform : {mat4(), scale_and_translate}) {
-        layer.geometry.positionTransform = transform;
-        // fractional corner radius creates a shader that is used during home button swipe
-        for (float roundedCornersRadius : {0.0f, 0.05f, 500.f}) {
+    for (bool identity : {true, false}) {
+        layer.geometry.positionTransform = identity ? mat4() : kScaleAndTranslate;
+        // Corner radii less than 0.5 creates a special shader. This likely occurs in real usage
+        // due to animating corner radius.
+        // For the non-idenity matrix, only the large corner radius will create a new shader.
+        for (float roundedCornersRadius : identity ? threeCornerRadii : oneCornerRadius) {
             // roundedCornersCrop is always set, but it is this radius that triggers the behavior
             layer.geometry.roundedCornersRadius = roundedCornersRadius;
-            // No need to check UNKNOWN, which is treated as SRGB.
-            for (auto dataspace : {ui::Dataspace::SRGB, ui::Dataspace::DISPLAY_P3}) {
-                layer.sourceDataspace = dataspace;
-                for (bool isOpaque : {true, false}) {
-                    layer.source.buffer.isOpaque = isOpaque;
-                    for (auto alpha : {half(.23999f), half(1.0f)}) {
-                        layer.alpha = alpha;
-                        renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache,
-                                                 base::unique_fd(), nullptr);
-                    }
+            for (bool isOpaque : {true, false}) {
+                layer.source.buffer.isOpaque = isOpaque;
+                for (auto alpha : {half(.23999f), half(1.0f)}) {
+                    layer.alpha = alpha;
+                    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+                                             base::unique_fd(), nullptr);
                 }
             }
         }
@@ -123,7 +136,34 @@
 }
 
 static void drawSolidLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
-                            sp<GraphicBuffer> dstBuffer) {
+                            const std::shared_ptr<ExternalTexture>& dstTexture) {
+    const Rect& displayRect = display.physicalDisplay;
+    FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+    LayerSettings layer{
+            .geometry =
+                    Geometry{
+                            .boundaries = rect,
+                    },
+            .source =
+                    PixelSource{
+                            .solidColor = half3(0.1f, 0.2f, 0.3f),
+                    },
+            .alpha = 1,
+    };
+
+    auto layers = std::vector<const LayerSettings*>{&layer};
+    for (auto transform : {mat4(), kScaleAndTranslate}) {
+        layer.geometry.positionTransform = transform;
+        for (float roundedCornersRadius : {0.0f, 0.05f, 50.f}) {
+            layer.geometry.roundedCornersRadius = roundedCornersRadius;
+            renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+                                     base::unique_fd(), nullptr);
+        }
+    }
+}
+
+static void drawBlurLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+                           const std::shared_ptr<ExternalTexture>& dstTexture) {
     const Rect& displayRect = display.physicalDisplay;
     FloatRect rect(0, 0, displayRect.width(), displayRect.height());
     LayerSettings layer{
@@ -132,18 +172,34 @@
                             .boundaries = rect,
                     },
             .alpha = 1,
-            .source =
-                    PixelSource{
-                            .solidColor = half3(0.1f, 0.2f, 0.3f),
-                    },
     };
 
     auto layers = std::vector<const LayerSettings*>{&layer};
-    renderengine->drawLayers(display, layers, dstBuffer, kUseFrameBufferCache, base::unique_fd(),
-                             nullptr);
+    for (int radius : {9, 60}) {
+        layer.backgroundBlurRadius = radius;
+        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+                                 base::unique_fd(), nullptr);
+    }
 }
 
+//
+// The collection of shaders cached here were found by using perfetto to record shader compiles
+// during actions that involve RenderEngine, logging the layer settings, and the shader code
+// and reproducing those settings here.
+//
+// It is helpful when debugging this to turn on
+// in SkGLRenderEngine.cpp:
+//    kPrintLayerSettings = true
+//    kFlushAfterEveryLayer = true
+// in external/skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
+//    gPrintSKSL = true
+//
+// TODO(b/184631553) cache the shader involved in youtube pip return.
 void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
+    const int previousCount = renderengine->reportShadersCompiled();
+    if (previousCount) {
+        ALOGD("%d Shaders already compiled before Cache::primeShaderCache ran\n", previousCount);
+    }
     const nsecs_t timeBefore = systemTime();
     // The dimensions should not matter, so long as we draw inside them.
     const Rect displayRect(0, 0, 1080, 2340);
@@ -151,7 +207,7 @@
             .physicalDisplay = displayRect,
             .clip = displayRect,
             .maxLuminance = 500,
-            .outputDataspace = ui::Dataspace::DISPLAY_P3,
+            .outputDataspace = kDestDataSpace,
     };
 
     const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
@@ -159,6 +215,9 @@
     sp<GraphicBuffer> dstBuffer =
             new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
                               usage, "primeShaderCache_dst");
+
+    const auto dstTexture = std::make_shared<ExternalTexture>(dstBuffer, *renderengine,
+                                                              ExternalTexture::Usage::WRITEABLE);
     // This buffer will be the source for the call to drawImageLayers. Draw
     // something to it as a placeholder for what an app draws. We should draw
     // something, but the details are not important. Make use of the shadow layer drawing step
@@ -166,12 +225,35 @@
     sp<GraphicBuffer> srcBuffer =
             new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
                               usage, "drawImageLayer_src");
-    drawSolidLayers(renderengine, display, dstBuffer);
-    drawShadowLayers(renderengine, display, srcBuffer);
-    drawImageLayers(renderengine, display, dstBuffer, srcBuffer);
+
+    const auto srcTexture =
+            std::make_shared<ExternalTexture>(srcBuffer, *renderengine,
+                                              ExternalTexture::Usage::READABLE |
+                                                      ExternalTexture::Usage::WRITEABLE);
+
+    drawSolidLayers(renderengine, display, dstTexture);
+    drawShadowLayers(renderengine, display, srcTexture);
+    drawBlurLayers(renderengine, display, dstTexture);
+    // The majority of shaders are related to sampling images.
+    drawImageLayers(renderengine, display, dstTexture, srcTexture);
+
+    // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
+    const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE;
+
+    sp<GraphicBuffer> externalBuffer =
+            new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
+                              usageExternal, "primeShaderCache_external");
+    const auto externalTexture =
+            std::make_shared<ExternalTexture>(externalBuffer, *renderengine,
+                                              ExternalTexture::Usage::READABLE);
+    // TODO(b/184665179) doubles number of image shader compilations, but only somewhere
+    // between 6 and 8 will occur in real uses.
+    drawImageLayers(renderengine, display, dstTexture, externalTexture);
+
     const nsecs_t timeAfter = systemTime();
     const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
-    ALOGD("shader cache generated in %f ms\n", compileTimeMs);
+    const int shadersCompiled = renderengine->reportShadersCompiled();
+    ALOGD("Shader cache generated %d shaders in %f ms\n", shadersCompiled, compileTimeMs);
 }
 
 } // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index c5ee15d..0a84754 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -28,6 +28,7 @@
 #include <SkColorFilter.h>
 #include <SkColorMatrix.h>
 #include <SkColorSpace.h>
+#include <SkGraphics.h>
 #include <SkImage.h>
 #include <SkImageFilters.h>
 #include <SkRegion.h>
@@ -40,13 +41,13 @@
 #include <ui/DebugUtils.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/Trace.h>
-#include "Cache.h"
 
 #include <cmath>
 #include <cstdint>
 #include <memory>
 
 #include "../gl/GLExtensions.h"
+#include "Cache.h"
 #include "ColorSpaces.h"
 #include "SkBlendMode.h"
 #include "SkImageInfo.h"
@@ -54,8 +55,15 @@
 #include "filters/LinearEffect.h"
 #include "log/log_main.h"
 #include "skia/debug/SkiaCapture.h"
+#include "skia/debug/SkiaMemoryReporter.h"
 #include "system/graphics-base-v1.0.h"
 
+namespace {
+// Debugging settings
+static const bool kPrintLayerSettings = false;
+static const bool kFlushAfterEveryLayer = false;
+} // namespace
+
 bool checkGlError(const char* op, int lineNumber);
 
 namespace android {
@@ -285,6 +293,10 @@
                         numShaders, cached);
 }
 
+int SkiaGLRenderEngine::reportShadersCompiled() {
+    return mSkSLCacheMonitor.shadersCachedSinceLastCall();
+}
+
 SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
                                        EGLContext ctxt, EGLSurface placeholder,
                                        EGLContext protectedContext, EGLSurface protectedPlaceholder)
@@ -294,12 +306,13 @@
         mPlaceholderSurface(placeholder),
         mProtectedEGLContext(protectedContext),
         mProtectedPlaceholderSurface(protectedPlaceholder),
+        mDefaultPixelFormat(static_cast<PixelFormat>(args.pixelFormat)),
         mUseColorManagement(args.useColorManagement) {
     sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
     LOG_ALWAYS_FATAL_IF(!glInterface.get());
 
     GrContextOptions options;
-    options.fPreferExternalImagesOverES3 = true;
+    options.fDisableDriverCorrectnessWorkarounds = true;
     options.fDisableDistanceFieldPaths = true;
     options.fPersistentCache = &mSkSLCacheMonitor;
     mGrContext = GrDirectContext::MakeGL(glInterface, options);
@@ -316,8 +329,6 @@
 }
 
 SkiaGLRenderEngine::~SkiaGLRenderEngine() {
-    cleanFramebufferCache();
-
     std::lock_guard<std::mutex> lock(mRenderingMutex);
     if (mBlurFilter) {
         delete mBlurFilter;
@@ -471,7 +482,8 @@
             sourceTransfer != destTransfer;
 }
 
-void SkiaGLRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+void SkiaGLRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
+                                                  bool isRenderable) {
     // Only run this if RE is running on its own thread. This way the access to GL
     // operations is guaranteed to be happening on the same thread.
     if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) {
@@ -492,25 +504,41 @@
     auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
 
     std::lock_guard<std::mutex> lock(mRenderingMutex);
-    auto iter = cache.find(buffer->getId());
-    if (iter != cache.end()) {
-        ALOGV("Texture already exists in cache.");
-    } else {
+    mGraphicBufferExternalRefs[buffer->getId()]++;
+
+    if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) {
         std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
-                std::make_shared<AutoBackendTexture::LocalRef>();
-        imageTextureRef->setTexture(
-                new AutoBackendTexture(grContext.get(), buffer->toAHardwareBuffer(), false));
+                std::make_shared<AutoBackendTexture::LocalRef>(grContext.get(),
+                                                               buffer->toAHardwareBuffer(),
+                                                               isRenderable);
         cache.insert({buffer->getId(), imageTextureRef});
     }
     // restore the original state of the protected context if necessary
     useProtectedContext(protectedContextState);
 }
 
-void SkiaGLRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
+void SkiaGLRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
     ATRACE_CALL();
     std::lock_guard<std::mutex> lock(mRenderingMutex);
-    mTextureCache.erase(bufferId);
-    mProtectedTextureCache.erase(bufferId);
+    if (const auto& iter = mGraphicBufferExternalRefs.find(buffer->getId());
+        iter != mGraphicBufferExternalRefs.end()) {
+        if (iter->second == 0) {
+            ALOGW("Attempted to unmap GraphicBuffer <id: %" PRId64
+                  "> from RenderEngine texture, but the "
+                  "ref count was already zero!",
+                  buffer->getId());
+            mGraphicBufferExternalRefs.erase(buffer->getId());
+            return;
+        }
+
+        iter->second--;
+
+        if (iter->second == 0) {
+            mTextureCache.erase(buffer->getId());
+            mProtectedTextureCache.erase(buffer->getId());
+            mGraphicBufferExternalRefs.erase(buffer->getId());
+        }
+    }
 }
 
 sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> shader,
@@ -608,8 +636,8 @@
 
 status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
                                         const std::vector<const LayerSettings*>& layers,
-                                        const sp<GraphicBuffer>& buffer,
-                                        const bool useFramebufferCache,
+                                        const std::shared_ptr<ExternalTexture>& buffer,
+                                        const bool /*useFramebufferCache*/,
                                         base::unique_fd&& bufferFence, base::unique_fd* drawFence) {
     ATRACE_NAME("SkiaGL::drawLayers");
 
@@ -632,38 +660,26 @@
         return BAD_VALUE;
     }
 
-    validateOutputBufferUsage(buffer);
+    validateOutputBufferUsage(buffer->getBuffer());
 
     auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
     auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
-    AHardwareBuffer_Desc bufferDesc;
-    AHardwareBuffer_describe(buffer->toAHardwareBuffer(), &bufferDesc);
 
-    std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef = nullptr;
-    if (useFramebufferCache) {
-        auto iter = cache.find(buffer->getId());
-        if (iter != cache.end()) {
-            ALOGV("Cache hit!");
-            ATRACE_NAME("Cache hit");
-            surfaceTextureRef = iter->second;
-        }
-    }
-
-    if (surfaceTextureRef == nullptr || surfaceTextureRef->getTexture() == nullptr) {
-        ATRACE_NAME("Cache miss");
-        surfaceTextureRef = std::make_shared<AutoBackendTexture::LocalRef>();
-        surfaceTextureRef->setTexture(
-                new AutoBackendTexture(grContext.get(), buffer->toAHardwareBuffer(), true));
-        if (useFramebufferCache) {
-            ALOGD("Adding to cache");
-            cache.insert({buffer->getId(), surfaceTextureRef});
-        }
+    std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef;
+    if (const auto& it = cache.find(buffer->getBuffer()->getId()); it != cache.end()) {
+        surfaceTextureRef = it->second;
+    } else {
+        surfaceTextureRef =
+                std::make_shared<AutoBackendTexture::LocalRef>(grContext.get(),
+                                                               buffer->getBuffer()
+                                                                       ->toAHardwareBuffer(),
+                                                               true);
     }
 
     const ui::Dataspace dstDataspace =
             mUseColorManagement ? display.outputDataspace : ui::Dataspace::UNKNOWN;
     sk_sp<SkSurface> dstSurface =
-            surfaceTextureRef->getTexture()->getOrCreateSurface(dstDataspace, grContext.get());
+            surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext.get());
 
     SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
     if (dstCanvas == nullptr) {
@@ -734,6 +750,17 @@
     for (const auto& layer : layers) {
         ATRACE_NAME("DrawLayer");
 
+        if (kPrintLayerSettings) {
+            std::stringstream ls;
+            PrintTo(*layer, &ls);
+            auto debugs = ls.str();
+            int pos = 0;
+            while (pos < debugs.size()) {
+                ALOGD("cache_debug %s", debugs.substr(pos, 1000).c_str());
+                pos += 1000;
+            }
+        }
+
         sk_sp<SkImage> blurInput;
         if (blurCompositionLayer == layer) {
             LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface);
@@ -852,26 +879,29 @@
         SkPaint paint;
         if (layer->source.buffer.buffer) {
             ATRACE_NAME("DrawImage");
-            validateInputBufferUsage(layer->source.buffer.buffer);
+            validateInputBufferUsage(layer->source.buffer.buffer->getBuffer());
             const auto& item = layer->source.buffer;
             std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr;
-            auto iter = cache.find(item.buffer->getId());
-            if (iter != cache.end()) {
+
+            if (const auto& iter = cache.find(item.buffer->getBuffer()->getId());
+                iter != cache.end()) {
                 imageTextureRef = iter->second;
             } else {
-                imageTextureRef = std::make_shared<AutoBackendTexture::LocalRef>();
-                imageTextureRef->setTexture(new AutoBackendTexture(grContext.get(),
-                                                                   item.buffer->toAHardwareBuffer(),
-                                                                   false));
-                cache.insert({item.buffer->getId(), imageTextureRef});
+                // If we didn't find the image in the cache, then create a local ref but don't cache
+                // it. If we're using skia, we're guaranteed to run on a dedicated GPU thread so if
+                // we didn't find anything in the cache then we intentionally did not cache this
+                // buffer's resources.
+                imageTextureRef = std::make_shared<
+                        AutoBackendTexture::LocalRef>(grContext.get(),
+                                                      item.buffer->getBuffer()->toAHardwareBuffer(),
+                                                      false);
             }
 
             sk_sp<SkImage> image =
-                    imageTextureRef->getTexture()->makeImage(layerDataspace,
-                                                             item.usePremultipliedAlpha
-                                                                     ? kPremul_SkAlphaType
-                                                                     : kUnpremul_SkAlphaType,
-                                                             grContext.get());
+                    imageTextureRef->makeImage(layerDataspace,
+                                               item.usePremultipliedAlpha ? kPremul_SkAlphaType
+                                                                          : kUnpremul_SkAlphaType,
+                                               grContext.get());
 
             auto texMatrix = getSkM44(item.textureTransform).asM33();
             // textureTansform was intended to be passed directly into a shader, so when
@@ -955,6 +985,10 @@
         } else {
             canvas->drawRect(bounds, paint);
         }
+        if (kFlushAfterEveryLayer) {
+            ATRACE_NAME("flush surface");
+            activeSurface->flush();
+        }
     }
     surfaceAutoSaveRestore.restore();
     mCapture->endCapture();
@@ -1172,21 +1206,33 @@
     return eglCreatePbufferSurface(display, placeholderConfig, attributes.data());
 }
 
-void SkiaGLRenderEngine::cleanFramebufferCache() {
-    // TODO(b/180767535) Remove this method and use b/180767535 instead, which would allow
-    // SF to control texture lifecycle more tightly rather than through custom hooks into RE.
-    std::lock_guard<std::mutex> lock(mRenderingMutex);
-    mRuntimeEffects.clear();
-    mProtectedTextureCache.clear();
-    mTextureCache.clear();
-}
-
 int SkiaGLRenderEngine::getContextPriority() {
     int value;
     eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value);
     return value;
 }
 
+void SkiaGLRenderEngine::onPrimaryDisplaySizeChanged(ui::Size size) {
+    // This cache multiplier was selected based on review of cache sizes relative
+    // to the screen resolution. Looking at the worst case memory needed by blur (~1.5x),
+    // shadows (~1x), and general data structures (e.g. vertex buffers) we selected this as a
+    // conservative default based on that analysis.
+    const float SURFACE_SIZE_MULTIPLIER = 3.5f * bytesPerPixel(mDefaultPixelFormat);
+    const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER;
+
+    // start by resizing the current context
+    auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+    grContext->setResourceCacheLimit(maxResourceBytes);
+
+    // if it is possible to switch contexts then we will resize the other context
+    if (useProtectedContext(!mInProtectedContext)) {
+        grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+        grContext->setResourceCacheLimit(maxResourceBytes);
+        // reset back to the initial context that was active when this method was called
+        useProtectedContext(!mInProtectedContext);
+    }
+}
+
 void SkiaGLRenderEngine::dump(std::string& result) {
     const gl::GLExtensions& extensions = gl::GLExtensions::getInstance();
 
@@ -1202,9 +1248,44 @@
     StringAppendF(&result, "RenderEngine shaders cached since last dump/primeCache: %d\n",
                   mSkSLCacheMonitor.shadersCachedSinceLastCall());
 
+    std::vector<ResourcePair> cpuResourceMap = {
+            {"skia/sk_resource_cache/bitmap_", "Bitmaps"},
+            {"skia/sk_resource_cache/rrect-blur_", "Masks"},
+            {"skia/sk_resource_cache/rects-blur_", "Masks"},
+            {"skia/sk_resource_cache/tessellated", "Shadows"},
+            {"skia", "Other"},
+    };
+    SkiaMemoryReporter cpuReporter(cpuResourceMap, false);
+    SkGraphics::DumpMemoryStatistics(&cpuReporter);
+    StringAppendF(&result, "Skia CPU Caches: ");
+    cpuReporter.logTotals(result);
+    cpuReporter.logOutput(result);
+
     {
         std::lock_guard<std::mutex> lock(mRenderingMutex);
-        StringAppendF(&result, "RenderEngine texture cache size: %zu\n", mTextureCache.size());
+
+        std::vector<ResourcePair> gpuResourceMap = {
+                {"texture_renderbuffer", "Texture/RenderBuffer"},
+                {"texture", "Texture"},
+                {"gr_text_blob_cache", "Text"},
+                {"skia", "Other"},
+        };
+        SkiaMemoryReporter gpuReporter(gpuResourceMap, true);
+        mGrContext->dumpMemoryStatistics(&gpuReporter);
+        StringAppendF(&result, "Skia's GPU Caches: ");
+        gpuReporter.logTotals(result);
+        gpuReporter.logOutput(result);
+        StringAppendF(&result, "Skia's Wrapped Objects:\n");
+        gpuReporter.logOutput(result, true);
+
+        StringAppendF(&result, "RenderEngine tracked buffers: %zu\n",
+                      mGraphicBufferExternalRefs.size());
+        StringAppendF(&result, "Dumping buffer ids...\n");
+        for (const auto& [id, refCounts] : mGraphicBufferExternalRefs) {
+            StringAppendF(&result, "- 0x%" PRIx64 " - %d refs \n", id, refCounts);
+        }
+        StringAppendF(&result, "RenderEngine AHB/BackendTexture cache size: %zu\n",
+                      mTextureCache.size());
         StringAppendF(&result, "Dumping buffer ids...\n");
         // TODO(178539829): It would be nice to know which layer these are coming from and what
         // the texture sizes are.
@@ -1212,7 +1293,18 @@
             StringAppendF(&result, "- 0x%" PRIx64 "\n", id);
         }
         StringAppendF(&result, "\n");
-        StringAppendF(&result, "RenderEngine protected texture cache size: %zu\n",
+
+        SkiaMemoryReporter gpuProtectedReporter(gpuResourceMap, true);
+        if (mProtectedGrContext) {
+            mProtectedGrContext->dumpMemoryStatistics(&gpuProtectedReporter);
+        }
+        StringAppendF(&result, "Skia's GPU Protected Caches: ");
+        gpuProtectedReporter.logTotals(result);
+        gpuProtectedReporter.logOutput(result);
+        StringAppendF(&result, "Skia's Protected Wrapped Objects:\n");
+        gpuProtectedReporter.logOutput(result, true);
+
+        StringAppendF(&result, "RenderEngine protected AHB/BackendTexture cache size: %zu\n",
                       mProtectedTextureCache.size());
         StringAppendF(&result, "Dumping buffer ids...\n");
         for (const auto& [id, unused] : mProtectedTextureCache) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 7605df9..e71c560 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -23,6 +23,7 @@
 #include <GrDirectContext.h>
 #include <SkSurface.h>
 #include <android-base/thread_annotations.h>
+#include <renderengine/ExternalTexture.h>
 #include <renderengine/RenderEngine.h>
 #include <sys/types.h>
 
@@ -52,24 +53,27 @@
     ~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
 
     void primeCache() override;
-    void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
-    void unbindExternalTextureBuffer(uint64_t bufferId) override;
     status_t drawLayers(const DisplaySettings& display,
                         const std::vector<const LayerSettings*>& layers,
-                        const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
-                        base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
-    void cleanFramebufferCache() override;
+                        const std::shared_ptr<ExternalTexture>& buffer,
+                        const bool useFramebufferCache, base::unique_fd&& bufferFence,
+                        base::unique_fd* drawFence) override;
+    void cleanFramebufferCache() override {}
     int getContextPriority() override;
     bool isProtected() const override { return mInProtectedContext; }
     bool supportsProtectedContent() const override;
     bool useProtectedContext(bool useProtectedContext) override;
     bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
     void assertShadersCompiled(int numShaders) override;
+    void onPrimaryDisplaySizeChanged(ui::Size size) override;
+    int reportShadersCompiled() override;
 
 protected:
     void dump(std::string& result) override;
     size_t getMaxTextureSize() const override;
     size_t getMaxViewportDims() const override;
+    void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
+    void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
 
 private:
     static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
@@ -109,9 +113,12 @@
     EGLSurface mProtectedPlaceholderSurface;
     BlurFilter* mBlurFilter = nullptr;
 
+    const PixelFormat mDefaultPixelFormat;
     const bool mUseColorManagement;
 
-    // Cache of GL textures that we'll store per GraphicBuffer ID
+    // Number of external holders of ExternalTexture references, per GraphicBuffer ID.
+    std::unordered_map<uint64_t, int32_t> mGraphicBufferExternalRefs GUARDED_BY(mRenderingMutex);
+    // Cache of GL textures that we'll store per GraphicBuffer ID, sliced by GPU context.
     std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
             GUARDED_BY(mRenderingMutex);
     std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>>
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 59d7e2f..308c5ff 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -42,15 +42,12 @@
     virtual void primeCache() override{};
     virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override{};
     virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
-    virtual void cacheExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/){};
-    virtual void unbindExternalTextureBuffer(uint64_t /*bufferId*/){};
-
     virtual bool isProtected() const override { return false; } // mInProtectedContext; }
     virtual bool supportsProtectedContent() const override { return false; };
     virtual bool useProtectedContext(bool /*useProtectedContext*/) override { return false; };
     virtual status_t drawLayers(const DisplaySettings& /*display*/,
                                 const std::vector<const LayerSettings*>& /*layers*/,
-                                const sp<GraphicBuffer>& /*buffer*/,
+                                const std::shared_ptr<ExternalTexture>& /*buffer*/,
                                 const bool /*useFramebufferCache*/,
                                 base::unique_fd&& /*bufferFence*/,
                                 base::unique_fd* /*drawFence*/) override {
@@ -59,6 +56,12 @@
     virtual bool cleanupPostRender(CleanupMode) override { return true; };
     virtual int getContextPriority() override { return 0; }
     virtual void assertShadersCompiled(int numShaders) {}
+    virtual int reportShadersCompiled() { return 0; }
+
+protected:
+    virtual void mapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/,
+                                          bool /*isRenderable*/) override;
+    virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/) override;
 };
 
 } // namespace skia
diff --git a/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp b/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
new file mode 100644
index 0000000..f24a4f1
--- /dev/null
+++ b/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "RenderEngine"
+
+#include "SkiaMemoryReporter.h"
+
+#include <SkString.h>
+#include <android-base/stringprintf.h>
+#include <log/log_main.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+using base::StringAppendF;
+
+SkiaMemoryReporter::SkiaMemoryReporter(const std::vector<ResourcePair>& resourceMap, bool itemize)
+      : mResourceMap(resourceMap),
+        mItemize(itemize),
+        mTotalSize("bytes", 0),
+        mPurgeableSize("bytes", 0) {}
+
+const char* SkiaMemoryReporter::mapName(const char* resourceName) {
+    for (auto& resource : mResourceMap) {
+        if (SkStrContains(resourceName, resource.first)) {
+            return resource.second;
+        }
+    }
+    return nullptr;
+}
+
+void SkiaMemoryReporter::resetCurrentElement() {
+    mCurrentElement.clear();
+    mCurrentValues.clear();
+    mIsCurrentValueWrapped = false;
+}
+
+void SkiaMemoryReporter::processCurrentElement() {
+    // compute the top level element name using the map
+    const char* resourceName = mCurrentElement.empty() ? nullptr : mapName(mCurrentElement.c_str());
+
+    // if we don't have a resource name then we don't know how to label the
+    // data and should abort.
+    if (resourceName == nullptr) {
+        resetCurrentElement();
+        return;
+    }
+
+    // Only count elements that contain "size"; other values just provide metadata.
+    auto sizeResult = mCurrentValues.find("size");
+    if (sizeResult != mCurrentValues.end() && sizeResult->second.value > 0) {
+        if (!mIsCurrentValueWrapped) {
+            mTotalSize.value += sizeResult->second.value;
+            mTotalSize.count++;
+        }
+    } else {
+        resetCurrentElement();
+        return;
+    }
+
+    // find the purgeable size if one exists
+    auto purgeableResult = mCurrentValues.find("purgeable_size");
+    if (!mIsCurrentValueWrapped && purgeableResult != mCurrentValues.end()) {
+        mPurgeableSize.value += purgeableResult->second.value;
+        mPurgeableSize.count++;
+    }
+
+    // do we store this element in the wrapped list or the skia managed list
+    auto& results = mIsCurrentValueWrapped ? mWrappedResults : mResults;
+
+    // insert a copy of the element and all of its keys. We must make a copy here instead of
+    // std::move() as we will continue to use these values later in the function and again
+    // when we move on to process the next element.
+    results.insert({mCurrentElement, mCurrentValues});
+
+    // insert the item into its mapped category
+    auto result = results.find(resourceName);
+    if (result != results.end()) {
+        auto& resourceValues = result->second;
+        auto totalResult = resourceValues.find(sizeResult->first);
+        if (totalResult != resourceValues.end()) {
+            ALOGE_IF(sizeResult->second.units != totalResult->second.units,
+                     "resource units do not match so the sum of resource type (%s) will be invalid",
+                     resourceName);
+            totalResult->second.value += sizeResult->second.value;
+            totalResult->second.count++;
+        } else {
+            ALOGE("an entry (%s) should not exist in the results without a size", resourceName);
+        }
+    } else {
+        // only store the size for the top level resource
+        results.insert({resourceName, {{sizeResult->first, sizeResult->second}}});
+    }
+
+    resetCurrentElement();
+}
+
+void SkiaMemoryReporter::dumpNumericValue(const char* dumpName, const char* valueName,
+                                          const char* units, uint64_t value) {
+    if (mCurrentElement != dumpName) {
+        processCurrentElement();
+        mCurrentElement = dumpName;
+    }
+    mCurrentValues.insert({valueName, {units, value}});
+}
+
+void SkiaMemoryReporter::dumpWrappedState(const char* dumpName, bool isWrappedObject) {
+    if (mCurrentElement != dumpName) {
+        processCurrentElement();
+        mCurrentElement = dumpName;
+    }
+    mIsCurrentValueWrapped = isWrappedObject;
+}
+
+void SkiaMemoryReporter::logOutput(std::string& log, bool wrappedResources) {
+    // process the current element before logging
+    processCurrentElement();
+
+    const auto& resultsMap = wrappedResources ? mWrappedResults : mResults;
+
+    // log each individual element based on the resource map
+    for (const auto& resourceCategory : mResourceMap) {
+        // find the named item and print the totals
+        const auto categoryItem = resultsMap.find(resourceCategory.second);
+        if (categoryItem != resultsMap.end()) {
+            auto result = categoryItem->second.find("size");
+            if (result != categoryItem->second.end()) {
+                TraceValue traceValue = convertUnits(result->second);
+                const char* entry = (traceValue.count > 1) ? "entries" : "entry";
+                StringAppendF(&log, "  %s: %.2f %s (%d %s)\n", categoryItem->first.c_str(),
+                              traceValue.value, traceValue.units, traceValue.count, entry);
+            }
+            if (mItemize) {
+                for (const auto& individualItem : resultsMap) {
+                    // if the individual item matches the category then print all its details or
+                    // in the case of wrapped resources just print the wrapped size
+                    const char* categoryMatch = mapName(individualItem.first.c_str());
+                    if (categoryMatch && strcmp(categoryMatch, resourceCategory.second) == 0) {
+                        auto result = individualItem.second.find("size");
+                        TraceValue size = convertUnits(result->second);
+                        StringAppendF(&log, "    %s: size[%.2f %s]", individualItem.first.c_str(),
+                                      size.value, size.units);
+                        if (!wrappedResources) {
+                            for (const auto& itemValues : individualItem.second) {
+                                if (strcmp("size", itemValues.first) == 0) {
+                                    continue;
+                                }
+                                TraceValue traceValue = convertUnits(itemValues.second);
+                                if (traceValue.value == 0.0f) {
+                                    StringAppendF(&log, " %s[%s]", itemValues.first,
+                                                  traceValue.units);
+                                } else {
+                                    StringAppendF(&log, " %s[%.2f %s]", itemValues.first,
+                                                  traceValue.value, traceValue.units);
+                                }
+                            }
+                        }
+                        StringAppendF(&log, "\n");
+                    }
+                }
+            }
+        }
+    }
+}
+
+void SkiaMemoryReporter::logTotals(std::string& log) {
+    // process the current element before logging
+    processCurrentElement();
+
+    TraceValue total = convertUnits(mTotalSize);
+    TraceValue purgeable = convertUnits(mPurgeableSize);
+    StringAppendF(&log, " %.0f bytes, %.2f %s (%.2f %s is purgeable)\n", mTotalSize.value,
+                  total.value, total.units, purgeable.value, purgeable.units);
+}
+
+SkiaMemoryReporter::TraceValue SkiaMemoryReporter::convertUnits(const TraceValue& value) {
+    TraceValue output(value);
+    if (SkString("bytes") == SkString(output.units) && output.value >= 1024) {
+        output.value = output.value / 1024.0f;
+        output.units = "KB";
+    }
+    if (SkString("KB") == SkString(output.units) && output.value >= 1024) {
+        output.value = output.value / 1024.0f;
+        output.units = "MB";
+    }
+    return output;
+}
+
+} /* namespace skia */
+} /* namespace renderengine */
+} /* namespace android */
diff --git a/libs/renderengine/skia/debug/SkiaMemoryReporter.h b/libs/renderengine/skia/debug/SkiaMemoryReporter.h
new file mode 100644
index 0000000..dbbd65b
--- /dev/null
+++ b/libs/renderengine/skia/debug/SkiaMemoryReporter.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <SkTraceMemoryDump.h>
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+// Mapping of resource substrings (1st element) that if found within a trace "dumpName"
+// should be mapped to the category name (2nd element). All char* used in a resourcePair
+// are expected to have a lifetime longer than the SkiaMemoryReporter in which they are used.
+typedef std::pair<const char*, const char*> ResourcePair;
+
+/*
+ * Utility class for logging the CPU/GPU usage of Skia caches in a format that is specific
+ * to RenderEngine.  HWUI has a similar logging class, but the data collected and the way
+ * it is formatted and reported on are intended to be unique to each use case.
+ */
+class SkiaMemoryReporter : public SkTraceMemoryDump {
+public:
+    /**
+     * Creates the reporter class that can be populated by various Skia entry points, like
+     * SkGraphics and GrContext, as well as format and log the results.
+     * @param resourceMap An array of values that maps a Skia dumpName into a user defined category.
+     *                    The first vector entry that matches the dumpName is used for the mapping.
+     * @param itemize if true when logging the categories the individual elements will be printed
+     *                directly after the category details are printed.  Otherwise, only the category
+     *                totals will be printed.
+     */
+    SkiaMemoryReporter(const std::vector<ResourcePair>& resourceMap, bool itemize);
+    ~SkiaMemoryReporter() override {}
+
+    void logOutput(std::string& log, bool wrappedResources = false);
+    void logTotals(std::string& log);
+
+    void dumpNumericValue(const char* dumpName, const char* valueName, const char* units,
+                          uint64_t value) override;
+
+    void dumpStringValue(const char* dumpName, const char* valueName, const char* value) override {
+        // for convenience we just store this in the same format as numerical values
+        dumpNumericValue(dumpName, valueName, value, 0);
+    }
+    void dumpWrappedState(const char* dumpName, bool isWrappedObject) override;
+
+    LevelOfDetail getRequestedDetails() const override {
+        return SkTraceMemoryDump::kLight_LevelOfDetail;
+    }
+
+    bool shouldDumpWrappedObjects() const override { return true; }
+    void setMemoryBacking(const char*, const char*, const char*) override {}
+    void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override {}
+
+private:
+    struct TraceValue {
+        TraceValue(const char* units, uint64_t value) : units(units), value(value), count(1) {}
+        TraceValue(const TraceValue& v) : units(v.units), value(v.value), count(v.count) {}
+
+        const char* units;
+        float value;
+        int count;
+    };
+
+    const char* mapName(const char* resourceName);
+    void processCurrentElement();
+    void resetCurrentElement();
+    TraceValue convertUnits(const TraceValue& value);
+
+    const std::vector<ResourcePair>& mResourceMap;
+    const bool mItemize;
+
+    // variables storing the size of all non-wrapped elements being dumped
+    TraceValue mTotalSize;
+    TraceValue mPurgeableSize;
+
+    // variables storing information on the current node being dumped
+    std::string mCurrentElement;
+    std::unordered_map<const char*, TraceValue> mCurrentValues;
+    bool mIsCurrentValueWrapped = false;
+
+    // variable that stores the final format of the data after the individual elements are processed
+    std::unordered_map<std::string, std::unordered_map<const char*, TraceValue>> mResults;
+    std::unordered_map<std::string, std::unordered_map<const char*, TraceValue>> mWrappedResults;
+};
+
+} /* namespace skia */
+} /* namespace renderengine */
+} /* namespace android */
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index ec710d9..6dd4161 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -34,7 +34,7 @@
 
 BlurFilter::BlurFilter() {
     SkString blurString(R"(
-        in shader input;
+        uniform shader input;
         uniform float2 in_blurOffset;
         uniform float2 in_maxSizeXY;
 
@@ -60,8 +60,8 @@
     mBlurEffect = std::move(blurEffect);
 
     SkString mixString(R"(
-        in shader blurredInput;
-        in shader originalInput;
+        uniform shader blurredInput;
+        uniform shader originalInput;
         uniform float mixFactor;
 
         half4 main(float2 xy) {
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index 84af016..8e8e42e 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -390,7 +390,7 @@
 
 static void generateEffectiveOOTF(bool undoPremultipliedAlpha, SkString& shader) {
     shader.append(R"(
-        in shader input;
+        uniform shader input;
         half4 main(float2 xy) {
             float4 c = float4(sample(input, xy));
     )");
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 7846156..34ef0a4 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -24,6 +24,7 @@
 
 #include <cutils/properties.h>
 #include <gtest/gtest.h>
+#include <renderengine/ExternalTexture.h>
 #include <renderengine/RenderEngine.h>
 #include <sync/sync.h>
 #include <ui/PixelFormat.h>
@@ -160,27 +161,42 @@
 
 class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> {
 public:
-    static sp<GraphicBuffer> allocateDefaultBuffer() {
-        return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
-                                 HAL_PIXEL_FORMAT_RGBA_8888, 1,
-                                 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
-                                         GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE,
-                                 "output");
+    std::shared_ptr<renderengine::ExternalTexture> allocateDefaultBuffer() {
+        return std::make_shared<
+                renderengine::
+                        ExternalTexture>(new GraphicBuffer(DEFAULT_DISPLAY_WIDTH,
+                                                           DEFAULT_DISPLAY_HEIGHT,
+                                                           HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                                           GRALLOC_USAGE_SW_READ_OFTEN |
+                                                                   GRALLOC_USAGE_SW_WRITE_OFTEN |
+                                                                   GRALLOC_USAGE_HW_RENDER |
+                                                                   GRALLOC_USAGE_HW_TEXTURE,
+                                                           "output"),
+                                         *mRE,
+                                         renderengine::ExternalTexture::Usage::READABLE |
+                                                 renderengine::ExternalTexture::Usage::WRITEABLE);
     }
 
     // Allocates a 1x1 buffer to fill with a solid color
-    static sp<GraphicBuffer> allocateSourceBuffer(uint32_t width, uint32_t height) {
-        return new GraphicBuffer(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1,
-                                 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
-                                         GRALLOC_USAGE_HW_TEXTURE,
-                                 "input");
+    std::shared_ptr<renderengine::ExternalTexture> allocateSourceBuffer(uint32_t width,
+                                                                        uint32_t height) {
+        return std::make_shared<
+                renderengine::
+                        ExternalTexture>(new GraphicBuffer(width, height,
+                                                           HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                                           GRALLOC_USAGE_SW_READ_OFTEN |
+                                                                   GRALLOC_USAGE_SW_WRITE_OFTEN |
+                                                                   GRALLOC_USAGE_HW_TEXTURE,
+                                                           "input"),
+                                         *mRE,
+                                         renderengine::ExternalTexture::Usage::READABLE |
+                                                 renderengine::ExternalTexture::Usage::WRITEABLE);
     }
 
     RenderEngineTest() {
         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());
-        mBuffer = allocateDefaultBuffer();
     }
 
     ~RenderEngineTest() {
@@ -211,20 +227,21 @@
         }
 
         uint8_t* pixels;
-        mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
-                      reinterpret_cast<void**>(&pixels));
+        mBuffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                   reinterpret_cast<void**>(&pixels));
 
         file << "P6\n";
-        file << mBuffer->getWidth() << "\n";
-        file << mBuffer->getHeight() << "\n";
+        file << mBuffer->getBuffer()->getWidth() << "\n";
+        file << mBuffer->getBuffer()->getHeight() << "\n";
         file << 255 << "\n";
 
-        std::vector<uint8_t> outBuffer(mBuffer->getWidth() * mBuffer->getHeight() * 3);
+        std::vector<uint8_t> outBuffer(mBuffer->getBuffer()->getWidth() *
+                                       mBuffer->getBuffer()->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++) {
+        for (int32_t j = 0; j < mBuffer->getBuffer()->getHeight(); j++) {
+            const uint8_t* src = pixels + (mBuffer->getBuffer()->getStride() * j) * 4;
+            for (int32_t i = 0; i < mBuffer->getBuffer()->getWidth(); i++) {
                 // Only copy R, G and B components
                 outPtr[0] = src[0];
                 outPtr[1] = src[1];
@@ -235,7 +252,7 @@
             }
         }
         file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
-        mBuffer->unlock();
+        mBuffer->getBuffer()->unlock();
     }
 
     void expectBufferColor(const Region& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
@@ -262,13 +279,13 @@
     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));
+        mBuffer->getBuffer()->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++) {
-            const uint8_t* src =
-                    pixels + (mBuffer->getStride() * (region.top + j) + region.left) * 4;
+            const uint8_t* src = pixels +
+                    (mBuffer->getBuffer()->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 = colorCompare(src, expected);
@@ -289,7 +306,7 @@
                 break;
             }
         }
-        mBuffer->unlock();
+        mBuffer->getBuffer()->unlock();
     }
 
     void expectAlpha(const Rect& rect, uint8_t a) {
@@ -387,7 +404,6 @@
         base::unique_fd fence;
         status_t status =
                 mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
-        mCurrentBuffer = mBuffer;
 
         int fd = fence.release();
         if (fd >= 0) {
@@ -397,7 +413,7 @@
 
         ASSERT_EQ(NO_ERROR, status);
         if (layers.size() > 0 && mGLESRE != nullptr) {
-            ASSERT_TRUE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getId()));
+            ASSERT_TRUE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId()));
         }
     }
 
@@ -503,17 +519,11 @@
     void initializeRenderEngine();
 
     std::unique_ptr<renderengine::RenderEngine> mRE;
+    std::shared_ptr<renderengine::ExternalTexture> mBuffer;
     // GLESRenderEngine for testing GLES-specific behavior.
     // Owened by mRE, but this is downcasted.
     renderengine::gl::GLESRenderEngine* mGLESRE = nullptr;
 
-    // Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
-    // be freed *after* RenderEngine is destroyed, so that the EGL image is
-    // destroyed first.
-    sp<GraphicBuffer> mCurrentBuffer;
-
-    sp<GraphicBuffer> mBuffer;
-
     std::vector<uint32_t> mTexNames;
 };
 
@@ -530,6 +540,7 @@
     } else {
         mRE = renderEngineFactory->createRenderEngine();
     }
+    mBuffer = allocateDefaultBuffer();
 }
 
 struct ColorSourceVariant {
@@ -566,18 +577,18 @@
 struct BufferSourceVariant {
     static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
                           RenderEngineTest* fixture) {
-        sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1);
+        const auto buf = fixture->allocateSourceBuffer(1, 1);
         uint32_t texName;
         fixture->mRE->genTextures(1, &texName);
         fixture->mTexNames.push_back(texName);
 
         uint8_t* pixels;
-        buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
-                  reinterpret_cast<void**>(&pixels));
+        buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                               reinterpret_cast<void**>(&pixels));
 
-        for (int32_t j = 0; j < buf->getHeight(); j++) {
-            uint8_t* iter = pixels + (buf->getStride() * j) * 4;
-            for (int32_t i = 0; i < buf->getWidth(); i++) {
+        for (int32_t j = 0; j < buf->getBuffer()->getHeight(); j++) {
+            uint8_t* iter = pixels + (buf->getBuffer()->getStride() * j) * 4;
+            for (int32_t i = 0; i < buf->getBuffer()->getWidth(); i++) {
                 iter[0] = uint8_t(r * 255);
                 iter[1] = uint8_t(g * 255);
                 iter[2] = uint8_t(b * 255);
@@ -586,7 +597,7 @@
             }
         }
 
-        buf->unlock();
+        buf->getBuffer()->unlock();
 
         layer.source.buffer.buffer = buf;
         layer.source.buffer.textureName = texName;
@@ -1012,14 +1023,14 @@
     layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     // Here will allocate a checker board texture, but transform texture
     // coordinates so that only the upper left is applied.
-    sp<GraphicBuffer> buf = allocateSourceBuffer(2, 2);
+    const auto buf = allocateSourceBuffer(2, 2);
     uint32_t texName;
     RenderEngineTest::mRE->genTextures(1, &texName);
     this->mTexNames.push_back(texName);
 
     uint8_t* pixels;
-    buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
-              reinterpret_cast<void**>(&pixels));
+    buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                           reinterpret_cast<void**>(&pixels));
     // Red top left, Green top right, Blue bottom left, Black bottom right
     pixels[0] = 255;
     pixels[1] = 0;
@@ -1033,7 +1044,7 @@
     pixels[9] = 0;
     pixels[10] = 255;
     pixels[11] = 255;
-    buf->unlock();
+    buf->getBuffer()->unlock();
 
     layer.source.buffer.buffer = buf;
     layer.source.buffer.textureName = texName;
@@ -1061,19 +1072,19 @@
     std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layer;
-    sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+    const auto buf = allocateSourceBuffer(1, 1);
     uint32_t texName;
     RenderEngineTest::mRE->genTextures(1, &texName);
     this->mTexNames.push_back(texName);
 
     uint8_t* pixels;
-    buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
-              reinterpret_cast<void**>(&pixels));
+    buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                           reinterpret_cast<void**>(&pixels));
     pixels[0] = 255;
     pixels[1] = 0;
     pixels[2] = 0;
     pixels[3] = 255;
-    buf->unlock();
+    buf->getBuffer()->unlock();
 
     layer.source.buffer.buffer = buf;
     layer.source.buffer.textureName = texName;
@@ -1100,19 +1111,19 @@
     std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layer;
-    sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+    const auto buf = allocateSourceBuffer(1, 1);
     uint32_t texName;
     RenderEngineTest::mRE->genTextures(1, &texName);
     this->mTexNames.push_back(texName);
 
     uint8_t* pixels;
-    buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
-              reinterpret_cast<void**>(&pixels));
+    buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                           reinterpret_cast<void**>(&pixels));
     pixels[0] = 255;
     pixels[1] = 0;
     pixels[2] = 0;
     pixels[3] = 255;
-    buf->unlock();
+    buf->getBuffer()->unlock();
 
     layer.source.buffer.buffer = buf;
     layer.source.buffer.textureName = texName;
@@ -1233,8 +1244,7 @@
 }
 
 TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) {
-    const auto& renderEngineFactory = GetParam();
-    mRE = renderEngineFactory->createRenderEngine();
+    initializeRenderEngine();
 
     renderengine::DisplaySettings settings;
     settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1295,7 +1305,6 @@
     layers.push_back(&layer);
 
     status_t status = mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr);
-    mCurrentBuffer = mBuffer;
     ASSERT_EQ(NO_ERROR, status);
     expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
 }
@@ -1323,9 +1332,8 @@
     layers.push_back(&layer);
 
     status_t status = mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr);
-    mCurrentBuffer = mBuffer;
     ASSERT_EQ(NO_ERROR, status);
-    ASSERT_FALSE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getId()));
+    ASSERT_FALSE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId()));
     expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
 }
 
@@ -1394,7 +1402,8 @@
     fillBufferColorTransformZeroLayerAlpha<ColorSourceVariant>();
 }
 
-TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) {
+// TODO(b/186010146): reenable once swiftshader is happy with this test
+TEST_P(RenderEngineTest, DISABLED_drawLayers_fillBufferAndBlurBackground_colorSource) {
     initializeRenderEngine();
     fillBufferAndBlurBackground<ColorSourceVariant>();
 }
@@ -1469,7 +1478,8 @@
     fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
-TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
+// TODO(b/186010146): reenable once swiftshader is happy with this test
+TEST_P(RenderEngineTest, DISABLED_drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
     initializeRenderEngine();
     fillBufferAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
@@ -1544,7 +1554,8 @@
     fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
-TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) {
+// TODO(b/186010146): reenable once swiftshader is happy with this test
+TEST_P(RenderEngineTest, DISABLED_drawLayers_fillBufferAndBlurBackground_bufferSource) {
     initializeRenderEngine();
     fillBufferAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
@@ -1574,98 +1585,6 @@
     clearRegion();
 }
 
-TEST_P(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) {
-    const auto& renderEngineFactory = GetParam();
-
-    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
-        // GLES-specific test
-        return;
-    }
-
-    initializeRenderEngine();
-
-    renderengine::DisplaySettings settings;
-    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
-    settings.physicalDisplay = fullscreenRect();
-    settings.clip = fullscreenRect();
-
-    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);
-    invokeDraw(settings, layers);
-    uint64_t bufferId = layer.source.buffer.buffer->getId();
-    EXPECT_TRUE(mGLESRE->isImageCachedForTesting(bufferId));
-    std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
-            mGLESRE->unbindExternalTextureBufferForTesting(bufferId);
-    std::lock_guard<std::mutex> lock(barrier->mutex);
-    ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
-                                            [&]() REQUIRES(barrier->mutex) {
-                                                return barrier->isOpen;
-                                            }));
-    EXPECT_FALSE(mGLESRE->isImageCachedForTesting(bufferId));
-    EXPECT_EQ(NO_ERROR, barrier->result);
-}
-
-TEST_P(RenderEngineTest, cacheExternalBuffer_withNullBuffer) {
-    const auto& renderEngineFactory = GetParam();
-
-    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
-        // GLES-specific test
-        return;
-    }
-
-    initializeRenderEngine();
-
-    std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
-            mGLESRE->cacheExternalTextureBufferForTesting(nullptr);
-    std::lock_guard<std::mutex> lock(barrier->mutex);
-    ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
-                                            [&]() REQUIRES(barrier->mutex) {
-                                                return barrier->isOpen;
-                                            }));
-    EXPECT_TRUE(barrier->isOpen);
-    EXPECT_EQ(BAD_VALUE, barrier->result);
-}
-
-TEST_P(RenderEngineTest, cacheExternalBuffer_cachesImages) {
-    const auto& renderEngineFactory = GetParam();
-
-    if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
-        // GLES-specific test
-        return;
-    }
-
-    initializeRenderEngine();
-
-    sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
-    uint64_t bufferId = buf->getId();
-    std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
-            mGLESRE->cacheExternalTextureBufferForTesting(buf);
-    {
-        std::lock_guard<std::mutex> lock(barrier->mutex);
-        ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
-                                                [&]() REQUIRES(barrier->mutex) {
-                                                    return barrier->isOpen;
-                                                }));
-        EXPECT_EQ(NO_ERROR, barrier->result);
-    }
-    EXPECT_TRUE(mGLESRE->isImageCachedForTesting(bufferId));
-    barrier = mGLESRE->unbindExternalTextureBufferForTesting(bufferId);
-    {
-        std::lock_guard<std::mutex> lock(barrier->mutex);
-        ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
-                                                [&]() REQUIRES(barrier->mutex) {
-                                                    return barrier->isOpen;
-                                                }));
-        EXPECT_EQ(NO_ERROR, barrier->result);
-    }
-    EXPECT_FALSE(mGLESRE->isImageCachedForTesting(bufferId));
-}
-
 TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) {
     initializeRenderEngine();
 
@@ -1858,7 +1777,7 @@
         sync_wait(fd, -1);
     }
 
-    uint64_t bufferId = layer.source.buffer.buffer->getId();
+    uint64_t bufferId = layer.source.buffer.buffer->getBuffer()->getId();
     uint32_t texName = layer.source.buffer.textureName;
     EXPECT_TRUE(mGLESRE->isImageCachedForTesting(bufferId));
     EXPECT_EQ(bufferId, mGLESRE->getBufferIdForTextureNameForTesting(texName));
@@ -1966,16 +1885,16 @@
 
     // The next layer will overwrite redLayer with a GraphicBuffer that is green
     // applied with a translucent alpha.
-    auto buf = allocateSourceBuffer(1, 1);
+    const auto buf = allocateSourceBuffer(1, 1);
     {
         uint8_t* pixels;
-        buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
-                  reinterpret_cast<void**>(&pixels));
+        buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                               reinterpret_cast<void**>(&pixels));
         pixels[0] = 0;
         pixels[1] = 255;
         pixels[2] = 0;
         pixels[3] = 255;
-        buf->unlock();
+        buf->getBuffer()->unlock();
     }
 
     const renderengine::LayerSettings greenLayer{
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index 63aa4c8..e3917cc 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -49,6 +49,9 @@
 TEST_F(RenderEngineThreadedTest, primeCache) {
     EXPECT_CALL(*mRenderEngine, primeCache());
     mThreadedRE->primeCache();
+    // need to call ANY synchronous function after primeCache to ensure that primeCache has
+    // completed asynchronously before the test completes execution.
+    mThreadedRE->getContextPriority();
 }
 
 TEST_F(RenderEngineThreadedTest, genTextures) {
@@ -159,15 +162,18 @@
 TEST_F(RenderEngineThreadedTest, drawLayers) {
     renderengine::DisplaySettings settings;
     std::vector<const renderengine::LayerSettings*> layers;
-    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared<
+            renderengine::ExternalTexture>(new GraphicBuffer(), *mRenderEngine,
+                                           renderengine::ExternalTexture::Usage::READABLE |
+                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
     base::unique_fd bufferFence;
     base::unique_fd drawFence;
 
     EXPECT_CALL(*mRenderEngine, drawLayers)
             .WillOnce([](const renderengine::DisplaySettings&,
                          const std::vector<const renderengine::LayerSettings*>&,
-                         const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
-                         base::unique_fd*) -> status_t { return NO_ERROR; });
+                         const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                         base::unique_fd&&, base::unique_fd*) -> status_t { return NO_ERROR; });
 
     status_t result = mThreadedRE->drawLayers(settings, layers, buffer, false,
                                               std::move(bufferFence), &drawFence);
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 6a91c7c..c87a836 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -74,6 +74,12 @@
     std::unique_lock<std::mutex> lock(mThreadMutex);
     pthread_setname_np(pthread_self(), mThreadName);
 
+    {
+        std::unique_lock<std::mutex> lock(mInitializedMutex);
+        mIsInitialized = true;
+    }
+    mInitializedCondition.notify_all();
+
     while (mRunning) {
         if (!mFunctionCalls.empty()) {
             auto task = mFunctionCalls.front();
@@ -84,21 +90,27 @@
             return !mRunning || !mFunctionCalls.empty();
         });
     }
+
+    // we must release the RenderEngine on the thread that created it
+    mRenderEngine.reset();
+}
+
+void RenderEngineThreaded::waitUntilInitialized() const {
+    std::unique_lock<std::mutex> lock(mInitializedMutex);
+    mInitializedCondition.wait(lock, [=] { return mIsInitialized; });
 }
 
 void RenderEngineThreaded::primeCache() {
-    std::promise<void> resultPromise;
-    std::future<void> resultFuture = resultPromise.get_future();
+    // This function is designed so it can run asynchronously, so we do not need to wait
+    // for the futures.
     {
         std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+        mFunctionCalls.push([](renderengine::RenderEngine& instance) {
             ATRACE_NAME("REThreaded::primeCache");
             instance.primeCache();
-            resultPromise.set_value();
         });
     }
     mCondition.notify_one();
-    resultFuture.wait();
 }
 
 void RenderEngineThreaded::dump(std::string& result) {
@@ -148,90 +160,54 @@
     resultFuture.wait();
 }
 
-void RenderEngineThreaded::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
+                                                    bool isRenderable) {
     // This function is designed so it can run asynchronously, so we do not need to wait
     // for the futures.
     {
         std::lock_guard lock(mThreadMutex);
         mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::cacheExternalTextureBuffer");
-            instance.cacheExternalTextureBuffer(buffer);
+            ATRACE_NAME("REThreaded::mapExternalTextureBuffer");
+            instance.mapExternalTextureBuffer(buffer, isRenderable);
         });
     }
     mCondition.notify_one();
 }
 
-void RenderEngineThreaded::unbindExternalTextureBuffer(uint64_t bufferId) {
+void RenderEngineThreaded::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
     // This function is designed so it can run asynchronously, so we do not need to wait
     // for the futures.
     {
         std::lock_guard lock(mThreadMutex);
         mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::unbindExternalTextureBuffer");
-            instance.unbindExternalTextureBuffer(bufferId);
+            ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
+            instance.unmapExternalTextureBuffer(buffer);
         });
     }
     mCondition.notify_one();
 }
 
 size_t RenderEngineThreaded::getMaxTextureSize() const {
-    std::promise<size_t> resultPromise;
-    std::future<size_t> resultFuture = resultPromise.get_future();
-    {
-        std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::getMaxTextureSize");
-            size_t size = instance.getMaxTextureSize();
-            resultPromise.set_value(size);
-        });
-    }
-    mCondition.notify_one();
-    return resultFuture.get();
+    waitUntilInitialized();
+    return mRenderEngine->getMaxTextureSize();
 }
 
 size_t RenderEngineThreaded::getMaxViewportDims() const {
-    std::promise<size_t> resultPromise;
-    std::future<size_t> resultFuture = resultPromise.get_future();
-    {
-        std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::getMaxViewportDims");
-            size_t size = instance.getMaxViewportDims();
-            resultPromise.set_value(size);
-        });
-    }
-    mCondition.notify_one();
-    return resultFuture.get();
+    waitUntilInitialized();
+    return mRenderEngine->getMaxViewportDims();
 }
 
 bool RenderEngineThreaded::isProtected() const {
-    std::promise<bool> resultPromise;
-    std::future<bool> resultFuture = resultPromise.get_future();
-    {
-        std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::isProtected");
-            bool returnValue = instance.isProtected();
-            resultPromise.set_value(returnValue);
-        });
-    }
-    mCondition.notify_one();
-    return resultFuture.get();
+    waitUntilInitialized();
+    // ensure that useProtectedContext is not currently being changed by some
+    // other thread.
+    std::lock_guard lock(mThreadMutex);
+    return mRenderEngine->isProtected();
 }
 
 bool RenderEngineThreaded::supportsProtectedContent() const {
-    std::promise<bool> resultPromise;
-    std::future<bool> resultFuture = resultPromise.get_future();
-    {
-        std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::supportsProtectedContent");
-            bool returnValue = instance.supportsProtectedContent();
-            resultPromise.set_value(returnValue);
-        });
-    }
-    mCondition.notify_one();
-    return resultFuture.get();
+    waitUntilInitialized();
+    return mRenderEngine->supportsProtectedContent();
 }
 
 bool RenderEngineThreaded::useProtectedContext(bool useProtectedContext) {
@@ -267,7 +243,7 @@
 
 status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display,
                                           const std::vector<const LayerSettings*>& layers,
-                                          const sp<GraphicBuffer>& buffer,
+                                          const std::shared_ptr<ExternalTexture>& buffer,
                                           const bool useFramebufferCache,
                                           base::unique_fd&& bufferFence,
                                           base::unique_fd* drawFence) {
@@ -288,18 +264,16 @@
 }
 
 void RenderEngineThreaded::cleanFramebufferCache() {
-    std::promise<void> resultPromise;
-    std::future<void> resultFuture = resultPromise.get_future();
+    // This function is designed so it can run asynchronously, so we do not need to wait
+    // for the futures.
     {
         std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+        mFunctionCalls.push([](renderengine::RenderEngine& instance) {
             ATRACE_NAME("REThreaded::cleanFramebufferCache");
             instance.cleanFramebufferCache();
-            resultPromise.set_value();
         });
     }
     mCondition.notify_one();
-    resultFuture.wait();
 }
 
 int RenderEngineThreaded::getContextPriority() {
@@ -318,18 +292,21 @@
 }
 
 bool RenderEngineThreaded::supportsBackgroundBlur() {
-    std::promise<bool> resultPromise;
-    std::future<bool> resultFuture = resultPromise.get_future();
+    waitUntilInitialized();
+    return mRenderEngine->supportsBackgroundBlur();
+}
+
+void RenderEngineThreaded::onPrimaryDisplaySizeChanged(ui::Size size) {
+    // This function is designed so it can run asynchronously, so we do not need to wait
+    // for the futures.
     {
         std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::supportsBackgroundBlur");
-            bool returnValue = instance.supportsBackgroundBlur();
-            resultPromise.set_value(returnValue);
+        mFunctionCalls.push([size](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::onPrimaryDisplaySizeChanged");
+            instance.onPrimaryDisplaySizeChanged(size);
         });
     }
     mCondition.notify_one();
-    return resultFuture.get();
 }
 
 } // namespace threaded
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 7694328..eb6098e 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -48,8 +48,6 @@
 
     void genTextures(size_t count, uint32_t* names) override;
     void deleteTextures(size_t count, uint32_t const* names) override;
-    void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
-    void unbindExternalTextureBuffer(uint64_t bufferId) override;
     size_t getMaxTextureSize() const override;
     size_t getMaxViewportDims() const override;
 
@@ -60,15 +58,22 @@
 
     status_t drawLayers(const DisplaySettings& display,
                         const std::vector<const LayerSettings*>& layers,
-                        const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
-                        base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
+                        const std::shared_ptr<ExternalTexture>& buffer,
+                        const bool useFramebufferCache, base::unique_fd&& bufferFence,
+                        base::unique_fd* drawFence) override;
 
     void cleanFramebufferCache() override;
     int getContextPriority() override;
     bool supportsBackgroundBlur() override;
+    void onPrimaryDisplaySizeChanged(ui::Size size) override;
+
+protected:
+    void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
+    void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
 
 private:
     void threadMain(CreateInstanceFactory factory);
+    void waitUntilInitialized() const;
 
     /* ------------------------------------------------------------------------
      * Threading
@@ -82,6 +87,12 @@
             GUARDED_BY(mThreadMutex);
     mutable std::condition_variable mCondition;
 
+    // Used to allow select thread safe methods to be accessed without requiring the
+    // method to be invoked on the RenderEngine thread
+    bool mIsInitialized = false;
+    mutable std::mutex mInitializedMutex;
+    mutable std::condition_variable mInitializedCondition;
+
     /* ------------------------------------------------------------------------
      * Render Engine
      */
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index 497c33c..edd453a 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -48,11 +48,10 @@
         "libutils",
         "liblog",
         "libhardware",
+        "libpermission",
     ],
 
     export_include_dirs: ["include"],
 
-    export_shared_lib_headers: ["libbinder", "libhardware"],
+    export_shared_lib_headers: ["libbinder", "libpermission", "libhardware"],
 }
-
-subdirs = ["tests"]
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
index bdfe04b..538c1d2 100644
--- a/libs/ui/FenceTime.cpp
+++ b/libs/ui/FenceTime.cpp
@@ -97,6 +97,34 @@
     return mState != State::INVALID;
 }
 
+status_t FenceTime::wait(int timeout) {
+    // See if we already have a cached value we can return.
+    nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+    if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+        return NO_ERROR;
+    }
+
+    // Hold a reference to the fence on the stack in case the class'
+    // reference is removed by another thread. This prevents the
+    // fence from being destroyed until the end of this method, where
+    // we conveniently do not have the lock held.
+    sp<Fence> fence;
+    {
+        // With the lock acquired this time, see if we have the cached
+        // value or if we need to poll the fence.
+        std::lock_guard<std::mutex> lock(mMutex);
+        if (!mFence.get()) {
+            // Another thread set the signal time just before we added the
+            // reference to mFence.
+            return NO_ERROR;
+        }
+        fence = mFence;
+    }
+
+    // Make the system call without the lock held.
+    return fence->wait(timeout);
+}
+
 nsecs_t FenceTime::getSignalTime() {
     // See if we already have a cached value we can return.
     nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
diff --git a/libs/ui/include/ui/FenceTime.h b/libs/ui/include/ui/FenceTime.h
index ecba7f7..ac75f43 100644
--- a/libs/ui/include/ui/FenceTime.h
+++ b/libs/ui/include/ui/FenceTime.h
@@ -112,6 +112,13 @@
     // Returns a snapshot of the FenceTime in its current state.
     Snapshot getSnapshot() const;
 
+    // wait waits for up to timeout milliseconds for the fence to signal.  If
+    // the fence signals then NO_ERROR is returned. If the timeout expires
+    // before the fence signals then -ETIME is returned.  A timeout of
+    // TIMEOUT_NEVER may be used to indicate that the call should wait
+    // indefinitely for the fence to signal.
+    status_t wait(int timeout);
+
     void signalForTest(nsecs_t signalTime);
 
 private:
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 6977396..8277f51 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2326,6 +2326,8 @@
     if (dropWindow) {
         vec2 local = dropWindow->getInfo()->transform.transform(x, y);
         notifyDropWindowLocked(dropWindow->getToken(), local.x, local.y);
+    } else {
+        notifyDropWindowLocked(nullptr, 0, 0);
     }
     mDragState.reset();
 }
@@ -2372,6 +2374,7 @@
     } else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
         finishDragAndDrop(entry.displayId, x, y);
     } else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+        notifyDropWindowLocked(nullptr, 0, 0);
         mDragState.reset();
     }
 }
@@ -3154,8 +3157,9 @@
                     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 "
-                          "event to it, status=%d",
-                          connection->getInputChannelName().c_str(), status);
+                          "event to it, status=%s(%d)",
+                          connection->getInputChannelName().c_str(), statusToString(status).c_str(),
+                          status);
                     abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
                 } else {
                     // Pipe is full and we are waiting for the app to finish process some events
@@ -3168,8 +3172,9 @@
                 }
             } else {
                 ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
-                      "status=%d",
-                      connection->getInputChannelName().c_str(), status);
+                      "status=%s(%d)",
+                      connection->getInputChannelName().c_str(), statusToString(status).c_str(),
+                      status);
                 abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
             }
             return;
@@ -3312,15 +3317,21 @@
             bool gotOne = false;
             status_t status = OK;
             for (;;) {
-                Result<InputPublisher::Finished> result =
-                        connection->inputPublisher.receiveFinishedSignal();
+                Result<InputPublisher::ConsumerResponse> result =
+                        connection->inputPublisher.receiveConsumerResponse();
                 if (!result.ok()) {
                     status = result.error().code();
                     break;
                 }
-                const InputPublisher::Finished& finished = *result;
-                d->finishDispatchCycleLocked(currentTime, connection, finished.seq,
-                                             finished.handled, finished.consumeTime);
+
+                if (std::holds_alternative<InputPublisher::Finished>(*result)) {
+                    const InputPublisher::Finished& finish =
+                            std::get<InputPublisher::Finished>(*result);
+                    d->finishDispatchCycleLocked(currentTime, connection, finish.seq,
+                                                 finish.handled, finish.consumeTime);
+                } else if (std::holds_alternative<InputPublisher::Timeline>(*result)) {
+                    // TODO(b/167947340): Report this data to LatencyTracker
+                }
                 gotOne = true;
             }
             if (gotOne) {
@@ -3332,8 +3343,9 @@
 
             notify = status != DEAD_OBJECT || !connection->monitor;
             if (notify) {
-                ALOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",
-                      connection->getInputChannelName().c_str(), status);
+                ALOGE("channel '%s' ~ Failed to receive finished signal.  status=%s(%d)",
+                      connection->getInputChannelName().c_str(), statusToString(status).c_str(),
+                      status);
             }
         } else {
             // Monitor channels are never explicitly unregistered.
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 1af70a4..7db32e3 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -24,6 +24,7 @@
 cc_library_headers {
     name: "libinputreader_headers",
     export_include_dirs: [
+        "controller",
         "include",
         "mapper",
         "mapper/accumulator",
@@ -35,17 +36,16 @@
     srcs: [
         "EventHub.cpp",
         "InputDevice.cpp",
+        "controller/PeripheralController.cpp",
         "mapper/accumulator/CursorButtonAccumulator.cpp",
         "mapper/accumulator/CursorScrollAccumulator.cpp",
         "mapper/accumulator/SingleTouchMotionAccumulator.cpp",
         "mapper/accumulator/TouchButtonAccumulator.cpp",
-        "mapper/BatteryInputMapper.cpp",
         "mapper/CursorInputMapper.cpp",
         "mapper/ExternalStylusInputMapper.cpp",
         "mapper/InputMapper.cpp",
         "mapper/JoystickInputMapper.cpp",
         "mapper/KeyboardInputMapper.cpp",
-        "mapper/LightInputMapper.cpp",
         "mapper/MultiTouchInputMapper.cpp",
         "mapper/RotaryEncoderInputMapper.cpp",
         "mapper/SensorInputMapper.cpp",
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 07011f5..e3e6c12 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -70,6 +70,20 @@
 static constexpr int32_t FF_STRONG_MAGNITUDE_CHANNEL_IDX = 0;
 static constexpr int32_t FF_WEAK_MAGNITUDE_CHANNEL_IDX = 1;
 
+// Mapping for input battery class node IDs lookup.
+// https://www.kernel.org/doc/Documentation/power/power_supply_class.txt
+static const std::unordered_map<std::string, InputBatteryClass> BATTERY_CLASSES =
+        {{"capacity", InputBatteryClass::CAPACITY},
+         {"capacity_level", InputBatteryClass::CAPACITY_LEVEL},
+         {"status", InputBatteryClass::STATUS}};
+
+// Mapping for input battery class node names lookup.
+// https://www.kernel.org/doc/Documentation/power/power_supply_class.txt
+static const std::unordered_map<InputBatteryClass, std::string> BATTERY_NODES =
+        {{InputBatteryClass::CAPACITY, "capacity"},
+         {InputBatteryClass::CAPACITY_LEVEL, "capacity_level"},
+         {InputBatteryClass::STATUS, "status"}};
+
 // must be kept in sync with definitions in kernel /drivers/power/supply/power_supply_sysfs.c
 static const std::unordered_map<std::string, int32_t> BATTERY_STATUS =
         {{"Unknown", BATTERY_STATUS_UNKNOWN},
@@ -349,7 +363,7 @@
         virtualKeyMap(nullptr),
         ffEffectPlaying(false),
         ffEffectId(-1),
-        nextLightId(0),
+        associatedDevice(nullptr),
         controllerNumber(0),
         enabled(true),
         isVirtual(fd < 0) {}
@@ -540,32 +554,36 @@
 }
 
 // Check the sysfs path for any input device batteries, returns true if battery found.
-bool EventHub::Device::configureBatteryLocked() {
-    if (!sysfsRootPath.has_value()) {
-        return false;
+bool EventHub::AssociatedDevice::configureBatteryLocked() {
+    nextBatteryId = 0;
+    // Check if device has any battery.
+    const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::POWER_SUPPLY);
+    for (const auto& nodePath : paths) {
+        RawBatteryInfo info;
+        info.id = ++nextBatteryId;
+        info.path = nodePath;
+        info.name = nodePath.filename();
+
+        // Scan the path for all the files
+        // Refer to https://www.kernel.org/doc/Documentation/leds/leds-class.txt
+        const auto& files = allFilesInPath(nodePath);
+        for (const auto& file : files) {
+            const auto it = BATTERY_CLASSES.find(file.filename().string());
+            if (it != BATTERY_CLASSES.end()) {
+                info.flags |= it->second;
+            }
+        }
+        batteryInfos.insert_or_assign(info.id, info);
+        ALOGD("configureBatteryLocked rawBatteryId %d name %s", info.id, info.name.c_str());
     }
-    // Check if device has any batteries.
-    std::vector<std::filesystem::path> batteryPaths =
-            findSysfsNodes(sysfsRootPath.value(), SysfsClass::POWER_SUPPLY);
-    // We only support single battery for an input device, if multiple batteries exist only the
-    // first one is supported.
-    if (batteryPaths.empty()) {
-        // Set path to be empty
-        sysfsBatteryPath = std::nullopt;
-        return false;
-    }
-    // If a battery exists
-    sysfsBatteryPath = batteryPaths[0];
-    return true;
+    return !batteryInfos.empty();
 }
 
 // Check the sysfs path for any input device lights, returns true if lights found.
-bool EventHub::Device::configureLightsLocked() {
-    if (!sysfsRootPath.has_value()) {
-        return false;
-    }
+bool EventHub::AssociatedDevice::configureLightsLocked() {
+    nextLightId = 0;
     // Check if device has any lights.
-    const auto& paths = findSysfsNodes(sysfsRootPath.value(), SysfsClass::LEDS);
+    const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::LEDS);
     for (const auto& nodePath : paths) {
         RawLightInfo info;
         info.id = ++nextLightId;
@@ -599,6 +617,7 @@
             }
         }
         lightInfos.insert_or_assign(info.id, info);
+        ALOGD("configureLightsLocked rawLightId %d name %s", info.id, info.name.c_str());
     }
     return !lightInfos.empty();
 }
@@ -963,42 +982,84 @@
     return Errorf("Device not found or device has no key layout.");
 }
 
+// Gets the battery info map from battery ID to RawBatteryInfo of the miscellaneous device
+// associated with the device ID. Returns an empty map if no miscellaneous device found.
+const std::unordered_map<int32_t, RawBatteryInfo>& EventHub::getBatteryInfoLocked(
+        int32_t deviceId) const {
+    static const std::unordered_map<int32_t, RawBatteryInfo> EMPTY_BATTERY_INFO = {};
+    Device* device = getDeviceLocked(deviceId);
+    if (device == nullptr || !device->associatedDevice) {
+        return EMPTY_BATTERY_INFO;
+    }
+    return device->associatedDevice->batteryInfos;
+}
+
+const std::vector<int32_t> EventHub::getRawBatteryIds(int32_t deviceId) {
+    std::scoped_lock _l(mLock);
+    std::vector<int32_t> batteryIds;
+
+    for (const auto [id, info] : getBatteryInfoLocked(deviceId)) {
+        batteryIds.push_back(id);
+    }
+
+    return batteryIds;
+}
+
+std::optional<RawBatteryInfo> EventHub::getRawBatteryInfo(int32_t deviceId, int32_t batteryId) {
+    std::scoped_lock _l(mLock);
+
+    const auto infos = getBatteryInfoLocked(deviceId);
+
+    auto it = infos.find(batteryId);
+    if (it != infos.end()) {
+        return it->second;
+    }
+
+    return std::nullopt;
+}
+
+// Gets the light info map from light ID to RawLightInfo of the miscellaneous device associated
+// with the deivice ID. Returns an empty map if no miscellaneous device found.
+const std::unordered_map<int32_t, RawLightInfo>& EventHub::getLightInfoLocked(
+        int32_t deviceId) const {
+    static const std::unordered_map<int32_t, RawLightInfo> EMPTY_LIGHT_INFO = {};
+    Device* device = getDeviceLocked(deviceId);
+    if (device == nullptr || !device->associatedDevice) {
+        return EMPTY_LIGHT_INFO;
+    }
+    return device->associatedDevice->lightInfos;
+}
+
 const std::vector<int32_t> EventHub::getRawLightIds(int32_t deviceId) {
     std::scoped_lock _l(mLock);
-    Device* device = getDeviceLocked(deviceId);
     std::vector<int32_t> lightIds;
 
-    if (device != nullptr) {
-        for (const auto [id, info] : device->lightInfos) {
-            lightIds.push_back(id);
-        }
+    for (const auto [id, info] : getLightInfoLocked(deviceId)) {
+        lightIds.push_back(id);
     }
+
     return lightIds;
 }
 
 std::optional<RawLightInfo> EventHub::getRawLightInfo(int32_t deviceId, int32_t lightId) {
     std::scoped_lock _l(mLock);
-    Device* device = getDeviceLocked(deviceId);
 
-    if (device != nullptr) {
-        auto it = device->lightInfos.find(lightId);
-        if (it != device->lightInfos.end()) {
-            return it->second;
-        }
+    const auto infos = getLightInfoLocked(deviceId);
+
+    auto it = infos.find(lightId);
+    if (it != infos.end()) {
+        return it->second;
     }
+
     return std::nullopt;
 }
 
 std::optional<int32_t> EventHub::getLightBrightness(int32_t deviceId, int32_t lightId) {
     std::scoped_lock _l(mLock);
 
-    Device* device = getDeviceLocked(deviceId);
-    if (device == nullptr) {
-        return std::nullopt;
-    }
-
-    auto it = device->lightInfos.find(lightId);
-    if (it == device->lightInfos.end()) {
+    const auto infos = getLightInfoLocked(deviceId);
+    auto it = infos.find(lightId);
+    if (it == infos.end()) {
         return std::nullopt;
     }
     std::string buffer;
@@ -1013,13 +1074,9 @@
         int32_t deviceId, int32_t lightId) {
     std::scoped_lock _l(mLock);
 
-    Device* device = getDeviceLocked(deviceId);
-    if (device == nullptr) {
-        return std::nullopt;
-    }
-
-    auto lightIt = device->lightInfos.find(lightId);
-    if (lightIt == device->lightInfos.end()) {
+    const auto infos = getLightInfoLocked(deviceId);
+    auto lightIt = infos.find(lightId);
+    if (lightIt == infos.end()) {
         return std::nullopt;
     }
 
@@ -1056,14 +1113,10 @@
 void EventHub::setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) {
     std::scoped_lock _l(mLock);
 
-    Device* device = getDeviceLocked(deviceId);
-    if (device == nullptr) {
-        ALOGE("Device Id %d does not exist", deviceId);
-        return;
-    }
-    auto lightIt = device->lightInfos.find(lightId);
-    if (lightIt == device->lightInfos.end()) {
-        ALOGE("Light Id %d does not exist.", lightId);
+    const auto infos = getLightInfoLocked(deviceId);
+    auto lightIt = infos.find(lightId);
+    if (lightIt == infos.end()) {
+        ALOGE("%s lightId %d not found ", __func__, lightId);
         return;
     }
 
@@ -1078,13 +1131,9 @@
                                    std::unordered_map<LightColor, int32_t> intensities) {
     std::scoped_lock _l(mLock);
 
-    Device* device = getDeviceLocked(deviceId);
-    if (device == nullptr) {
-        ALOGE("Device Id %d does not exist", deviceId);
-        return;
-    }
-    auto lightIt = device->lightInfos.find(lightId);
-    if (lightIt == device->lightInfos.end()) {
+    const auto infos = getLightInfoLocked(deviceId);
+    auto lightIt = infos.find(lightId);
+    if (lightIt == infos.end()) {
         ALOGE("Light Id %d does not exist.", lightId);
         return;
     }
@@ -1352,51 +1401,56 @@
     return nullptr;
 }
 
-std::optional<int32_t> EventHub::getBatteryCapacity(int32_t deviceId) const {
+std::optional<int32_t> EventHub::getBatteryCapacity(int32_t deviceId, int32_t batteryId) const {
     std::scoped_lock _l(mLock);
-    Device* device = getDeviceLocked(deviceId);
-    std::string buffer;
 
-    if (device == nullptr || !device->sysfsBatteryPath.has_value()) {
+    const auto infos = getBatteryInfoLocked(deviceId);
+    auto it = infos.find(batteryId);
+    if (it == infos.end()) {
         return std::nullopt;
     }
+    std::string buffer;
 
     // Some devices report battery capacity as an integer through the "capacity" file
-    if (base::ReadFileToString(device->sysfsBatteryPath.value() / "capacity", &buffer)) {
+    if (base::ReadFileToString(it->second.path / BATTERY_NODES.at(InputBatteryClass::CAPACITY),
+                               &buffer)) {
         return std::stoi(base::Trim(buffer));
     }
 
     // Other devices report capacity as an enum value POWER_SUPPLY_CAPACITY_LEVEL_XXX
     // These values are taken from kernel source code include/linux/power_supply.h
-    if (base::ReadFileToString(device->sysfsBatteryPath.value() / "capacity_level", &buffer)) {
+    if (base::ReadFileToString(it->second.path /
+                                       BATTERY_NODES.at(InputBatteryClass::CAPACITY_LEVEL),
+                               &buffer)) {
         // Remove any white space such as trailing new line
-        const auto it = BATTERY_LEVEL.find(base::Trim(buffer));
-        if (it != BATTERY_LEVEL.end()) {
-            return it->second;
+        const auto levelIt = BATTERY_LEVEL.find(base::Trim(buffer));
+        if (levelIt != BATTERY_LEVEL.end()) {
+            return levelIt->second;
         }
     }
+
     return std::nullopt;
 }
 
-std::optional<int32_t> EventHub::getBatteryStatus(int32_t deviceId) const {
+std::optional<int32_t> EventHub::getBatteryStatus(int32_t deviceId, int32_t batteryId) const {
     std::scoped_lock _l(mLock);
-    Device* device = getDeviceLocked(deviceId);
-    std::string buffer;
-
-    if (device == nullptr || !device->sysfsBatteryPath.has_value()) {
+    const auto infos = getBatteryInfoLocked(deviceId);
+    auto it = infos.find(batteryId);
+    if (it == infos.end()) {
         return std::nullopt;
     }
+    std::string buffer;
 
-    if (!base::ReadFileToString(device->sysfsBatteryPath.value() / "status", &buffer)) {
+    if (!base::ReadFileToString(it->second.path / BATTERY_NODES.at(InputBatteryClass::STATUS),
+                                &buffer)) {
         ALOGE("Failed to read sysfs battery info: %s", strerror(errno));
         return std::nullopt;
     }
 
     // Remove white space like trailing new line
-    const auto it = BATTERY_STATUS.find(base::Trim(buffer));
-
-    if (it != BATTERY_STATUS.end()) {
-        return it->second;
+    const auto statusIt = BATTERY_STATUS.find(base::Trim(buffer));
+    if (statusIt != BATTERY_STATUS.end()) {
+        return statusIt->second;
     }
 
     return std::nullopt;
@@ -1879,11 +1933,26 @@
     // Load the configuration file for the device.
     device->loadConfigurationLocked();
 
-    // Grab the device's sysfs path
-    device->sysfsRootPath = getSysfsRootPath(devicePath.c_str());
-    // find related components
-    bool hasBattery = device->configureBatteryLocked();
-    bool hasLights = device->configureLightsLocked();
+    bool hasBattery = false;
+    bool hasLights = false;
+    // Check the sysfs root path
+    std::optional<std::filesystem::path> sysfsRootPath = getSysfsRootPath(devicePath.c_str());
+    if (sysfsRootPath.has_value()) {
+        std::shared_ptr<AssociatedDevice> associatedDevice;
+        for (const auto& [id, dev] : mDevices) {
+            if (device->identifier.descriptor == dev->identifier.descriptor &&
+                !dev->associatedDevice) {
+                associatedDevice = dev->associatedDevice;
+            }
+        }
+        if (!associatedDevice) {
+            associatedDevice = std::make_shared<AssociatedDevice>(sysfsRootPath.value());
+        }
+        hasBattery = associatedDevice->configureBatteryLocked();
+        hasLights = associatedDevice->configureLightsLocked();
+
+        device->associatedDevice = associatedDevice;
+    }
 
     // Figure out the kinds of events the device reports.
     device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask);
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index dd1abeb..8f75d22 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -21,14 +21,13 @@
 #include <input/Flags.h>
 #include <algorithm>
 
-#include "BatteryInputMapper.h"
 #include "CursorInputMapper.h"
 #include "ExternalStylusInputMapper.h"
 #include "InputReaderContext.h"
 #include "JoystickInputMapper.h"
 #include "KeyboardInputMapper.h"
-#include "LightInputMapper.h"
 #include "MultiTouchInputMapper.h"
+#include "PeripheralController.h"
 #include "RotaryEncoderInputMapper.h"
 #include "SensorInputMapper.h"
 #include "SingleTouchInputMapper.h"
@@ -131,6 +130,9 @@
     }
 
     for_each_mapper([&dump](InputMapper& mapper) { mapper.dump(dump); });
+    if (mController) {
+        mController->dump(dump);
+    }
 }
 
 void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) {
@@ -162,22 +164,10 @@
         mappers.push_back(std::make_unique<VibratorInputMapper>(*contextPtr));
     }
 
-    // Battery-like devices. Only one battery mapper for each EventHub device.
-    if (classes.test(InputDeviceClass::BATTERY)) {
-        InputDeviceInfo deviceInfo;
-        getDeviceInfo(&deviceInfo);
-        if (!deviceInfo.hasBattery()) {
-            mappers.push_back(std::make_unique<BatteryInputMapper>(*contextPtr));
-        }
-    }
-
-    // Light-containing devices. Only one light mapper for each EventHub device.
-    if (classes.test(InputDeviceClass::LIGHT)) {
-        InputDeviceInfo deviceInfo;
-        getDeviceInfo(&deviceInfo);
-        if (deviceInfo.getLightIds().empty()) {
-            mappers.push_back(std::make_unique<LightInputMapper>(*contextPtr));
-        }
+    // Battery-like devices or light-containing devices.
+    // PeripheralController will be created with associated EventHub device.
+    if (classes.test(InputDeviceClass::BATTERY) || classes.test(InputDeviceClass::LIGHT)) {
+        mController = std::make_unique<PeripheralController>(*contextPtr);
     }
 
     // Keyboard-like devices.
@@ -409,6 +399,10 @@
                               mHasMic);
     for_each_mapper(
             [outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(outDeviceInfo); });
+
+    if (mController) {
+        mController->populateDeviceInfo(outDeviceInfo);
+    }
 }
 
 int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
@@ -511,38 +505,27 @@
 }
 
 std::optional<int32_t> InputDevice::getBatteryCapacity() {
-    return first_in_mappers<int32_t>(
-            [](InputMapper& mapper) { return mapper.getBatteryCapacity(); });
+    return mController ? mController->getBatteryCapacity(DEFAULT_BATTERY_ID) : std::nullopt;
 }
 
 std::optional<int32_t> InputDevice::getBatteryStatus() {
-    return first_in_mappers<int32_t>([](InputMapper& mapper) { return mapper.getBatteryStatus(); });
+    return mController ? mController->getBatteryStatus(DEFAULT_BATTERY_ID) : std::nullopt;
 }
 
 bool InputDevice::setLightColor(int32_t lightId, int32_t color) {
-    bool success = true;
-    for_each_mapper([&success, lightId, color](InputMapper& mapper) {
-        success &= mapper.setLightColor(lightId, color);
-    });
-    return success;
+    return mController ? mController->setLightColor(lightId, color) : false;
 }
 
 bool InputDevice::setLightPlayerId(int32_t lightId, int32_t playerId) {
-    bool success = true;
-    for_each_mapper([&success, lightId, playerId](InputMapper& mapper) {
-        success &= mapper.setLightPlayerId(lightId, playerId);
-    });
-    return success;
+    return mController ? mController->setLightPlayerId(lightId, playerId) : false;
 }
 
 std::optional<int32_t> InputDevice::getLightColor(int32_t lightId) {
-    return first_in_mappers<int32_t>(
-            [lightId](InputMapper& mapper) { return mapper.getLightColor(lightId); });
+    return mController ? mController->getLightColor(lightId) : std::nullopt;
 }
 
 std::optional<int32_t> InputDevice::getLightPlayerId(int32_t lightId) {
-    return first_in_mappers<int32_t>(
-            [lightId](InputMapper& mapper) { return mapper.getLightPlayerId(lightId); });
+    return mController ? mController->getLightPlayerId(lightId) : std::nullopt;
 }
 
 int32_t InputDevice::getMetaState() {
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
new file mode 100644
index 0000000..1a40d06
--- /dev/null
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <locale>
+#include <regex>
+
+#include "../Macros.h"
+
+#include "PeripheralController.h"
+#include "input/NamedEnum.h"
+
+// Log detailed debug messages about input device lights.
+static constexpr bool DEBUG_LIGHT_DETAILS = false;
+
+namespace android {
+
+static inline int32_t getAlpha(int32_t color) {
+    return (color >> 24) & 0xff;
+}
+
+static inline int32_t getRed(int32_t color) {
+    return (color >> 16) & 0xff;
+}
+
+static inline int32_t getGreen(int32_t color) {
+    return (color >> 8) & 0xff;
+}
+
+static inline int32_t getBlue(int32_t color) {
+    return color & 0xff;
+}
+
+static inline int32_t toArgb(int32_t brightness, int32_t red, int32_t green, int32_t blue) {
+    return (brightness & 0xff) << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff);
+}
+
+/**
+ * Input controller owned by InputReader device, implements the native API for querying input
+ * lights, getting and setting the lights brightness and color, by interacting with EventHub
+ * devices.
+ */
+PeripheralController::PeripheralController(InputDeviceContext& deviceContext)
+      : mDeviceContext(deviceContext) {
+    configureBattries();
+    configureLights();
+}
+
+PeripheralController::~PeripheralController() {}
+
+std::optional<std::int32_t> PeripheralController::Light::getRawLightBrightness(int32_t rawLightId) {
+    std::optional<RawLightInfo> rawInfoOpt = context.getRawLightInfo(rawLightId);
+    if (!rawInfoOpt.has_value()) {
+        return std::nullopt;
+    }
+    std::optional<int32_t> brightnessOpt = context.getLightBrightness(rawLightId);
+    if (!brightnessOpt.has_value()) {
+        return std::nullopt;
+    }
+    int brightness = brightnessOpt.value();
+
+    // If the light node doesn't have max brightness, use the default max brightness.
+    int rawMaxBrightness = rawInfoOpt->maxBrightness.value_or(MAX_BRIGHTNESS);
+    float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
+    // Scale the returned brightness in [0, rawMaxBrightness] to [0, 255]
+    if (rawMaxBrightness != MAX_BRIGHTNESS) {
+        brightness = brightness * ratio;
+    }
+    if (DEBUG_LIGHT_DETAILS) {
+        ALOGD("getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
+              brightness, ratio);
+    }
+    return brightness;
+}
+
+void PeripheralController::Light::setRawLightBrightness(int32_t rawLightId, int32_t brightness) {
+    std::optional<RawLightInfo> rawInfo = context.getRawLightInfo(rawLightId);
+    if (!rawInfo.has_value()) {
+        return;
+    }
+    // If the light node doesn't have max brightness, use the default max brightness.
+    int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS);
+    float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
+    // Scale the requested brightness in [0, 255] to [0, rawMaxBrightness]
+    if (rawMaxBrightness != MAX_BRIGHTNESS) {
+        brightness = ceil(brightness / ratio);
+    }
+    if (DEBUG_LIGHT_DETAILS) {
+        ALOGD("setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
+              brightness, ratio);
+    }
+    context.setLightBrightness(rawLightId, brightness);
+}
+
+bool PeripheralController::SingleLight::setLightColor(int32_t color) {
+    int32_t brightness = getAlpha(color);
+    setRawLightBrightness(rawId, brightness);
+
+    return true;
+}
+
+bool PeripheralController::RgbLight::setLightColor(int32_t color) {
+    // Compose color value as per:
+    // https://developer.android.com/reference/android/graphics/Color?hl=en
+    // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
+    // The alpha component is used to scale the R,G,B leds brightness, with the ratio to
+    // MAX_BRIGHTNESS.
+    brightness = getAlpha(color);
+    int32_t red = 0;
+    int32_t green = 0;
+    int32_t blue = 0;
+    if (brightness > 0) {
+        float ratio = MAX_BRIGHTNESS / brightness;
+        red = ceil(getRed(color) / ratio);
+        green = ceil(getGreen(color) / ratio);
+        blue = ceil(getBlue(color) / ratio);
+    }
+    setRawLightBrightness(rawRgbIds.at(LightColor::RED), red);
+    setRawLightBrightness(rawRgbIds.at(LightColor::GREEN), green);
+    setRawLightBrightness(rawRgbIds.at(LightColor::BLUE), blue);
+    if (rawGlobalId.has_value()) {
+        setRawLightBrightness(rawGlobalId.value(), brightness);
+    }
+
+    return true;
+}
+
+bool PeripheralController::MultiColorLight::setLightColor(int32_t color) {
+    std::unordered_map<LightColor, int32_t> intensities;
+    intensities.emplace(LightColor::RED, getRed(color));
+    intensities.emplace(LightColor::GREEN, getGreen(color));
+    intensities.emplace(LightColor::BLUE, getBlue(color));
+
+    context.setLightIntensities(rawId, intensities);
+    setRawLightBrightness(rawId, getAlpha(color));
+    return true;
+}
+
+std::optional<int32_t> PeripheralController::SingleLight::getLightColor() {
+    std::optional<int32_t> brightness = getRawLightBrightness(rawId);
+    if (!brightness.has_value()) {
+        return std::nullopt;
+    }
+
+    return toArgb(brightness.value(), 0 /* red */, 0 /* green */, 0 /* blue */);
+}
+
+std::optional<int32_t> PeripheralController::RgbLight::getLightColor() {
+    // If the Alpha component is zero, then return color 0.
+    if (brightness == 0) {
+        return 0;
+    }
+    // Compose color value as per:
+    // https://developer.android.com/reference/android/graphics/Color?hl=en
+    // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
+    std::optional<int32_t> redOr = getRawLightBrightness(rawRgbIds.at(LightColor::RED));
+    std::optional<int32_t> greenOr = getRawLightBrightness(rawRgbIds.at(LightColor::GREEN));
+    std::optional<int32_t> blueOr = getRawLightBrightness(rawRgbIds.at(LightColor::BLUE));
+    // If we can't get brightness for any of the RGB light
+    if (!redOr.has_value() || !greenOr.has_value() || !blueOr.has_value()) {
+        return std::nullopt;
+    }
+
+    // Compose the ARGB format color. As the R,G,B color led brightness is scaled by Alpha
+    // value, scale it back to return the nominal color value.
+    float ratio = MAX_BRIGHTNESS / brightness;
+    int32_t red = round(redOr.value() * ratio);
+    int32_t green = round(greenOr.value() * ratio);
+    int32_t blue = round(blueOr.value() * ratio);
+
+    if (red > MAX_BRIGHTNESS || green > MAX_BRIGHTNESS || blue > MAX_BRIGHTNESS) {
+        // Previously stored brightness isn't valid for current LED values, so just reset to max
+        // brightness since an app couldn't have provided these values in the first place.
+        red = redOr.value();
+        green = greenOr.value();
+        blue = blueOr.value();
+        brightness = MAX_BRIGHTNESS;
+    }
+
+    return toArgb(brightness, red, green, blue);
+}
+
+std::optional<int32_t> PeripheralController::MultiColorLight::getLightColor() {
+    auto ret = context.getLightIntensities(rawId);
+    if (!ret.has_value()) {
+        return std::nullopt;
+    }
+    std::unordered_map<LightColor, int32_t> intensities = ret.value();
+    // Get red, green, blue colors
+    int32_t color = toArgb(0 /* brightness */, intensities.at(LightColor::RED) /* red */,
+                           intensities.at(LightColor::GREEN) /* green */,
+                           intensities.at(LightColor::BLUE) /* blue */);
+    // Get brightness
+    std::optional<int32_t> brightness = getRawLightBrightness(rawId);
+    if (brightness.has_value()) {
+        return toArgb(brightness.value() /* A */, 0, 0, 0) | color;
+    }
+    return std::nullopt;
+}
+
+bool PeripheralController::PlayerIdLight::setLightPlayerId(int32_t playerId) {
+    if (rawLightIds.find(playerId) == rawLightIds.end()) {
+        return false;
+    }
+    for (const auto& [id, rawId] : rawLightIds) {
+        if (playerId == id) {
+            setRawLightBrightness(rawId, MAX_BRIGHTNESS);
+        } else {
+            setRawLightBrightness(rawId, 0);
+        }
+    }
+    return true;
+}
+
+std::optional<int32_t> PeripheralController::PlayerIdLight::getLightPlayerId() {
+    for (const auto& [id, rawId] : rawLightIds) {
+        std::optional<int32_t> brightness = getRawLightBrightness(rawId);
+        if (brightness.has_value() && brightness.value() > 0) {
+            return id;
+        }
+    }
+    return std::nullopt;
+}
+
+void PeripheralController::SingleLight::dump(std::string& dump) {
+    dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
+}
+
+void PeripheralController::PlayerIdLight::dump(std::string& dump) {
+    dump += StringPrintf(INDENT4 "PlayerId: %d\n", getLightPlayerId().value_or(-1));
+    dump += StringPrintf(INDENT4 "Raw Player ID LEDs:");
+    for (const auto& [id, rawId] : rawLightIds) {
+        dump += StringPrintf("id %d -> %d ", id, rawId);
+    }
+    dump += "\n";
+}
+
+void PeripheralController::RgbLight::dump(std::string& dump) {
+    dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
+    dump += StringPrintf(INDENT4 "Raw RGB LEDs: [%d, %d, %d] ", rawRgbIds.at(LightColor::RED),
+                         rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
+    if (rawGlobalId.has_value()) {
+        dump += StringPrintf(INDENT4 "Raw Global LED: [%d] ", rawGlobalId.value());
+    }
+    dump += "\n";
+}
+
+void PeripheralController::MultiColorLight::dump(std::string& dump) {
+    dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
+}
+
+void PeripheralController::populateDeviceInfo(InputDeviceInfo* deviceInfo) {
+    // TODO: b/180733860 Remove this after enabling multi-battery
+    if (!mBatteries.empty()) {
+        deviceInfo->setHasBattery(true);
+    }
+
+    for (const auto& [batteryId, battery] : mBatteries) {
+        InputDeviceBatteryInfo batteryInfo(battery->name, battery->id);
+        deviceInfo->addBatteryInfo(batteryInfo);
+    }
+
+    for (const auto& [lightId, light] : mLights) {
+        // Input device light doesn't support ordinal, always pass 1.
+        InputDeviceLightInfo lightInfo(light->name, light->id, light->type, 1 /* ordinal */);
+        deviceInfo->addLightInfo(lightInfo);
+    }
+}
+
+void PeripheralController::dump(std::string& dump) {
+    dump += INDENT2 "Input Controller:\n";
+    if (!mLights.empty()) {
+        dump += INDENT3 "Lights:\n";
+        for (const auto& [lightId, light] : mLights) {
+            dump += StringPrintf(INDENT4 "Id: %d", lightId);
+            dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str());
+            dump += StringPrintf(INDENT4 "Type: %s", NamedEnum::string(light->type).c_str());
+            light->dump(dump);
+        }
+    }
+    // Dump raw lights
+    dump += INDENT3 "RawLights:\n";
+    dump += INDENT4 "Id:\t Name:\t Flags:\t Max brightness:\t Brightness\n";
+    const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
+    // Map from raw light id to raw light info
+    std::unordered_map<int32_t, RawLightInfo> rawInfos;
+    for (const auto& rawId : rawLightIds) {
+        std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
+        if (!rawInfo.has_value()) {
+            continue;
+        }
+        dump += StringPrintf(INDENT4 "%d", rawId);
+        dump += StringPrintf(INDENT4 "%s", rawInfo->name.c_str());
+        dump += StringPrintf(INDENT4 "%s", rawInfo->flags.string().c_str());
+        dump += StringPrintf(INDENT4 "%d", rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS));
+        dump += StringPrintf(INDENT4 "%d\n",
+                             getDeviceContext().getLightBrightness(rawId).value_or(-1));
+    }
+
+    if (!mBatteries.empty()) {
+        dump += INDENT3 "Batteries:\n";
+        for (const auto& [batteryId, battery] : mBatteries) {
+            dump += StringPrintf(INDENT4 "Id: %d", batteryId);
+            dump += StringPrintf(INDENT4 "Name: %s", battery->name.c_str());
+            dump += getBatteryCapacity(batteryId).has_value()
+                    ? StringPrintf(INDENT3 "Capacity: %d\n", getBatteryCapacity(batteryId).value())
+                    : StringPrintf(INDENT3 "Capacity: Unknown");
+
+            std::string status;
+            switch (getBatteryStatus(batteryId).value_or(BATTERY_STATUS_UNKNOWN)) {
+                case BATTERY_STATUS_CHARGING:
+                    status = "Charging";
+                    break;
+                case BATTERY_STATUS_DISCHARGING:
+                    status = "Discharging";
+                    break;
+                case BATTERY_STATUS_NOT_CHARGING:
+                    status = "Not charging";
+                    break;
+                case BATTERY_STATUS_FULL:
+                    status = "Full";
+                    break;
+                default:
+                    status = "Unknown";
+            }
+            dump += StringPrintf(INDENT3 "Status: %s\n", status.c_str());
+        }
+    }
+}
+
+void PeripheralController::configureBattries() {
+    // Check raw batteries
+    const std::vector<int32_t> rawBatteryIds = getDeviceContext().getRawBatteryIds();
+
+    for (const auto& rawId : rawBatteryIds) {
+        std::optional<RawBatteryInfo> rawInfo = getDeviceContext().getRawBatteryInfo(rawId);
+        if (!rawInfo.has_value()) {
+            continue;
+        }
+        std::unique_ptr<Battery> battery =
+                std::make_unique<Battery>(getDeviceContext(), rawInfo->name, rawInfo->id);
+        mBatteries.insert_or_assign(rawId, std::move(battery));
+    }
+}
+
+void PeripheralController::configureLights() {
+    bool hasRedLed = false;
+    bool hasGreenLed = false;
+    bool hasBlueLed = false;
+    std::optional<int32_t> rawGlobalId = std::nullopt;
+    // Player ID light common name string
+    std::string playerIdName;
+    // Raw RGB color to raw light ID
+    std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds;
+    // Map from player Id to raw light Id
+    std::unordered_map<int32_t, int32_t> playerIdLightIds;
+
+    // Check raw lights
+    const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
+    // Map from raw light id to raw light info
+    std::unordered_map<int32_t, RawLightInfo> rawInfos;
+    for (const auto& rawId : rawLightIds) {
+        std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
+        if (!rawInfo.has_value()) {
+            continue;
+        }
+        rawInfos.insert_or_assign(rawId, rawInfo.value());
+        // Check if this is a group LEDs for player ID
+        std::regex lightPattern("([a-z]+)([0-9]+)");
+        std::smatch results;
+        if (std::regex_match(rawInfo->name, results, lightPattern)) {
+            std::string commonName = results[1].str();
+            int32_t playerId = std::stoi(results[2]);
+            if (playerIdLightIds.empty()) {
+                playerIdName = commonName;
+                playerIdLightIds.insert_or_assign(playerId, rawId);
+            } else {
+                // Make sure the player ID leds have common string name
+                if (playerIdName.compare(commonName) == 0 &&
+                    playerIdLightIds.find(playerId) == playerIdLightIds.end()) {
+                    playerIdLightIds.insert_or_assign(playerId, rawId);
+                }
+            }
+        }
+        // Check if this is an LED of RGB light
+        if (rawInfo->flags.test(InputLightClass::RED)) {
+            hasRedLed = true;
+            rawRgbIds.emplace(LightColor::RED, rawId);
+        }
+        if (rawInfo->flags.test(InputLightClass::GREEN)) {
+            hasGreenLed = true;
+            rawRgbIds.emplace(LightColor::GREEN, rawId);
+        }
+        if (rawInfo->flags.test(InputLightClass::BLUE)) {
+            hasBlueLed = true;
+            rawRgbIds.emplace(LightColor::BLUE, rawId);
+        }
+        if (rawInfo->flags.test(InputLightClass::GLOBAL)) {
+            rawGlobalId = rawId;
+        }
+        if (DEBUG_LIGHT_DETAILS) {
+            ALOGD("Light rawId %d name %s max %d flags %s \n", rawInfo->id, rawInfo->name.c_str(),
+                  rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS), rawInfo->flags.string().c_str());
+        }
+    }
+
+    // Construct a player ID light
+    if (playerIdLightIds.size() > 1) {
+        std::unique_ptr<Light> light =
+                std::make_unique<PlayerIdLight>(getDeviceContext(), playerIdName, ++mNextId,
+                                                playerIdLightIds);
+        mLights.insert_or_assign(light->id, std::move(light));
+        // Remove these raw lights from raw light info as they've been used to compose a
+        // Player ID light, so we do not expose these raw lights as single lights.
+        for (const auto& [playerId, rawId] : playerIdLightIds) {
+            rawInfos.erase(rawId);
+        }
+    }
+    // Construct a RGB light for composed RGB light
+    if (hasRedLed && hasGreenLed && hasBlueLed) {
+        if (DEBUG_LIGHT_DETAILS) {
+            ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED),
+                  rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
+        }
+        std::unique_ptr<Light> light =
+                std::make_unique<RgbLight>(getDeviceContext(), ++mNextId, rawRgbIds, rawGlobalId);
+        mLights.insert_or_assign(light->id, std::move(light));
+        // Remove from raw light info as they've been composed a RBG light.
+        rawInfos.erase(rawRgbIds.at(LightColor::RED));
+        rawInfos.erase(rawRgbIds.at(LightColor::GREEN));
+        rawInfos.erase(rawRgbIds.at(LightColor::BLUE));
+        if (rawGlobalId.has_value()) {
+            rawInfos.erase(rawGlobalId.value());
+        }
+    }
+
+    // Check the rest of raw light infos
+    for (const auto& [rawId, rawInfo] : rawInfos) {
+        // If the node is multi-color led, construct a MULTI_COLOR light
+        if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) &&
+            rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) {
+            if (DEBUG_LIGHT_DETAILS) {
+                ALOGD("Multicolor light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
+            }
+            std::unique_ptr<Light> light =
+                    std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name, ++mNextId,
+                                                      rawInfo.id);
+            mLights.insert_or_assign(light->id, std::move(light));
+            continue;
+        }
+        // Construct a single LED light
+        if (DEBUG_LIGHT_DETAILS) {
+            ALOGD("Single light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
+        }
+        std::unique_ptr<Light> light =
+                std::make_unique<SingleLight>(getDeviceContext(), rawInfo.name, ++mNextId,
+                                              rawInfo.id);
+
+        mLights.insert_or_assign(light->id, std::move(light));
+    }
+}
+
+std::optional<int32_t> PeripheralController::getBatteryCapacity(int batteryId) {
+    return getDeviceContext().getBatteryCapacity(batteryId);
+}
+
+std::optional<int32_t> PeripheralController::getBatteryStatus(int batteryId) {
+    return getDeviceContext().getBatteryStatus(batteryId);
+}
+
+bool PeripheralController::setLightColor(int32_t lightId, int32_t color) {
+    auto it = mLights.find(lightId);
+    if (it == mLights.end()) {
+        return false;
+    }
+    auto& light = it->second;
+    if (DEBUG_LIGHT_DETAILS) {
+        ALOGD("setLightColor lightId %d type %s color 0x%x", lightId,
+              NamedEnum::string(light->type).c_str(), color);
+    }
+    return light->setLightColor(color);
+}
+
+std::optional<int32_t> PeripheralController::getLightColor(int32_t lightId) {
+    auto it = mLights.find(lightId);
+    if (it == mLights.end()) {
+        return std::nullopt;
+    }
+    auto& light = it->second;
+    std::optional<int32_t> color = light->getLightColor();
+    if (DEBUG_LIGHT_DETAILS) {
+        ALOGD("getLightColor lightId %d type %s color 0x%x", lightId,
+              NamedEnum::string(light->type).c_str(), color.value_or(0));
+    }
+    return color;
+}
+
+bool PeripheralController::setLightPlayerId(int32_t lightId, int32_t playerId) {
+    auto it = mLights.find(lightId);
+    if (it == mLights.end()) {
+        return false;
+    }
+    auto& light = it->second;
+    return light->setLightPlayerId(playerId);
+}
+
+std::optional<int32_t> PeripheralController::getLightPlayerId(int32_t lightId) {
+    auto it = mLights.find(lightId);
+    if (it == mLights.end()) {
+        return std::nullopt;
+    }
+    auto& light = it->second;
+    return light->getLightPlayerId();
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/LightInputMapper.h b/services/inputflinger/reader/controller/PeripheralController.h
similarity index 80%
rename from services/inputflinger/reader/mapper/LightInputMapper.h
rename to services/inputflinger/reader/controller/PeripheralController.h
index 43141b8..ff3607f 100644
--- a/services/inputflinger/reader/mapper/LightInputMapper.h
+++ b/services/inputflinger/reader/controller/PeripheralController.h
@@ -14,35 +14,49 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H
-#define _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H
+#ifndef _UI_INPUTREADER_LIGHT_CONTROLLER_H
+#define _UI_INPUTREADER_LIGHT_CONTROLLER_H
 
-#include "InputMapper.h"
+#include "PeripheralControllerInterface.h"
 
 namespace android {
 
-class LightInputMapper : public InputMapper {
+class PeripheralController : public PeripheralControllerInterface {
     // Refer to https://developer.android.com/reference/kotlin/android/graphics/Color
     /* Number of colors : {red, green, blue} */
     static constexpr size_t COLOR_NUM = 3;
     static constexpr int32_t MAX_BRIGHTNESS = 0xff;
 
 public:
-    explicit LightInputMapper(InputDeviceContext& deviceContext);
-    ~LightInputMapper() override;
+    explicit PeripheralController(InputDeviceContext& deviceContext);
+    ~PeripheralController() override;
 
-    uint32_t getSources() override;
     void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     void dump(std::string& dump) override;
-    void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override;
-    void reset(nsecs_t when) override;
-    void process(const RawEvent* rawEvent) override;
     bool setLightColor(int32_t lightId, int32_t color) override;
     bool setLightPlayerId(int32_t lightId, int32_t playerId) override;
     std::optional<int32_t> getLightColor(int32_t lightId) override;
     std::optional<int32_t> getLightPlayerId(int32_t lightId) override;
+    std::optional<int32_t> getBatteryCapacity(int32_t batteryId) override;
+    std::optional<int32_t> getBatteryStatus(int32_t batteryId) override;
 
 private:
+    inline int32_t getDeviceId() { return mDeviceContext.getId(); }
+    inline InputDeviceContext& getDeviceContext() { return mDeviceContext; }
+
+    InputDeviceContext& mDeviceContext;
+    void configureLights();
+    void configureBattries();
+
+    struct Battery {
+        explicit Battery(InputDeviceContext& context, const std::string& name, int32_t id)
+              : context(context), name(name), id(id) {}
+        virtual ~Battery() {}
+        InputDeviceContext& context;
+        std::string name;
+        int32_t id;
+    };
+
     struct Light {
         explicit Light(InputDeviceContext& context, const std::string& name, int32_t id,
                        InputDeviceLightType type)
@@ -128,8 +142,11 @@
 
     // Light map from light ID to Light
     std::unordered_map<int32_t, std::unique_ptr<Light>> mLights;
+
+    // Battery map from battery ID to battery
+    std::unordered_map<int32_t, std::unique_ptr<Battery>> mBatteries;
 };
 
 } // namespace android
 
-#endif // _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H
+#endif // _UI_INPUTREADER_LIGHT_CONTROLLER_H
diff --git a/services/inputflinger/reader/controller/PeripheralControllerInterface.h b/services/inputflinger/reader/controller/PeripheralControllerInterface.h
new file mode 100644
index 0000000..7688a43
--- /dev/null
+++ b/services/inputflinger/reader/controller/PeripheralControllerInterface.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_INPUTREADER_INPUT_CONTROLLER_H
+#define _UI_INPUTREADER_INPUT_CONTROLLER_H
+
+#include "EventHub.h"
+#include "InputDevice.h"
+#include "InputListener.h"
+#include "InputReaderContext.h"
+
+namespace android {
+
+/* A peripheral controller manages the input device peripherals associated with the input device,
+ * like the sysfs based battery and light class devices.
+ *
+ */
+class PeripheralControllerInterface {
+public:
+    PeripheralControllerInterface() {}
+    virtual ~PeripheralControllerInterface() {}
+
+    // Interface methods for Battery
+    virtual std::optional<int32_t> getBatteryCapacity(int32_t batteryId) = 0;
+    virtual std::optional<int32_t> getBatteryStatus(int32_t batteryId) = 0;
+
+    // Interface methods for Light
+    virtual bool setLightColor(int32_t lightId, int32_t color) = 0;
+    virtual bool setLightPlayerId(int32_t lightId, int32_t playerId) = 0;
+    virtual std::optional<int32_t> getLightColor(int32_t lightId) = 0;
+    virtual std::optional<int32_t> getLightPlayerId(int32_t lightId) = 0;
+
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) = 0;
+    virtual void dump(std::string& dump) = 0;
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_INPUT_CONTROLLER_H
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 2afaa85..410a706 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -173,6 +173,15 @@
     MAX_BRIGHTNESS = 0x00000080,
 };
 
+enum class InputBatteryClass : uint32_t {
+    /* The input device battery has capacity node. */
+    CAPACITY = 0x00000001,
+    /* The input device battery has capacity_level node. */
+    CAPACITY_LEVEL = 0x00000002,
+    /* The input device battery has status node. */
+    STATUS = 0x00000004,
+};
+
 /* Describes a raw light. */
 struct RawLightInfo {
     int32_t id;
@@ -183,6 +192,14 @@
     std::filesystem::path path;
 };
 
+/* Describes a raw battery. */
+struct RawBatteryInfo {
+    int32_t id;
+    std::string name;
+    Flags<InputBatteryClass> flags;
+    std::filesystem::path path;
+};
+
 /*
  * Gets the class that owns an axis, in cases where multiple classes might claim
  * the same axis for different purposes.
@@ -263,6 +280,12 @@
     virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) = 0;
     virtual base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t deviceId,
                                                                               int32_t absCode) = 0;
+    // Raw batteries are sysfs power_supply nodes we found from the EventHub device sysfs node,
+    // containing the raw info of the sysfs node structure.
+    virtual const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) = 0;
+    virtual std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
+                                                            int32_t BatteryId) = 0;
+
     // Raw lights are sysfs led light nodes we found from the EventHub device sysfs node,
     // containing the raw info of the sysfs node structure.
     virtual const std::vector<int32_t> getRawLightIds(int32_t deviceId) = 0;
@@ -307,10 +330,11 @@
     virtual std::vector<int32_t> getVibratorIds(int32_t deviceId) = 0;
 
     /* Query battery level. */
-    virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId) const = 0;
+    virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId,
+                                                      int32_t batteryId) const = 0;
 
     /* Query battery status. */
-    virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId) const = 0;
+    virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId, int32_t batteryId) const = 0;
 
     /* Requests the EventHub to reopen all input devices on the next call to getEvents(). */
     virtual void requestReopenDevices() = 0;
@@ -435,6 +459,10 @@
     base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
             int32_t deviceId, int32_t absCode) override final;
 
+    const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) override final;
+    std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
+                                                    int32_t BatteryId) override final;
+
     const std::vector<int32_t> getRawLightIds(int32_t deviceId) override final;
 
     std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) override final;
@@ -485,9 +513,11 @@
 
     void monitor() override final;
 
-    std::optional<int32_t> getBatteryCapacity(int32_t deviceId) const override final;
+    std::optional<int32_t> getBatteryCapacity(int32_t deviceId,
+                                              int32_t batteryId) const override final;
 
-    std::optional<int32_t> getBatteryStatus(int32_t deviceId) const override final;
+    std::optional<int32_t> getBatteryStatus(int32_t deviceId,
+                                            int32_t batteryId) const override final;
 
     bool isDeviceEnabled(int32_t deviceId) override final;
 
@@ -498,6 +528,22 @@
     ~EventHub() override;
 
 private:
+    struct AssociatedDevice {
+        // The device descriptor from evdev device the misc device associated with.
+        std::string descriptor;
+        // The sysfs root path of the misc device.
+        std::filesystem::path sysfsRootPath;
+
+        int32_t nextBatteryId;
+        int32_t nextLightId;
+        std::unordered_map<int32_t, RawBatteryInfo> batteryInfos;
+        std::unordered_map<int32_t, RawLightInfo> lightInfos;
+        explicit AssociatedDevice(std::filesystem::path sysfsRootPath)
+              : sysfsRootPath(sysfsRootPath), nextBatteryId(0), nextLightId(0) {}
+        bool configureBatteryLocked();
+        bool configureLightsLocked();
+    };
+
     struct Device {
         int fd; // may be -1 if device is closed
         const int32_t id;
@@ -527,12 +573,9 @@
         bool ffEffectPlaying;
         int16_t ffEffectId; // initially -1
 
-        // The paths are invalid when they're std::nullopt
-        std::optional<std::filesystem::path> sysfsRootPath;
-        std::optional<std::filesystem::path> sysfsBatteryPath;
-        // maps from light id to light info
-        std::unordered_map<int32_t, RawLightInfo> lightInfos;
-        int32_t nextLightId;
+        // A shared_ptr of a device associated with the input device.
+        // The input devices with same descriptor has the same associated device.
+        std::shared_ptr<AssociatedDevice> associatedDevice;
 
         int32_t controllerNumber;
 
@@ -563,8 +606,6 @@
         void setLedForControllerLocked();
         status_t mapLed(int32_t led, int32_t* outScanCode) const;
         void setLedStateLocked(int32_t led, bool on);
-        bool configureBatteryLocked();
-        bool configureLightsLocked();
     };
 
     /**
@@ -616,6 +657,12 @@
     void reportDeviceAddedForStatisticsLocked(const InputDeviceIdentifier& identifier,
                                               Flags<InputDeviceClass> classes) REQUIRES(mLock);
 
+    const std::unordered_map<int32_t, RawBatteryInfo>& getBatteryInfoLocked(int32_t deviceId) const
+            REQUIRES(mLock);
+
+    const std::unordered_map<int32_t, RawLightInfo>& getLightInfoLocked(int32_t deviceId) const
+            REQUIRES(mLock);
+
     // Protect all internal state.
     mutable std::mutex mLock;
 
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 863cd41..b2b23e5 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -32,7 +32,11 @@
 #include "InputReaderContext.h"
 
 namespace android {
+// TODO b/180733860 support multiple battery in API and remove this.
+constexpr int32_t DEFAULT_BATTERY_ID = 1;
 
+class PeripheralController;
+class PeripheralControllerInterface;
 class InputDeviceContext;
 class InputMapper;
 
@@ -131,6 +135,20 @@
         return *mapper;
     }
 
+    // construct and add a controller to the input device
+    template <class T>
+    T& addController(int32_t eventHubId) {
+        // ensure a device entry exists for this eventHubId
+        addEventHubDevice(eventHubId, false);
+
+        // create controller
+        auto& devicePair = mDevices[eventHubId];
+        auto& deviceContext = devicePair.first;
+
+        mController = std::make_unique<T>(*deviceContext);
+        return *(reinterpret_cast<T*>(mController.get()));
+    }
+
 private:
     InputReaderContext* mContext;
     int32_t mId;
@@ -143,7 +161,10 @@
     // map from eventHubId to device context and mappers
     using MapperVector = std::vector<std::unique_ptr<InputMapper>>;
     using DevicePair = std::pair<std::unique_ptr<InputDeviceContext>, MapperVector>;
+    // Map from EventHub ID to pair of device context and vector of mapper.
     std::unordered_map<int32_t, DevicePair> mDevices;
+    // Misc devices controller for lights, battery, etc.
+    std::unique_ptr<PeripheralControllerInterface> mController;
 
     uint32_t mSources;
     bool mIsExternal;
@@ -319,11 +340,21 @@
 
     inline std::vector<int32_t> getVibratorIds() { return mEventHub->getVibratorIds(mId); }
 
-    inline std::optional<int32_t> getBatteryCapacity() {
-        return mEventHub->getBatteryCapacity(mId);
+    inline const std::vector<int32_t> getRawBatteryIds() {
+        return mEventHub->getRawBatteryIds(mId);
     }
 
-    inline std::optional<int32_t> getBatteryStatus() { return mEventHub->getBatteryStatus(mId); }
+    inline std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t batteryId) {
+        return mEventHub->getRawBatteryInfo(mId, batteryId);
+    }
+
+    inline std::optional<int32_t> getBatteryCapacity(int32_t batteryId) {
+        return mEventHub->getBatteryCapacity(mId, batteryId);
+    }
+
+    inline std::optional<int32_t> getBatteryStatus(int32_t batteryId) {
+        return mEventHub->getBatteryStatus(mId, batteryId);
+    }
 
     inline bool hasAbsoluteAxis(int32_t code) const {
         RawAbsoluteAxisInfo info;
diff --git a/services/inputflinger/reader/mapper/BatteryInputMapper.cpp b/services/inputflinger/reader/mapper/BatteryInputMapper.cpp
deleted file mode 100644
index e4fb3a6..0000000
--- a/services/inputflinger/reader/mapper/BatteryInputMapper.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 "../Macros.h"
-
-#include "BatteryInputMapper.h"
-
-namespace android {
-
-BatteryInputMapper::BatteryInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext) {}
-
-uint32_t BatteryInputMapper::getSources() {
-    return AINPUT_SOURCE_UNKNOWN;
-}
-
-void BatteryInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
-    InputMapper::populateDeviceInfo(info);
-
-    info->setHasBattery(true);
-}
-
-void BatteryInputMapper::process(const RawEvent* rawEvent) {}
-
-std::optional<int32_t> BatteryInputMapper::getBatteryCapacity() {
-    return getDeviceContext().getBatteryCapacity();
-}
-
-std::optional<int32_t> BatteryInputMapper::getBatteryStatus() {
-    return getDeviceContext().getBatteryStatus();
-}
-
-void BatteryInputMapper::dump(std::string& dump) {
-    dump += INDENT2 "Battery Input Mapper:\n";
-    dump += getBatteryCapacity().has_value()
-            ? StringPrintf(INDENT3 "Capacity: %d\n", getBatteryCapacity().value())
-            : StringPrintf(INDENT3 "Capacity: Unknown");
-
-    std::string status;
-    switch (getBatteryStatus().value_or(BATTERY_STATUS_UNKNOWN)) {
-        case BATTERY_STATUS_CHARGING:
-            status = "Charging";
-            break;
-        case BATTERY_STATUS_DISCHARGING:
-            status = "Discharging";
-            break;
-        case BATTERY_STATUS_NOT_CHARGING:
-            status = "Not charging";
-            break;
-        case BATTERY_STATUS_FULL:
-            status = "Full";
-            break;
-        default:
-            status = "Unknown";
-    }
-    dump += StringPrintf(INDENT3 "Status: %s\n", status.c_str());
-}
-
-} // namespace android
diff --git a/services/inputflinger/reader/mapper/BatteryInputMapper.h b/services/inputflinger/reader/mapper/BatteryInputMapper.h
deleted file mode 100644
index 4fe373e..0000000
--- a/services/inputflinger/reader/mapper/BatteryInputMapper.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_INPUTREADER_BATTERY_INPUT_MAPPER_H
-#define _UI_INPUTREADER_BATTERY_INPUT_MAPPER_H
-
-#include "InputMapper.h"
-
-namespace android {
-
-class BatteryInputMapper : public InputMapper {
-public:
-    explicit BatteryInputMapper(InputDeviceContext& deviceContext);
-    virtual ~BatteryInputMapper(){};
-
-    uint32_t getSources() override;
-    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
-    void process(const RawEvent* rawEvent) override;
-
-    std::optional<int32_t> getBatteryCapacity() override;
-    std::optional<int32_t> getBatteryStatus() override;
-
-    void dump(std::string& dump) override;
-};
-
-} // namespace android
-
-#endif // _UI_INPUTREADER_BATTERY_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/LightInputMapper.cpp b/services/inputflinger/reader/mapper/LightInputMapper.cpp
deleted file mode 100644
index be1f722..0000000
--- a/services/inputflinger/reader/mapper/LightInputMapper.cpp
+++ /dev/null
@@ -1,478 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 <locale>
-#include <regex>
-
-#include "../Macros.h"
-
-#include "LightInputMapper.h"
-#include "input/NamedEnum.h"
-
-// Log detailed debug messages about input device lights.
-static constexpr bool DEBUG_LIGHT_DETAILS = false;
-
-namespace android {
-
-static inline int32_t getAlpha(int32_t color) {
-    return (color >> 24) & 0xff;
-}
-
-static inline int32_t getRed(int32_t color) {
-    return (color >> 16) & 0xff;
-}
-
-static inline int32_t getGreen(int32_t color) {
-    return (color >> 8) & 0xff;
-}
-
-static inline int32_t getBlue(int32_t color) {
-    return color & 0xff;
-}
-
-static inline int32_t toArgb(int32_t brightness, int32_t red, int32_t green, int32_t blue) {
-    return (brightness & 0xff) << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff);
-}
-
-/**
- * Light input mapper owned by InputReader device, implements the native API for querying input
- * lights, getting and setting the lights brightness and color, by interacting with EventHub
- * devices.
- * TODO b/180342233: Reconsider the inputflinger design to accommodate the device class
- * like lights and battery.
- */
-LightInputMapper::LightInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext) {}
-
-LightInputMapper::~LightInputMapper() {}
-
-std::optional<std::int32_t> LightInputMapper::Light::getRawLightBrightness(int32_t rawLightId) {
-    std::optional<RawLightInfo> rawInfo = context.getRawLightInfo(rawLightId);
-    std::optional<int32_t> ret = context.getLightBrightness(rawLightId);
-    if (!rawInfo.has_value() || !ret.has_value()) {
-        return std::nullopt;
-    }
-    int brightness = ret.value();
-
-    // If the light node doesn't have max brightness, use the default max brightness.
-    int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS);
-    float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
-    // Scale the returned brightness in [0, rawMaxBrightness] to [0, 255]
-    if (rawMaxBrightness != MAX_BRIGHTNESS) {
-        brightness = brightness * ratio;
-    }
-    if (DEBUG_LIGHT_DETAILS) {
-        ALOGD("getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
-              brightness, ratio);
-    }
-    return brightness;
-}
-
-void LightInputMapper::Light::setRawLightBrightness(int32_t rawLightId, int32_t brightness) {
-    std::optional<RawLightInfo> rawInfo = context.getRawLightInfo(rawLightId);
-    if (!rawInfo.has_value()) {
-        return;
-    }
-    // If the light node doesn't have max brightness, use the default max brightness.
-    int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS);
-    float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
-    // Scale the requested brightness in [0, 255] to [0, rawMaxBrightness]
-    if (rawMaxBrightness != MAX_BRIGHTNESS) {
-        brightness = ceil(brightness / ratio);
-    }
-    if (DEBUG_LIGHT_DETAILS) {
-        ALOGD("setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
-              brightness, ratio);
-    }
-    context.setLightBrightness(rawLightId, brightness);
-}
-
-bool LightInputMapper::SingleLight::setLightColor(int32_t color) {
-    int32_t brightness = getAlpha(color);
-    setRawLightBrightness(rawId, brightness);
-
-    return true;
-}
-
-bool LightInputMapper::RgbLight::setLightColor(int32_t color) {
-    // Compose color value as per:
-    // https://developer.android.com/reference/android/graphics/Color?hl=en
-    // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
-    // The alpha component is used to scale the R,G,B leds brightness, with the ratio to
-    // MAX_BRIGHTNESS.
-    brightness = getAlpha(color);
-    int32_t red = 0;
-    int32_t green = 0;
-    int32_t blue = 0;
-    if (brightness > 0) {
-        float ratio = MAX_BRIGHTNESS / brightness;
-        red = ceil(getRed(color) / ratio);
-        green = ceil(getGreen(color) / ratio);
-        blue = ceil(getBlue(color) / ratio);
-    }
-    setRawLightBrightness(rawRgbIds.at(LightColor::RED), red);
-    setRawLightBrightness(rawRgbIds.at(LightColor::GREEN), green);
-    setRawLightBrightness(rawRgbIds.at(LightColor::BLUE), blue);
-    if (rawGlobalId.has_value()) {
-        setRawLightBrightness(rawGlobalId.value(), brightness);
-    }
-
-    return true;
-}
-
-bool LightInputMapper::MultiColorLight::setLightColor(int32_t color) {
-    std::unordered_map<LightColor, int32_t> intensities;
-    intensities.emplace(LightColor::RED, getRed(color));
-    intensities.emplace(LightColor::GREEN, getGreen(color));
-    intensities.emplace(LightColor::BLUE, getBlue(color));
-
-    context.setLightIntensities(rawId, intensities);
-    setRawLightBrightness(rawId, getAlpha(color));
-    return true;
-}
-
-std::optional<int32_t> LightInputMapper::SingleLight::getLightColor() {
-    std::optional<int32_t> brightness = getRawLightBrightness(rawId);
-    if (!brightness.has_value()) {
-        return std::nullopt;
-    }
-
-    return toArgb(brightness.value(), 0 /* red */, 0 /* green */, 0 /* blue */);
-}
-
-std::optional<int32_t> LightInputMapper::RgbLight::getLightColor() {
-    // If the Alpha component is zero, then return color 0.
-    if (brightness == 0) {
-        return 0;
-    }
-    // Compose color value as per:
-    // https://developer.android.com/reference/android/graphics/Color?hl=en
-    // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
-    std::optional<int32_t> redOr = getRawLightBrightness(rawRgbIds.at(LightColor::RED));
-    std::optional<int32_t> greenOr = getRawLightBrightness(rawRgbIds.at(LightColor::GREEN));
-    std::optional<int32_t> blueOr = getRawLightBrightness(rawRgbIds.at(LightColor::BLUE));
-    // If we can't get brightness for any of the RGB light
-    if (!redOr.has_value() || !greenOr.has_value() || !blueOr.has_value()) {
-        return std::nullopt;
-    }
-
-    // Compose the ARGB format color. As the R,G,B color led brightness is scaled by Alpha
-    // value, scale it back to return the nominal color value.
-    float ratio = MAX_BRIGHTNESS / brightness;
-    int32_t red = round(redOr.value() * ratio);
-    int32_t green = round(greenOr.value() * ratio);
-    int32_t blue = round(blueOr.value() * ratio);
-
-    if (red > MAX_BRIGHTNESS || green > MAX_BRIGHTNESS || blue > MAX_BRIGHTNESS) {
-        // Previously stored brightness isn't valid for current LED values, so just reset to max
-        // brightness since an app couldn't have provided these values in the first place.
-        red = redOr.value();
-        green = greenOr.value();
-        blue = blueOr.value();
-        brightness = MAX_BRIGHTNESS;
-    }
-
-    return toArgb(brightness, red, green, blue);
-}
-
-std::optional<int32_t> LightInputMapper::MultiColorLight::getLightColor() {
-    auto ret = context.getLightIntensities(rawId);
-    if (!ret.has_value()) {
-        return std::nullopt;
-    }
-    std::unordered_map<LightColor, int32_t> intensities = ret.value();
-    // Get red, green, blue colors
-    int32_t color = toArgb(0 /* brightness */, intensities.at(LightColor::RED) /* red */,
-                           intensities.at(LightColor::GREEN) /* green */,
-                           intensities.at(LightColor::BLUE) /* blue */);
-    // Get brightness
-    std::optional<int32_t> brightness = getRawLightBrightness(rawId);
-    if (brightness.has_value()) {
-        return toArgb(brightness.value() /* A */, 0, 0, 0) | color;
-    }
-    return std::nullopt;
-}
-
-bool LightInputMapper::PlayerIdLight::setLightPlayerId(int32_t playerId) {
-    if (rawLightIds.find(playerId) == rawLightIds.end()) {
-        return false;
-    }
-    for (const auto& [id, rawId] : rawLightIds) {
-        if (playerId == id) {
-            setRawLightBrightness(rawId, MAX_BRIGHTNESS);
-        } else {
-            setRawLightBrightness(rawId, 0);
-        }
-    }
-    return true;
-}
-
-std::optional<int32_t> LightInputMapper::PlayerIdLight::getLightPlayerId() {
-    for (const auto& [id, rawId] : rawLightIds) {
-        std::optional<int32_t> brightness = getRawLightBrightness(rawId);
-        if (brightness.has_value() && brightness.value() > 0) {
-            return id;
-        }
-    }
-    return std::nullopt;
-}
-
-void LightInputMapper::SingleLight::dump(std::string& dump) {
-    dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
-}
-
-void LightInputMapper::PlayerIdLight::dump(std::string& dump) {
-    dump += StringPrintf(INDENT4 "PlayerId: %d\n", getLightPlayerId().value_or(-1));
-    dump += StringPrintf(INDENT4 "Raw Player ID LEDs:");
-    for (const auto& [id, rawId] : rawLightIds) {
-        dump += StringPrintf("id %d -> %d ", id, rawId);
-    }
-    dump += "\n";
-}
-
-void LightInputMapper::RgbLight::dump(std::string& dump) {
-    dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
-    dump += StringPrintf(INDENT4 "Raw RGB LEDs: [%d, %d, %d] ", rawRgbIds.at(LightColor::RED),
-                         rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
-    if (rawGlobalId.has_value()) {
-        dump += StringPrintf(INDENT4 "Raw Global LED: [%d] ", rawGlobalId.value());
-    }
-    dump += "\n";
-}
-
-void LightInputMapper::MultiColorLight::dump(std::string& dump) {
-    dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
-}
-
-uint32_t LightInputMapper::getSources() {
-    return AINPUT_SOURCE_UNKNOWN;
-}
-
-void LightInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
-    InputMapper::populateDeviceInfo(info);
-
-    for (const auto& [lightId, light] : mLights) {
-        // Input device light doesn't support ordinal, always pass 1.
-        InputDeviceLightInfo lightInfo(light->name, light->id, light->type, 1 /* ordinal */);
-        info->addLightInfo(lightInfo);
-    }
-}
-
-void LightInputMapper::dump(std::string& dump) {
-    dump += INDENT2 "Light Input Mapper:\n";
-    dump += INDENT3 "Lights:\n";
-    for (const auto& [lightId, light] : mLights) {
-        dump += StringPrintf(INDENT4 "Id: %d", lightId);
-        dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str());
-        dump += StringPrintf(INDENT4 "Type: %s", NamedEnum::string(light->type).c_str());
-        light->dump(dump);
-    }
-    // Dump raw lights
-    dump += INDENT3 "RawLights:\n";
-    dump += INDENT4 "Id:\t Name:\t Flags:\t Max brightness:\t Brightness\n";
-    const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
-    // Map from raw light id to raw light info
-    std::unordered_map<int32_t, RawLightInfo> rawInfos;
-    for (const auto& rawId : rawLightIds) {
-        std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
-        if (!rawInfo.has_value()) {
-            continue;
-        }
-        dump += StringPrintf(INDENT4 "%d", rawId);
-        dump += StringPrintf(INDENT4 "%s", rawInfo->name.c_str());
-        dump += StringPrintf(INDENT4 "%s", rawInfo->flags.string().c_str());
-        dump += StringPrintf(INDENT4 "%d", rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS));
-        dump += StringPrintf(INDENT4 "%d\n",
-                             getDeviceContext().getLightBrightness(rawId).value_or(-1));
-    }
-}
-
-void LightInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                                 uint32_t changes) {
-    InputMapper::configure(when, config, changes);
-
-    if (!changes) { // first time only
-        bool hasRedLed = false;
-        bool hasGreenLed = false;
-        bool hasBlueLed = false;
-        std::optional<int32_t> rawGlobalId = std::nullopt;
-        // Player ID light common name string
-        std::string playerIdName;
-        // Raw RGB color to raw light ID
-        std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds;
-        // Map from player Id to raw light Id
-        std::unordered_map<int32_t, int32_t> playerIdLightIds;
-        mLights.clear();
-
-        // Check raw lights
-        const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
-        // Map from raw light id to raw light info
-        std::unordered_map<int32_t, RawLightInfo> rawInfos;
-        for (const auto& rawId : rawLightIds) {
-            std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
-            if (!rawInfo.has_value()) {
-                continue;
-            }
-            rawInfos.insert_or_assign(rawId, rawInfo.value());
-            // Check if this is a group LEDs for player ID
-            std::regex lightPattern("([a-z]+)([0-9]+)");
-            std::smatch results;
-            if (std::regex_match(rawInfo->name, results, lightPattern)) {
-                std::string commonName = results[1].str();
-                int32_t playerId = std::stoi(results[2]);
-                if (playerIdLightIds.empty()) {
-                    playerIdName = commonName;
-                    playerIdLightIds.insert_or_assign(playerId, rawId);
-                } else {
-                    // Make sure the player ID leds have common string name
-                    if (playerIdName.compare(commonName) == 0 &&
-                        playerIdLightIds.find(playerId) == playerIdLightIds.end()) {
-                        playerIdLightIds.insert_or_assign(playerId, rawId);
-                    }
-                }
-            }
-            // Check if this is an LED of RGB light
-            if (rawInfo->flags.test(InputLightClass::RED)) {
-                hasRedLed = true;
-                rawRgbIds.emplace(LightColor::RED, rawId);
-            }
-            if (rawInfo->flags.test(InputLightClass::GREEN)) {
-                hasGreenLed = true;
-                rawRgbIds.emplace(LightColor::GREEN, rawId);
-            }
-            if (rawInfo->flags.test(InputLightClass::BLUE)) {
-                hasBlueLed = true;
-                rawRgbIds.emplace(LightColor::BLUE, rawId);
-            }
-            if (rawInfo->flags.test(InputLightClass::GLOBAL)) {
-                rawGlobalId = rawId;
-            }
-            if (DEBUG_LIGHT_DETAILS) {
-                ALOGD("Light rawId %d name %s max %d flags %s \n", rawInfo->id,
-                      rawInfo->name.c_str(), rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS),
-                      rawInfo->flags.string().c_str());
-            }
-        }
-
-        // Construct a player ID light
-        if (playerIdLightIds.size() > 1) {
-            std::unique_ptr<Light> light =
-                    std::make_unique<PlayerIdLight>(getDeviceContext(), playerIdName, ++mNextId,
-                                                    playerIdLightIds);
-            mLights.insert_or_assign(light->id, std::move(light));
-            // Remove these raw lights from raw light info as they've been used to compose a
-            // Player ID light, so we do not expose these raw lights as single lights.
-            for (const auto& [playerId, rawId] : playerIdLightIds) {
-                rawInfos.erase(rawId);
-            }
-        }
-        // Construct a RGB light for composed RGB light
-        if (hasRedLed && hasGreenLed && hasBlueLed) {
-            if (DEBUG_LIGHT_DETAILS) {
-                ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED),
-                      rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
-            }
-            std::unique_ptr<Light> light = std::make_unique<RgbLight>(getDeviceContext(), ++mNextId,
-                                                                      rawRgbIds, rawGlobalId);
-            mLights.insert_or_assign(light->id, std::move(light));
-            // Remove from raw light info as they've been composed a RBG light.
-            rawInfos.erase(rawRgbIds.at(LightColor::RED));
-            rawInfos.erase(rawRgbIds.at(LightColor::GREEN));
-            rawInfos.erase(rawRgbIds.at(LightColor::BLUE));
-            if (rawGlobalId.has_value()) {
-                rawInfos.erase(rawGlobalId.value());
-            }
-        }
-
-        // Check the rest of raw light infos
-        for (const auto& [rawId, rawInfo] : rawInfos) {
-            // If the node is multi-color led, construct a MULTI_COLOR light
-            if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) &&
-                rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) {
-                if (DEBUG_LIGHT_DETAILS) {
-                    ALOGD("Multicolor light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
-                }
-                std::unique_ptr<Light> light =
-                        std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name,
-                                                          ++mNextId, rawInfo.id);
-                mLights.insert_or_assign(light->id, std::move(light));
-                continue;
-            }
-            // Construct a single LED light
-            if (DEBUG_LIGHT_DETAILS) {
-                ALOGD("Single light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
-            }
-            std::unique_ptr<Light> light =
-                    std::make_unique<SingleLight>(getDeviceContext(), rawInfo.name, ++mNextId,
-                                                  rawInfo.id);
-
-            mLights.insert_or_assign(light->id, std::move(light));
-        }
-    }
-}
-
-void LightInputMapper::reset(nsecs_t when) {
-    InputMapper::reset(when);
-}
-
-void LightInputMapper::process(const RawEvent* rawEvent) {}
-
-bool LightInputMapper::setLightColor(int32_t lightId, int32_t color) {
-    auto it = mLights.find(lightId);
-    if (it == mLights.end()) {
-        return false;
-    }
-    auto& light = it->second;
-    if (DEBUG_LIGHT_DETAILS) {
-        ALOGD("setLightColor lightId %d type %s color 0x%x", lightId,
-              NamedEnum::string(light->type).c_str(), color);
-    }
-    return light->setLightColor(color);
-}
-
-std::optional<int32_t> LightInputMapper::getLightColor(int32_t lightId) {
-    auto it = mLights.find(lightId);
-    if (it == mLights.end()) {
-        return std::nullopt;
-    }
-    auto& light = it->second;
-    std::optional<int32_t> color = light->getLightColor();
-    if (DEBUG_LIGHT_DETAILS) {
-        ALOGD("getLightColor lightId %d type %s color 0x%x", lightId,
-              NamedEnum::string(light->type).c_str(), color.value_or(0));
-    }
-    return color;
-}
-
-bool LightInputMapper::setLightPlayerId(int32_t lightId, int32_t playerId) {
-    auto it = mLights.find(lightId);
-    if (it == mLights.end()) {
-        return false;
-    }
-    auto& light = it->second;
-    return light->setLightPlayerId(playerId);
-}
-
-std::optional<int32_t> LightInputMapper::getLightPlayerId(int32_t lightId) {
-    auto it = mLights.find(lightId);
-    if (it == mLights.end()) {
-        return std::nullopt;
-    }
-    auto& light = it->second;
-    return light->getLightPlayerId();
-}
-
-} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 485fc99..31d6900 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -268,7 +268,9 @@
 
     void assertDropTargetEquals(const sp<IBinder>& targetToken) {
         std::scoped_lock lock(mLock);
+        ASSERT_TRUE(mNotifyDropWindowWasCalled);
         ASSERT_EQ(targetToken, mDropTargetWindowToken);
+        mNotifyDropWindowWasCalled = false;
     }
 
 private:
@@ -290,6 +292,7 @@
     std::condition_variable mNotifyAnr;
 
     sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock);
+    bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false;
 
     void notifyConfigurationChanged(nsecs_t when) override {
         std::scoped_lock lock(mLock);
@@ -386,7 +389,9 @@
 
     void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
 
-    bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
+    bool checkInjectEventsPermissionNonReentrant(int32_t pid, int32_t uid) override {
+        return pid == INJECTOR_PID && uid == INJECTOR_UID;
+    }
 
     void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
         std::scoped_lock lock(mLock);
@@ -401,6 +406,7 @@
 
     void notifyDropWindow(const sp<IBinder>& token, float x, float y) override {
         std::scoped_lock lock(mLock);
+        mNotifyDropWindowWasCalled = true;
         mDropTargetWindowToken = token;
     }
 
@@ -740,6 +746,11 @@
         ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
     }
 
+    void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
+        const status_t status = mConsumer->sendTimeline(inputEventId, timeline);
+        ASSERT_EQ(OK, status);
+    }
+
     void consumeEvent(int32_t expectedEventType, int32_t expectedAction,
                       std::optional<int32_t> expectedDisplayId,
                       std::optional<int32_t> expectedFlags) {
@@ -1052,6 +1063,11 @@
         mInputReceiver->finishEvent(sequenceNum);
     }
 
+    void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
+        ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver";
+        mInputReceiver->sendTimeline(inputEventId, timeline);
+    }
+
     InputEvent* consume() {
         if (mInputReceiver == nullptr) {
             return nullptr;
@@ -1090,7 +1106,8 @@
         const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
         int32_t displayId = ADISPLAY_ID_NONE,
         InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT,
-        std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT) {
+        std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
+        bool allowKeyRepeat = true) {
     KeyEvent event;
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
@@ -1099,10 +1116,13 @@
                      INVALID_HMAC, action, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE,
                      repeatCount, currentTime, currentTime);
 
+    int32_t policyFlags = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
+    if (!allowKeyRepeat) {
+        policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
+    }
     // Inject event until dispatch out.
     return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, syncMode,
-                                        injectionTimeout,
-                                        POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+                                        injectionTimeout, policyFlags);
 }
 
 static InputEventInjectionResult injectKeyDown(const sp<InputDispatcher>& dispatcher,
@@ -1110,6 +1130,16 @@
     return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId);
 }
 
+// Inject a down event that has key repeat disabled. This allows InputDispatcher to idle without
+// sending a subsequent key up. When key repeat is enabled, the dispatcher cannot idle because it
+// has to be woken up to process the repeating key.
+static InputEventInjectionResult injectKeyDownNoRepeat(const sp<InputDispatcher>& dispatcher,
+                                                       int32_t displayId = ADISPLAY_ID_NONE) {
+    return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId,
+                     InputEventInjectionSync::WAIT_FOR_RESULT, INJECT_EVENT_TIMEOUT,
+                     /* allowKeyRepeat */ false);
+}
+
 static InputEventInjectionResult injectKeyUp(const sp<InputDispatcher>& dispatcher,
                                              int32_t displayId = ADISPLAY_ID_NONE) {
     return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId);
@@ -1254,7 +1284,7 @@
                                 .build();
 
     // Inject event until dispatch out.
-    return injectMotionEvent(dispatcher, event);
+    return injectMotionEvent(dispatcher, event, injectionTimeout, injectionMode);
 }
 
 static InputEventInjectionResult injectMotionDown(const sp<InputDispatcher>& dispatcher,
@@ -1954,6 +1984,21 @@
     secondWindow->assertNoEvents();
 }
 
+TEST_F(InputDispatcherTest, SendTimeline_DoesNotCrashDispatcher) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+    graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2;
+    graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 3;
+
+    window->sendTimeline(1 /*inputEventId*/, graphicsTimeline);
+    window->assertNoEvents();
+    mDispatcher->waitForIdle();
+}
+
 class FakeMonitorReceiver {
 public:
     FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
@@ -2749,13 +2794,14 @@
 
 TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) {
     // Test inject a key down with display id specified.
-    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectKeyDownNoRepeat(mDispatcher, ADISPLAY_ID_DEFAULT))
             << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
     windowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
     windowInSecondary->assertNoEvents();
 
     // Test inject a key down without display id specified.
-    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher))
             << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
     windowInPrimary->assertNoEvents();
     windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
@@ -2768,7 +2814,7 @@
                                     AKEY_EVENT_FLAG_CANCELED);
 
     // Test inject a key down, should timeout because of no target window.
-    ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+    ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDownNoRepeat(mDispatcher))
             << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
     windowInPrimary->assertNoEvents();
     windowInSecondary->consumeFocusEvent(false);
@@ -2990,7 +3036,8 @@
 // Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't
 // have focus. Ensure no window received the onPointerDownOutsideFocus callback.
 TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
-    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectKeyDownNoRepeat(mDispatcher, ADISPLAY_ID_DEFAULT))
             << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
     mFocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
 
@@ -3271,7 +3318,7 @@
 
 // Send a regular key and respond, which should not cause an ANR.
 TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) {
-    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher));
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher));
     mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
     ASSERT_TRUE(mDispatcher->waitForIdle());
     mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -3284,7 +3331,8 @@
 
     InputEventInjectionResult result =
             injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
-                      InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/);
+                      InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/,
+                      false /* allowKeyRepeat */);
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
     // Key will not go to window because we have no focused window.
     // The 'no focused window' ANR timer should start instead.
@@ -3320,7 +3368,7 @@
 // Send a key to the app and have the app not respond right away.
 TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) {
     // Inject a key, and don't respond - expect that ANR is called.
-    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher));
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(mDispatcher));
     std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
     ASSERT_TRUE(sequenceNum);
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -3347,7 +3395,7 @@
     // injection times out (instead of failing).
     const InputEventInjectionResult result =
             injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
-                      InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+                      InputEventInjectionSync::WAIT_FOR_RESULT, 10ms, false /* allowKeyRepeat */);
     ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
     const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
     mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(timeout, mApplication);
@@ -3366,7 +3414,7 @@
     // injection times out (instead of failing).
     const InputEventInjectionResult result =
             injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
-                      InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+                      InputEventInjectionSync::WAIT_FOR_RESULT, 10ms, false /* allowKeyRepeat */);
     ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
     const std::chrono::duration appTimeout =
             mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -3955,7 +4003,8 @@
     // Key will not be sent anywhere because we have no focused window. It will remain pending.
     InputEventInjectionResult result =
             injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
-                      InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/);
+                      InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/,
+                      false /* allowKeyRepeat */);
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
 
     // Wait until dispatcher starts the "no focused window" timer. If we don't wait here,
@@ -4906,4 +4955,40 @@
     mSecondWindow->assertNoEvents();
 }
 
+TEST_F(InputDispatcherDragTests, DragAndDrop_InvalidWindow) {
+    performDrag();
+
+    // Set second window invisible.
+    mSecondWindow->setVisible(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
+
+    // Move on window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+    mWindow->consumeDragEvent(false, 50, 50);
+    mSecondWindow->assertNoEvents();
+
+    // Move to another window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+    mWindow->consumeDragEvent(true, 150, 50);
+    mSecondWindow->assertNoEvents();
+
+    // drop to another window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                             {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+    mFakePolicy->assertDropTargetEquals(nullptr);
+    mWindow->assertNoEvents();
+    mSecondWindow->assertNoEvents();
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index d69bb6a..0e721e9 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <BatteryInputMapper.h>
 #include <CursorInputMapper.h>
 #include <InputDevice.h>
 #include <InputMapper.h>
@@ -22,8 +21,8 @@
 #include <InputReaderBase.h>
 #include <InputReaderFactory.h>
 #include <KeyboardInputMapper.h>
-#include <LightInputMapper.h>
 #include <MultiTouchInputMapper.h>
+#include <PeripheralController.h>
 #include <SensorInputMapper.h>
 #include <SingleTouchInputMapper.h>
 #include <SwitchInputMapper.h>
@@ -71,6 +70,7 @@
 static constexpr int32_t FIRST_TRACKING_ID = 0;
 static constexpr int32_t SECOND_TRACKING_ID = 1;
 static constexpr int32_t THIRD_TRACKING_ID = 2;
+static constexpr int32_t DEFAULT_BATTERY = 1;
 static constexpr int32_t BATTERY_STATUS = 4;
 static constexpr int32_t BATTERY_CAPACITY = 66;
 static constexpr int32_t LIGHT_BRIGHTNESS = 0x55000000;
@@ -895,9 +895,19 @@
 
     std::vector<int32_t> getVibratorIds(int32_t deviceId) override { return mVibrators; };
 
-    std::optional<int32_t> getBatteryCapacity(int32_t) const override { return BATTERY_CAPACITY; }
+    std::optional<int32_t> getBatteryCapacity(int32_t, int32_t) const override {
+        return BATTERY_CAPACITY;
+    }
 
-    std::optional<int32_t> getBatteryStatus(int32_t) const override { return BATTERY_STATUS; }
+    std::optional<int32_t> getBatteryStatus(int32_t, int32_t) const override {
+        return BATTERY_STATUS;
+    }
+
+    const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) { return {}; }
+
+    std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId, int32_t batteryId) {
+        return std::nullopt;
+    }
 
     const std::vector<int32_t> getRawLightIds(int32_t deviceId) override {
         std::vector<int32_t> ids;
@@ -2005,56 +2015,25 @@
     ASSERT_EQ(mReader->getVibratorIds(deviceId).size(), 2U);
 }
 
-class FakeBatteryInputMapper : public FakeInputMapper {
-public:
-    FakeBatteryInputMapper(InputDeviceContext& deviceContext, uint32_t sources)
-          : FakeInputMapper(deviceContext, sources) {}
+// --- FakePeripheralController ---
 
-    std::optional<int32_t> getBatteryCapacity() override {
-        return getDeviceContext().getBatteryCapacity();
+class FakePeripheralController : public PeripheralControllerInterface {
+public:
+    FakePeripheralController(InputDeviceContext& deviceContext) : mDeviceContext(deviceContext) {}
+
+    ~FakePeripheralController() override {}
+
+    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override {}
+
+    void dump(std::string& dump) override {}
+
+    std::optional<int32_t> getBatteryCapacity(int32_t batteryId) override {
+        return getDeviceContext().getBatteryCapacity(batteryId);
     }
 
-    std::optional<int32_t> getBatteryStatus() override {
-        return getDeviceContext().getBatteryStatus();
+    std::optional<int32_t> getBatteryStatus(int32_t batteryId) override {
+        return getDeviceContext().getBatteryStatus(batteryId);
     }
-};
-
-TEST_F(InputReaderTest, BatteryGetCapacity) {
-    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
-    Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
-    constexpr int32_t eventHubId = 1;
-    const char* DEVICE_LOCATION = "BLUETOOTH";
-    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
-    FakeBatteryInputMapper& mapper =
-            device->addMapper<FakeBatteryInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
-    mReader->pushNextDevice(device);
-
-    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
-    ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
-
-    ASSERT_EQ(mReader->getBatteryCapacity(deviceId), BATTERY_CAPACITY);
-}
-
-TEST_F(InputReaderTest, BatteryGetStatus) {
-    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
-    Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
-    constexpr int32_t eventHubId = 1;
-    const char* DEVICE_LOCATION = "BLUETOOTH";
-    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
-    FakeBatteryInputMapper& mapper =
-            device->addMapper<FakeBatteryInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
-    mReader->pushNextDevice(device);
-
-    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
-    ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
-
-    ASSERT_EQ(mReader->getBatteryStatus(deviceId), BATTERY_STATUS);
-}
-
-class FakeLightInputMapper : public FakeInputMapper {
-public:
-    FakeLightInputMapper(InputDeviceContext& deviceContext, uint32_t sources)
-          : FakeInputMapper(deviceContext, sources) {}
 
     bool setLightColor(int32_t lightId, int32_t color) override {
         getDeviceContext().setLightBrightness(lightId, color >> 24);
@@ -2068,16 +2047,57 @@
         }
         return result.value() << 24;
     }
+
+    bool setLightPlayerId(int32_t lightId, int32_t playerId) override { return true; }
+
+    std::optional<int32_t> getLightPlayerId(int32_t lightId) override { return std::nullopt; }
+
+private:
+    InputDeviceContext& mDeviceContext;
+    inline int32_t getDeviceId() { return mDeviceContext.getId(); }
+    inline InputDeviceContext& getDeviceContext() { return mDeviceContext; }
 };
 
+TEST_F(InputReaderTest, BatteryGetCapacity) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
+    constexpr int32_t eventHubId = 1;
+    const char* DEVICE_LOCATION = "BLUETOOTH";
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
+    FakePeripheralController& controller =
+            device->addController<FakePeripheralController>(eventHubId);
+    mReader->pushNextDevice(device);
+
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
+
+    ASSERT_EQ(controller.getBatteryCapacity(DEFAULT_BATTERY), BATTERY_CAPACITY);
+    ASSERT_EQ(mReader->getBatteryCapacity(deviceId), BATTERY_CAPACITY);
+}
+
+TEST_F(InputReaderTest, BatteryGetStatus) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
+    constexpr int32_t eventHubId = 1;
+    const char* DEVICE_LOCATION = "BLUETOOTH";
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
+    FakePeripheralController& controller =
+            device->addController<FakePeripheralController>(eventHubId);
+    mReader->pushNextDevice(device);
+
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
+
+    ASSERT_EQ(controller.getBatteryStatus(DEFAULT_BATTERY), BATTERY_STATUS);
+    ASSERT_EQ(mReader->getBatteryStatus(deviceId), BATTERY_STATUS);
+}
+
 TEST_F(InputReaderTest, LightGetColor) {
     constexpr int32_t deviceId = END_RESERVED_ID + 1000;
     Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::LIGHT;
     constexpr int32_t eventHubId = 1;
     const char* DEVICE_LOCATION = "BLUETOOTH";
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
-    FakeLightInputMapper& mapper =
-            device->addMapper<FakeLightInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
+    FakePeripheralController& controller =
+            device->addController<FakePeripheralController>(eventHubId);
     mReader->pushNextDevice(device);
     RawLightInfo info = {.id = 1,
                          .name = "Mono",
@@ -2088,8 +2108,9 @@
     mFakeEventHub->fakeLightBrightness(1 /* rawId */, 0x55);
 
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
-    ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
 
+    ASSERT_TRUE(controller.setLightColor(1 /* lightId */, LIGHT_BRIGHTNESS));
+    ASSERT_EQ(controller.getLightColor(1 /* lightId */), LIGHT_BRIGHTNESS);
     ASSERT_TRUE(mReader->setLightColor(deviceId, 1 /* lightId */, LIGHT_BRIGHTNESS));
     ASSERT_EQ(mReader->getLightColor(deviceId, 1 /* lightId */), LIGHT_BRIGHTNESS);
 }
@@ -2989,154 +3010,6 @@
     mapper.flushSensor(InputDeviceSensorType::GYROSCOPE);
 }
 
-// --- BatteryInputMapperTest ---
-class BatteryInputMapperTest : public InputMapperTest {
-protected:
-    void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::BATTERY); }
-};
-
-TEST_F(BatteryInputMapperTest, GetSources) {
-    BatteryInputMapper& mapper = addMapperAndConfigure<BatteryInputMapper>();
-
-    ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mapper.getSources());
-}
-
-TEST_F(BatteryInputMapperTest, GetBatteryCapacity) {
-    BatteryInputMapper& mapper = addMapperAndConfigure<BatteryInputMapper>();
-
-    ASSERT_TRUE(mapper.getBatteryCapacity());
-    ASSERT_EQ(mapper.getBatteryCapacity().value_or(-1), BATTERY_CAPACITY);
-}
-
-TEST_F(BatteryInputMapperTest, GetBatteryStatus) {
-    BatteryInputMapper& mapper = addMapperAndConfigure<BatteryInputMapper>();
-
-    ASSERT_TRUE(mapper.getBatteryStatus());
-    ASSERT_EQ(mapper.getBatteryStatus().value_or(-1), BATTERY_STATUS);
-}
-
-// --- LightInputMapperTest ---
-class LightInputMapperTest : public InputMapperTest {
-protected:
-    void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::LIGHT); }
-};
-
-TEST_F(LightInputMapperTest, GetSources) {
-    LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>();
-
-    ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mapper.getSources());
-}
-
-TEST_F(LightInputMapperTest, SingleLight) {
-    RawLightInfo infoSingle = {.id = 1,
-                               .name = "Mono",
-                               .maxBrightness = 255,
-                               .flags = InputLightClass::BRIGHTNESS,
-                               .path = ""};
-    mFakeEventHub->addRawLightInfo(infoSingle.id, std::move(infoSingle));
-
-    LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>();
-    InputDeviceInfo info;
-    mapper.populateDeviceInfo(&info);
-    const auto& ids = info.getLightIds();
-    ASSERT_EQ(1UL, ids.size());
-    ASSERT_EQ(InputDeviceLightType::SINGLE, info.getLightInfo(ids[0])->type);
-
-    ASSERT_TRUE(mapper.setLightColor(ids[0], LIGHT_BRIGHTNESS));
-    ASSERT_EQ(mapper.getLightColor(ids[0]).value_or(-1), LIGHT_BRIGHTNESS);
-}
-
-TEST_F(LightInputMapperTest, RGBLight) {
-    RawLightInfo infoRed = {.id = 1,
-                            .name = "red",
-                            .maxBrightness = 255,
-                            .flags = InputLightClass::BRIGHTNESS | InputLightClass::RED,
-                            .path = ""};
-    RawLightInfo infoGreen = {.id = 2,
-                              .name = "green",
-                              .maxBrightness = 255,
-                              .flags = InputLightClass::BRIGHTNESS | InputLightClass::GREEN,
-                              .path = ""};
-    RawLightInfo infoBlue = {.id = 3,
-                             .name = "blue",
-                             .maxBrightness = 255,
-                             .flags = InputLightClass::BRIGHTNESS | InputLightClass::BLUE,
-                             .path = ""};
-    mFakeEventHub->addRawLightInfo(infoRed.id, std::move(infoRed));
-    mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen));
-    mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue));
-
-    LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>();
-    InputDeviceInfo info;
-    mapper.populateDeviceInfo(&info);
-    const auto& ids = info.getLightIds();
-    ASSERT_EQ(1UL, ids.size());
-    ASSERT_EQ(InputDeviceLightType::RGB, info.getLightInfo(ids[0])->type);
-
-    ASSERT_TRUE(mapper.setLightColor(ids[0], LIGHT_COLOR));
-    ASSERT_EQ(mapper.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR);
-}
-
-TEST_F(LightInputMapperTest, MultiColorRGBLight) {
-    RawLightInfo infoColor = {.id = 1,
-                              .name = "red",
-                              .maxBrightness = 255,
-                              .flags = InputLightClass::BRIGHTNESS |
-                                      InputLightClass::MULTI_INTENSITY |
-                                      InputLightClass::MULTI_INDEX,
-                              .path = ""};
-
-    mFakeEventHub->addRawLightInfo(infoColor.id, std::move(infoColor));
-
-    LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>();
-    InputDeviceInfo info;
-    mapper.populateDeviceInfo(&info);
-    const auto& ids = info.getLightIds();
-    ASSERT_EQ(1UL, ids.size());
-    ASSERT_EQ(InputDeviceLightType::MULTI_COLOR, info.getLightInfo(ids[0])->type);
-
-    ASSERT_TRUE(mapper.setLightColor(ids[0], LIGHT_COLOR));
-    ASSERT_EQ(mapper.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR);
-}
-
-TEST_F(LightInputMapperTest, PlayerIdLight) {
-    RawLightInfo info1 = {.id = 1,
-                          .name = "player1",
-                          .maxBrightness = 255,
-                          .flags = InputLightClass::BRIGHTNESS,
-                          .path = ""};
-    RawLightInfo info2 = {.id = 2,
-                          .name = "player2",
-                          .maxBrightness = 255,
-                          .flags = InputLightClass::BRIGHTNESS,
-                          .path = ""};
-    RawLightInfo info3 = {.id = 3,
-                          .name = "player3",
-                          .maxBrightness = 255,
-                          .flags = InputLightClass::BRIGHTNESS,
-                          .path = ""};
-    RawLightInfo info4 = {.id = 4,
-                          .name = "player4",
-                          .maxBrightness = 255,
-                          .flags = InputLightClass::BRIGHTNESS,
-                          .path = ""};
-    mFakeEventHub->addRawLightInfo(info1.id, std::move(info1));
-    mFakeEventHub->addRawLightInfo(info2.id, std::move(info2));
-    mFakeEventHub->addRawLightInfo(info3.id, std::move(info3));
-    mFakeEventHub->addRawLightInfo(info4.id, std::move(info4));
-
-    LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>();
-    InputDeviceInfo info;
-    mapper.populateDeviceInfo(&info);
-    const auto& ids = info.getLightIds();
-    ASSERT_EQ(1UL, ids.size());
-    ASSERT_EQ(InputDeviceLightType::PLAYER_ID, info.getLightInfo(ids[0])->type);
-
-    ASSERT_FALSE(mapper.setLightColor(ids[0], LIGHT_COLOR));
-    ASSERT_TRUE(mapper.setLightPlayerId(ids[0], LIGHT_PLAYER_ID));
-    ASSERT_EQ(mapper.getLightPlayerId(ids[0]).value_or(-1), LIGHT_PLAYER_ID);
-}
-
 // --- KeyboardInputMapperTest ---
 
 class KeyboardInputMapperTest : public InputMapperTest {
@@ -8728,4 +8601,218 @@
     ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
 }
 
+// --- PeripheralControllerTest ---
+
+class PeripheralControllerTest : 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 Flags<InputDeviceClass> DEVICE_CLASSES;
+    static const int32_t EVENTHUB_ID;
+
+    std::shared_ptr<FakeEventHub> mFakeEventHub;
+    sp<FakeInputReaderPolicy> mFakePolicy;
+    sp<TestInputListener> mFakeListener;
+    std::unique_ptr<InstrumentedInputReader> mReader;
+    std::shared_ptr<InputDevice> mDevice;
+
+    virtual void SetUp(Flags<InputDeviceClass> classes) {
+        mFakeEventHub = std::make_unique<FakeEventHub>();
+        mFakePolicy = new FakeInputReaderPolicy();
+        mFakeListener = new TestInputListener();
+        mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+                                                            mFakeListener);
+        mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
+    }
+
+    void SetUp() override { SetUp(DEVICE_CLASSES); }
+
+    void TearDown() override {
+        mFakeListener.clear();
+        mFakePolicy.clear();
+    }
+
+    void configureDevice(uint32_t changes) {
+        if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+            mReader->requestRefreshConfiguration(changes);
+            mReader->loopOnce();
+        }
+        mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+    }
+
+    std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+                                           const std::string& location, int32_t eventHubId,
+                                           Flags<InputDeviceClass> classes) {
+        InputDeviceIdentifier identifier;
+        identifier.name = name;
+        identifier.location = location;
+        std::shared_ptr<InputDevice> device =
+                std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
+                                              identifier);
+        mReader->pushNextDevice(device);
+        mFakeEventHub->addDevice(eventHubId, name, classes);
+        mReader->loopOnce();
+        return device;
+    }
+
+    template <class T, typename... Args>
+    T& addControllerAndConfigure(Args... args) {
+        T& controller = mDevice->addController<T>(EVENTHUB_ID, args...);
+
+        return controller;
+    }
+};
+
+const char* PeripheralControllerTest::DEVICE_NAME = "device";
+const char* PeripheralControllerTest::DEVICE_LOCATION = "BLUETOOTH";
+const int32_t PeripheralControllerTest::DEVICE_ID = END_RESERVED_ID + 1000;
+const int32_t PeripheralControllerTest::DEVICE_GENERATION = 2;
+const int32_t PeripheralControllerTest::DEVICE_CONTROLLER_NUMBER = 0;
+const Flags<InputDeviceClass> PeripheralControllerTest::DEVICE_CLASSES =
+        Flags<InputDeviceClass>(0); // not needed for current tests
+const int32_t PeripheralControllerTest::EVENTHUB_ID = 1;
+
+// --- BatteryControllerTest ---
+class BatteryControllerTest : public PeripheralControllerTest {
+protected:
+    void SetUp() override {
+        PeripheralControllerTest::SetUp(DEVICE_CLASSES | InputDeviceClass::BATTERY);
+    }
+};
+
+TEST_F(BatteryControllerTest, GetBatteryCapacity) {
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+
+    ASSERT_TRUE(controller.getBatteryCapacity(DEFAULT_BATTERY));
+    ASSERT_EQ(controller.getBatteryCapacity(DEFAULT_BATTERY).value_or(-1), BATTERY_CAPACITY);
+}
+
+TEST_F(BatteryControllerTest, GetBatteryStatus) {
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+
+    ASSERT_TRUE(controller.getBatteryStatus(DEFAULT_BATTERY));
+    ASSERT_EQ(controller.getBatteryStatus(DEFAULT_BATTERY).value_or(-1), BATTERY_STATUS);
+}
+
+// --- LightControllerTest ---
+class LightControllerTest : public PeripheralControllerTest {
+protected:
+    void SetUp() override {
+        PeripheralControllerTest::SetUp(DEVICE_CLASSES | InputDeviceClass::LIGHT);
+    }
+};
+
+TEST_F(LightControllerTest, SingleLight) {
+    RawLightInfo infoSingle = {.id = 1,
+                               .name = "Mono",
+                               .maxBrightness = 255,
+                               .flags = InputLightClass::BRIGHTNESS,
+                               .path = ""};
+    mFakeEventHub->addRawLightInfo(infoSingle.id, std::move(infoSingle));
+
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    const auto& ids = info.getLightIds();
+    ASSERT_EQ(1UL, ids.size());
+    ASSERT_EQ(InputDeviceLightType::SINGLE, info.getLightInfo(ids[0])->type);
+
+    ASSERT_TRUE(controller.setLightColor(ids[0], LIGHT_BRIGHTNESS));
+    ASSERT_EQ(controller.getLightColor(ids[0]).value_or(-1), LIGHT_BRIGHTNESS);
+}
+
+TEST_F(LightControllerTest, RGBLight) {
+    RawLightInfo infoRed = {.id = 1,
+                            .name = "red",
+                            .maxBrightness = 255,
+                            .flags = InputLightClass::BRIGHTNESS | InputLightClass::RED,
+                            .path = ""};
+    RawLightInfo infoGreen = {.id = 2,
+                              .name = "green",
+                              .maxBrightness = 255,
+                              .flags = InputLightClass::BRIGHTNESS | InputLightClass::GREEN,
+                              .path = ""};
+    RawLightInfo infoBlue = {.id = 3,
+                             .name = "blue",
+                             .maxBrightness = 255,
+                             .flags = InputLightClass::BRIGHTNESS | InputLightClass::BLUE,
+                             .path = ""};
+    mFakeEventHub->addRawLightInfo(infoRed.id, std::move(infoRed));
+    mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen));
+    mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue));
+
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    const auto& ids = info.getLightIds();
+    ASSERT_EQ(1UL, ids.size());
+    ASSERT_EQ(InputDeviceLightType::RGB, info.getLightInfo(ids[0])->type);
+
+    ASSERT_TRUE(controller.setLightColor(ids[0], LIGHT_COLOR));
+    ASSERT_EQ(controller.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR);
+}
+
+TEST_F(LightControllerTest, MultiColorRGBLight) {
+    RawLightInfo infoColor = {.id = 1,
+                              .name = "red",
+                              .maxBrightness = 255,
+                              .flags = InputLightClass::BRIGHTNESS |
+                                      InputLightClass::MULTI_INTENSITY |
+                                      InputLightClass::MULTI_INDEX,
+                              .path = ""};
+
+    mFakeEventHub->addRawLightInfo(infoColor.id, std::move(infoColor));
+
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    const auto& ids = info.getLightIds();
+    ASSERT_EQ(1UL, ids.size());
+    ASSERT_EQ(InputDeviceLightType::MULTI_COLOR, info.getLightInfo(ids[0])->type);
+
+    ASSERT_TRUE(controller.setLightColor(ids[0], LIGHT_COLOR));
+    ASSERT_EQ(controller.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR);
+}
+
+TEST_F(LightControllerTest, PlayerIdLight) {
+    RawLightInfo info1 = {.id = 1,
+                          .name = "player1",
+                          .maxBrightness = 255,
+                          .flags = InputLightClass::BRIGHTNESS,
+                          .path = ""};
+    RawLightInfo info2 = {.id = 2,
+                          .name = "player2",
+                          .maxBrightness = 255,
+                          .flags = InputLightClass::BRIGHTNESS,
+                          .path = ""};
+    RawLightInfo info3 = {.id = 3,
+                          .name = "player3",
+                          .maxBrightness = 255,
+                          .flags = InputLightClass::BRIGHTNESS,
+                          .path = ""};
+    RawLightInfo info4 = {.id = 4,
+                          .name = "player4",
+                          .maxBrightness = 255,
+                          .flags = InputLightClass::BRIGHTNESS,
+                          .path = ""};
+    mFakeEventHub->addRawLightInfo(info1.id, std::move(info1));
+    mFakeEventHub->addRawLightInfo(info2.id, std::move(info2));
+    mFakeEventHub->addRawLightInfo(info3.id, std::move(info3));
+    mFakeEventHub->addRawLightInfo(info4.id, std::move(info4));
+
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    const auto& ids = info.getLightIds();
+    ASSERT_EQ(1UL, ids.size());
+    ASSERT_EQ(InputDeviceLightType::PLAYER_ID, info.getLightInfo(ids[0])->type);
+
+    ASSERT_FALSE(controller.setLightColor(ids[0], LIGHT_COLOR));
+    ASSERT_TRUE(controller.setLightPlayerId(ids[0], LIGHT_PLAYER_ID));
+    ASSERT_EQ(controller.getLightPlayerId(ids[0]).value_or(-1), LIGHT_PLAYER_ID);
+}
+
 } // namespace android
diff --git a/services/memtrackproxy/Android.bp b/services/memtrackproxy/Android.bp
new file mode 100644
index 0000000..7d78f3b
--- /dev/null
+++ b/services/memtrackproxy/Android.bp
@@ -0,0 +1,50 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_shared {
+    name: "libmemtrackproxy",
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "libbinder",
+        "libhidlbase",
+        "liblog",
+        "libcutils",
+        "libutils",
+        "android.hardware.memtrack@1.0",
+        "android.hardware.memtrack-V1-ndk_platform",
+    ],
+    srcs: [
+        "MemtrackProxy.cpp",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    local_include_dirs: [
+        "include/memtrackproxy",
+    ],
+    export_shared_lib_headers: [
+        "android.hardware.memtrack@1.0",
+        "android.hardware.memtrack-V1-ndk_platform",
+    ],
+}
diff --git a/services/memtrackproxy/MemtrackProxy.cpp b/services/memtrackproxy/MemtrackProxy.cpp
new file mode 100644
index 0000000..4676167
--- /dev/null
+++ b/services/memtrackproxy/MemtrackProxy.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "MemtrackProxy.h"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <private/android_filesystem_config.h>
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace memtrack {
+
+// Check Memtrack Flags
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SMAPS_ACCOUNTED) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SMAPS_ACCOUNTED));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SMAPS_UNACCOUNTED) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SMAPS_UNACCOUNTED));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SHARED) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SHARED));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SHARED_PSS) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SHARED_PSS));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::PRIVATE) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_PRIVATE));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SYSTEM) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SYSTEM));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::DEDICATED) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_DEDICATED));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::NONSECURE) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_NONSECURE));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackFlag::SECURE) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackRecord::FLAG_SECURE));
+
+// Check Memtrack Types
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::OTHER) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackType::OTHER));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::GL) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackType::GL));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::GRAPHICS) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackType::GRAPHICS));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::MULTIMEDIA) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackType::MULTIMEDIA));
+static_assert(static_cast<uint32_t>(V1_0_hidl::MemtrackType::CAMERA) ==
+              static_cast<uint32_t>(V1_aidl::MemtrackType::CAMERA));
+
+__attribute__((warn_unused_result)) bool translate(const V1_0_hidl::MemtrackRecord& in,
+                                                   V1_aidl::MemtrackRecord* out) {
+    // Convert uint64_t to int64_t (long in AIDL). AIDL doesn't support unsigned types.
+    if (in.sizeInBytes > std::numeric_limits<int64_t>::max() || in.sizeInBytes < 0) {
+        return false;
+    }
+    out->sizeInBytes = static_cast<int64_t>(in.sizeInBytes);
+
+    // It's ok to just assign directly, since this is a bitmap.
+    out->flags = in.flags;
+    return true;
+}
+
+sp<V1_0_hidl::IMemtrack> MemtrackProxy::MemtrackHidlInstance() {
+    return V1_0_hidl::IMemtrack::getService();
+}
+
+std::shared_ptr<V1_aidl::IMemtrack> MemtrackProxy::MemtrackAidlInstance() {
+    const auto instance = std::string() + V1_aidl::IMemtrack::descriptor + "/default";
+    bool declared = AServiceManager_isDeclared(instance.c_str());
+    if (!declared) {
+        return nullptr;
+    }
+    ndk::SpAIBinder memtrack_binder =
+            ndk::SpAIBinder(AServiceManager_waitForService(instance.c_str()));
+    return V1_aidl::IMemtrack::fromBinder(memtrack_binder);
+}
+
+bool MemtrackProxy::CheckUid(uid_t calling_uid) {
+    // Allow AID_SHELL for adb shell dumpsys meminfo
+    return calling_uid == AID_SYSTEM || calling_uid == AID_ROOT || calling_uid == AID_SHELL;
+}
+
+bool MemtrackProxy::CheckPid(pid_t calling_pid, pid_t request_pid) {
+    return calling_pid == request_pid;
+}
+
+MemtrackProxy::MemtrackProxy()
+      : memtrack_hidl_instance_(MemtrackProxy::MemtrackHidlInstance()),
+        memtrack_aidl_instance_(MemtrackProxy::MemtrackAidlInstance()) {}
+
+ndk::ScopedAStatus MemtrackProxy::getMemory(int pid, MemtrackType type,
+                                            std::vector<MemtrackRecord>* _aidl_return) {
+    if (pid < 0) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+
+    if (!MemtrackProxy::CheckPid(AIBinder_getCallingPid(), pid) &&
+        !MemtrackProxy::CheckUid(AIBinder_getCallingUid())) {
+        return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+                EX_SECURITY,
+                "Only AID_ROOT, AID_SYSTEM and AID_SHELL can request getMemory() for PIDs other "
+                "than the calling PID");
+    }
+
+    if (type != MemtrackType::OTHER && type != MemtrackType::GL && type != MemtrackType::GRAPHICS &&
+        type != MemtrackType::MULTIMEDIA && type != MemtrackType::CAMERA) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+
+    _aidl_return->clear();
+
+    if (memtrack_aidl_instance_) {
+        return memtrack_aidl_instance_->getMemory(pid, type, _aidl_return);
+    } else if (memtrack_hidl_instance_) {
+        ndk::ScopedAStatus aidl_status;
+
+        Return<void> ret = memtrack_hidl_instance_->getMemory(
+                pid, static_cast<V1_0_hidl::MemtrackType>(type),
+                [&_aidl_return, &aidl_status](V1_0_hidl::MemtrackStatus status,
+                                              hidl_vec<V1_0_hidl::MemtrackRecord> records) {
+                    switch (status) {
+                        case V1_0_hidl::MemtrackStatus::SUCCESS:
+                            aidl_status = ndk::ScopedAStatus::ok();
+                            break;
+                        case V1_0_hidl::MemtrackStatus::MEMORY_TRACKING_NOT_SUPPORTED:
+                            [[fallthrough]];
+                        case V1_0_hidl::MemtrackStatus::TYPE_NOT_SUPPORTED:
+                            [[fallthrough]];
+                        default:
+                            aidl_status =
+                                    ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+                            return;
+                    }
+
+                    _aidl_return->resize(records.size());
+                    for (size_t i = 0; i < records.size(); i++) {
+                        if (!translate(records[i], &(*_aidl_return)[i])) {
+                            aidl_status = ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+                                    EX_SERVICE_SPECIFIC,
+                                    "Failed to convert HIDL MemtrackRecord to AIDL");
+                            return;
+                        }
+                    }
+                });
+
+        // Check HIDL return
+        if (!ret.isOk()) {
+            const char* err_msg = "HIDL Memtrack::getMemory() failed";
+            aidl_status =
+                    ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC, err_msg);
+            LOG(ERROR) << err_msg << ": " << ret.description();
+        }
+
+        return aidl_status;
+    }
+
+    return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
+                                                            "Memtrack HAL service not available");
+}
+
+ndk::ScopedAStatus MemtrackProxy::getGpuDeviceInfo(std::vector<DeviceInfo>* _aidl_return) {
+    if (!MemtrackProxy::CheckUid(AIBinder_getCallingUid())) {
+        return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_SECURITY,
+                "Only AID_ROOT, AID_SYSTEM and AID_SHELL can request getGpuDeviceInfo()");
+    }
+
+    _aidl_return->clear();
+
+    if (memtrack_aidl_instance_ ||
+        (memtrack_aidl_instance_ = MemtrackProxy::MemtrackAidlInstance())) {
+        return memtrack_aidl_instance_->getGpuDeviceInfo(_aidl_return);
+    }
+
+    return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_NULL_POINTER,
+                                                            "Memtrack HAL service not available");
+}
+
+} // namespace memtrack
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/services/memtrackproxy/include/memtrackproxy/MemtrackProxy.h b/services/memtrackproxy/include/memtrackproxy/MemtrackProxy.h
new file mode 100644
index 0000000..5ac1fbf
--- /dev/null
+++ b/services/memtrackproxy/include/memtrackproxy/MemtrackProxy.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/memtrack/BnMemtrack.h>
+#include <aidl/android/hardware/memtrack/DeviceInfo.h>
+#include <aidl/android/hardware/memtrack/IMemtrack.h>
+#include <aidl/android/hardware/memtrack/MemtrackRecord.h>
+#include <aidl/android/hardware/memtrack/MemtrackType.h>
+#include <android/hardware/memtrack/1.0/IMemtrack.h>
+
+using ::android::sp;
+
+namespace V1_0_hidl = ::android::hardware::memtrack::V1_0;
+namespace V1_aidl = ::aidl::android::hardware::memtrack;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace memtrack {
+
+__attribute__((warn_unused_result)) bool translate(const V1_0_hidl::MemtrackRecord& in,
+                                                   V1_aidl::MemtrackRecord* out);
+
+class MemtrackProxy : public BnMemtrack {
+public:
+    MemtrackProxy();
+    ndk::ScopedAStatus getMemory(int pid, MemtrackType type,
+                                 std::vector<MemtrackRecord>* _aidl_return) override;
+    ndk::ScopedAStatus getGpuDeviceInfo(std::vector<DeviceInfo>* _aidl_return) override;
+
+private:
+    static sp<V1_0_hidl::IMemtrack> MemtrackHidlInstance();
+    static std::shared_ptr<V1_aidl::IMemtrack> MemtrackAidlInstance();
+    static bool CheckUid(uid_t calling_uid);
+    static bool CheckPid(pid_t calling_pid, pid_t request_pid);
+
+    sp<V1_0_hidl::IMemtrack> memtrack_hidl_instance_;
+    std::shared_ptr<V1_aidl::IMemtrack> memtrack_aidl_instance_;
+};
+
+} // namespace memtrack
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/services/memtrackproxy/test/Android.bp b/services/memtrackproxy/test/Android.bp
new file mode 100644
index 0000000..f943761
--- /dev/null
+++ b/services/memtrackproxy/test/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_test {
+    name: "memtrackproxy_test",
+    srcs: [
+        "MemtrackProxyTest.cpp",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "libmemtrackproxy",
+        "android.hardware.memtrack-V1-ndk_platform",
+    ],
+    test_suites: ["general-tests"],
+    require_root: true,
+}
diff --git a/services/memtrackproxy/test/MemtrackProxyTest.cpp b/services/memtrackproxy/test/MemtrackProxyTest.cpp
new file mode 100644
index 0000000..16dfba0
--- /dev/null
+++ b/services/memtrackproxy/test/MemtrackProxyTest.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/memtrack/DeviceInfo.h>
+#include <aidl/android/hardware/memtrack/IMemtrack.h>
+#include <aidl/android/hardware/memtrack/MemtrackRecord.h>
+#include <aidl/android/hardware/memtrack/MemtrackType.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+
+using aidl::android::hardware::memtrack::DeviceInfo;
+using aidl::android::hardware::memtrack::IMemtrack;
+using aidl::android::hardware::memtrack::MemtrackRecord;
+using aidl::android::hardware::memtrack::MemtrackType;
+
+class MemtrackProxyTest : public ::testing::Test {
+public:
+    virtual void SetUp() override {
+        const char* kMemtrackProxyService = "memtrack.proxy";
+        auto memtrackProxyBinder =
+                ndk::SpAIBinder(AServiceManager_waitForService(kMemtrackProxyService));
+        memtrack_proxy_ = IMemtrack::fromBinder(memtrackProxyBinder);
+        ASSERT_NE(memtrack_proxy_, nullptr);
+    }
+
+    std::shared_ptr<IMemtrack> memtrack_proxy_;
+};
+
+TEST_F(MemtrackProxyTest, GetMemoryForInvalidPid) {
+    int pid = -1;
+
+    for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+        std::vector<MemtrackRecord> records;
+
+        auto status = memtrack_proxy_->getMemory(pid, type, &records);
+
+        EXPECT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+    }
+}
+
+TEST_F(MemtrackProxyTest, GetMemoryForCallingPid) {
+    int pid = getpid();
+
+    for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+        std::vector<MemtrackRecord> records;
+
+        auto status = memtrack_proxy_->getMemory(pid, type, &records);
+
+        EXPECT_TRUE(status.isOk());
+    }
+}
+
+TEST_F(MemtrackProxyTest, GetMemoryForOtherPid) {
+    int pid = 1;
+
+    for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+        std::vector<MemtrackRecord> records;
+
+        auto status = memtrack_proxy_->getMemory(pid, type, &records);
+
+        // Test is run as root
+        EXPECT_TRUE(status.isOk());
+    }
+}
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 1d3e5b5..d828aa9 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -38,7 +38,7 @@
         "libutils",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
-        "android.hardware.power-V1-cpp",
+        "android.hardware.power-V2-cpp",
     ],
 
     cflags: [
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
index 178f545..8c225d5 100644
--- a/services/powermanager/PowerHalController.cpp
+++ b/services/powermanager/PowerHalController.cpp
@@ -18,6 +18,7 @@
 #include <android/hardware/power/1.1/IPower.h>
 #include <android/hardware/power/Boost.h>
 #include <android/hardware/power/IPower.h>
+#include <android/hardware/power/IPowerHintSession.h>
 #include <android/hardware/power/Mode.h>
 #include <powermanager/PowerHalController.h>
 #include <powermanager/PowerHalLoader.h>
@@ -73,9 +74,10 @@
 
 // Check if a call to Power HAL function failed; if so, log the failure and
 // invalidate the current Power HAL handle.
-HalResult PowerHalController::processHalResult(HalResult result, const char* fnName) {
-    if (result == HalResult::FAILED) {
-        ALOGE("%s() failed: power HAL service not available.", fnName);
+template <typename T>
+HalResult<T> PowerHalController::processHalResult(HalResult<T> result, const char* fnName) {
+    if (result.isFailed()) {
+        ALOGE("%s failed: %s", fnName, result.errorMessage());
         std::lock_guard<std::mutex> lock(mConnectedHalMutex);
         // Drop Power HAL handle. This will force future api calls to reconnect.
         mConnectedHal = nullptr;
@@ -84,18 +86,31 @@
     return result;
 }
 
-HalResult PowerHalController::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> PowerHalController::setBoost(Boost boost, int32_t durationMs) {
     std::shared_ptr<HalWrapper> handle = initHal();
     auto result = handle->setBoost(boost, durationMs);
     return processHalResult(result, "setBoost");
 }
 
-HalResult PowerHalController::setMode(Mode mode, bool enabled) {
+HalResult<void> PowerHalController::setMode(Mode mode, bool enabled) {
     std::shared_ptr<HalWrapper> handle = initHal();
     auto result = handle->setMode(mode, enabled);
     return processHalResult(result, "setMode");
 }
 
+HalResult<sp<IPowerHintSession>> PowerHalController::createHintSession(
+        int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) {
+    std::shared_ptr<HalWrapper> handle = initHal();
+    auto result = handle->createHintSession(tgid, uid, threadIds, durationNanos);
+    return processHalResult(result, "createHintSession");
+}
+
+HalResult<int64_t> PowerHalController::getHintSessionPreferredRate() {
+    std::shared_ptr<HalWrapper> handle = initHal();
+    auto result = handle->getHintSessionPreferredRate();
+    return processHalResult(result, "getHintSessionPreferredRate");
+}
+
 } // namespace power
 
 } // namespace android
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
index 2f32827..d74bd23 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -16,11 +16,17 @@
 
 #define LOG_TAG "HalWrapper"
 #include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPowerHintSession.h>
 #include <android/hardware/power/Mode.h>
 #include <powermanager/PowerHalWrapper.h>
 #include <utils/Log.h>
 
+#include <cinttypes>
+
 using namespace android::hardware::power;
+namespace V1_0 = android::hardware::power::V1_0;
+namespace V1_1 = android::hardware::power::V1_1;
+namespace Aidl = android::hardware::power;
 
 namespace android {
 
@@ -28,49 +34,88 @@
 
 // -------------------------------------------------------------------------------------------------
 
-inline HalResult toHalResult(const binder::Status& result) {
+inline HalResult<void> toHalResult(const binder::Status& result) {
     if (result.isOk()) {
-        return HalResult::SUCCESSFUL;
+        return HalResult<void>::ok();
     }
     ALOGE("Power HAL request failed: %s", result.toString8().c_str());
-    return HalResult::FAILED;
+    return HalResult<void>::fromStatus(result);
 }
 
 template <typename T>
-inline HalResult toHalResult(const hardware::Return<T>& result) {
-    if (result.isOk()) {
-        return HalResult::SUCCESSFUL;
-    }
-    ALOGE("Power HAL request failed: %s", result.description().c_str());
-    return HalResult::FAILED;
+template <typename R>
+HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) {
+    return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description());
+}
+
+template <typename T>
+template <typename R>
+HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) {
+    return ret.isOk() ? HalResult<T>::fromStatus(status, data)
+                      : HalResult<T>::failed(ret.description());
 }
 
 // -------------------------------------------------------------------------------------------------
 
-HalResult EmptyHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> HalResult<void>::fromStatus(status_t status) {
+    if (status == android::OK) {
+        return HalResult<void>::ok();
+    }
+    return HalResult<void>::failed(statusToString(status));
+}
+
+HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
+    if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+        return HalResult<void>::unsupported();
+    }
+    if (status.isOk()) {
+        return HalResult<void>::ok();
+    }
+    return HalResult<void>::failed(std::string(status.toString8().c_str()));
+}
+
+template <typename R>
+HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) {
+    return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
+}
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> EmptyHalWrapper::setBoost(Boost boost, int32_t durationMs) {
     ALOGV("Skipped setBoost %s with duration %dms because Power HAL not available",
           toString(boost).c_str(), durationMs);
-    return HalResult::UNSUPPORTED;
+    return HalResult<void>::unsupported();
 }
 
-HalResult EmptyHalWrapper::setMode(Mode mode, bool enabled) {
+HalResult<void> EmptyHalWrapper::setMode(Mode mode, bool enabled) {
     ALOGV("Skipped setMode %s to %s because Power HAL not available", toString(mode).c_str(),
           enabled ? "true" : "false");
-    return HalResult::UNSUPPORTED;
+    return HalResult<void>::unsupported();
+}
+
+HalResult<sp<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSession(
+        int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
+    ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available",
+          threadIds.size());
+    return HalResult<sp<Aidl::IPowerHintSession>>::unsupported();
+}
+
+HalResult<int64_t> EmptyHalWrapper::getHintSessionPreferredRate() {
+    ALOGV("Skipped getHintSessionPreferredRate because Power HAL not available");
+    return HalResult<int64_t>::unsupported();
 }
 
 // -------------------------------------------------------------------------------------------------
 
-HalResult HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) {
     if (boost == Boost::INTERACTION) {
         return sendPowerHint(V1_0::PowerHint::INTERACTION, durationMs);
     } else {
         ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str());
-        return HalResult::UNSUPPORTED;
+        return HalResult<void>::unsupported();
     }
 }
 
-HalResult HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) {
+HalResult<void> HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) {
     uint32_t data = enabled ? 1 : 0;
     switch (mode) {
         case Mode::LAUNCH:
@@ -88,38 +133,54 @@
         default:
             ALOGV("Skipped setMode %s because Power HAL AIDL not available",
                   toString(mode).c_str());
-            return HalResult::UNSUPPORTED;
+            return HalResult<void>::unsupported();
     }
 }
 
-HalResult HidlHalWrapperV1_0::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
-    return toHalResult(mHandleV1_0->powerHint(hintId, data));
+HalResult<void> HidlHalWrapperV1_0::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
+    auto ret = mHandleV1_0->powerHint(hintId, data);
+    return HalResult<void>::fromReturn(ret);
 }
 
-HalResult HidlHalWrapperV1_0::setInteractive(bool enabled) {
-    return toHalResult(mHandleV1_0->setInteractive(enabled));
+HalResult<void> HidlHalWrapperV1_0::setInteractive(bool enabled) {
+    auto ret = mHandleV1_0->setInteractive(enabled);
+    return HalResult<void>::fromReturn(ret);
 }
 
-HalResult HidlHalWrapperV1_0::setFeature(V1_0::Feature feature, bool enabled) {
-    return toHalResult(mHandleV1_0->setFeature(feature, enabled));
+HalResult<void> HidlHalWrapperV1_0::setFeature(V1_0::Feature feature, bool enabled) {
+    auto ret = mHandleV1_0->setFeature(feature, enabled);
+    return HalResult<void>::fromReturn(ret);
+}
+
+HalResult<sp<Aidl::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession(
+        int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
+    ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available",
+          threadIds.size());
+    return HalResult<sp<Aidl::IPowerHintSession>>::unsupported();
+}
+
+HalResult<int64_t> HidlHalWrapperV1_0::getHintSessionPreferredRate() {
+    ALOGV("Skipped getHintSessionPreferredRate because Power HAL not available");
+    return HalResult<int64_t>::unsupported();
 }
 
 // -------------------------------------------------------------------------------------------------
 
-HalResult HidlHalWrapperV1_1::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
-    return toHalResult(mHandleV1_1->powerHintAsync(hintId, data));
+HalResult<void> HidlHalWrapperV1_1::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
+    auto ret = mHandleV1_1->powerHintAsync(hintId, data);
+    return HalResult<void>::fromReturn(ret);
 }
 
 // -------------------------------------------------------------------------------------------------
 
-HalResult AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) {
+HalResult<void> AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) {
     std::unique_lock<std::mutex> lock(mBoostMutex);
     size_t idx = static_cast<size_t>(boost);
 
     // Quick return if boost is not supported by HAL
     if (idx >= mBoostSupportedArray.size() || mBoostSupportedArray[idx] == HalSupport::OFF) {
         ALOGV("Skipped setBoost %s because Power HAL doesn't support it", toString(boost).c_str());
-        return HalResult::UNSUPPORTED;
+        return HalResult<void>::unsupported();
     }
 
     if (mBoostSupportedArray[idx] == HalSupport::UNKNOWN) {
@@ -128,14 +189,15 @@
         if (!isSupportedRet.isOk()) {
             ALOGE("Skipped setBoost %s because check support failed with: %s",
                   toString(boost).c_str(), isSupportedRet.toString8().c_str());
-            return HalResult::FAILED;
+            // return HalResult::FAILED;
+            return HalResult<void>::fromStatus(isSupportedRet);
         }
 
         mBoostSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
         if (!isSupported) {
             ALOGV("Skipped setBoost %s because Power HAL doesn't support it",
                   toString(boost).c_str());
-            return HalResult::UNSUPPORTED;
+            return HalResult<void>::unsupported();
         }
     }
     lock.unlock();
@@ -143,30 +205,28 @@
     return toHalResult(mHandle->setBoost(boost, durationMs));
 }
 
-HalResult AidlHalWrapper::setMode(Mode mode, bool enabled) {
+HalResult<void> AidlHalWrapper::setMode(Mode mode, bool enabled) {
     std::unique_lock<std::mutex> lock(mModeMutex);
     size_t idx = static_cast<size_t>(mode);
 
     // Quick return if mode is not supported by HAL
     if (idx >= mModeSupportedArray.size() || mModeSupportedArray[idx] == HalSupport::OFF) {
         ALOGV("Skipped setMode %s because Power HAL doesn't support it", toString(mode).c_str());
-        return HalResult::UNSUPPORTED;
+        return HalResult<void>::unsupported();
     }
 
     if (mModeSupportedArray[idx] == HalSupport::UNKNOWN) {
         bool isSupported = false;
         auto isSupportedRet = mHandle->isModeSupported(mode, &isSupported);
         if (!isSupportedRet.isOk()) {
-            ALOGE("Skipped setMode %s because check support failed with: %s",
-                  toString(mode).c_str(), isSupportedRet.toString8().c_str());
-            return HalResult::FAILED;
+            return HalResult<void>::failed(isSupportedRet.toString8().c_str());
         }
 
         mModeSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
         if (!isSupported) {
             ALOGV("Skipped setMode %s because Power HAL doesn't support it",
                   toString(mode).c_str());
-            return HalResult::UNSUPPORTED;
+            return HalResult<void>::unsupported();
         }
     }
     lock.unlock();
@@ -174,6 +234,20 @@
     return toHalResult(mHandle->setMode(mode, enabled));
 }
 
+HalResult<sp<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSession(
+        int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) {
+    sp<IPowerHintSession> appSession;
+    return HalResult<sp<Aidl::IPowerHintSession>>::
+            fromStatus(mHandle->createHintSession(tgid, uid, threadIds, durationNanos, &appSession),
+                       appSession);
+}
+
+HalResult<int64_t> AidlHalWrapper::getHintSessionPreferredRate() {
+    int64_t rate = -1;
+    auto result = mHandle->getHintSessionPreferredRate(&rate);
+    return HalResult<int64_t>::fromStatus(result, rate);
+}
+
 // -------------------------------------------------------------------------------------------------
 
 } // namespace power
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
index a489253..3997929 100644
--- a/services/powermanager/benchmarks/Android.bp
+++ b/services/powermanager/benchmarks/Android.bp
@@ -38,7 +38,7 @@
         "libutils",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
-        "android.hardware.power-V1-cpp",
+        "android.hardware.power-V2-cpp",
     ],
     static_libs: [
         "libtestUtil",
diff --git a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
index 1004828..1100cad 100644
--- a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
@@ -18,7 +18,9 @@
 
 #include <android/hardware/power/Boost.h>
 #include <android/hardware/power/IPower.h>
+#include <android/hardware/power/IPowerHintSession.h>
 #include <android/hardware/power/Mode.h>
+#include <android/hardware/power/WorkDuration.h>
 #include <benchmark/benchmark.h>
 #include <binder/IServiceManager.h>
 #include <testUtil.h>
@@ -26,7 +28,9 @@
 
 using android::hardware::power::Boost;
 using android::hardware::power::IPower;
+using android::hardware::power::IPowerHintSession;
 using android::hardware::power::Mode;
+using android::hardware::power::WorkDuration;
 using std::chrono::microseconds;
 
 using namespace android;
@@ -38,6 +42,21 @@
 static constexpr int64_t FIRST_MODE = static_cast<int64_t>(Mode::DOUBLE_TAP_TO_WAKE);
 static constexpr int64_t LAST_MODE = static_cast<int64_t>(Mode::CAMERA_STREAMING_HIGH);
 
+class DurationWrapper : public WorkDuration {
+public:
+    DurationWrapper(int64_t dur, int64_t time) {
+        durationNanos = dur;
+        timeStampNanos = time;
+    }
+};
+
+static const std::vector<WorkDuration> DURATIONS = {
+        DurationWrapper(1L, 1L),
+        DurationWrapper(1000L, 2L),
+        DurationWrapper(1000000L, 3L),
+        DurationWrapper(1000000000L, 4L),
+};
+
 // Delay between oneway method calls to avoid overflowing the binder buffers.
 static constexpr microseconds ONEWAY_API_DELAY = 100us;
 
@@ -68,6 +87,47 @@
     }
 }
 
+template <class R, class... Args0, class... Args1>
+static void runSessionBenchmark(benchmark::State& state, R (IPowerHintSession::*fn)(Args0...),
+                                Args1&&... args1) {
+    sp<IPower> pwHal = waitForVintfService<IPower>();
+
+    if (pwHal == nullptr) {
+        ALOGI("Power HAL not available, skipping test...");
+        return;
+    }
+
+    // do not use tid from the benchmark process, use 1 for init
+    std::vector<int32_t> threadIds{1};
+    int64_t durationNanos = 16666666L;
+    sp<IPowerHintSession> hal;
+
+    auto status = pwHal->createHintSession(1, 0, threadIds, durationNanos, &hal);
+
+    if (hal == nullptr) {
+        ALOGI("Power HAL doesn't support session, skipping test...");
+        return;
+    }
+
+    binder::Status ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+    if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+        ALOGI("Power HAL does not support this operation, skipping test...");
+        return;
+    }
+
+    while (state.KeepRunning()) {
+        ret = (*hal.*fn)(std::forward<Args1>(args1)...);
+        state.PauseTiming();
+        if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+        if (ONEWAY_API_DELAY > 0us) {
+            testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY)
+                                  .count());
+        }
+        state.ResumeTiming();
+    }
+    hal->close();
+}
+
 static void BM_PowerHalAidlBenchmarks_isBoostSupported(benchmark::State& state) {
     bool isSupported;
     Boost boost = static_cast<Boost>(state.range(0));
@@ -90,7 +150,53 @@
     runBenchmark(state, ONEWAY_API_DELAY, &IPower::setMode, mode, false);
 }
 
+static void BM_PowerHalAidlBenchmarks_createHintSession(benchmark::State& state) {
+    std::vector<int32_t> threadIds{static_cast<int32_t>(state.range(0))};
+    int64_t durationNanos = 16666666L;
+    int32_t tgid = 999;
+    int32_t uid = 1001;
+    sp<IPowerHintSession> appSession;
+    sp<IPower> hal = waitForVintfService<IPower>();
+
+    if (hal == nullptr) {
+        ALOGI("Power HAL not available, skipping test...");
+        return;
+    }
+
+    binder::Status ret = hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
+    if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) {
+        ALOGI("Power HAL does not support this operation, skipping test...");
+        return;
+    }
+
+    while (state.KeepRunning()) {
+        ret = hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
+        state.PauseTiming();
+        if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str());
+        appSession->close();
+        state.ResumeTiming();
+    }
+}
+
+static void BM_PowerHalAidlBenchmarks_getHintSessionPreferredRate(benchmark::State& state) {
+    int64_t rate;
+    runBenchmark(state, 0us, &IPower::getHintSessionPreferredRate, &rate);
+}
+
+static void BM_PowerHalAidlBenchmarks_updateTargetWorkDuration(benchmark::State& state) {
+    int64_t duration = 1000;
+    runSessionBenchmark(state, &IPowerHintSession::updateTargetWorkDuration, duration);
+}
+
+static void BM_PowerHalAidlBenchmarks_reportActualWorkDuration(benchmark::State& state) {
+    runSessionBenchmark(state, &IPowerHintSession::reportActualWorkDuration, DURATIONS);
+}
+
 BENCHMARK(BM_PowerHalAidlBenchmarks_isBoostSupported)->DenseRange(FIRST_BOOST, LAST_BOOST, 1);
 BENCHMARK(BM_PowerHalAidlBenchmarks_isModeSupported)->DenseRange(FIRST_MODE, LAST_MODE, 1);
 BENCHMARK(BM_PowerHalAidlBenchmarks_setBoost)->DenseRange(FIRST_BOOST, LAST_BOOST, 1);
 BENCHMARK(BM_PowerHalAidlBenchmarks_setMode)->DenseRange(FIRST_MODE, LAST_MODE, 1);
+BENCHMARK(BM_PowerHalAidlBenchmarks_createHintSession)->Arg(1);
+BENCHMARK(BM_PowerHalAidlBenchmarks_getHintSessionPreferredRate);
+BENCHMARK(BM_PowerHalAidlBenchmarks_updateTargetWorkDuration);
+BENCHMARK(BM_PowerHalAidlBenchmarks_reportActualWorkDuration);
diff --git a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
index 598080b..f8abc7a 100644
--- a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
@@ -40,29 +40,29 @@
 // Delay between oneway method calls to avoid overflowing the binder buffers.
 static constexpr std::chrono::microseconds ONEWAY_API_DELAY = 100us;
 
-template <class... Args0, class... Args1>
-static void runBenchmark(benchmark::State& state, HalResult (PowerHalController::*fn)(Args0...),
+template <typename T, class... Args0, class... Args1>
+static void runBenchmark(benchmark::State& state, HalResult<T> (PowerHalController::*fn)(Args0...),
                          Args1&&... args1) {
     while (state.KeepRunning()) {
         PowerHalController controller;
-        HalResult ret = (controller.*fn)(std::forward<Args1>(args1)...);
+        HalResult<T> ret = (controller.*fn)(std::forward<Args1>(args1)...);
         state.PauseTiming();
-        if (ret == HalResult::FAILED) state.SkipWithError("Power HAL request failed");
+        if (ret.isFailed()) state.SkipWithError("Power HAL request failed");
         state.ResumeTiming();
     }
 }
 
-template <class... Args0, class... Args1>
+template <typename T, class... Args0, class... Args1>
 static void runCachedBenchmark(benchmark::State& state,
-                               HalResult (PowerHalController::*fn)(Args0...), Args1&&... args1) {
+                               HalResult<T> (PowerHalController::*fn)(Args0...), Args1&&... args1) {
     PowerHalController controller;
     // First call out of test, to cache HAL service and isSupported result.
     (controller.*fn)(std::forward<Args1>(args1)...);
 
     while (state.KeepRunning()) {
-        HalResult ret = (controller.*fn)(std::forward<Args1>(args1)...);
+        HalResult<T> ret = (controller.*fn)(std::forward<Args1>(args1)...);
         state.PauseTiming();
-        if (ret == HalResult::FAILED) {
+        if (ret.isFailed()) {
             state.SkipWithError("Power HAL request failed");
         }
         testDelaySpin(
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 69e4041..659b2d2 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -46,7 +46,7 @@
         "libutils",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
-        "android.hardware.power-V1-cpp",
+        "android.hardware.power-V2-cpp",
     ],
     static_libs: [
         "libgmock",
diff --git a/services/powermanager/tests/PowerHalControllerTest.cpp b/services/powermanager/tests/PowerHalControllerTest.cpp
index 141b244..6cc7a6f 100644
--- a/services/powermanager/tests/PowerHalControllerTest.cpp
+++ b/services/powermanager/tests/PowerHalControllerTest.cpp
@@ -134,9 +134,9 @@
     // Still works with EmptyPowerHalWrapper as fallback ignoring every api call
     // and logging.
     auto result = halController.setBoost(Boost::INTERACTION, 1000);
-    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+    ASSERT_TRUE(result.isUnsupported());
     result = halController.setMode(Mode::LAUNCH, true);
-    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+    ASSERT_TRUE(result.isUnsupported());
 
     // PowerHalConnector was called every time to attempt to reconnect with
     // underlying service.
@@ -159,9 +159,9 @@
     }
 
     auto result = mHalController->setBoost(Boost::INTERACTION, 100);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
     result = mHalController->setMode(Mode::LAUNCH, true);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
 
     // PowerHalConnector was called only once and never reset.
     powerHalConnectCount = mHalConnector->getConnectCount();
@@ -182,13 +182,13 @@
     EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(4));
 
     auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
     result = mHalController->setMode(Mode::LAUNCH, true);
-    ASSERT_EQ(HalResult::FAILED, result);
+    ASSERT_TRUE(result.isFailed());
     result = mHalController->setMode(Mode::VR, false);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
     result = mHalController->setMode(Mode::LOW_POWER, true);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
 
     // PowerHalConnector was called only twice: on first api call and after failed
     // call.
@@ -204,9 +204,9 @@
     EXPECT_EQ(powerHalConnectCount, 0);
 
     auto result = mHalController->setBoost(Boost::CAMERA_LAUNCH, 1000);
-    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+    ASSERT_TRUE(result.isUnsupported());
     result = mHalController->setMode(Mode::CAMERA_STREAMING_HIGH, true);
-    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+    ASSERT_TRUE(result.isUnsupported());
 
     // PowerHalConnector was called only once and never reset.
     powerHalConnectCount = mHalConnector->getConnectCount();
@@ -225,7 +225,7 @@
     for (int i = 0; i < 10; i++) {
         threads.push_back(std::thread([&]() {
             auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
-            ASSERT_EQ(HalResult::SUCCESSFUL, result);
+            ASSERT_TRUE(result.isOk());
         }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
@@ -253,19 +253,19 @@
     for (int i = 0; i < 10; i++) {
         threads.push_back(std::thread([&]() {
             auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
-            ASSERT_EQ(HalResult::SUCCESSFUL, result);
+            ASSERT_TRUE(result.isOk());
         }));
         threads.push_back(std::thread([&]() {
             auto result = mHalController->setMode(Mode::LAUNCH, true);
-            ASSERT_EQ(HalResult::FAILED, result);
+            ASSERT_TRUE(result.isFailed());
         }));
         threads.push_back(std::thread([&]() {
             auto result = mHalController->setMode(Mode::LOW_POWER, false);
-            ASSERT_EQ(HalResult::SUCCESSFUL, result);
+            ASSERT_TRUE(result.isOk());
         }));
         threads.push_back(std::thread([&]() {
             auto result = mHalController->setMode(Mode::VR, true);
-            ASSERT_EQ(HalResult::SUCCESSFUL, result);
+            ASSERT_TRUE(result.isOk());
         }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index a765659..d890f5c 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "PowerHalWrapperAidlTest"
 
 #include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPowerHintSession.h>
 #include <android/hardware/power/Mode.h>
 #include <binder/IServiceManager.h>
 #include <gmock/gmock.h>
@@ -24,11 +25,13 @@
 #include <powermanager/PowerHalWrapper.h>
 #include <utils/Log.h>
 
+#include <unistd.h>
 #include <thread>
 
 using android::binder::Status;
 using android::hardware::power::Boost;
 using android::hardware::power::IPower;
+using android::hardware::power::IPowerHintSession;
 using android::hardware::power::Mode;
 
 using namespace android;
@@ -44,6 +47,11 @@
     MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override));
     MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override));
     MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override));
+    MOCK_METHOD(Status, createHintSession,
+                (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+                 int64_t durationNanos, sp<IPowerHintSession>* session),
+                (override));
+    MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * rate), (override));
     MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
     MOCK_METHOD(std::string, getInterfaceHash, (), (override));
     MOCK_METHOD(IBinder*, onAsBinder, (), (override));
@@ -65,7 +73,7 @@
 void PowerHalWrapperAidlTest::SetUp() {
     mMockHal = new StrictMock<MockIPower>();
     mWrapper = std::make_unique<AidlHalWrapper>(mMockHal);
-    ASSERT_NE(mWrapper, nullptr);
+    ASSERT_NE(nullptr, mWrapper);
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -81,7 +89,7 @@
     }
 
     auto result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 100);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
 }
 
 TEST_F(PowerHalWrapperAidlTest, TestSetBoostFailed) {
@@ -99,9 +107,9 @@
     }
 
     auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
-    ASSERT_EQ(HalResult::FAILED, result);
+    ASSERT_TRUE(result.isFailed());
     result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 1000);
-    ASSERT_EQ(HalResult::FAILED, result);
+    ASSERT_TRUE(result.isFailed());
 }
 
 TEST_F(PowerHalWrapperAidlTest, TestSetBoostUnsupported) {
@@ -110,9 +118,9 @@
             .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
 
     auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
-    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+    ASSERT_TRUE(result.isUnsupported());
     result = mWrapper->setBoost(Boost::CAMERA_SHOT, 10);
-    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+    ASSERT_TRUE(result.isUnsupported());
 }
 
 TEST_F(PowerHalWrapperAidlTest, TestSetBoostMultiThreadCheckSupportedOnlyOnce) {
@@ -128,7 +136,7 @@
     for (int i = 0; i < 10; i++) {
         threads.push_back(std::thread([&]() {
             auto result = mWrapper->setBoost(Boost::INTERACTION, 100);
-            ASSERT_EQ(HalResult::SUCCESSFUL, result);
+            ASSERT_TRUE(result.isOk());
         }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
@@ -145,7 +153,7 @@
     }
 
     auto result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
 }
 
 TEST_F(PowerHalWrapperAidlTest, TestSetModeFailed) {
@@ -163,9 +171,9 @@
     }
 
     auto result = mWrapper->setMode(Mode::LAUNCH, true);
-    ASSERT_EQ(HalResult::FAILED, result);
+    ASSERT_TRUE(result.isFailed());
     result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false);
-    ASSERT_EQ(HalResult::FAILED, result);
+    ASSERT_TRUE(result.isFailed());
 }
 
 TEST_F(PowerHalWrapperAidlTest, TestSetModeUnsupported) {
@@ -174,9 +182,9 @@
             .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
 
     auto result = mWrapper->setMode(Mode::LAUNCH, true);
-    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+    ASSERT_TRUE(result.isUnsupported());
     result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
-    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+    ASSERT_TRUE(result.isUnsupported());
 }
 
 TEST_F(PowerHalWrapperAidlTest, TestSetModeMultiThreadCheckSupportedOnlyOnce) {
@@ -192,8 +200,41 @@
     for (int i = 0; i < 10; i++) {
         threads.push_back(std::thread([&]() {
             auto result = mWrapper->setMode(Mode::LAUNCH, false);
-            ASSERT_EQ(HalResult::SUCCESSFUL, result);
+            ASSERT_TRUE(result.isOk());
         }));
     }
     std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
 }
+
+TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionSuccessful) {
+    std::vector<int> threadIds{gettid()};
+    int32_t tgid = 999;
+    int32_t uid = 1001;
+    int64_t durationNanos = 16666666L;
+    EXPECT_CALL(*mMockHal.get(),
+                createHintSession(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos), _))
+            .Times(Exactly(1));
+    auto result = mWrapper->createHintSession(tgid, uid, threadIds, durationNanos);
+    ASSERT_TRUE(result.isOk());
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionFailed) {
+    int32_t tgid = 999;
+    int32_t uid = 1001;
+    std::vector<int> threadIds{};
+    int64_t durationNanos = 16666666L;
+    EXPECT_CALL(*mMockHal.get(),
+                createHintSession(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos), _))
+            .Times(Exactly(1))
+            .WillRepeatedly(Return(Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT)));
+    auto result = mWrapper->createHintSession(tgid, uid, threadIds, durationNanos);
+    ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestGetHintSessionPreferredRate) {
+    EXPECT_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).Times(Exactly(1));
+    auto result = mWrapper->getHintSessionPreferredRate();
+    ASSERT_TRUE(result.isOk());
+    int64_t rate = result.value();
+    ASSERT_GE(0, rate);
+}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
index 6693d0b..b54762c 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
@@ -72,7 +72,7 @@
     EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(1000))).Times(Exactly(1));
 
     auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
 }
 
 TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostFailed) {
@@ -83,12 +83,12 @@
             });
 
     auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
-    ASSERT_EQ(HalResult::FAILED, result);
+    ASSERT_TRUE(result.isFailed());
 }
 
 TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostUnsupported) {
     auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
-    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+    ASSERT_TRUE(result.isUnsupported());
 }
 
 TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeSuccessful) {
@@ -106,17 +106,17 @@
     }
 
     auto result = mWrapper->setMode(Mode::LAUNCH, true);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
     result = mWrapper->setMode(Mode::LOW_POWER, false);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
     result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
     result = mWrapper->setMode(Mode::VR, false);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
     result = mWrapper->setMode(Mode::INTERACTIVE, true);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
     result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
 }
 
 TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeFailed) {
@@ -127,10 +127,10 @@
             });
 
     auto result = mWrapper->setMode(Mode::LAUNCH, 1);
-    ASSERT_EQ(HalResult::FAILED, result);
+    ASSERT_TRUE(result.isFailed());
 }
 
 TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeIgnored) {
     auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
-    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+    ASSERT_TRUE(result.isUnsupported());
 }
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
index 55bbd6d..d30e8d2 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
@@ -89,7 +89,7 @@
             .Times(Exactly(1));
 
     auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
 }
 
 TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostFailed) {
@@ -100,12 +100,12 @@
             });
 
     auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
-    ASSERT_EQ(HalResult::FAILED, result);
+    ASSERT_TRUE(result.isFailed());
 }
 
 TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostUnsupported) {
     auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
-    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+    ASSERT_TRUE(result.isUnsupported());
 }
 
 TEST_F(PowerHalWrapperHidlV1_1Test, TestSetMode) {
@@ -127,17 +127,17 @@
     }
 
     auto result = mWrapper->setMode(Mode::LAUNCH, true);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
     result = mWrapper->setMode(Mode::LOW_POWER, false);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
     result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
     result = mWrapper->setMode(Mode::VR, false);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
     result = mWrapper->setMode(Mode::INTERACTIVE, true);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
     result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
-    ASSERT_EQ(HalResult::SUCCESSFUL, result);
+    ASSERT_TRUE(result.isOk());
 }
 
 TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeFailed) {
@@ -148,10 +148,10 @@
             });
 
     auto result = mWrapper->setMode(Mode::LAUNCH, 1);
-    ASSERT_EQ(HalResult::FAILED, result);
+    ASSERT_TRUE(result.isFailed());
 }
 
 TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeIgnored) {
     auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
-    ASSERT_EQ(HalResult::UNSUPPORTED, result);
+    ASSERT_TRUE(result.isUnsupported());
 }
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index ca9ff7c..4151b45 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -7,9 +7,6 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
-subdirs = [
-    "hidl"
-]
 cc_library_shared {
     name: "libsensorservice",
 
@@ -52,9 +49,12 @@
         "libhardware_legacy",
         "libutils",
         "liblog",
+        "libactivitymanager_aidl",
+        "libbatterystats_aidl",
         "libbinder",
         "libsensor",
         "libsensorprivacy",
+        "libpermission",
         "libprotoutil",
         "libcrypto",
         "libbase",
@@ -71,8 +71,12 @@
 
     generated_headers: ["framework-cppstream-protos"],
 
-    // our public headers depend on libsensor and libsensorprivacy
-    export_shared_lib_headers: ["libsensor", "libsensorprivacy"],
+    export_shared_lib_headers: [
+        "libactivitymanager_aidl",
+        "libsensor",
+        "libsensorprivacy",
+        "libpermission",
+    ],
 }
 
 cc_binary {
diff --git a/services/sensorservice/BatteryService.h b/services/sensorservice/BatteryService.h
index 43a750c..09eb2c1 100644
--- a/services/sensorservice/BatteryService.h
+++ b/services/sensorservice/BatteryService.h
@@ -17,7 +17,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <binder/IBatteryStats.h>
+#include <batterystats/IBatteryStats.h>
 #include <utils/Singleton.h>
 
 namespace android {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index b976eb5..9885352 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -60,7 +60,6 @@
         "libnativewindow",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
-        "libstatslog",
         "libsync",
         "libtimestats",
         "libui",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 4ace4c2..d243989 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -185,10 +185,14 @@
             return std::nullopt;
         }
     }
-    bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
+    const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
             (isSecure() && !targetSettings.isSecure);
+    const bool bufferCanBeUsedAsHwTexture =
+            mBufferInfo.mBuffer->getBuffer()->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
     compositionengine::LayerFE::LayerSettings& layer = *result;
-    if (blackOutLayer) {
+    if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
+        ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
+                 mName.c_str());
         prepareClearClientComposition(layer, true /* blackout */);
         return layer;
     }
@@ -209,7 +213,7 @@
             ? mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel
             : defaultMaxContentLuminance;
     layer.frameNumber = mCurrentFrameNumber;
-    layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0;
+    layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer()->getId() : 0;
 
     const bool useFiltering =
             targetSettings.needsFiltering || mNeedsFiltering || bufferNeedsFiltering();
@@ -310,7 +314,7 @@
                 : Hwc2::IComposerClient::Composition::DEVICE;
     }
 
-    compositionState->buffer = mBufferInfo.mBuffer;
+    compositionState->buffer = mBufferInfo.mBuffer->getBuffer();
     compositionState->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
             ? 0
             : mBufferInfo.mBufferSlot;
@@ -325,6 +329,37 @@
     mRefreshPending = false;
     return hasReadyFrame();
 }
+namespace {
+TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) {
+    using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
+    using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
+    const auto frameRateCompatibility = [frameRate] {
+        switch (frameRate.type) {
+            case Layer::FrameRateCompatibility::Default:
+                return FrameRateCompatibility::Default;
+            case Layer::FrameRateCompatibility::ExactOrMultiple:
+                return FrameRateCompatibility::ExactOrMultiple;
+            default:
+                return FrameRateCompatibility::Undefined;
+        }
+    }();
+
+    const auto seamlessness = [frameRate] {
+        switch (frameRate.seamlessness) {
+            case scheduler::Seamlessness::OnlySeamless:
+                return Seamlessness::ShouldBeSeamless;
+            case scheduler::Seamlessness::SeamedAndSeamless:
+                return Seamlessness::NotRequired;
+            default:
+                return Seamlessness::Undefined;
+        }
+    }();
+
+    return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(),
+                                       .frameRateCompatibility = frameRateCompatibility,
+                                       .seamlessness = seamlessness};
+}
+} // namespace
 
 bool BufferLayer::onPostComposition(const DisplayDevice* display,
                                     const std::shared_ptr<FenceTime>& glDoneFence,
@@ -377,7 +412,9 @@
     const std::optional<Fps> renderRate = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
     if (presentFence->isValid()) {
         mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
-                                              refreshRate, renderRate);
+                                              refreshRate, renderRate,
+                                              frameRateToSetFrameRateVotePayload(
+                                                      mDrawingState.frameRate));
         mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
                                            presentFence, FrameTracer::FrameEvent::PRESENT_FENCE);
         mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
@@ -389,7 +426,9 @@
         // timestamp instead.
         const nsecs_t actualPresentTime = display->getRefreshTimestamp();
         mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
-                                             refreshRate, renderRate);
+                                             refreshRate, renderRate,
+                                             frameRateToSetFrameRateVotePayload(
+                                                     mDrawingState.frameRate));
         mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
                                                actualPresentTime,
                                                FrameTracer::FrameEvent::PRESENT_FENCE);
@@ -403,7 +442,7 @@
 
 void BufferLayer::gatherBufferInfo() {
     mBufferInfo.mPixelFormat =
-            !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->format;
+            !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->getBuffer()->format;
     mBufferInfo.mFrameLatencyNeeded = true;
 }
 
@@ -468,11 +507,6 @@
 
     BufferInfo oldBufferInfo = mBufferInfo;
 
-    if (!allTransactionsSignaled(expectedPresentTime)) {
-        mFlinger->setTransactionFlags(eTraversalNeeded);
-        return false;
-    }
-
     status_t err = updateTexImage(recomputeVisibleRegions, latchTime, expectedPresentTime);
     if (err != NO_ERROR) {
         return false;
@@ -505,10 +539,10 @@
     }
 
     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)) {
+        uint32_t bufWidth = mBufferInfo.mBuffer->getBuffer()->getWidth();
+        uint32_t bufHeight = mBufferInfo.mBuffer->getBuffer()->getHeight();
+        if (bufWidth != uint32_t(oldBufferInfo.mBuffer->getBuffer()->width) ||
+            bufHeight != uint32_t(oldBufferInfo.mBuffer->getBuffer()->height)) {
             recomputeVisibleRegions = true;
         }
     }
@@ -517,53 +551,9 @@
         recomputeVisibleRegions = true;
     }
 
-    // Remove any sync points corresponding to the buffer which was just
-    // latched
-    {
-        Mutex::Autolock lock(mLocalSyncPointMutex);
-        auto point = mLocalSyncPoints.begin();
-        while (point != mLocalSyncPoints.end()) {
-            if (!(*point)->frameIsAvailable() || !(*point)->transactionIsApplied()) {
-                // This sync point must have been added since we started
-                // latching. Don't drop it yet.
-                ++point;
-                continue;
-            }
-
-            if ((*point)->getFrameNumber() <= mCurrentFrameNumber) {
-                std::stringstream ss;
-                ss << "Dropping sync point " << (*point)->getFrameNumber();
-                ATRACE_NAME(ss.str().c_str());
-                point = mLocalSyncPoints.erase(point);
-            } else {
-                ++point;
-            }
-        }
-    }
-
     return true;
 }
 
-// transaction
-void BufferLayer::notifyAvailableFrames(nsecs_t expectedPresentTime) {
-    const auto headFrameNumber = getHeadFrameNumber(expectedPresentTime);
-    const bool headFenceSignaled = fenceHasSignaled();
-    const bool presentTimeIsCurrent = framePresentTimeIsCurrent(expectedPresentTime);
-    Mutex::Autolock lock(mLocalSyncPointMutex);
-    for (auto& point : mLocalSyncPoints) {
-        if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled &&
-            presentTimeIsCurrent) {
-            point->setFrameAvailable();
-            sp<Layer> requestedSyncLayer = point->getRequestedSyncLayer();
-            if (requestedSyncLayer) {
-                // Need to update the transaction flag to ensure the layer's pending transaction
-                // gets applied.
-                requestedSyncLayer->setTransactionFlags(eTransactionNeeded);
-            }
-        }
-    }
-}
-
 bool BufferLayer::hasReadyFrame() const {
     return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh();
 }
@@ -573,35 +563,8 @@
 }
 
 bool BufferLayer::isProtected() const {
-    const sp<GraphicBuffer>& buffer(mBufferInfo.mBuffer);
-    return (buffer != 0) && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
-}
-
-// h/w composer set-up
-bool BufferLayer::allTransactionsSignaled(nsecs_t expectedPresentTime) {
-    const auto headFrameNumber = getHeadFrameNumber(expectedPresentTime);
-    bool matchingFramesFound = false;
-    bool allTransactionsApplied = true;
-    Mutex::Autolock lock(mLocalSyncPointMutex);
-
-    for (auto& point : mLocalSyncPoints) {
-        if (point->getFrameNumber() > headFrameNumber) {
-            break;
-        }
-        matchingFramesFound = true;
-
-        if (!point->frameIsAvailable()) {
-            // We haven't notified the remote layer that the frame for
-            // this point is available yet. Notify it now, and then
-            // abort this attempt to latch.
-            point->setFrameAvailable();
-            allTransactionsApplied = false;
-            break;
-        }
-
-        allTransactionsApplied = allTransactionsApplied && point->transactionIsApplied();
-    }
-    return !matchingFramesFound || allTransactionsApplied;
+    return (mBufferInfo.mBuffer != nullptr) &&
+            (mBufferInfo.mBuffer->getBuffer()->getUsage() & GRALLOC_USAGE_PROTECTED);
 }
 
 // As documented in libhardware header, formats in the range
@@ -688,8 +651,8 @@
         return Rect::INVALID_RECT;
     }
 
-    uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
-    uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
+    uint32_t bufWidth = mBufferInfo.mBuffer->getBuffer()->getWidth();
+    uint32_t bufHeight = mBufferInfo.mBuffer->getBuffer()->getHeight();
 
     // Undo any transformations on the buffer and return the result.
     if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
@@ -720,8 +683,8 @@
         return parentBounds;
     }
 
-    uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
-    uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
+    uint32_t bufWidth = mBufferInfo.mBuffer->getBuffer()->getWidth();
+    uint32_t bufHeight = mBufferInfo.mBuffer->getBuffer()->getHeight();
 
     // Undo any transformations on the buffer and return the result.
     if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
@@ -763,7 +726,7 @@
         return mBufferInfo.mCrop;
     } else if (mBufferInfo.mBuffer != nullptr) {
         // otherwise we use the whole buffer
-        return mBufferInfo.mBuffer->getBounds();
+        return mBufferInfo.mBuffer->getBuffer()->getBounds();
     } else {
         // if we don't have a buffer yet, we use an empty/invalid crop
         return Rect();
@@ -808,12 +771,14 @@
 }
 
 sp<GraphicBuffer> BufferLayer::getBuffer() const {
-    return mBufferInfo.mBuffer;
+    return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr;
 }
 
 void BufferLayer::getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) {
-    GLConsumer::computeTransformMatrix(outMatrix, mBufferInfo.mBuffer, mBufferInfo.mCrop,
-                                       mBufferInfo.mTransform, filteringEnabled);
+    GLConsumer::computeTransformMatrix(outMatrix,
+                                       mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer()
+                                                           : nullptr,
+                                       mBufferInfo.mCrop, mBufferInfo.mTransform, filteringEnabled);
 }
 
 void BufferLayer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index b8d3f12..cd3d80e 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -90,8 +90,6 @@
 
     bool isBufferLatched() const override { return mRefreshPending; }
 
-    void notifyAvailableFrames(nsecs_t expectedPresentTime) override;
-
     bool hasReadyFrame() const override;
 
     // Returns the current scaling mode
@@ -134,7 +132,7 @@
         PixelFormat mPixelFormat{PIXEL_FORMAT_NONE};
         bool mTransformToDisplayInverse{false};
 
-        sp<GraphicBuffer> mBuffer;
+        std::shared_ptr<renderengine::ExternalTexture> mBuffer;
         int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
 
         bool mFrameLatencyNeeded{false};
@@ -153,11 +151,6 @@
     bool onPreComposition(nsecs_t) override;
     void preparePerFrameCompositionState() override;
 
-    // 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(nsecs_t expectedPresentTime);
-
     static bool getOpacityForFormat(uint32_t format);
 
     // from graphics API
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 69d2d11..96b2247 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -40,7 +40,6 @@
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
 #include <private/gui/ComposerService.h>
-#include <renderengine/Image.h>
 #include <renderengine/RenderEngine.h>
 #include <utils/Log.h>
 #include <utils/String8.h>
@@ -167,7 +166,7 @@
     }
 
     auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer
-                                            : mCurrentTextureBuffer->graphicBuffer();
+                                            : mCurrentTextureBuffer->getBuffer();
     auto err = addReleaseFence(slot, buffer, fence);
     if (err != OK) {
         BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err);
@@ -206,9 +205,11 @@
     // before, so we need to clean up old references.
     if (item->mGraphicBuffer != nullptr) {
         std::lock_guard<std::mutex> lock(mImagesMutex);
-        if (mImages[item->mSlot] == nullptr || mImages[item->mSlot]->graphicBuffer() == nullptr ||
-            mImages[item->mSlot]->graphicBuffer()->getId() != item->mGraphicBuffer->getId()) {
-            mImages[item->mSlot] = std::make_shared<Image>(item->mGraphicBuffer, mRE);
+        if (mImages[item->mSlot] == nullptr || mImages[item->mSlot]->getBuffer() == nullptr ||
+            mImages[item->mSlot]->getBuffer()->getId() != item->mGraphicBuffer->getId()) {
+            mImages[item->mSlot] = std::make_shared<
+                    renderengine::ExternalTexture>(item->mGraphicBuffer, mRE,
+                                                   renderengine::ExternalTexture::Usage::READABLE);
         }
     }
 
@@ -222,8 +223,8 @@
     int slot = item.mSlot;
 
     BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
-             (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->graphicBuffer() != nullptr)
-                     ? mCurrentTextureBuffer->graphicBuffer()->handle
+             (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->getBuffer() != nullptr)
+                     ? mCurrentTextureBuffer->getBuffer()->handle
                      : 0,
              slot, mSlots[slot].mGraphicBuffer->handle);
 
@@ -231,7 +232,7 @@
     // releaseBufferLocked() if we're in shared buffer mode and both buffers are
     // the same.
 
-    std::shared_ptr<Image> nextTextureBuffer;
+    std::shared_ptr<renderengine::ExternalTexture> nextTextureBuffer;
     {
         std::lock_guard<std::mutex> lock(mImagesMutex);
         nextTextureBuffer = mImages[slot];
@@ -241,7 +242,7 @@
     if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
         if (pendingRelease == nullptr) {
             status_t status =
-                    releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer->graphicBuffer());
+                    releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer->getBuffer());
             if (status < NO_ERROR) {
                 BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
                          status);
@@ -250,7 +251,7 @@
             }
         } else {
             pendingRelease->currentTexture = mCurrentTexture;
-            pendingRelease->graphicBuffer = mCurrentTextureBuffer->graphicBuffer();
+            pendingRelease->graphicBuffer = mCurrentTextureBuffer->getBuffer();
             pendingRelease->isPending = true;
         }
     }
@@ -301,14 +302,14 @@
 
 void BufferLayerConsumer::computeCurrentTransformMatrixLocked() {
     BLC_LOGV("computeCurrentTransformMatrixLocked");
-    if (mCurrentTextureBuffer == nullptr || mCurrentTextureBuffer->graphicBuffer() == nullptr) {
+    if (mCurrentTextureBuffer == nullptr || mCurrentTextureBuffer->getBuffer() == nullptr) {
         BLC_LOGD("computeCurrentTransformMatrixLocked: "
                  "mCurrentTextureBuffer is nullptr");
     }
     GLConsumer::computeTransformMatrix(mCurrentTransformMatrix,
                                        mCurrentTextureBuffer == nullptr
                                                ? nullptr
-                                               : mCurrentTextureBuffer->graphicBuffer(),
+                                               : mCurrentTextureBuffer->getBuffer(),
                                        getCurrentCropLocked(), mCurrentTransform,
                                        mFilteringEnabled);
 }
@@ -360,7 +361,8 @@
     return mCurrentApi;
 }
 
-sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot, sp<Fence>* outFence) const {
+std::shared_ptr<renderengine::ExternalTexture> BufferLayerConsumer::getCurrentBuffer(
+        int* outSlot, sp<Fence>* outFence) const {
     Mutex::Autolock lock(mMutex);
 
     if (outSlot != nullptr) {
@@ -371,7 +373,7 @@
         *outFence = mCurrentFence;
     }
 
-    return mCurrentTextureBuffer == nullptr ? nullptr : mCurrentTextureBuffer->graphicBuffer();
+    return mCurrentTextureBuffer == nullptr ? nullptr : mCurrentTextureBuffer;
 }
 
 Rect BufferLayerConsumer::getCurrentCrop() const {
@@ -456,10 +458,12 @@
 void BufferLayerConsumer::onBufferAvailable(const BufferItem& item) {
     if (item.mGraphicBuffer != nullptr && item.mSlot != BufferQueue::INVALID_BUFFER_SLOT) {
         std::lock_guard<std::mutex> lock(mImagesMutex);
-        const std::shared_ptr<Image>& oldImage = mImages[item.mSlot];
-        if (oldImage == nullptr || oldImage->graphicBuffer() == nullptr ||
-            oldImage->graphicBuffer()->getId() != item.mGraphicBuffer->getId()) {
-            mImages[item.mSlot] = std::make_shared<Image>(item.mGraphicBuffer, mRE);
+        const std::shared_ptr<renderengine::ExternalTexture>& oldImage = mImages[item.mSlot];
+        if (oldImage == nullptr || oldImage->getBuffer() == nullptr ||
+            oldImage->getBuffer()->getId() != item.mGraphicBuffer->getId()) {
+            mImages[item.mSlot] = std::make_shared<
+                    renderengine::ExternalTexture>(item.mGraphicBuffer, mRE,
+                                                   renderengine::ExternalTexture::Usage::READABLE);
         }
     }
 }
@@ -499,22 +503,6 @@
 
     ConsumerBase::dumpLocked(result, prefix);
 }
-
-BufferLayerConsumer::Image::Image(const sp<GraphicBuffer>& graphicBuffer,
-                                  renderengine::RenderEngine& engine)
-      : mGraphicBuffer(graphicBuffer), mRE(engine) {
-    if (graphicBuffer != nullptr && (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED)) {
-        return;
-    }
-    mRE.cacheExternalTextureBuffer(mGraphicBuffer);
-}
-
-BufferLayerConsumer::Image::~Image() {
-    if (mGraphicBuffer != nullptr) {
-        ALOGV("Destroying buffer: %" PRId64, mGraphicBuffer->getId());
-        mRE.unbindExternalTextureBuffer(mGraphicBuffer->getId());
-    }
-}
 }; // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index dd39214..9ed80b4 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -21,12 +21,11 @@
 #include <gui/BufferQueueDefs.h>
 #include <gui/ConsumerBase.h>
 #include <gui/HdrMetadata.h>
-
+#include <renderengine/ExternalTexture.h>
 #include <ui/FenceTime.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Region.h>
-
 #include <utils/String8.h>
 #include <utils/Vector.h>
 #include <utils/threads.h>
@@ -39,7 +38,6 @@
 
 namespace renderengine {
 class RenderEngine;
-class Image;
 } // namespace renderengine
 
 /*
@@ -153,7 +151,8 @@
     // When outSlot is not nullptr, the current buffer slot index is also
     // returned. Simiarly, when outFence is not nullptr, the current output
     // fence is returned.
-    sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr, sp<Fence>* outFence = nullptr) const;
+    std::shared_ptr<renderengine::ExternalTexture> getCurrentBuffer(
+            int* outSlot = nullptr, sp<Fence>* outFence = nullptr) const;
 
     // getCurrentCrop returns the cropping rectangle of the current buffer.
     Rect getCurrentCrop() const;
@@ -258,7 +257,7 @@
     // mCurrentTextureBuffer is the buffer containing the current texture. It's
     // possible that this buffer is not associated with any buffer slot, so we
     // must track it separately in order to support the getCurrentBuffer method.
-    std::shared_ptr<Image> mCurrentTextureBuffer;
+    std::shared_ptr<renderengine::ExternalTexture> mCurrentTextureBuffer;
 
     // mCurrentCrop is the crop rectangle that applies to the current texture.
     // It gets set each time updateTexImage is called.
@@ -337,7 +336,8 @@
     int mCurrentTexture;
 
     // Shadow buffer cache for cleaning up renderengine references.
-    std::shared_ptr<Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS] GUARDED_BY(mImagesMutex);
+    std::shared_ptr<renderengine::ExternalTexture>
+            mImages[BufferQueueDefs::NUM_BUFFER_SLOTS] GUARDED_BY(mImagesMutex);
 
     // Separate mutex guarding the shadow buffer cache.
     // mImagesMutex can be manipulated with binder threads (e.g. onBuffersAllocated)
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index a974dc4..7a5b20d 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -71,14 +71,8 @@
     // 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());
-        const uint64_t bufferId = mBufferInfo.mBuffer->getId();
-        engine.unbindExternalTextureBuffer(bufferId);
-        callReleaseBufferCallback(mDrawingState.releaseBufferListener, mBufferInfo.mBuffer,
-                                  mBufferInfo.mFence);
+        callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+                                  mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFence);
     }
 }
 
@@ -259,28 +253,8 @@
              (mCurrentState.buffer != nullptr || mCurrentState.bgColorLayer != nullptr)));
 }
 
-/* TODO: vhau uncomment once deferred transaction migration complete in
- * WindowManager
-void BufferStateLayer::pushPendingState() {
-    if (!mCurrentState.modified) {
-        return;
-    }
-    mPendingStates.push_back(mCurrentState);
-    ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
-}
-*/
-
-bool BufferStateLayer::applyPendingStates(Layer::State* stateToCommit) {
-    mCurrentStateModified = mCurrentState.modified;
-    bool stateUpdateAvailable = Layer::applyPendingStates(stateToCommit);
-    mCurrentStateModified = stateUpdateAvailable && mCurrentStateModified;
-    mCurrentState.modified = false;
-    return stateUpdateAvailable;
-}
-
-// Crop that applies to the window
-Rect BufferStateLayer::getCrop(const Layer::State& /*s*/) const {
-    return Rect::INVALID_RECT;
+Rect BufferStateLayer::getCrop(const Layer::State& s) const {
+    return s.crop;
 }
 
 bool BufferStateLayer::setTransform(uint32_t transform) {
@@ -301,57 +275,53 @@
 }
 
 bool BufferStateLayer::setCrop(const Rect& crop) {
-    Rect c = crop;
-    if (c.left < 0) {
-        c.left = 0;
-    }
-    if (c.top < 0) {
-        c.top = 0;
-    }
-    // If the width and/or height are < 0, make it [0, 0, -1, -1] so the equality comparision below
-    // treats all invalid rectangles the same.
-    if (!c.isValid()) {
-        c.makeInvalid();
-    }
+    if (mCurrentState.crop == crop) return false;
+    mCurrentState.sequence++;
+    mCurrentState.crop = crop;
 
-    if (mCurrentState.crop == c) return false;
-    mCurrentState.crop = c;
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
 
-bool BufferStateLayer::setFrame(const Rect& frame) {
-    int x = frame.left;
-    int y = frame.top;
-    int w = frame.getWidth();
-    int h = frame.getHeight();
-
-    if (x < 0) {
-        x = 0;
-        w = frame.right;
-    }
-
-    if (y < 0) {
-        y = 0;
-        h = frame.bottom;
-    }
-
-    if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y &&
-        mCurrentState.width == w && mCurrentState.height == h) {
+bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix,
+                                 bool allowNonRectPreservingTransforms) {
+    if (mCurrentState.transform.dsdx() == matrix.dsdx &&
+        mCurrentState.transform.dtdy() == matrix.dtdy &&
+        mCurrentState.transform.dtdx() == matrix.dtdx &&
+        mCurrentState.transform.dsdy() == matrix.dsdy) {
         return false;
     }
 
-    if (!frame.isValid()) {
-        x = y = w = h = 0;
+    ui::Transform t;
+    t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
+
+    if (!allowNonRectPreservingTransforms && !t.preserveRects()) {
+        ALOGW("Attempt to set rotation matrix without permission ACCESS_SURFACE_FLINGER nor "
+              "ROTATE_SURFACE_FLINGER ignored");
+        return false;
     }
-    mCurrentState.transform.set(x, y);
-    mCurrentState.width = w;
-    mCurrentState.height = h;
+
+    mCurrentState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
 
     mCurrentState.sequence++;
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
+
+    return true;
+}
+
+bool BufferStateLayer::setPosition(float x, float y) {
+    if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) {
+        return false;
+    }
+
+    mCurrentState.transform.set(x, y);
+
+    mCurrentState.sequence++;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+
     return true;
 }
 
@@ -368,8 +338,9 @@
     return true;
 }
 
-bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
-                                 nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp,
+bool BufferStateLayer::setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+                                 const sp<Fence>& acquireFence, nsecs_t postTime,
+                                 nsecs_t desiredPresentTime, bool isAutoTimestamp,
                                  const client_cache_t& clientCacheId, uint64_t frameNumber,
                                  std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info,
                                  const sp<ITransactionCompletedListener>& releaseBufferListener) {
@@ -377,12 +348,14 @@
 
     if (mCurrentState.buffer) {
         mReleasePreviousBuffer = true;
-        if (mCurrentState.buffer != mDrawingState.buffer) {
+        if (!mDrawingState.buffer ||
+            mCurrentState.buffer->getBuffer() != mDrawingState.buffer->getBuffer()) {
             // If mCurrentState has a buffer, and we are about to update again
             // before swapping to drawing state, then the first buffer will be
             // dropped and we should decrement the pending buffer count and
             // call any release buffer callbacks if set.
-            callReleaseBufferCallback(mCurrentState.releaseBufferListener, mCurrentState.buffer,
+            callReleaseBufferCallback(mCurrentState.releaseBufferListener,
+                                      mCurrentState.buffer->getBuffer(),
                                       mCurrentState.acquireFence);
             decrementPendingBufferCount();
             if (mCurrentState.bufferSurfaceFrameTX != nullptr) {
@@ -420,22 +393,28 @@
 
     setFrameTimelineVsyncForBufferTransaction(info, postTime);
 
-    if (dequeueTime && *dequeueTime != 0) {
-        const uint64_t bufferId = buffer->getId();
+    if (buffer && dequeueTime && *dequeueTime != 0) {
+        const uint64_t bufferId = buffer->getBuffer()->getId();
         mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
         mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, *dequeueTime,
                                                FrameTracer::FrameEvent::DEQUEUE);
         mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, postTime,
                                                FrameTracer::FrameEvent::QUEUE);
     }
+
+    mCurrentState.width = mCurrentState.buffer->getBuffer()->getWidth();
+    mCurrentState.height = mCurrentState.buffer->getBuffer()->getHeight();
+
     return true;
 }
 
 bool BufferStateLayer::setAcquireFence(const sp<Fence>& fence) {
-    // The acquire fences of BufferStateLayers have already signaled before they are set
-    mCallbackHandleAcquireTime = fence->getSignalTime();
-
     mCurrentState.acquireFence = fence;
+    mCurrentState.acquireFenceTime = std::make_unique<FenceTime>(fence);
+
+    // The acquire fences of BufferStateLayers have already signaled before they are set
+    mCallbackHandleAcquireTime = mCurrentState.acquireFenceTime->getSignalTime();
+
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
@@ -673,7 +652,7 @@
     }
 
     const int32_t layerId = getSequence();
-    const uint64_t bufferId = mDrawingState.buffer->getId();
+    const uint64_t bufferId = mDrawingState.buffer->getBuffer()->getId();
     const uint64_t frameNumber = mDrawingState.frameNumber;
     const auto acquireFence = std::make_shared<FenceTime>(mDrawingState.acquireFence);
     mFlinger->mTimeStats->setAcquireFence(layerId, frameNumber, acquireFence);
@@ -691,9 +670,15 @@
         // bufferSurfaceFrame could be seen here if a pending state was applied successfully and we
         // are processing the next state.
         addSurfaceFramePresentedForBuffer(bufferSurfaceFrame,
-                                          mDrawingState.acquireFence->getSignalTime(), latchTime);
+                                          mDrawingState.acquireFenceTime->getSignalTime(),
+                                          latchTime);
     }
 
+    std::deque<sp<CallbackHandle>> remainingHandles;
+    mFlinger->getTransactionCallbackInvoker()
+            .finalizeOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles);
+    mDrawingState.callbackHandles = remainingHandles;
+
     mCurrentStateModified = false;
 
     return NO_ERROR;
@@ -706,7 +691,7 @@
         return BAD_VALUE;
     }
 
-    if (s.buffer != mBufferInfo.mBuffer) {
+    if (!mBufferInfo.mBuffer || s.buffer->getBuffer() != mBufferInfo.mBuffer->getBuffer()) {
         decrementPendingBufferCount();
     }
 
@@ -810,7 +795,7 @@
     mBufferInfo.mFence = s.acquireFence;
     mBufferInfo.mTransform = s.bufferTransform;
     mBufferInfo.mDataspace = translateDataspace(s.dataspace);
-    mBufferInfo.mCrop = computeCrop(s);
+    mBufferInfo.mCrop = computeBufferCrop(s);
     mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
     mBufferInfo.mSurfaceDamage = s.surfaceDamageRegion;
     mBufferInfo.mHdrMetadata = s.hdrMetadata;
@@ -823,27 +808,11 @@
    return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
 }
 
-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;
+Rect BufferStateLayer::computeBufferCrop(const State& s) {
+    if (s.buffer) {
+        return s.buffer->getBuffer()->getBounds();
     }
-    return s.crop;
+    return Rect::INVALID_RECT;
 }
 
 sp<Layer> BufferStateLayer::createClone() {
@@ -855,41 +824,14 @@
     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.transform.tx()),
-                                        static_cast<float>(s.transform.ty()),
-                                        static_cast<float>(s.transform.tx() + s.width),
-                                        static_cast<float>(s.transform.ty() + s.height)),
-                              radius);
-}
-
 bool BufferStateLayer::bufferNeedsFiltering() const {
     const State& s(getDrawingState());
     if (!s.buffer) {
         return false;
     }
 
-    uint32_t bufferWidth = s.buffer->width;
-    uint32_t bufferHeight = s.buffer->height;
+    uint32_t bufferWidth = s.buffer->getBuffer()->width;
+    uint32_t bufferHeight = s.buffer->getBuffer()->height;
 
     // Undo any transformations on the buffer and return the result.
     if (s.bufferTransform & ui::Transform::ROT_90) {
@@ -916,18 +858,50 @@
     ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers);
 }
 
-void BufferStateLayer::bufferMayChange(sp<GraphicBuffer>& newBuffer) {
-    if (mDrawingState.buffer != nullptr && mDrawingState.buffer != mBufferInfo.mBuffer &&
-        newBuffer != mDrawingState.buffer) {
+void BufferStateLayer::bufferMayChange(const sp<GraphicBuffer>& newBuffer) {
+    if (mDrawingState.buffer != nullptr &&
+        (!mBufferInfo.mBuffer ||
+         mDrawingState.buffer->getBuffer() != mBufferInfo.mBuffer->getBuffer()) &&
+        newBuffer != mDrawingState.buffer->getBuffer()) {
         // If we are about to update mDrawingState.buffer but it has not yet latched
         // then we will drop a buffer and should decrement the pending buffer count and
         // call any release buffer callbacks if set.
-        callReleaseBufferCallback(mDrawingState.releaseBufferListener, mDrawingState.buffer,
-                                  mDrawingState.acquireFence);
+        callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+                                  mDrawingState.buffer->getBuffer(), mDrawingState.acquireFence);
         decrementPendingBufferCount();
     }
 }
 
+/*
+ * We don't want to send the layer's transform to input, but rather the
+ * parent's transform. This is because BufferStateLayer's transform is
+ * information about how the buffer is placed on screen. The parent's
+ * transform makes more sense to send since it's information about how the
+ * layer is placed on screen. This transform is used by input to determine
+ * how to go from screen space back to window space.
+ */
+ui::Transform BufferStateLayer::getInputTransform() const {
+    sp<Layer> parent = mDrawingParent.promote();
+    if (parent == nullptr) {
+        return ui::Transform();
+    }
+
+    return parent->getTransform();
+}
+
+/**
+ * Similar to getInputTransform, we need to update the bounds to include the transform.
+ * This is because bounds for BSL doesn't include buffer transform, where the input assumes
+ * that's already included.
+ */
+Rect BufferStateLayer::getInputBounds() const {
+    Rect bufferBounds = getCroppedBufferSize(getDrawingState());
+    if (mDrawingState.transform.getType() == ui::Transform::IDENTITY || !bufferBounds.isValid()) {
+        return bufferBounds;
+    }
+    return mDrawingState.transform.transform(bufferBounds);
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 7a3da6f..af4fcae 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -50,10 +50,6 @@
     uint32_t doTransactionResize(uint32_t flags, Layer::State* /*stateToCommit*/) override {
         return flags;
     }
-    /*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.width; }
     uint32_t getActiveHeight(const Layer::State& s) const override { return s.height; }
@@ -66,10 +62,9 @@
     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, const sp<Fence>& acquireFence, nsecs_t postTime,
-                   nsecs_t desiredPresentTime, bool isAutoTimestamp,
-                   const client_cache_t& clientCacheId, uint64_t frameNumber,
+    bool setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+                   const sp<Fence>& acquireFence, nsecs_t postTime, nsecs_t desiredPresentTime,
+                   bool isAutoTimestamp, const client_cache_t& clientCacheId, uint64_t frameNumber,
                    std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info,
                    const sp<ITransactionCompletedListener>& transactionListener) override;
     bool setAcquireFence(const sp<Fence>& fence) override;
@@ -81,23 +76,16 @@
     bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override;
     bool addFrameEvent(const sp<Fence>& acquireFence, nsecs_t postedTime,
                        nsecs_t requestedPresentTime) override;
+    bool setPosition(float /*x*/, float /*y*/) override;
+    bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/,
+                   bool /*allowNonRectPreservingTransforms*/);
 
     // 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*/) override { return false; }
     bool setTransparentRegionHint(const Region& transparent) override;
-    bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/,
-                   bool /*allowNonRectPreservingTransforms*/) override {
-        return false;
-    }
-    void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/,
-                                      uint64_t /*frameNumber*/) override {}
-    void deferTransactionUntil_legacy(const sp<Layer>& /*barrierLayer*/,
-                                      uint64_t /*frameNumber*/) override {}
 
     Rect getBufferSize(const State& s) const override;
     FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
-    Layer::RoundedCornerState getRoundedCornerState() const override;
     void setAutoRefresh(bool autoRefresh) override;
 
     // -----------------------------------------------------------------------
@@ -112,7 +100,7 @@
 
     // See mPendingBufferTransactions
     void decrementPendingBufferCount();
-    void bufferMayChange(sp<GraphicBuffer>& newBuffer) override;
+    void bufferMayChange(const sp<GraphicBuffer>& newBuffer) override;
     std::atomic<int32_t>* getPendingBufferCounter() override { return &mPendingBufferTransactions; }
     std::string getPendingBufferCounterName() override { return mBlastTransactionName; }
 
@@ -122,6 +110,8 @@
     void gatherBufferInfo() override;
     uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
     void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame);
+    ui::Transform getInputTransform() const override;
+    Rect getInputBounds() const override;
 
 private:
     friend class SlotGenerationTest;
@@ -150,7 +140,7 @@
     sp<Layer> createClone() override;
 
     // Crop that applies to the buffer
-    Rect computeCrop(const State& s);
+    Rect computeBufferCrop(const State& s);
 
     bool willPresentCurrentTransaction() const;
 
@@ -167,7 +157,6 @@
     uint64_t mPreviousBufferId = 0;
     uint64_t mPreviousReleasedFrameNumber = 0;
 
-    mutable bool mCurrentStateModified = false;
     bool mReleasePreviousBuffer = false;
 
     // Stores the last set acquire fence signal time used to populate the callback handle's acquire
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index 2ae49fa..f310738 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -48,7 +48,7 @@
 
     auto bufItr = processBuffers.find(id);
     if (bufItr == processBuffers.end()) {
-        ALOGE("failed to get buffer, invalid buffer id");
+        ALOGV("failed to get buffer, invalid buffer id");
         return false;
     }
 
@@ -102,7 +102,12 @@
         return false;
     }
 
-    processBuffers[id].buffer = buffer;
+    LOG_ALWAYS_FATAL_IF(mRenderEngine == nullptr,
+                        "Attempted to build the ClientCache before a RenderEngine instance was "
+                        "ready!");
+    processBuffers[id].buffer = std::make_shared<
+            renderengine::ExternalTexture>(buffer, *mRenderEngine,
+                                           renderengine::ExternalTexture::Usage::READABLE);
     return true;
 }
 
@@ -132,7 +137,7 @@
     }
 }
 
-sp<GraphicBuffer> ClientCache::get(const client_cache_t& cacheId) {
+std::shared_ptr<renderengine::ExternalTexture> ClientCache::get(const client_cache_t& cacheId) {
     std::lock_guard lock(mMutex);
 
     ClientCacheBuffer* buf = nullptr;
@@ -150,7 +155,7 @@
 
     ClientCacheBuffer* buf = nullptr;
     if (!getBuffer(cacheId, &buf)) {
-        ALOGE("failed to register erased recipient, could not retrieve buffer");
+        ALOGV("failed to register erased recipient, could not retrieve buffer");
         return false;
     }
     buf->recipients.insert(recipient);
@@ -213,8 +218,8 @@
         auto &buffers = i.second.second;
         for (auto& [id, clientCacheBuffer] : buffers) {
             StringAppendF(&result, "\t ID: %d, Width/Height: %d,%d\n", (int)id,
-                          (int)clientCacheBuffer.buffer->getWidth(),
-                          (int)clientCacheBuffer.buffer->getHeight());
+                          (int)clientCacheBuffer.buffer->getBuffer()->getWidth(),
+                          (int)clientCacheBuffer.buffer->getBuffer()->getHeight());
         }
     }
 }
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
index 0d597c8..a9b8177 100644
--- a/services/surfaceflinger/ClientCache.h
+++ b/services/surfaceflinger/ClientCache.h
@@ -19,6 +19,7 @@
 #include <android-base/thread_annotations.h>
 #include <binder/IBinder.h>
 #include <gui/LayerState.h>
+#include <renderengine/RenderEngine.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/RefBase.h>
 #include <utils/Singleton.h>
@@ -39,7 +40,11 @@
     bool add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer);
     void erase(const client_cache_t& cacheId);
 
-    sp<GraphicBuffer> get(const client_cache_t& cacheId);
+    std::shared_ptr<renderengine::ExternalTexture> get(const client_cache_t& cacheId);
+
+    // Always called immediately after setup. Will be set to non-null, and then should never be
+    // called again.
+    void setRenderEngine(renderengine::RenderEngine* renderEngine) { mRenderEngine = renderEngine; }
 
     void removeProcess(const wp<IBinder>& processToken);
 
@@ -59,7 +64,7 @@
     std::mutex mMutex;
 
     struct ClientCacheBuffer {
-        sp<GraphicBuffer> buffer;
+        std::shared_ptr<renderengine::ExternalTexture> buffer;
         std::set<wp<ErasedRecipient>> recipients;
     };
     std::map<wp<IBinder> /*caching process*/,
@@ -73,6 +78,7 @@
     };
 
     sp<CacheDeathRecipient> mDeathRecipient;
+    renderengine::RenderEngine* mRenderEngine = nullptr;
 
     bool getBuffer(const client_cache_t& cacheId, ClientCacheBuffer** outClientCacheBuffer)
             REQUIRES(mMutex);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index a0606b4..289cb11 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -79,6 +79,9 @@
 
     // If set, causes the dirty regions to flash with the delay
     std::optional<std::chrono::microseconds> devOptFlashDirtyRegionsDelay;
+
+    // The earliest time to send the present command to the HAL
+    std::chrono::steady_clock::time_point earliestPresentTime;
 };
 
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 8402149..a45be8a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -55,6 +55,16 @@
     std::vector<uint8_t> value;
 
     std::string dumpAsString() const;
+
+    struct Hasher {
+        size_t operator()(const GenericLayerMetadataEntry& entry) const {
+            size_t hash = 0;
+            for (const auto value : entry.value) {
+                hashCombineSingleHashed(hash, value);
+            }
+            return hash;
+        }
+    };
 };
 
 inline bool operator==(const GenericLayerMetadataEntry& lhs, const GenericLayerMetadataEntry& rhs) {
@@ -70,6 +80,8 @@
 
 /*
  * Used by LayerFE::getCompositionState
+ * Note that fields that affect HW composer state may need to be mirrored into
+ * android::compositionengine::impl::planner::LayerState
  */
 struct LayerFECompositionState {
     // If set to true, forces client composition on all output layers until
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
index f680460..daee83b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -16,15 +16,16 @@
 
 #pragma once
 
-#include <cstdint>
-#include <vector>
-
+#include <renderengine/ExternalTexture.h>
 #include <ui/Fence.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Size.h>
 #include <utils/Errors.h>
 #include <utils/StrongPointer.h>
 
+#include <cstdint>
+#include <vector>
+
 namespace android {
 
 class GraphicBuffer;
@@ -80,7 +81,8 @@
     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;
+    virtual std::shared_ptr<renderengine::ExternalTexture> 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.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
index a1230b3..a8d372c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
@@ -45,6 +45,8 @@
 
     // The DisplaySurface for this surface
     sp<DisplaySurface> displaySurface;
+
+    size_t maxTextureCacheSize;
 };
 
 /**
@@ -81,6 +83,11 @@
         return *this;
     }
 
+    RenderSurfaceCreationArgsBuilder& setMaxTextureCacheSize(size_t maxTextureCacheSize) {
+        mArgs.maxTextureCacheSize = maxTextureCacheSize;
+        return *this;
+    }
+
 private:
     RenderSurfaceCreationArgs mArgs;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 8f767d3..f0ef6d6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -115,6 +115,9 @@
     // Current target dataspace
     ui::Dataspace targetDataspace{ui::Dataspace::UNKNOWN};
 
+    // The earliest time to send the present command to the HAL
+    std::chrono::steady_clock::time_point earliestPresentTime;
+
     // 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 f113c34..ae88e78 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -55,7 +55,6 @@
     std::vector<LayerFE::LayerSettings> getOverrideCompositionList() const override;
 
     void dump(std::string&) const override;
-
     virtual FloatRect calculateOutputSourceCrop() const;
     virtual Rect calculateOutputDisplayFrame() const;
     virtual uint32_t calculateOutputRelativeBufferTransform(
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index a3e84e2..c61ec59 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -16,17 +16,18 @@
 
 #pragma once
 
-#include <cstdint>
-#include <optional>
-#include <string>
-
+#include <compositionengine/ProjectionSpace.h>
 #include <compositionengine/impl/HwcBufferCache.h>
-#include <renderengine/Mesh.h>
+#include <renderengine/ExternalTexture.h>
 #include <ui/FloatRect.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
+#include <cstdint>
+#include <optional>
+#include <string>
+
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
@@ -47,6 +48,8 @@
 
 namespace compositionengine::impl {
 
+// Note that fields that affect HW composer state may need to be mirrored into
+// android::compositionengine::impl::planner::LayerState
 struct OutputLayerCompositionState {
     // The portion of the layer that is not obscured by opaque layers on top
     Region visibleRegion;
@@ -86,10 +89,13 @@
 
     // Overrides the buffer, acquire fence, and display frame stored in LayerFECompositionState
     struct {
-        sp<GraphicBuffer> buffer = nullptr;
+        std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
         sp<Fence> acquireFence = nullptr;
         Rect displayFrame = {};
         ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
+        ProjectionSpace displaySpace;
+        Region damageRegion = Region::INVALID_REGION;
+        Region visibleRegion;
     } overrideInfo;
 
     /*
@@ -109,6 +115,9 @@
         // The buffer cache for this layer. This is used to lower the
         // cost of sending reused buffers to the HWC.
         HwcBufferCache hwcBufferCache;
+
+        // Set to true when overridden info has been sent to HW composer
+        bool stateOverridden = false;
     };
 
     // The HWC state is optional, and is only set up if there is any potential
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
index 5127a6f..a8a5380 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -16,12 +16,16 @@
 
 #pragma once
 
-#include <memory>
-
 #include <android-base/unique_fd.h>
 #include <compositionengine/RenderSurface.h>
 #include <utils/StrongPointer.h>
 
+#include <memory>
+#include <vector>
+
+#include "renderengine/ExternalTexture.h"
+#include "renderengine/RenderEngine.h"
+
 struct ANativeWindow;
 
 namespace android {
@@ -54,7 +58,8 @@
     void setProtected(bool useProtected) override;
     status_t beginFrame(bool mustRecompose) override;
     void prepareFrame(bool usesClientComposition, bool usesDeviceComposition) override;
-    sp<GraphicBuffer> dequeueBuffer(base::unique_fd* bufferFence) override;
+    std::shared_ptr<renderengine::ExternalTexture> dequeueBuffer(
+            base::unique_fd* bufferFence) override;
     void queueBuffer(base::unique_fd readyFence) override;
     void onPresentDisplayCompleted() override;
     void flip() override;
@@ -66,7 +71,7 @@
     // Testing
     void setPageFlipCountForTest(std::uint32_t);
     void setSizeForTest(const ui::Size&);
-    sp<GraphicBuffer>& mutableGraphicBufferForTest();
+    std::shared_ptr<renderengine::ExternalTexture>& mutableTextureForTest();
     base::unique_fd& mutableBufferReadyForTest();
 
 private:
@@ -75,10 +80,13 @@
 
     // ANativeWindow being rendered into
     const sp<ANativeWindow> mNativeWindow;
-    // Current buffer being rendered into
-    sp<GraphicBuffer> mGraphicBuffer;
+
+    std::vector<std::shared_ptr<renderengine::ExternalTexture>> mTextureCache;
+    // Current texture being rendered into
+    std::shared_ptr<renderengine::ExternalTexture> mTexture;
     const sp<DisplaySurface> mDisplaySurface;
     ui::Size mSize;
+    const size_t mMaxTextureCacheSize;
     bool mProtected{false};
     std::uint32_t mPageFlipCount{0};
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index b0e42b7..53f4a30 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <compositionengine/Output.h>
+#include <compositionengine/ProjectionSpace.h>
 #include <compositionengine/impl/planner/LayerState.h>
 #include <renderengine/RenderEngine.h>
 
@@ -38,7 +40,10 @@
         const LayerState* getState() const { return mState; }
         const std::string& getName() const { return mState->getName(); }
         Rect getDisplayFrame() const { return mState->getDisplayFrame(); }
-        const sp<GraphicBuffer>& getBuffer() const { return mState->getBuffer(); }
+        const Region& getVisibleRegion() const { return mState->getVisibleRegion(); }
+        const sp<GraphicBuffer>& getBuffer() const {
+            return mState->getOutputLayer()->getLayerFE().getCompositionState()->buffer;
+        }
         int64_t getFramesSinceBufferUpdate() const { return mState->getFramesSinceBufferUpdate(); }
         NonBufferHash getHash() const { return mHash; }
         std::chrono::steady_clock::time_point getLastUpdate() const { return mLastUpdate; }
@@ -59,9 +64,11 @@
     size_t getLayerCount() const { return mLayers.size(); }
     const Layer& getFirstLayer() const { return mLayers[0]; }
     const Rect& getBounds() const { return mBounds; }
+    const Region& getVisibleRegion() const { return mVisibleRegion; }
     size_t getAge() const { return mAge; }
-    const sp<GraphicBuffer>& getBuffer() const { return mTexture.getBuffer(); }
+    const std::shared_ptr<renderengine::ExternalTexture>& getBuffer() const { return mTexture; }
     const sp<Fence>& getDrawFence() const { return mDrawFence; }
+    const ProjectionSpace& getOutputSpace() const { return mOutputSpace; }
     ui::Dataspace getOutputDataspace() const { return mOutputDataspace; }
 
     NonBufferHash getNonBufferHash() const;
@@ -70,7 +77,7 @@
     size_t getCreationCost() const;
     size_t getDisplayCost() const;
 
-    bool hasBufferUpdate(std::vector<const LayerState*>::const_iterator layers) const;
+    bool hasBufferUpdate() const;
     bool hasReadyBuffer() const;
 
     // Decomposes this CachedSet into a vector of its layers as individual CachedSets
@@ -80,7 +87,7 @@
 
     void setLastUpdate(std::chrono::steady_clock::time_point now) { mLastUpdate = now; }
     void append(const CachedSet& other) {
-        mTexture.setBuffer(nullptr, nullptr);
+        mTexture = nullptr;
         mOutputDataspace = ui::Dataspace::UNKNOWN;
         mDrawFence = nullptr;
 
@@ -89,50 +96,30 @@
         boundingRegion.orSelf(mBounds);
         boundingRegion.orSelf(other.mBounds);
         mBounds = boundingRegion.getBounds();
+        mVisibleRegion.orSelf(other.mVisibleRegion);
     }
     void incrementAge() { ++mAge; }
 
-    // Renders the cached set with the supplied output dataspace.
-    void render(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
+    // Renders the cached set with the supplied output composition state.
+    void render(renderengine::RenderEngine& re, const OutputCompositionState& outputState);
 
     void dump(std::string& result) const;
 
 private:
     CachedSet() = default;
 
-    NonBufferHash mFingerprint = 0;
+    const NonBufferHash mFingerprint;
     std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now();
     std::vector<Layer> mLayers;
     Rect mBounds = Rect::EMPTY_RECT;
+    Region mVisibleRegion;
     size_t mAge = 0;
 
-    class Texture {
-    public:
-        ~Texture() { setBuffer(nullptr, nullptr); }
-
-        void setBuffer(const sp<GraphicBuffer>& buffer, renderengine::RenderEngine* re) {
-            if (mRE && mBuffer) {
-                mRE->unbindExternalTextureBuffer(mBuffer->getId());
-            }
-
-            mBuffer = buffer;
-            mRE = re;
-
-            if (mRE && mBuffer) {
-                mRE->cacheExternalTextureBuffer(mBuffer);
-            }
-        }
-
-        const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
-
-    private:
-        sp<GraphicBuffer> mBuffer = nullptr;
-        renderengine::RenderEngine* mRE = nullptr;
-    };
-
-    Texture mTexture;
+    std::shared_ptr<renderengine::ExternalTexture> mTexture;
     sp<Fence> mDrawFence;
+    ProjectionSpace mOutputSpace;
     ui::Dataspace mOutputDataspace;
+    ui::Transform::RotationFlags mOrientation = ui::Transform::ROT_0;
 
     static const bool sDebugHighlighLayers;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 5b9a9f0..2f2ad4c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <compositionengine/Output.h>
 #include <compositionengine/impl/planner/CachedSet.h>
 #include <compositionengine/impl/planner/LayerState.h>
 
@@ -42,10 +43,9 @@
     NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash,
                                 std::chrono::steady_clock::time_point now);
 
-    // Renders the newest cached sets with the supplied output dataspace
-    void renderCachedSets(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
-
-    void reset();
+    // Renders the newest cached sets with the supplied output composition state
+    void renderCachedSets(renderengine::RenderEngine& re,
+                          const OutputCompositionState& outputState);
 
     void dump(std::string& result) const;
 
@@ -54,7 +54,7 @@
 
     void resetActivities(NonBufferHash, std::chrono::steady_clock::time_point now);
 
-    void updateLayersHash();
+    NonBufferHash computeLayersHash() const;
 
     bool mergeWithCachedSets(const std::vector<const LayerState*>& layers,
                              std::chrono::steady_clock::time_point now);
@@ -69,7 +69,6 @@
     std::chrono::steady_clock::time_point mLastGeometryUpdate;
 
     std::vector<CachedSet> mLayers;
-    NonBufferHash mLayersHash = 0;
     std::optional<CachedSet> mNewCachedSet;
 
     // Statistics
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index a3beadc..f2f6c0b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -32,6 +32,14 @@
 struct hash<android::sp<T>> {
     size_t operator()(const android::sp<T>& p) { return std::hash<void*>()(p.get()); }
 };
+
+template <typename T>
+struct hash<android::wp<T>> {
+    size_t operator()(const android::wp<T>& p) {
+        android::sp<T> promoted = p.promote();
+        return std::hash<void*>()(promoted ? promoted.get() : nullptr);
+    }
+};
 } // namespace std
 
 namespace android::compositionengine::impl::planner {
@@ -49,13 +57,16 @@
     BufferTransform = 1u << 5,
     BlendMode       = 1u << 6,
     Alpha           = 1u << 7,
-    VisibleRegion   = 1u << 8,
-    Dataspace       = 1u << 9,
-    ColorTransform  = 1u << 10,
-    CompositionType = 1u << 11,
-    SidebandStream  = 1u << 12,
-    Buffer          = 1u << 13,
-    SolidColor      = 1u << 14,
+    LayerMetadata   = 1u << 8,
+    VisibleRegion   = 1u << 9,
+    Dataspace       = 1u << 10,
+    PixelFormat     = 1u << 11,
+    ColorTransform  = 1u << 12,
+    SurfaceDamage   = 1u << 13,
+    CompositionType = 1u << 14,
+    SidebandStream  = 1u << 15,
+    Buffer          = 1u << 16,
+    SolidColor      = 1u << 17,
 };
 // clang-format on
 
@@ -70,7 +81,7 @@
 
     virtual Flags<LayerStateField> update(const compositionengine::OutputLayer* layer) = 0;
 
-    virtual size_t getHash(Flags<LayerStateField> skipFields) const = 0;
+    virtual size_t getHash() const = 0;
 
     virtual LayerStateField getField() const = 0;
 
@@ -87,6 +98,7 @@
     using ReadFromLayerState = std::function<T(const compositionengine::OutputLayer* layer)>;
     using ToStrings = std::function<std::vector<std::string>(const T&)>;
     using Equals = std::function<bool(const T&, const T&)>;
+    using Hashes = std::function<size_t(const T&)>;
 
     static ToStrings getDefaultToStrings() {
         return [](const T& value) {
@@ -99,20 +111,48 @@
         return [](const T& value) { return std::vector<std::string>{toString(value)}; };
     }
 
+    static ToStrings getRegionToStrings() {
+        return [](const Region& region) {
+            using namespace std::string_literals;
+            std::string dump;
+            region.dump(dump, "");
+            std::vector<std::string> split = base::Split(dump, "\n"s);
+            split.erase(split.begin()); // Strip the header
+            split.pop_back();           // Strip the last (empty) line
+            for (std::string& line : split) {
+                line.erase(0, 4); // Strip leading padding before each rect
+            }
+            return split;
+        };
+    }
+
     static Equals getDefaultEquals() {
         return [](const T& lhs, const T& rhs) { return lhs == rhs; };
     }
 
+    static Equals getRegionEquals() {
+        return [](const Region& lhs, const Region& rhs) { return lhs.hasSameRects(rhs); };
+    }
+
+    static Hashes getDefaultHashes() {
+        return [](const T& value) { return std::hash<T>{}(value); };
+    }
+
     OutputLayerState(ReadFromLayerState reader,
                      ToStrings toStrings = OutputLayerState::getDefaultToStrings(),
-                     Equals equals = OutputLayerState::getDefaultEquals())
-          : mReader(reader), mToStrings(toStrings), mEquals(equals) {}
+                     Equals equals = OutputLayerState::getDefaultEquals(),
+                     Hashes hashes = OutputLayerState::getDefaultHashes())
+          : mReader(reader), mToStrings(toStrings), mEquals(equals), mHashes(hashes) {}
 
     ~OutputLayerState() override = default;
 
     // Returns this member's field flag if it was changed
     Flags<LayerStateField> update(const compositionengine::OutputLayer* layer) override {
         T newValue = mReader(layer);
+        return update(newValue);
+    }
+
+    Flags<LayerStateField> update(const T& newValue) {
         if (!mEquals(mValue, newValue)) {
             mValue = newValue;
             mHash = {};
@@ -124,12 +164,9 @@
     LayerStateField getField() const override { return FIELD; }
     const T& get() const { return mValue; }
 
-    size_t getHash(Flags<LayerStateField> skipFields) const override {
-        if (skipFields.test(FIELD)) {
-            return 0;
-        }
+    size_t getHash() const override {
         if (!mHash) {
-            mHash = std::hash<T>{}(mValue);
+            mHash = mHashes(mValue);
         }
         return *mHash;
     }
@@ -163,6 +200,7 @@
     const ReadFromLayerState mReader;
     const ToStrings mToStrings;
     const Equals mEquals;
+    const Hashes mHashes;
     T mValue = {};
     mutable std::optional<size_t> mHash = {};
 };
@@ -175,22 +213,22 @@
     Flags<LayerStateField> update(compositionengine::OutputLayer*);
 
     // Computes a hash for this LayerState.
-    // The hash is only computed from NonUniqueFields.
-    size_t getHash(Flags<LayerStateField> skipFields) const;
+    // The hash is only computed from NonUniqueFields, and excludes GraphicBuffers since they are
+    // not guaranteed to live longer than the LayerState object.
+    size_t getHash() const;
 
     // Returns the bit-set of differing fields between this LayerState and another LayerState.
-    // This bit-set is based on NonUniqueFields only
-    Flags<LayerStateField> getDifferingFields(const LayerState& other,
-                                              Flags<LayerStateField> skipFields) const;
+    // This bit-set is based on NonUniqueFields only, and excludes GraphicBuffers.
+    Flags<LayerStateField> getDifferingFields(const LayerState& other) const;
 
     compositionengine::OutputLayer* getOutputLayer() const { return mOutputLayer; }
     int32_t getId() const { return mId.get(); }
     const std::string& getName() const { return mName.get(); }
     Rect getDisplayFrame() const { return mDisplayFrame.get(); }
+    const Region& getVisibleRegion() const { return mVisibleRegion.get(); }
     hardware::graphics::composer::hal::Composition getCompositionType() const {
         return mCompositionType.get();
     }
-    const sp<GraphicBuffer>& getBuffer() const { return mBuffer.get(); }
 
     void incrementFramesSinceBufferUpdate() { ++mFramesSinceBufferUpdate; }
     void resetFramesSinceBufferUpdate() { mFramesSinceBufferUpdate = 0; }
@@ -255,39 +293,72 @@
     OutputLayerState<float, LayerStateField::Alpha> mAlpha{
             [](auto layer) { return layer->getLayerFE().getCompositionState()->alpha; }};
 
-    // TODO(b/180638831): Generic layer metadata
+    using LayerMetadataState =
+            OutputLayerState<GenericLayerMetadataMap, LayerStateField::LayerMetadata>;
+    LayerMetadataState
+            mLayerMetadata{[](auto layer) {
+                               return layer->getLayerFE().getCompositionState()->metadata;
+                           },
+                           [](const GenericLayerMetadataMap& metadata) {
+                               std::vector<std::string> result;
+                               if (metadata.empty()) {
+                                   result.push_back("{}");
+                                   return result;
+                               }
+                               result.push_back("{");
+                               for (const auto& [key, value] : metadata) {
+                                   std::string keyValueDump;
+                                   keyValueDump.append("           ");
+                                   keyValueDump.append(key);
+                                   keyValueDump.append("=");
+                                   keyValueDump.append(value.dumpAsString());
+                                   result.push_back(keyValueDump);
+                               }
+                               result.push_back("}");
+                               return result;
+                           },
+                           LayerMetadataState::getDefaultEquals(),
+                           [](const GenericLayerMetadataMap& metadata) {
+                               size_t hash = 0;
+                               for (const auto& [key, value] : metadata) {
+                                   size_t entryHash = 0;
+                                   hashCombineSingleHashed(entryHash,
+                                                           std::hash<std::string>{}(key));
+                                   hashCombineSingleHashed(entryHash,
+                                                           GenericLayerMetadataEntry::Hasher{}(
+                                                                   value));
+                                   hash ^= entryHash;
+                               }
+                               return hash;
+                           }};
 
     // Output-dependent per-frame state
 
-    OutputLayerState<Region, LayerStateField::VisibleRegion>
-            mVisibleRegion{[](auto layer) { return layer->getState().visibleRegion; },
-                           [](const Region& region) {
-                               using namespace std::string_literals;
-                               std::string dump;
-                               region.dump(dump, "");
-                               std::vector<std::string> split = base::Split(dump, "\n"s);
-                               split.erase(split.begin()); // Strip the header
-                               split.pop_back();           // Strip the last (empty) line
-                               for (std::string& line : split) {
-                                   line.erase(0, 4); // Strip leading padding before each rect
-                               }
-                               return split;
-                           },
-                           [](const Region& lhs, const Region& rhs) {
-                               return lhs.hasSameRects(rhs);
-                           }};
+    using VisibleRegionState = OutputLayerState<Region, LayerStateField::VisibleRegion>;
+    VisibleRegionState mVisibleRegion{[](auto layer) { return layer->getState().visibleRegion; },
+                                      VisibleRegionState::getRegionToStrings(),
+                                      VisibleRegionState::getRegionEquals()};
 
     using DataspaceState = OutputLayerState<ui::Dataspace, LayerStateField::Dataspace>;
     DataspaceState mOutputDataspace{[](auto layer) { return layer->getState().dataspace; },
                                     DataspaceState::getHalToStrings()};
 
-    // TODO(b/180638831): Buffer format
-
     // Output-independent per-frame state
 
-    OutputLayerState<mat4, LayerStateField::ColorTransform> mColorTransform;
+    using PixelFormatState = OutputLayerState<hardware::graphics::composer::hal::PixelFormat,
+                                              LayerStateField::PixelFormat>;
+    PixelFormatState
+            mPixelFormat{[](auto layer) {
+                             return layer->getLayerFE().getCompositionState()->buffer
+                                     ? static_cast<hardware::graphics::composer::hal::PixelFormat>(
+                                               layer->getLayerFE()
+                                                       .getCompositionState()
+                                                       ->buffer->getPixelFormat())
+                                     : hardware::graphics::composer::hal::PixelFormat::RGBA_8888;
+                         },
+                         PixelFormatState::getHalToStrings()};
 
-    // TODO(b/180638831): Surface damage
+    OutputLayerState<mat4, LayerStateField::ColorTransform> mColorTransform;
 
     using CompositionTypeState = OutputLayerState<hardware::graphics::composer::hal::Composition,
                                                   LayerStateField::CompositionType>;
@@ -311,10 +382,14 @@
                                 return std::vector<std::string>{base::StringPrintf("%p", p)};
                             }};
 
-    OutputLayerState<sp<GraphicBuffer>, LayerStateField::Buffer>
+    OutputLayerState<wp<GraphicBuffer>, LayerStateField::Buffer>
             mBuffer{[](auto layer) { return layer->getLayerFE().getCompositionState()->buffer; },
-                    [](const sp<GraphicBuffer>& buffer) {
-                        return std::vector<std::string>{base::StringPrintf("%p", buffer.get())};
+                    [](const wp<GraphicBuffer>& buffer) {
+                        sp<GraphicBuffer> promotedBuffer = buffer.promote();
+                        return std::vector<std::string>{
+                                base::StringPrintf("%p",
+                                                   promotedBuffer ? promotedBuffer.get()
+                                                                  : nullptr)};
                     }};
 
     int64_t mFramesSinceBufferUpdate = 0;
@@ -327,10 +402,12 @@
                             return std::vector<std::string>{stream.str()};
                         }};
 
-    std::array<StateInterface*, 13> getNonUniqueFields() {
-        std::array<const StateInterface*, 13> constFields =
+    static const constexpr size_t kNumNonUniqueFields = 15;
+
+    std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() {
+        std::array<const StateInterface*, kNumNonUniqueFields> constFields =
                 const_cast<const LayerState*>(this)->getNonUniqueFields();
-        std::array<StateInterface*, 13> fields;
+        std::array<StateInterface*, kNumNonUniqueFields> fields;
         std::transform(constFields.cbegin(), constFields.cend(), fields.begin(),
                        [](const StateInterface* constField) {
                            return const_cast<StateInterface*>(constField);
@@ -338,12 +415,12 @@
         return fields;
     }
 
-    std::array<const StateInterface*, 13> getNonUniqueFields() const {
+    std::array<const StateInterface*, kNumNonUniqueFields> getNonUniqueFields() const {
         return {
-                &mDisplayFrame,   &mSourceCrop,      &mZOrder,         &mBufferTransform,
-                &mBlendMode,      &mAlpha,           &mVisibleRegion,  &mOutputDataspace,
-                &mColorTransform, &mCompositionType, &mSidebandStream, &mBuffer,
-                &mSolidColor,
+                &mDisplayFrame,    &mSourceCrop,     &mZOrder,         &mBufferTransform,
+                &mBlendMode,       &mAlpha,          &mLayerMetadata,  &mVisibleRegion,
+                &mOutputDataspace, &mPixelFormat,    &mColorTransform, &mCompositionType,
+                &mSidebandStream,  &mBuffer,         &mSolidColor,
         };
     }
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index 89de34d..e6d2b63 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -59,7 +59,8 @@
             compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers);
 
     // The planner will call to the Flattener to render any pending cached set
-    void renderCachedSets(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
+    void renderCachedSets(renderengine::RenderEngine& re,
+                          const OutputCompositionState& outputState);
 
     void dump(const Vector<String16>& args, std::string&);
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
index a0cae6f..fe858c2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -39,7 +39,7 @@
     MOCK_METHOD1(setBufferPixelFormat, void(ui::PixelFormat));
     MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
     MOCK_METHOD2(prepareFrame, void(bool, bool));
-    MOCK_METHOD1(dequeueBuffer, sp<GraphicBuffer>(base::unique_fd*));
+    MOCK_METHOD1(dequeueBuffer, std::shared_ptr<renderengine::ExternalTexture>(base::unique_fd*));
     MOCK_METHOD1(queueBuffer, void(base::unique_fd));
     MOCK_METHOD0(onPresentDisplayCompleted, void());
     MOCK_METHOD0(flip, void());
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index a605fe1..1ffb1c8 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -367,6 +367,11 @@
         return fences;
     }
 
+    {
+        ATRACE_NAME("wait for earliest present time");
+        std::this_thread::sleep_until(getState().earliestPresentTime);
+    }
+
     auto& hwc = getCompositionEngine().getHwComposer();
     hwc.presentAndGetReleaseFences(*halDisplayIdOpt);
 
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index ded2dcc..faa4b74 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#include <thread>
-
+#include <SurfaceFlingerProperties.sysprop.h>
 #include <android-base/stringprintf.h>
 #include <compositionengine/CompositionEngine.h>
 #include <compositionengine/CompositionRefreshArgs.h>
@@ -29,7 +28,9 @@
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/impl/planner/Planner.h>
 
-#include <SurfaceFlingerProperties.sysprop.h>
+#include <thread>
+
+#include "renderengine/ExternalTexture.h"
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
@@ -710,22 +711,21 @@
         return;
     }
 
+    editState().earliestPresentTime = refreshArgs.earliestPresentTime;
+
     sp<GraphicBuffer> previousOverride = nullptr;
     for (auto* layer : getOutputLayersOrderedByZ()) {
         bool skipLayer = false;
         if (layer->getState().overrideInfo.buffer != nullptr) {
             if (previousOverride != nullptr &&
-                layer->getState().overrideInfo.buffer == previousOverride) {
+                layer->getState().overrideInfo.buffer->getBuffer() == previousOverride) {
                 ALOGV("Skipping redundant buffer");
                 skipLayer = true;
             }
-            previousOverride = layer->getState().overrideInfo.buffer;
+            previousOverride = layer->getState().overrideInfo.buffer->getBuffer();
         }
 
-        // TODO(b/181172795): We now update geometry for all flattened layers. We should update it
-        // only when the geometry actually changes
-        const bool includeGeometry = refreshArgs.updatingGeometryThisFrame ||
-                layer->getState().overrideInfo.buffer != nullptr || skipLayer;
+        const bool includeGeometry = refreshArgs.updatingGeometryThisFrame;
         layer->writeStateToHWC(includeGeometry, skipLayer);
     }
 }
@@ -981,14 +981,15 @@
     }
 
     base::unique_fd fd;
-    sp<GraphicBuffer> buf;
+
+    std::shared_ptr<renderengine::ExternalTexture> tex;
 
     // If we aren't doing client composition on this output, but do have a
     // flipClientTarget request for this frame on this output, we still need to
     // dequeue a buffer.
     if (hasClientComposition || outputState.flipClientTarget) {
-        buf = mRenderSurface->dequeueBuffer(&fd);
-        if (buf == nullptr) {
+        tex = mRenderSurface->dequeueBuffer(&fd);
+        if (tex == nullptr) {
             ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
                   "client composition for this frame",
                   mName.c_str());
@@ -1033,13 +1034,14 @@
     // 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,
+        if (mClientCompositionRequestCache->exists(tex->getBuffer()->getId(),
+                                                   clientCompositionDisplay,
                                                    clientCompositionLayers)) {
             outputCompositionState.reusedClientComposition = true;
             setExpensiveRenderingExpected(false);
             return readyFence;
         }
-        mClientCompositionRequestCache->add(buf->getId(), clientCompositionDisplay,
+        mClientCompositionRequestCache->add(tex->getBuffer()->getId(), clientCompositionDisplay,
                                             clientCompositionLayers);
     }
 
@@ -1072,12 +1074,12 @@
     // over to RenderEngine, in which case this flag can be removed from the drawLayers interface.
     const bool useFramebufferCache = outputState.layerStackInternal;
     status_t status =
-            renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buf,
+            renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, tex,
                                     useFramebufferCache, std::move(fd), &readyFence);
 
     if (status != NO_ERROR && mClientCompositionRequestCache) {
         // If rendering was not successful, remove the request from the cache.
-        mClientCompositionRequestCache->remove(buf->getId());
+        mClientCompositionRequestCache->remove(tex->getBuffer()->getId());
     }
 
     auto& timeStats = getCompositionEngine().getTimeStats();
@@ -1104,6 +1106,7 @@
     Region stubRegion;
 
     bool disableBlurs = false;
+    sp<GraphicBuffer> previousOverrideBuffer = nullptr;
 
     for (auto* layer : getOutputLayersOrderedByZ()) {
         const auto& layerState = layer->getState();
@@ -1153,8 +1156,14 @@
 
             std::vector<LayerFE::LayerSettings> results;
             if (layer->getState().overrideInfo.buffer != nullptr) {
-                results = layer->getOverrideCompositionList();
-                ALOGV("Replacing [%s] with override in RE", layer->getLayerFE().getDebugName());
+                if (layer->getState().overrideInfo.buffer->getBuffer() != previousOverrideBuffer) {
+                    results = layer->getOverrideCompositionList();
+                    previousOverrideBuffer = layer->getState().overrideInfo.buffer->getBuffer();
+                    ALOGV("Replacing [%s] with override in RE", layer->getLayerFE().getDebugName());
+                } else {
+                    ALOGV("Skipping redundant override buffer for [%s] in RE",
+                          layer->getLayerFE().getDebugName());
+                }
             } else {
                 results = layerFE.prepareClientCompositionList(targetSettings);
                 if (realContentIsVisible && !results.empty()) {
@@ -1252,7 +1261,7 @@
 
 void Output::renderCachedSets() {
     if (mPlanner) {
-        mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine(), getState().dataspace);
+        mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine(), getState());
     }
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index b364649..9ca8914 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -333,7 +333,11 @@
 
     auto requestedCompositionType = outputIndependentState->compositionType;
 
-    if (includeGeometry) {
+    // TODO(b/181172795): We now update geometry for all flattened layers. We should update it
+    // only when the geometry actually changes
+    const bool isOverridden = state.overrideInfo.buffer != nullptr;
+    const bool prevOverridden = state.hwc->stateOverridden;
+    if (isOverridden || prevOverridden || skipLayer || includeGeometry) {
         writeOutputDependentGeometryStateToHWC(hwcLayer.get(), requestedCompositionType);
         writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), *outputIndependentState,
                                                  skipLayer);
@@ -346,6 +350,8 @@
 
     // Always set the layer color after setting the composition type.
     writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
+
+    editState().hwc->stateOverridden = isOverridden;
 }
 
 void OutputLayer::writeOutputDependentGeometryStateToHWC(
@@ -354,7 +360,8 @@
 
     Rect displayFrame = outputDependentState.displayFrame;
     FloatRect sourceCrop = outputDependentState.sourceCrop;
-    if (outputDependentState.overrideInfo.buffer != nullptr) { // adyabr
+
+    if (outputDependentState.overrideInfo.buffer != nullptr) {
         displayFrame = outputDependentState.overrideInfo.displayFrame;
         sourceCrop = displayFrame.toFloatRect();
     }
@@ -380,8 +387,9 @@
               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 != hal::Composition::SOLID_COLOR
+    // Solid-color layers and overridden buffers should always use an identity transform.
+    const auto bufferTransform = (requestedCompositionType != hal::Composition::SOLID_COLOR &&
+                                  getState().overrideInfo.buffer == nullptr)
             ? outputDependentState.bufferTransform
             : static_cast<hal::Transform>(0);
     if (auto error = hwcLayer->setTransform(static_cast<hal::Transform>(bufferTransform));
@@ -395,14 +403,17 @@
 void OutputLayer::writeOutputIndependentGeometryStateToHWC(
         HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState,
         bool skipLayer) {
-    if (auto error = hwcLayer->setBlendMode(outputIndependentState.blendMode);
-        error != hal::Error::NONE) {
+    const auto blendMode = getState().overrideInfo.buffer
+            ? hardware::graphics::composer::hal::BlendMode::PREMULTIPLIED
+            : outputIndependentState.blendMode;
+    if (auto error = hwcLayer->setBlendMode(blendMode); error != hal::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));
+              toString(blendMode).c_str(), to_string(error).c_str(), static_cast<int32_t>(error));
     }
 
-    const float alpha = skipLayer ? 0.0f : outputIndependentState.alpha;
+    const float alpha = skipLayer
+            ? 0.0f
+            : (getState().overrideInfo.buffer ? 1.0f : outputIndependentState.alpha);
     ALOGV("Writing alpha %f", alpha);
 
     if (auto error = hwcLayer->setPlaneAlpha(alpha); error != hal::Error::NONE) {
@@ -424,18 +435,22 @@
 
     // TODO(lpique): b/121291683 outputSpaceVisibleRegion is output-dependent geometry
     // state and should not change every frame.
-    if (auto error = hwcLayer->setVisibleRegion(outputDependentState.outputSpaceVisibleRegion);
-        error != hal::Error::NONE) {
+    Region visibleRegion = outputDependentState.overrideInfo.buffer
+            ? Region(outputDependentState.overrideInfo.visibleRegion)
+            : outputDependentState.outputSpaceVisibleRegion;
+    if (auto error = hwcLayer->setVisibleRegion(visibleRegion); error != hal::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 != hal::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));
+    const auto dataspace = outputDependentState.overrideInfo.buffer
+            ? outputDependentState.overrideInfo.dataspace
+            : outputDependentState.dataspace;
+
+    if (auto error = hwcLayer->setDataspace(dataspace); error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", getLayerFE().getDebugName(), dataspace,
+              to_string(error).c_str(), static_cast<int32_t>(error));
     }
 }
 
@@ -452,8 +467,11 @@
                   to_string(error).c_str(), static_cast<int32_t>(error));
     }
 
-    if (auto error = hwcLayer->setSurfaceDamage(outputIndependentState.surfaceDamage);
-        error != hal::Error::NONE) {
+    const Region& surfaceDamage = getState().overrideInfo.buffer
+            ? getState().overrideInfo.damageRegion
+            : outputIndependentState.surfaceDamage;
+
+    if (auto error = hwcLayer->setSurfaceDamage(surfaceDamage); error != hal::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);
@@ -519,7 +537,7 @@
     sp<GraphicBuffer> buffer = outputIndependentState.buffer;
     sp<Fence> acquireFence = outputIndependentState.acquireFence;
     if (getState().overrideInfo.buffer != nullptr) {
-        buffer = getState().overrideInfo.buffer;
+        buffer = getState().overrideInfo.buffer->getBuffer();
         acquireFence = getState().overrideInfo.acquireFence;
     }
 
@@ -671,16 +689,26 @@
         return {};
     }
 
+    // Compute the geometry boundaries in layer stack space: we need to transform from the
+    // framebuffer space of the override buffer to layer space.
+    const ProjectionSpace& layerSpace = getOutput().getState().layerStackSpace;
+    const ui::Transform transform = getState().overrideInfo.displaySpace.getTransform(layerSpace);
+    const Rect boundaries = transform.transform(getState().overrideInfo.displayFrame);
+
     LayerFE::LayerSettings settings;
     settings.geometry = renderengine::Geometry{
-            .boundaries = getState().overrideInfo.displayFrame.toFloatRect(),
+            .boundaries = boundaries.toFloatRect(),
     };
-    settings.bufferId = getState().overrideInfo.buffer->getId();
-    settings.source =
-            renderengine::PixelSource{.buffer = renderengine::Buffer{
-                                              .buffer = getState().overrideInfo.buffer,
-                                              .fence = getState().overrideInfo.acquireFence,
-                                      }};
+    settings.bufferId = getState().overrideInfo.buffer->getBuffer()->getId();
+    settings.source = renderengine::PixelSource{
+            .buffer = renderengine::Buffer{
+                    .buffer = getState().overrideInfo.buffer,
+                    .fence = getState().overrideInfo.acquireFence,
+                    // If the transform from layer space to display space contains a rotation, we
+                    // need to undo the rotation in the texture transform
+                    .textureTransform =
+                            ui::Transform(transform.inverse().getOrientation(), 1, 1).asMatrix4(),
+            }};
     settings.sourceDataspace = getState().overrideInfo.dataspace;
     settings.alpha = 1.0f;
 
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index 45dce98..efd23dc 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -72,6 +72,13 @@
     dumpVal(out, "override acquire fence", overrideInfo.acquireFence.get());
     dumpVal(out, "override display frame", overrideInfo.displayFrame);
     dumpVal(out, "override dataspace", toString(overrideInfo.dataspace), overrideInfo.dataspace);
+    dumpVal(out, "override display space", to_string(overrideInfo.displaySpace));
+    std::string damageRegionString;
+    overrideInfo.damageRegion.dump(damageRegionString, "");
+    dumpVal(out, "override damage region", damageRegionString);
+    std::string visibleRegionString;
+    overrideInfo.visibleRegion.dump(visibleRegionString, "");
+    dumpVal(out, "override visible region", visibleRegionString);
 
     if (hwc) {
         dumpHwc(*hwc, out);
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 3bef77d..ef50870 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -25,8 +25,8 @@
 #include <compositionengine/impl/DumpHelpers.h>
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/RenderSurface.h>
-
 #include <log/log.h>
+#include <renderengine/ExternalTexture.h>
 #include <renderengine/RenderEngine.h>
 #include <system/window.h>
 #include <ui/GraphicBuffer.h>
@@ -63,7 +63,8 @@
         mDisplay(display),
         mNativeWindow(args.nativeWindow),
         mDisplaySurface(args.displaySurface),
-        mSize(args.displayWidth, args.displayHeight) {
+        mSize(args.displayWidth, args.displayHeight),
+        mMaxTextureCacheSize(args.maxTextureCacheSize) {
     LOG_ALWAYS_FATAL_IF(!mNativeWindow);
 }
 
@@ -146,7 +147,8 @@
     }
 }
 
-sp<GraphicBuffer> RenderSurface::dequeueBuffer(base::unique_fd* bufferFence) {
+std::shared_ptr<renderengine::ExternalTexture> RenderSurface::dequeueBuffer(
+        base::unique_fd* bufferFence) {
     ATRACE_CALL();
     int fd = -1;
     ANativeWindowBuffer* buffer = nullptr;
@@ -158,16 +160,41 @@
               mDisplay.getName().c_str(), result);
         // Return fast here as we can't do much more - any rendering we do
         // now will just be wrong.
-        return mGraphicBuffer;
+        return mTexture;
     }
 
-    ALOGW_IF(mGraphicBuffer != nullptr, "Clobbering a non-null pointer to a buffer [%p].",
-             mGraphicBuffer->getNativeBuffer()->handle);
-    mGraphicBuffer = GraphicBuffer::from(buffer);
+    ALOGW_IF(mTexture != nullptr, "Clobbering a non-null pointer to a buffer [%p].",
+             mTexture->getBuffer()->getNativeBuffer()->handle);
+
+    sp<GraphicBuffer> newBuffer = GraphicBuffer::from(buffer);
+
+    std::shared_ptr<renderengine::ExternalTexture> texture;
+
+    for (auto it = mTextureCache.begin(); it != mTextureCache.end(); it++) {
+        const auto& cachedTexture = *it;
+        if (cachedTexture->getBuffer()->getId() == newBuffer->getId()) {
+            texture = cachedTexture;
+            mTextureCache.erase(it);
+            break;
+        }
+    }
+
+    if (texture) {
+        mTexture = texture;
+    } else {
+        mTexture = std::make_shared<
+                renderengine::ExternalTexture>(GraphicBuffer::from(buffer),
+                                               mCompositionEngine.getRenderEngine(),
+                                               renderengine::ExternalTexture::Usage::WRITEABLE);
+    }
+    mTextureCache.push_back(mTexture);
+    if (mTextureCache.size() > mMaxTextureCacheSize) {
+        mTextureCache.erase(mTextureCache.begin());
+    }
 
     *bufferFence = base::unique_fd(fd);
 
-    return mGraphicBuffer;
+    return mTexture;
 }
 
 void RenderSurface::queueBuffer(base::unique_fd readyFence) {
@@ -177,24 +204,24 @@
         // hasFlipClientTargetRequest could return true even if we haven't
         // dequeued a buffer before. Try dequeueing one if we don't have a
         // buffer ready.
-        if (mGraphicBuffer == nullptr) {
+        if (mTexture == nullptr) {
             ALOGI("Attempting to queue a client composited buffer without one "
                   "previously dequeued for display [%s]. Attempting to dequeue "
                   "a scratch buffer now",
                   mDisplay.getName().c_str());
-            // We shouldn't deadlock here, since mGraphicBuffer == nullptr only
+            // We shouldn't deadlock here, since mTexture == nullptr only
             // after a successful call to queueBuffer, or if dequeueBuffer has
             // never been called.
             base::unique_fd unused;
             dequeueBuffer(&unused);
         }
 
-        if (mGraphicBuffer == nullptr) {
+        if (mTexture == nullptr) {
             ALOGE("No buffer is ready for display [%s]", mDisplay.getName().c_str());
         } else {
-            status_t result =
-                    mNativeWindow->queueBuffer(mNativeWindow.get(),
-                                               mGraphicBuffer->getNativeBuffer(), dup(readyFence));
+            status_t result = mNativeWindow->queueBuffer(mNativeWindow.get(),
+                                                         mTexture->getBuffer()->getNativeBuffer(),
+                                                         dup(readyFence));
             if (result != NO_ERROR) {
                 ALOGE("Error when queueing buffer for display [%s]: %d", mDisplay.getName().c_str(),
                       result);
@@ -204,11 +231,12 @@
                     LOG_ALWAYS_FATAL("ANativeWindow::queueBuffer failed with error: %d", result);
                 } else {
                     mNativeWindow->cancelBuffer(mNativeWindow.get(),
-                                                mGraphicBuffer->getNativeBuffer(), dup(readyFence));
+                                                mTexture->getBuffer()->getNativeBuffer(),
+                                                dup(readyFence));
                 }
             }
 
-            mGraphicBuffer = nullptr;
+            mTexture = nullptr;
         }
     }
 
@@ -256,8 +284,8 @@
     mSize = size;
 }
 
-sp<GraphicBuffer>& RenderSurface::mutableGraphicBufferForTest() {
-    return mGraphicBuffer;
+std::shared_ptr<renderengine::ExternalTexture>& RenderSurface::mutableTextureForTest() {
+    return mTexture;
 }
 
 } // namespace impl
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 137697b..9955e29 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -19,6 +19,7 @@
 // #define LOG_NDEBUG 0
 
 #include <android-base/properties.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/planner/CachedSet.h>
 #include <math/HashCombine.h>
 #include <renderengine/DisplaySettings.h>
@@ -50,17 +51,18 @@
 }
 
 CachedSet::Layer::Layer(const LayerState* state, std::chrono::steady_clock::time_point lastUpdate)
-      : mState(state), mHash(state->getHash(LayerStateField::Buffer)), mLastUpdate(lastUpdate) {}
+      : mState(state), mHash(state->getHash()), mLastUpdate(lastUpdate) {}
 
 CachedSet::CachedSet(const LayerState* layer, std::chrono::steady_clock::time_point lastUpdate)
-      : mFingerprint(layer->getHash(LayerStateField::Buffer)), mLastUpdate(lastUpdate) {
+      : mFingerprint(layer->getHash()), mLastUpdate(lastUpdate) {
     addLayer(layer, lastUpdate);
 }
 
 CachedSet::CachedSet(Layer layer)
       : mFingerprint(layer.getHash()),
         mLastUpdate(layer.getLastUpdate()),
-        mBounds(layer.getDisplayFrame()) {
+        mBounds(layer.getDisplayFrame()),
+        mVisibleRegion(layer.getVisibleRegion()) {
     mLayers.emplace_back(std::move(layer));
 }
 
@@ -72,6 +74,7 @@
     boundingRegion.orSelf(mBounds);
     boundingRegion.orSelf(layer->getDisplayFrame());
     mBounds = boundingRegion.getBounds();
+    mVisibleRegion.orSelf(layer->getVisibleRegion());
 }
 
 NonBufferHash CachedSet::getNonBufferHash() const {
@@ -84,6 +87,7 @@
     size_t hash = 0;
     android::hashCombineSingle(hash, mBounds);
     android::hashCombineSingle(hash, mOutputDataspace);
+    android::hashCombineSingle(hash, mOrientation);
     return hash;
 }
 
@@ -116,18 +120,17 @@
     return static_cast<size_t>(mBounds.width() * mBounds.height());
 }
 
-bool CachedSet::hasBufferUpdate(std::vector<const LayerState*>::const_iterator layers) const {
+bool CachedSet::hasBufferUpdate() const {
     for (const Layer& layer : mLayers) {
         if (layer.getFramesSinceBufferUpdate() == 0) {
             return true;
         }
-        ++layers;
     }
     return false;
 }
 
 bool CachedSet::hasReadyBuffer() const {
-    return mTexture.getBuffer() != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
+    return mTexture != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
 }
 
 std::vector<CachedSet> CachedSet::decompose() const {
@@ -149,17 +152,22 @@
     }
 }
 
-void CachedSet::render(renderengine::RenderEngine& renderEngine, ui::Dataspace outputDataspace) {
+void CachedSet::render(renderengine::RenderEngine& renderEngine,
+                       const OutputCompositionState& outputState) {
+    const Rect& viewport = outputState.layerStackSpace.content;
+    const ui::Dataspace& outputDataspace = outputState.dataspace;
+    const ui::Transform::RotationFlags orientation =
+            ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation);
     renderengine::DisplaySettings displaySettings{
             .physicalDisplay = Rect(0, 0, mBounds.getWidth(), mBounds.getHeight()),
-            .clip = mBounds,
+            .clip = viewport,
             .outputDataspace = outputDataspace,
+            .orientation = orientation,
     };
 
     Region clearRegion = Region::INVALID_REGION;
-    Rect viewport = mBounds;
     LayerFE::ClientCompositionTargetSettings targetSettings{
-            .clip = Region(mBounds),
+            .clip = Region(viewport),
             .needsFiltering = false,
             .isSecure = true,
             .supportsProtectedContent = false,
@@ -172,6 +180,7 @@
     };
 
     std::vector<renderengine::LayerSettings> layerSettings;
+    renderengine::LayerSettings highlight;
     for (const auto& layer : mLayers) {
         const auto clientCompositionList =
                 layer.getState()->getOutputLayer()->getLayerFE().prepareClientCompositionList(
@@ -186,7 +195,7 @@
                    [](const renderengine::LayerSettings& settings) { return &settings; });
 
     if (sDebugHighlighLayers) {
-        renderengine::LayerSettings highlight{
+        highlight = {
                 .geometry =
                         renderengine::Geometry{
                                 .boundaries = FloatRect(0.0f, 0.0f,
@@ -208,16 +217,27 @@
     sp<GraphicBuffer> buffer = new GraphicBuffer(static_cast<uint32_t>(mBounds.getWidth()),
                                                  static_cast<uint32_t>(mBounds.getHeight()),
                                                  HAL_PIXEL_FORMAT_RGBA_8888, 1, usageFlags);
+    const auto texture = std::make_shared<
+            renderengine::ExternalTexture>(buffer, renderEngine,
+                                           renderengine::ExternalTexture::Usage::READABLE |
+                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
     LOG_ALWAYS_FATAL_IF(buffer->initCheck() != OK);
     base::unique_fd drawFence;
 
-    status_t result = renderEngine.drawLayers(displaySettings, layerSettingsPointers, buffer, false,
-                                              base::unique_fd(), &drawFence);
+    status_t result = renderEngine.drawLayers(displaySettings, layerSettingsPointers, texture,
+                                              false, base::unique_fd(), &drawFence);
 
     if (result == NO_ERROR) {
-        mTexture.setBuffer(buffer, &renderEngine);
         mDrawFence = new Fence(drawFence.release());
+        mOutputSpace = ProjectionSpace(ui::Size(outputState.framebufferSpace.bounds.getWidth(),
+                                                outputState.framebufferSpace.bounds.getHeight()),
+                                       mBounds);
+        mTexture = std::move(texture);
+        mOutputSpace.orientation = outputState.framebufferSpace.orientation;
         mOutputDataspace = outputDataspace;
+        mOrientation = orientation;
+    } else {
+        mTexture = nullptr;
     }
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 30b5761..9c9649c 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -40,36 +40,28 @@
 
     ++mInitialLayerCounts[layers.size()];
 
-    if (mergeWithCachedSets(layers, now)) {
-        hash = mLayersHash;
-    }
+    // Only buildCachedSets if these layers are already stored in mLayers.
+    // Otherwise (i.e. mergeWithCachedSets returns false), the time has not
+    // changed, so buildCachedSets will never find any runs.
+    const bool alreadyHadCachedSets = mergeWithCachedSets(layers, now);
 
     ++mFinalLayerCounts[mLayers.size()];
 
-    buildCachedSets(now);
+    if (alreadyHadCachedSets) {
+        buildCachedSets(now);
+        hash = computeLayersHash();
+    }
 
     return hash;
 }
 
 void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine,
-                                 ui::Dataspace outputDataspace) {
+                                 const OutputCompositionState& outputState) {
     if (!mNewCachedSet) {
         return;
     }
 
-    mNewCachedSet->render(renderEngine, outputDataspace);
-}
-
-void Flattener::reset() {
-    resetActivities(0, std::chrono::steady_clock::now());
-
-    mUnflattenedDisplayCost = 0;
-    mFlattenedDisplayCost = 0;
-    mInitialLayerCounts.clear();
-    mFinalLayerCounts.clear();
-    mCachedSetCreationCount = 0;
-    mCachedSetCreationCost = 0;
-    mInvalidatedCachedSetAges.clear();
+    mNewCachedSet->render(renderEngine, outputState);
 }
 
 void Flattener::dump(std::string& result) const {
@@ -169,14 +161,17 @@
     }
 }
 
-void Flattener::updateLayersHash() {
+NonBufferHash Flattener::computeLayersHash() const{
     size_t hash = 0;
     for (const auto& layer : mLayers) {
         android::hashCombineSingleHashed(hash, layer.getNonBufferHash());
     }
-    mLayersHash = hash;
+    return hash;
 }
 
+// Only called if the geometry matches the last frame. Return true if mLayers
+// was already populated with these layers, i.e. on the second and following
+// calls with the same geometry.
 bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers, time_point now) {
     std::vector<CachedSet> merged;
 
@@ -205,10 +200,8 @@
     auto currentLayerIter = mLayers.begin();
     auto incomingLayerIter = layers.begin();
     while (incomingLayerIter != layers.end()) {
-        if (mNewCachedSet &&
-            mNewCachedSet->getFingerprint() ==
-                    (*incomingLayerIter)->getHash(LayerStateField::Buffer)) {
-            if (mNewCachedSet->hasBufferUpdate(incomingLayerIter)) {
+        if (mNewCachedSet && mNewCachedSet->getFingerprint() == (*incomingLayerIter)->getHash()) {
+            if (mNewCachedSet->hasBufferUpdate()) {
                 ALOGV("[%s] Dropping new cached set", __func__);
                 ++mInvalidatedCachedSetAges[0];
                 mNewCachedSet = std::nullopt;
@@ -225,6 +218,9 @@
                                 .acquireFence = mNewCachedSet->getDrawFence(),
                                 .displayFrame = mNewCachedSet->getBounds(),
                                 .dataspace = mNewCachedSet->getOutputDataspace(),
+                                .displaySpace = mNewCachedSet->getOutputSpace(),
+                                .damageRegion = Region::INVALID_REGION,
+                                .visibleRegion = mNewCachedSet->getVisibleRegion(),
                         };
                         ++incomingLayerIter;
                     }
@@ -242,7 +238,7 @@
             }
         }
 
-        if (!currentLayerIter->hasBufferUpdate(incomingLayerIter)) {
+        if (!currentLayerIter->hasBufferUpdate()) {
             currentLayerIter->incrementAge();
             merged.emplace_back(*currentLayerIter);
 
@@ -256,6 +252,9 @@
                         .acquireFence = currentLayerIter->getDrawFence(),
                         .displayFrame = currentLayerIter->getBounds(),
                         .dataspace = currentLayerIter->getOutputDataspace(),
+                        .displaySpace = currentLayerIter->getOutputSpace(),
+                        .damageRegion = Region(),
+                        .visibleRegion = currentLayerIter->getVisibleRegion(),
                 };
                 ++incomingLayerIter;
             }
@@ -280,7 +279,6 @@
     }
 
     mLayers = std::move(merged);
-    updateLayersHash();
     return true;
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index 222b2be..ab85997 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -58,24 +58,24 @@
     return differences;
 }
 
-size_t LayerState::getHash(
-        Flags<LayerStateField> skipFields = static_cast<LayerStateField>(0)) const {
+size_t LayerState::getHash() const {
     size_t hash = 0;
     for (const StateInterface* field : getNonUniqueFields()) {
-        android::hashCombineSingleHashed(hash, field->getHash(skipFields));
+        if (field->getField() == LayerStateField::Buffer) {
+            continue;
+        }
+        android::hashCombineSingleHashed(hash, field->getHash());
     }
 
     return hash;
 }
 
-Flags<LayerStateField> LayerState::getDifferingFields(
-        const LayerState& other,
-        Flags<LayerStateField> skipFields = static_cast<LayerStateField>(0)) const {
+Flags<LayerStateField> LayerState::getDifferingFields(const LayerState& other) const {
     Flags<LayerStateField> differences;
     auto myFields = getNonUniqueFields();
     auto otherFields = other.getNonUniqueFields();
     for (size_t i = 0; i < myFields.size(); ++i) {
-        if (skipFields.test(myFields[i]->getField())) {
+        if (myFields[i]->getField() == LayerStateField::Buffer) {
             continue;
         }
 
@@ -157,8 +157,9 @@
     return lhs.mId == rhs.mId && lhs.mName == rhs.mName && lhs.mDisplayFrame == rhs.mDisplayFrame &&
             lhs.mSourceCrop == rhs.mSourceCrop && lhs.mZOrder == rhs.mZOrder &&
             lhs.mBufferTransform == rhs.mBufferTransform && lhs.mBlendMode == rhs.mBlendMode &&
-            lhs.mAlpha == rhs.mAlpha && lhs.mVisibleRegion == rhs.mVisibleRegion &&
-            lhs.mOutputDataspace == rhs.mOutputDataspace &&
+            lhs.mAlpha == rhs.mAlpha && lhs.mLayerMetadata == rhs.mLayerMetadata &&
+            lhs.mVisibleRegion == rhs.mVisibleRegion &&
+            lhs.mOutputDataspace == rhs.mOutputDataspace && lhs.mPixelFormat == rhs.mPixelFormat &&
             lhs.mColorTransform == rhs.mColorTransform &&
             lhs.mCompositionType == rhs.mCompositionType &&
             lhs.mSidebandStream == rhs.mSidebandStream && lhs.mBuffer == rhs.mBuffer &&
@@ -169,7 +170,7 @@
 NonBufferHash getNonBufferHash(const std::vector<const LayerState*>& layers) {
     size_t hash = 0;
     for (const auto layer : layers) {
-        android::hashCombineSingleHashed(hash, layer->getHash(LayerStateField::Buffer));
+        android::hashCombineSingleHashed(hash, layer->getHash());
     }
 
     return hash;
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 87721c7..3a2534b 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -111,7 +111,12 @@
     const GraphicBuffer* currentOverrideBuffer = nullptr;
     bool hasSkippedLayers = false;
     for (auto layer : layers) {
-        const GraphicBuffer* overrideBuffer = layer->getState().overrideInfo.buffer.get();
+        if (!layer->getState().overrideInfo.buffer) {
+            continue;
+        }
+
+        const GraphicBuffer* overrideBuffer =
+                layer->getState().overrideInfo.buffer->getBuffer().get();
         if (overrideBuffer != nullptr && overrideBuffer == currentOverrideBuffer) {
             // Skip this layer since it is part of a previous cached set
             hasSkippedLayers = true;
@@ -134,8 +139,8 @@
 }
 
 void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine,
-                               ui::Dataspace outputDataspace) {
-    mFlattener.renderCachedSets(renderEngine, outputDataspace);
+                               const OutputCompositionState& outputState) {
+    mFlattener.renderCachedSets(renderEngine, outputState);
 }
 
 void Planner::dump(const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
index 07920b8..8226ef7 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
@@ -33,8 +33,7 @@
     std::optional<ApproximateMatch> approximateMatch = {};
     for (size_t i = 0; i < mLayers.size(); ++i) {
         // Skip identical layers
-        if (mLayers[i].getHash(LayerStateField::Buffer) ==
-            other[i]->getHash(LayerStateField::Buffer)) {
+        if (mLayers[i].getHash() == other[i]->getHash()) {
             continue;
         }
 
@@ -56,8 +55,7 @@
             return std::nullopt;
         }
 
-        Flags<LayerStateField> differingFields =
-                mLayers[i].getDifferingFields(*other[i], LayerStateField::Buffer);
+        Flags<LayerStateField> differingFields = mLayers[i].getDifferingFields(*other[i]);
 
         // If we don't find an approximate match on this layer, then the LayerStacks differ
         // by too much, so return nothing
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 9dd199d..4c3f494 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -26,6 +26,7 @@
 #include "MockHWC2.h"
 #include "MockHWComposer.h"
 #include "RegionMatcher.h"
+#include "renderengine/mock/RenderEngine.h"
 
 namespace android::compositionengine {
 namespace {
@@ -690,10 +691,15 @@
     static constexpr FloatRect kSourceCrop{11.f, 12.f, 13.f, 14.f};
     static constexpr uint32_t kZOrder = 21u;
     static constexpr Hwc2::Transform kBufferTransform = static_cast<Hwc2::Transform>(31);
+    static constexpr Hwc2::Transform kOverrideBufferTransform = static_cast<Hwc2::Transform>(0);
     static constexpr Hwc2::IComposerClient::BlendMode kBlendMode =
             static_cast<Hwc2::IComposerClient::BlendMode>(41);
+    static constexpr Hwc2::IComposerClient::BlendMode kOverrideBlendMode =
+            Hwc2::IComposerClient::BlendMode::PREMULTIPLIED;
     static constexpr float kAlpha = 51.f;
+    static constexpr float kOverrideAlpha = 1.f;
     static constexpr ui::Dataspace kDataspace = static_cast<ui::Dataspace>(71);
+    static constexpr ui::Dataspace kOverrideDataspace = static_cast<ui::Dataspace>(72);
     static constexpr int kSupportedPerFrameMetadata = 101;
     static constexpr int kExpectedHwcSlot = 0;
     static constexpr bool kLayerGenericMetadata1Mandatory = true;
@@ -701,19 +707,29 @@
 
     static const half4 kColor;
     static const Rect kDisplayFrame;
+    static const Rect kOverrideDisplayFrame;
     static const Region kOutputSpaceVisibleRegion;
+    static const Region kOverrideVisibleRegion;
     static const mat4 kColorTransform;
     static const Region kSurfaceDamage;
+    static const Region kOverrideSurfaceDamage;
     static const HdrMetadata kHdrMetadata;
     static native_handle_t* kSidebandStreamHandle;
     static const sp<GraphicBuffer> kBuffer;
+    std::shared_ptr<renderengine::ExternalTexture> kOverrideBuffer;
     static const sp<Fence> kFence;
+    static const sp<Fence> kOverrideFence;
     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() {
+        kOverrideBuffer = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine,
+                                               renderengine::ExternalTexture::Usage::READABLE |
+                                                       renderengine::ExternalTexture::Usage::
+                                                               WRITEABLE);
         auto& outputLayerState = mOutputLayer.editState();
         outputLayerState.hwc = impl::OutputLayerCompositionState::Hwc(mHwcLayer);
 
@@ -751,26 +767,42 @@
                                                              kLayerGenericMetadata2Value};
     }
 
-    void expectGeometryCommonCalls() {
-        EXPECT_CALL(*mHwcLayer, setDisplayFrame(kDisplayFrame)).WillOnce(Return(kError));
-        EXPECT_CALL(*mHwcLayer, setSourceCrop(kSourceCrop)).WillOnce(Return(kError));
-        EXPECT_CALL(*mHwcLayer, setZOrder(kZOrder)).WillOnce(Return(kError));
-        EXPECT_CALL(*mHwcLayer, setTransform(kBufferTransform)).WillOnce(Return(kError));
+    void includeOverrideInfo() {
+        auto& overrideInfo = mOutputLayer.editState().overrideInfo;
 
-        EXPECT_CALL(*mHwcLayer, setBlendMode(kBlendMode)).WillOnce(Return(kError));
-        EXPECT_CALL(*mHwcLayer, setPlaneAlpha(kAlpha)).WillOnce(Return(kError));
+        overrideInfo.buffer = kOverrideBuffer;
+        overrideInfo.acquireFence = kOverrideFence;
+        overrideInfo.displayFrame = kOverrideDisplayFrame;
+        overrideInfo.dataspace = kOverrideDataspace;
+        overrideInfo.damageRegion = kOverrideSurfaceDamage;
+        overrideInfo.visibleRegion = kOverrideVisibleRegion;
     }
 
-    void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None) {
-        EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(kOutputSpaceVisibleRegion)))
-                .WillOnce(Return(kError));
-        EXPECT_CALL(*mHwcLayer, setDataspace(kDataspace)).WillOnce(Return(kError));
+    void expectGeometryCommonCalls(Rect displayFrame = kDisplayFrame,
+                                   FloatRect sourceCrop = kSourceCrop,
+                                   Hwc2::Transform bufferTransform = kBufferTransform,
+                                   Hwc2::IComposerClient::BlendMode blendMode = kBlendMode,
+                                   float alpha = kAlpha) {
+        EXPECT_CALL(*mHwcLayer, setDisplayFrame(displayFrame)).WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setSourceCrop(sourceCrop)).WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setZOrder(kZOrder)).WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setTransform(bufferTransform)).WillOnce(Return(kError));
+
+        EXPECT_CALL(*mHwcLayer, setBlendMode(blendMode)).WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setPlaneAlpha(alpha)).WillOnce(Return(kError));
+    }
+
+    void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None,
+                                   ui::Dataspace dataspace = kDataspace,
+                                   const Region& visibleRegion = kOutputSpaceVisibleRegion,
+                                   const Region& surfaceDamage = kSurfaceDamage) {
+        EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(visibleRegion))).WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setDataspace(dataspace)).WillOnce(Return(kError));
         EXPECT_CALL(*mHwcLayer, setColorTransform(kColorTransform))
                 .WillOnce(Return(unsupported == SimulateUnsupported::ColorTransform
                                          ? hal::Error::UNSUPPORTED
                                          : hal::Error::NONE));
-        EXPECT_CALL(*mHwcLayer, setSurfaceDamage(RegionEq(kSurfaceDamage)))
-                .WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setSurfaceDamage(RegionEq(surfaceDamage))).WillOnce(Return(kError));
     }
 
     void expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition compositionType) {
@@ -793,9 +825,10 @@
         EXPECT_CALL(*mHwcLayer, setSidebandStream(kSidebandStreamHandle));
     }
 
-    void expectSetHdrMetadataAndBufferCalls() {
+    void expectSetHdrMetadataAndBufferCalls(sp<GraphicBuffer> buffer = kBuffer,
+                                            sp<Fence> fence = kFence) {
         EXPECT_CALL(*mHwcLayer, setPerFrameMetadata(kSupportedPerFrameMetadata, kHdrMetadata));
-        EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, kBuffer, kFence));
+        EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, buffer, fence));
     }
 
     void expectGenericLayerMetadataCalls() {
@@ -812,23 +845,28 @@
 
     std::shared_ptr<HWC2::mock::Layer> mHwcLayer{std::make_shared<StrictMock<HWC2::mock::Layer>>()};
     StrictMock<mock::DisplayColorProfile> mDisplayColorProfile;
+    renderengine::mock::RenderEngine mRenderEngine;
 };
 
 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 Rect OutputLayerWriteStateToHWCTest::kOverrideDisplayFrame{1002, 1003, 1004, 20044};
 const Region OutputLayerWriteStateToHWCTest::kOutputSpaceVisibleRegion{
         Rect{1005, 1006, 1007, 1008}};
+const Region OutputLayerWriteStateToHWCTest::kOverrideVisibleRegion{Rect{1006, 1007, 1008, 1009}};
 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 Region OutputLayerWriteStateToHWCTest::kOverrideSurfaceDamage{Rect{1026, 1027, 1028, 1029}};
 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 sp<Fence> OutputLayerWriteStateToHWCTest::kOverrideFence = new Fence();
 const std::string OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Key =
         "com.example.metadata.1";
 const std::vector<uint8_t> OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Value{{1, 2, 3}};
@@ -983,6 +1021,20 @@
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
 }
 
+TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    includeOverrideInfo();
+
+    expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideDisplayFrame.toFloatRect(),
+                              kOverrideBufferTransform, kOverrideBlendMode, kOverrideAlpha);
+    expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
+                              kOverrideSurfaceDamage);
+    expectSetHdrMetadataAndBufferCalls(kOverrideBuffer->getBuffer(), kOverrideFence);
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
+}
+
 /*
  * OutputLayer::writeCursorPositionToHWC()
  */
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 5f0b0ee..e80100c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#include <cmath>
-
 #include <android-base/stringprintf.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/impl/Output.h>
@@ -31,9 +29,12 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
+#include <cmath>
+
 #include "CallOrderStateMachineHelper.h"
 #include "MockHWC2.h"
 #include "RegionMatcher.h"
+#include "renderengine/ExternalTexture.h"
 
 namespace android::compositionengine {
 namespace {
@@ -2960,7 +2961,10 @@
     mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
     mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
     StrictMock<OutputPartialMock> mOutput;
-    sp<GraphicBuffer> mOutputBuffer = new GraphicBuffer();
+    std::shared_ptr<renderengine::ExternalTexture> mOutputBuffer = std::make_shared<
+            renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine,
+                                           renderengine::ExternalTexture::Usage::READABLE |
+                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
 
     std::optional<base::unique_fd> mReadyFence;
 };
@@ -3173,7 +3177,10 @@
     EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
             .WillRepeatedly(Return());
 
-    sp<GraphicBuffer> otherOutputBuffer = new GraphicBuffer();
+    const auto otherOutputBuffer = std::make_shared<
+            renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine,
+                                           renderengine::ExternalTexture::Usage::READABLE |
+                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_))
             .WillOnce(Return(mOutputBuffer))
             .WillOnce(Return(otherOutputBuffer));
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index 5ef5d7b..9aeb290 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -15,6 +15,8 @@
  */
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include "renderengine/ExternalTexture.h"
+#include "ui/GraphicBuffer.h"
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wextra"
 
@@ -239,9 +241,9 @@
                     DoAll(SetArgPointee<0>(buffer.get()), SetArgPointee<1>(-1), Return(NO_ERROR)));
 
     base::unique_fd fence;
-    EXPECT_EQ(buffer.get(), mSurface.dequeueBuffer(&fence).get());
+    EXPECT_EQ(buffer.get(), mSurface.dequeueBuffer(&fence)->getBuffer().get());
 
-    EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
+    EXPECT_EQ(buffer.get(), mSurface.mutableTextureForTest()->getBuffer().get());
 }
 
 /*
@@ -249,8 +251,11 @@
  */
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesNoClientComposition) {
-    sp<GraphicBuffer> buffer = new GraphicBuffer();
-    mSurface.mutableGraphicBufferForTest() = buffer;
+    const auto buffer = std::make_shared<
+            renderengine::ExternalTexture>(new GraphicBuffer(), mRenderEngine,
+                                           renderengine::ExternalTexture::Usage::READABLE |
+                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
+    mSurface.mutableTextureForTest() = buffer;
 
     impl::OutputCompositionState state;
     state.usesClientComposition = false;
@@ -261,43 +266,45 @@
 
     mSurface.queueBuffer(base::unique_fd());
 
-    EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
+    EXPECT_EQ(buffer.get(), mSurface.mutableTextureForTest().get());
 }
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesClientComposition) {
-    sp<GraphicBuffer> buffer = new GraphicBuffer();
-    mSurface.mutableGraphicBufferForTest() = buffer;
+    const auto buffer = std::make_shared<renderengine::ExternalTexture>(new GraphicBuffer(),
+                                                                        mRenderEngine, false);
+    mSurface.mutableTextureForTest() = buffer;
 
     impl::OutputCompositionState state;
     state.usesClientComposition = true;
     state.flipClientTarget = false;
 
     EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
-    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getBuffer()->getNativeBuffer(), -1))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
     mSurface.queueBuffer(base::unique_fd());
 
-    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+    EXPECT_EQ(nullptr, mSurface.mutableTextureForTest().get());
 }
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequest) {
-    sp<GraphicBuffer> buffer = new GraphicBuffer();
-    mSurface.mutableGraphicBufferForTest() = buffer;
+    const auto buffer = std::make_shared<renderengine::ExternalTexture>(new GraphicBuffer(),
+                                                                        mRenderEngine, false);
+    mSurface.mutableTextureForTest() = buffer;
 
     impl::OutputCompositionState state;
     state.usesClientComposition = false;
     state.flipClientTarget = true;
 
     EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
-    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getBuffer()->getNativeBuffer(), -1))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
     mSurface.queueBuffer(base::unique_fd());
 
-    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+    EXPECT_EQ(nullptr, mSurface.mutableTextureForTest().get());
 }
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequestWithNoBufferYetDequeued) {
@@ -317,27 +324,28 @@
 
     mSurface.queueBuffer(base::unique_fd());
 
-    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+    EXPECT_EQ(nullptr, mSurface.mutableTextureForTest().get());
 }
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesNativeWindowQueueBufferFailureOnVirtualDisplay) {
-    sp<GraphicBuffer> buffer = new GraphicBuffer();
-    mSurface.mutableGraphicBufferForTest() = buffer;
+    const auto buffer = std::make_shared<renderengine::ExternalTexture>(new GraphicBuffer(),
+                                                                        mRenderEngine, false);
+    mSurface.mutableTextureForTest() = buffer;
 
     impl::OutputCompositionState state;
     state.usesClientComposition = true;
 
     EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
-    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getBuffer()->getNativeBuffer(), -1))
             .WillOnce(Return(INVALID_OPERATION));
     EXPECT_CALL(mDisplay, isVirtual()).WillOnce(Return(true));
-    EXPECT_CALL(*mNativeWindow, cancelBuffer(buffer->getNativeBuffer(), -1))
+    EXPECT_CALL(*mNativeWindow, cancelBuffer(buffer->getBuffer()->getNativeBuffer(), -1))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
     mSurface.queueBuffer(base::unique_fd());
 
-    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+    EXPECT_EQ(nullptr, mSurface.mutableTextureForTest().get());
 }
 
 /*
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 377f817..283c692 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/planner/CachedSet.h>
 #include <compositionengine/impl/planner/LayerState.h>
 #include <compositionengine/mock/LayerFE.h>
@@ -59,6 +60,7 @@
 
     static constexpr size_t kNumLayers = 5;
     std::vector<std::unique_ptr<TestLayer>> mTestLayers;
+    impl::OutputCompositionState mOutputState;
 
     android::renderengine::mock::RenderEngine mRenderEngine;
 };
@@ -68,6 +70,8 @@
         auto testLayer = std::make_unique<TestLayer>();
         auto pos = static_cast<int32_t>(i);
         testLayer->outputLayerCompositionState.displayFrame = Rect(pos, pos, pos + 1, pos + 1);
+        testLayer->outputLayerCompositionState.visibleRegion =
+                Region(Rect(pos + 1, pos + 1, pos + 2, pos + 2));
 
         testLayer->layerFE = sp<mock::LayerFE>::make();
 
@@ -87,6 +91,12 @@
                 std::make_unique<CachedSet::Layer>(testLayer->layerState.get(), kStartTime);
 
         mTestLayers.emplace_back(std::move(testLayer));
+
+        // set up minimium params needed for rendering
+        mOutputState.dataspace = ui::Dataspace::SRGB;
+        mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5));
+        mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+        mOutputState.layerStackSpace = ProjectionSpace(ui::Size(20, 10), Rect(5, 10));
     }
 }
 
@@ -98,6 +108,7 @@
     EXPECT_EQ(layer.getHash(), cachedSet.getFingerprint());
     EXPECT_EQ(layer.getLastUpdate(), cachedSet.getLastUpdate());
     EXPECT_EQ(layer.getDisplayFrame(), cachedSet.getBounds());
+    EXPECT_TRUE(layer.getVisibleRegion().hasSameRects(cachedSet.getVisibleRegion()));
     EXPECT_EQ(1u, cachedSet.getLayerCount());
     EXPECT_EQ(layer.getState(), cachedSet.getFirstLayer().getState());
     EXPECT_EQ(0u, cachedSet.getAge());
@@ -146,6 +157,10 @@
     EXPECT_EQ(layer1.getHash(), cachedSet.getFingerprint());
     EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
     EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getBounds());
+    Region expectedRegion;
+    expectedRegion.orSelf(Rect(1, 1, 2, 2));
+    expectedRegion.orSelf(Rect(2, 2, 3, 3));
+    EXPECT_TRUE(cachedSet.getVisibleRegion().hasSameRects(expectedRegion));
     EXPECT_EQ(2u, cachedSet.getLayerCount());
     EXPECT_EQ(0u, cachedSet.getAge());
     expectNoBuffer(cachedSet);
@@ -201,13 +216,7 @@
     cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
     cachedSet.addLayer(layer3.getState(), kStartTime + 20ms);
 
-    std::vector<const LayerState*> incomingLayers = {
-            layer1.getState(),
-            layer2.getState(),
-            layer3.getState(),
-    };
-
-    EXPECT_FALSE(cachedSet.hasBufferUpdate(incomingLayers.begin()));
+    EXPECT_FALSE(cachedSet.hasBufferUpdate());
 }
 
 TEST_F(CachedSetTest, hasBufferUpdate_BufferUpdate) {
@@ -221,13 +230,7 @@
 
     mTestLayers[1]->layerState->resetFramesSinceBufferUpdate();
 
-    std::vector<const LayerState*> incomingLayers = {
-            layer1.getState(),
-            layer2.getState(),
-            layer3.getState(),
-    };
-
-    EXPECT_TRUE(cachedSet.hasBufferUpdate(incomingLayers.begin()));
+    EXPECT_TRUE(cachedSet.hasBufferUpdate());
 }
 
 TEST_F(CachedSetTest, append) {
@@ -243,6 +246,11 @@
     EXPECT_EQ(layer1.getHash(), cachedSet1.getFingerprint());
     EXPECT_EQ(kStartTime, cachedSet1.getLastUpdate());
     EXPECT_EQ(Rect(0, 0, 3, 3), cachedSet1.getBounds());
+    Region expectedRegion;
+    expectedRegion.orSelf(Rect(1, 1, 2, 2));
+    expectedRegion.orSelf(Rect(2, 2, 3, 3));
+    expectedRegion.orSelf(Rect(3, 3, 4, 4));
+    EXPECT_TRUE(cachedSet1.getVisibleRegion().hasSameRects(expectedRegion));
     EXPECT_EQ(3u, cachedSet1.getLayerCount());
     EXPECT_EQ(0u, cachedSet1.getAge());
     expectNoBuffer(cachedSet1);
@@ -297,10 +305,12 @@
 
     const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
                                 const std::vector<const renderengine::LayerSettings*>& layers,
-                                const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
-                                base::unique_fd*) -> size_t {
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&, base::unique_fd*) -> size_t {
         EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.physicalDisplay);
-        EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.clip);
+        EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
+        EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+                  displaySettings.orientation);
         EXPECT_EQ(0.5f, layers[0]->alpha);
         EXPECT_EQ(0.75f, layers[1]->alpha);
         EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
@@ -311,12 +321,15 @@
     EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
     EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
-    EXPECT_CALL(mRenderEngine, cacheExternalTextureBuffer(_));
-    cachedSet.render(mRenderEngine, ui::Dataspace::SRGB);
+    cachedSet.render(mRenderEngine, mOutputState);
     expectReadyBuffer(cachedSet);
 
+    EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getOutputSpace().content);
+    EXPECT_EQ(Rect(mOutputState.framebufferSpace.bounds.getWidth(),
+                   mOutputState.framebufferSpace.bounds.getHeight()),
+              cachedSet.getOutputSpace().bounds);
+
     // Now check that appending a new cached set properly cleans up RenderEngine resources.
-    EXPECT_CALL(mRenderEngine, unbindExternalTextureBuffer(_));
     CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
     cachedSet.append(CachedSet(layer3));
 }
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index bd77559..c528087 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/planner/CachedSet.h>
 #include <compositionengine/impl/planner/Flattener.h>
 #include <compositionengine/impl/planner/LayerState.h>
@@ -76,6 +77,7 @@
 
     static constexpr size_t kNumLayers = 5;
     std::vector<std::unique_ptr<TestLayer>> mTestLayers;
+    impl::OutputCompositionState mOutputState;
 };
 
 void FlattenerTest::SetUp() {
@@ -87,6 +89,8 @@
         testLayer->name = ss.str();
 
         testLayer->outputLayerCompositionState.displayFrame = Rect(pos, pos, pos + 1, pos + 1);
+        testLayer->outputLayerCompositionState.visibleRegion =
+                Region(Rect(pos + 1, pos + 1, pos + 2, pos + 2));
 
         testLayer->layerFECompositionState.buffer =
                 new GraphicBuffer(100, 100, HAL_PIXEL_FORMAT_RGBA_8888, 1,
@@ -120,6 +124,11 @@
         testLayer->layerState->incrementFramesSinceBufferUpdate();
 
         mTestLayers.emplace_back(std::move(testLayer));
+
+        // set up minimium params needed for rendering
+        mOutputState.dataspace = ui::Dataspace::SRGB;
+        mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5));
+        mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
     }
 }
 
@@ -134,13 +143,13 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     // same geometry, update the internal layer stack
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 }
 
 void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
@@ -150,7 +159,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -160,7 +169,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     const auto buffer = layers[0]->getOutputLayer()->getState().overrideInfo.buffer;
     EXPECT_NE(nullptr, buffer);
@@ -195,7 +204,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 }
 
 TEST_F(FlattenerTest, flattenLayers_basicFlatten) {
@@ -241,13 +250,87 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
     EXPECT_EQ(overrideBuffer2, overrideBuffer3);
 }
 
+TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsProjectionSpace) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideDisplaySpace =
+            layerState1->getOutputLayer()->getState().overrideInfo.displaySpace;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // make all layers inactive
+    mTime += 200ms;
+    expectAllLayersFlattened(layers);
+
+    EXPECT_EQ(overrideDisplaySpace.bounds,
+              Rect(mOutputState.framebufferSpace.bounds.getWidth(),
+                   mOutputState.framebufferSpace.bounds.getHeight()));
+    EXPECT_EQ(overrideDisplaySpace.content, Rect(0, 0, 2, 2));
+    EXPECT_EQ(overrideDisplaySpace.orientation, mOutputState.framebufferSpace.orientation);
+}
+
+TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsDamageRegions) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideDamageRegion =
+            layerState1->getOutputLayer()->getState().overrideInfo.damageRegion;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // make all layers inactive
+    mTime += 200ms;
+    expectAllLayersFlattened(layers);
+    EXPECT_TRUE(overrideDamageRegion.isRect() &&
+                overrideDamageRegion.bounds() == Rect::INVALID_RECT);
+
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    EXPECT_TRUE(overrideDamageRegion.isRect() && overrideDamageRegion.bounds() == Rect::EMPTY_RECT);
+}
+
+TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsVisibleRegion) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideVisibleRegion =
+            layerState1->getOutputLayer()->getState().overrideInfo.visibleRegion;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // make all layers inactive
+    mTime += 200ms;
+    expectAllLayersFlattened(layers);
+    Region expectedRegion;
+    expectedRegion.orSelf(Rect(1, 1, 2, 2));
+    expectedRegion.orSelf(Rect(2, 2, 3, 3));
+    EXPECT_TRUE(overrideVisibleRegion.hasSameRects(expectedRegion));
+}
+
 TEST_F(FlattenerTest, flattenLayers_addLayerToFlattenedCauseReset) {
     auto& layerState1 = mTestLayers[0]->layerState;
     const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
@@ -276,7 +359,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -313,7 +396,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -322,7 +405,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_NE(nullptr, overrideBuffer2);
@@ -335,7 +418,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_NE(nullptr, overrideBuffer2);
@@ -344,7 +427,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -386,7 +469,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -399,7 +482,8 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -411,7 +495,8 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mOutputState.framebufferSpace.orientation = ui::ROTATION_180;
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -426,7 +511,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -437,7 +522,8 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+    mOutputState.framebufferSpace.orientation = ui::ROTATION_270;
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index 8f235ab..83cc19b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -24,6 +24,9 @@
 #include <gtest/gtest.h>
 #include <log/log.h>
 
+#include "android/hardware_buffer.h"
+#include "compositionengine/LayerFECompositionState.h"
+
 namespace android::compositionengine::impl::planner {
 namespace {
 
@@ -48,7 +51,15 @@
 native_handle_t* const sFakeSidebandStreamOne = reinterpret_cast<native_handle_t*>(10);
 native_handle_t* const sFakeSidebandStreamTwo = reinterpret_cast<native_handle_t*>(11);
 const half4 sHalf4One = half4(0.2f, 0.3f, 0.4f, 0.5f);
-const half4 sHalf4Two = half4(0.5f, 0.4f, 0.43, 0.2f);
+const half4 sHalf4Two = half4(0.5f, 0.4f, 0.3f, 0.2f);
+const std::string sMetadataKeyOne = std::string("Meta!");
+const std::string sMetadataKeyTwo = std::string("Data!");
+const GenericLayerMetadataEntry sMetadataValueOne = GenericLayerMetadataEntry{
+        .value = std::vector<uint8_t>({1, 2}),
+};
+const GenericLayerMetadataEntry sMetadataValueTwo = GenericLayerMetadataEntry{
+        .value = std::vector<uint8_t>({1, 3}),
+};
 
 struct LayerStateTest : public testing::Test {
     LayerStateTest() {
@@ -75,6 +86,21 @@
         EXPECT_CALL(layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
     }
 
+    void verifyUniqueDifferingFields(const LayerState& lhs, const LayerState& rhs) {
+        EXPECT_EQ(lhs.getHash(), rhs.getHash());
+
+        EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None), lhs.getDifferingFields(rhs));
+        EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None), rhs.getDifferingFields(lhs));
+    }
+
+    void verifyNonUniqueDifferingFields(const LayerState& lhs, const LayerState& rhs,
+                                        Flags<LayerStateField> fields) {
+        EXPECT_NE(lhs.getHash(), rhs.getHash());
+
+        EXPECT_EQ(fields, lhs.getDifferingFields(rhs));
+        EXPECT_EQ(fields, rhs.getDifferingFields(lhs));
+    }
+
     mock::LayerFE mLayerFE;
     mock::OutputLayer mOutputLayer;
     std::unique_ptr<LayerState> mLayerState;
@@ -129,21 +155,7 @@
     EXPECT_NE(mLayerState->getId(), otherLayerState->getId());
 
     // Id is a unique field, so it's not computed in the hash for a layer state.
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::Id),
-              otherLayerState->getHash(LayerStateField::Id));
-
-    // Similarly, Id cannot be included in differing fields.
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Id));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Id));
-
+    verifyUniqueDifferingFields(*mLayerState, *otherLayerState);
     EXPECT_FALSE(mLayerState->compare(*otherLayerState));
     EXPECT_FALSE(otherLayerState->compare(*mLayerState));
 }
@@ -188,21 +200,7 @@
     EXPECT_NE(mLayerState->getName(), otherLayerState->getName());
 
     // Name is a unique field, so it's not computed in the hash for a layer state.
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::Name),
-              otherLayerState->getHash(LayerStateField::Name));
-
-    // Similarly, Name cannot be included in differing fields.
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Name));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Name));
-
+    verifyUniqueDifferingFields(*mLayerState, *otherLayerState);
     EXPECT_FALSE(mLayerState->compare(*otherLayerState));
     EXPECT_FALSE(otherLayerState->compare(*mLayerState));
 }
@@ -253,20 +251,7 @@
 
     EXPECT_NE(mLayerState->getDisplayFrame(), otherLayerState->getDisplayFrame());
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::DisplayFrame),
-              otherLayerState->getHash(LayerStateField::DisplayFrame));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::DisplayFrame),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::DisplayFrame));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::DisplayFrame),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::DisplayFrame));
-
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::DisplayFrame);
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
 }
@@ -337,34 +322,12 @@
 
     EXPECT_NE(mLayerState->getCompositionType(), otherLayerState->getCompositionType());
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::CompositionType),
-              otherLayerState->getHash(LayerStateField::CompositionType));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::CompositionType));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::CompositionType));
-
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState,
+                                   LayerStateField::CompositionType);
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
 }
 
-TEST_F(LayerStateTest, getBuffer) {
-    OutputLayerCompositionState outputLayerCompositionState;
-    LayerFECompositionState layerFECompositionState;
-    layerFECompositionState.buffer = new GraphicBuffer();
-    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
-                       layerFECompositionState);
-    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
-    EXPECT_EQ(layerFECompositionState.buffer, mLayerState->getBuffer());
-}
-
 TEST_F(LayerStateTest, updateBuffer) {
     OutputLayerCompositionState outputLayerCompositionState;
     LayerFECompositionState layerFECompositionState;
@@ -380,7 +343,6 @@
     setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
                        layerFECompositionStateTwo);
     Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
-    EXPECT_EQ(layerFECompositionStateTwo.buffer, mLayerState->getBuffer());
     EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer), updates);
 }
 
@@ -399,21 +361,8 @@
                        layerFECompositionStateTwo);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getBuffer(), otherLayerState->getBuffer());
-
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::Buffer),
-              otherLayerState->getHash(LayerStateField::Buffer));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Buffer));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Buffer));
+    // A buffer is not a unique field, but the assertions are the same.
+    verifyUniqueDifferingFields(*mLayerState, *otherLayerState);
 
     // Buffers are explicitly excluded from comparison
     EXPECT_FALSE(mLayerState->compare(*otherLayerState));
@@ -453,19 +402,7 @@
                        layerFECompositionState);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::SourceCrop),
-              otherLayerState->getHash(LayerStateField::SourceCrop));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SourceCrop),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SourceCrop));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SourceCrop),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::SourceCrop));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::SourceCrop);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -504,19 +441,7 @@
                        layerFECompositionState);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::ZOrder),
-              otherLayerState->getHash(LayerStateField::ZOrder));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::ZOrder));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::ZOrder));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::ZOrder);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -555,19 +480,8 @@
                        layerFECompositionState);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::BufferTransform),
-              otherLayerState->getHash(LayerStateField::BufferTransform));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BufferTransform),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::BufferTransform));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BufferTransform),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::BufferTransform));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState,
+                                   LayerStateField::BufferTransform);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -606,19 +520,7 @@
                        layerFECompositionStateTwo);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::BlendMode),
-              otherLayerState->getHash(LayerStateField::BlendMode));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlendMode),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::BlendMode));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlendMode),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::BlendMode));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::BlendMode);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -657,24 +559,61 @@
                        layerFECompositionStateTwo);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::Alpha),
-              otherLayerState->getHash(LayerStateField::Alpha));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Alpha),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Alpha));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Alpha),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Alpha));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::Alpha);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
 }
 
+TEST_F(LayerStateTest, updateLayerMetadata) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.metadata[sMetadataKeyOne] = sMetadataValueOne;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.metadata[sMetadataKeyTwo] = sMetadataValueTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::LayerMetadata), updates);
+}
+
+TEST_F(LayerStateTest, compareLayerMetadata) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.metadata[sMetadataKeyOne] = sMetadataValueOne;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.metadata[sMetadataKeyTwo] = sMetadataValueTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::LayerMetadata);
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getVisibleRegion) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.visibleRegion = sRegionOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_TRUE(mLayerState->getVisibleRegion().hasSameRects(sRegionOne));
+}
+
 TEST_F(LayerStateTest, updateVisibleRegion) {
     OutputLayerCompositionState outputLayerCompositionState;
     outputLayerCompositionState.visibleRegion = sRegionOne;
@@ -708,19 +647,7 @@
                        layerFECompositionState);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::VisibleRegion),
-              otherLayerState->getHash(LayerStateField::VisibleRegion));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::VisibleRegion),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::VisibleRegion));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::VisibleRegion),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::VisibleRegion));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::VisibleRegion);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -759,19 +686,65 @@
                        layerFECompositionState);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::Dataspace),
-              otherLayerState->getHash(LayerStateField::Dataspace));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::Dataspace);
 
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Dataspace),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Dataspace));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Dataspace),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Dataspace));
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updatePixelFormat) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.buffer =
+            new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888,
+                              AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+                                      AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+                              "buffer1");
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.buffer =
+            new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBX_8888,
+                              AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+                                      AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+                              "buffer2");
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer) |
+                      Flags<LayerStateField>(LayerStateField::PixelFormat),
+              updates);
+}
+
+TEST_F(LayerStateTest, comparePixelFormat) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.buffer =
+            new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888,
+                              AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+                                      AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+                              "buffer1");
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.buffer =
+            new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBX_8888,
+                              AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+                                      AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+                              "buffer2");
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState,
+                                   Flags<LayerStateField>(LayerStateField::PixelFormat));
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -814,19 +787,7 @@
                        layerFECompositionStateTwo);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::ColorTransform),
-              otherLayerState->getHash(LayerStateField::ColorTransform));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ColorTransform),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::ColorTransform));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ColorTransform),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::ColorTransform));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::ColorTransform);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -865,19 +826,7 @@
                        layerFECompositionStateTwo);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::SidebandStream),
-              otherLayerState->getHash(LayerStateField::SidebandStream));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SidebandStream),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SidebandStream));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SidebandStream),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::SidebandStream));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::SidebandStream);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
@@ -916,19 +865,7 @@
                        layerFECompositionStateTwo);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
 
-    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
-              otherLayerState->getHash(LayerStateField::None));
-    EXPECT_EQ(mLayerState->getHash(LayerStateField::SolidColor),
-              otherLayerState->getHash(LayerStateField::SolidColor));
-
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SolidColor),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SolidColor));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SolidColor),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
-              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::SolidColor));
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::SolidColor);
 
     EXPECT_TRUE(mLayerState->compare(*otherLayerState));
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index b7b2cc6..8692ee6 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -70,11 +70,13 @@
         mIsPrimary(args.isPrimary) {
     mCompositionDisplay->editState().isSecure = args.isSecure;
     mCompositionDisplay->createRenderSurface(
-            compositionengine::RenderSurfaceCreationArgs{ANativeWindow_getWidth(
-                                                                 args.nativeWindow.get()),
-                                                         ANativeWindow_getHeight(
-                                                                 args.nativeWindow.get()),
-                                                         args.nativeWindow, args.displaySurface});
+            compositionengine::
+                    RenderSurfaceCreationArgs{ANativeWindow_getWidth(args.nativeWindow.get()),
+                                              ANativeWindow_getHeight(args.nativeWindow.get()),
+                                              args.nativeWindow, args.displaySurface,
+                                              static_cast<size_t>(
+                                                      SurfaceFlinger::
+                                                              maxFrameBufferAcquiredBuffers)});
 
     if (!mFlinger->mDisableClientCompositionCache &&
         SurfaceFlinger::maxFrameBufferAcquiredBuffers > 0) {
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 1bf43da..fd70988 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -20,11 +20,7 @@
 
 #undef LOG_TAG
 #define LOG_TAG "HwcComposer"
-
-#include <log/log.h>
-
-#include <algorithm>
-#include <cinttypes>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "ComposerHal.h"
 
@@ -32,6 +28,11 @@
 #include <gui/BufferQueue.h>
 #include <hidl/HidlTransportSupport.h>
 #include <hidl/HidlTransportUtils.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <cinttypes>
 
 namespace android {
 
@@ -492,6 +493,7 @@
 
 Error Composer::presentDisplay(Display display, int* outPresentFence)
 {
+    ATRACE_NAME("HwcPresentDisplay");
     mWriter.selectDisplay(display);
     mWriter.presentDisplay();
 
@@ -586,6 +588,7 @@
 Error Composer::validateDisplay(Display display, uint32_t* outNumTypes,
         uint32_t* outNumRequests)
 {
+    ATRACE_NAME("HwcValidateDisplay");
     mWriter.selectDisplay(display);
     mWriter.validateDisplay();
 
@@ -601,13 +604,14 @@
 
 Error Composer::presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
                                uint32_t* outNumRequests, int* outPresentFence, uint32_t* state) {
-   mWriter.selectDisplay(display);
-   mWriter.presentOrvalidateDisplay();
+    ATRACE_NAME("HwcPresentOrValidateDisplay");
+    mWriter.selectDisplay(display);
+    mWriter.presentOrvalidateDisplay();
 
-   Error error = execute();
-   if (error != Error::NONE) {
-       return error;
-   }
+    Error error = execute();
+    if (error != Error::NONE) {
+        return error;
+    }
 
    mReader.takePresentOrValidateStage(display, state);
 
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index f7fc162..8d685cf 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -76,14 +76,14 @@
     mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_FB |
                                        GRALLOC_USAGE_HW_RENDER |
                                        GRALLOC_USAGE_HW_COMPOSER);
-    const auto limitedSize = limitFramebufferSize(size);
+    const auto limitedSize = limitSize(size);
     mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
     mConsumer->setMaxAcquiredBufferCount(
             SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1);
 }
 
 void FramebufferSurface::resizeBuffers(const ui::Size& newSize) {
-    const auto limitedSize = limitFramebufferSize(newSize);
+    const auto limitedSize = limitSize(newSize);
     mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
 }
 
@@ -179,19 +179,23 @@
     }
 }
 
-ui::Size FramebufferSurface::limitFramebufferSize(const ui::Size& size) {
+ui::Size FramebufferSurface::limitSize(const ui::Size& size) {
+    return limitSizeInternal(size, mMaxSize);
+}
+
+ui::Size FramebufferSurface::limitSizeInternal(const ui::Size& size, const ui::Size& maxSize) {
     ui::Size limitedSize = size;
     bool wasLimited = false;
-    if (size.width > mMaxSize.width && mMaxSize.width != 0) {
+    if (size.width > maxSize.width && maxSize.width != 0) {
         const float aspectRatio = static_cast<float>(size.width) / size.height;
-        limitedSize.height = mMaxSize.width / aspectRatio;
-        limitedSize.width = mMaxSize.width;
+        limitedSize.height = maxSize.width / aspectRatio;
+        limitedSize.width = maxSize.width;
         wasLimited = true;
     }
-    if (size.height > mMaxSize.height && mMaxSize.height != 0) {
+    if (limitedSize.height > maxSize.height && maxSize.height != 0) {
         const float aspectRatio = static_cast<float>(size.width) / size.height;
-        limitedSize.height = mMaxSize.height;
-        limitedSize.width = mMaxSize.height * aspectRatio;
+        limitedSize.height = maxSize.height;
+        limitedSize.width = maxSize.height * aspectRatio;
         wasLimited = true;
     }
     ALOGI_IF(wasLimited, "framebuffer size has been limited to [%dx%d] from [%dx%d]",
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 5d1e131..3123351 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -55,15 +55,20 @@
     virtual const sp<Fence>& getClientTargetAcquireFence() const override;
 
 private:
+    friend class FramebufferSurfaceTest;
+
+    // Limits the width and height by the maximum width specified.
+    ui::Size limitSize(const ui::Size&);
+
+    // Used for testing purposes.
+    static ui::Size limitSizeInternal(const ui::Size&, const ui::Size& maxSize);
+
     virtual ~FramebufferSurface() { }; // this class cannot be overloaded
 
     virtual void freeBufferLocked(int slotIndex);
 
     virtual void dumpLocked(String8& result, const char* prefix) const;
 
-    // Limits the width and height by the maximum width specified in the constructor.
-    ui::Size limitFramebufferSize(const ui::Size&);
-
     // nextBuffer waits for and then latches the next buffer from the
     // BufferQueue and releases the previously latched buffer to the
     // BufferQueue.  The new buffer is returned in the 'buffer' argument.
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index ccfaa76..b73d032 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -322,8 +322,11 @@
 }
 
 bool HWComposer::isConnected(PhysicalDisplayId displayId) const {
-    RETURN_IF_INVALID_DISPLAY(displayId, false);
-    return mDisplayData.at(displayId).hwcDisplay->isConnected();
+    if (mDisplayData.count(displayId)) {
+        return mDisplayData.at(displayId).hwcDisplay->isConnected();
+    }
+
+    return false;
 }
 
 std::vector<HWComposer::HWCDisplayMode> HWComposer::getModes(PhysicalDisplayId displayId) const {
@@ -964,8 +967,10 @@
     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));
+        if (error != hardware::graphics::composer::V2_4::Error::UNSUPPORTED) {
+            ALOGE("%s: %s failed: %s (%d)", __FUNCTION__, "getLayerGenericMetadataKeys",
+                  toString(error).c_str(), static_cast<int32_t>(error));
+        }
         return;
     }
 
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 2784861..7468ac3 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -575,13 +575,9 @@
             }
         } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
             // Finish late, Present late
-            if (displayFrameJankType == JankType::None) {
-                // Display frame is not janky, so purely app's fault
-                mJankType |= JankType::AppDeadlineMissed;
-            } else {
-                // Propagate DisplayFrame's jankType if it is janky
-                mJankType |= displayFrameJankType;
-            }
+            mJankType |= JankType::AppDeadlineMissed;
+            // Propagate DisplayFrame's jankType if it is janky
+            mJankType |= displayFrameJankType;
         }
     }
 }
@@ -756,14 +752,13 @@
 }
 
 FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
-                             JankClassificationThresholds thresholds, nsecs_t hwcDuration)
+                             JankClassificationThresholds thresholds)
       : mMaxDisplayFrames(kDefaultMaxDisplayFrames),
         mTimeStats(std::move(timeStats)),
         mSurfaceFlingerPid(surfaceFlingerPid),
-        mJankClassificationThresholds(thresholds),
-        mHwcDuration(hwcDuration) {
-    mCurrentDisplayFrame = std::make_shared<DisplayFrame>(mTimeStats, thresholds, hwcDuration,
-                                                          &mTraceCookieCounter);
+        mJankClassificationThresholds(thresholds) {
+    mCurrentDisplayFrame =
+            std::make_shared<DisplayFrame>(mTimeStats, thresholds, &mTraceCookieCounter);
 }
 
 void FrameTimeline::onBootFinished() {
@@ -808,13 +803,11 @@
 
 FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats,
                                           JankClassificationThresholds thresholds,
-                                          nsecs_t hwcDuration,
                                           TraceCookieCounter* traceCookieCounter)
       : mSurfaceFlingerPredictions(TimelineItem()),
         mSurfaceFlingerActuals(TimelineItem()),
         mTimeStats(timeStats),
         mJankClassificationThresholds(thresholds),
-        mHwcDuration(hwcDuration),
         mTraceCookieCounter(*traceCookieCounter) {
     mSurfaceFrames.reserve(kNumSurfaceFramesInitial);
 }
@@ -834,13 +827,11 @@
 
 void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
                                  const std::shared_ptr<FenceTime>& presentFence,
-                                 bool gpuComposition) {
+                                 const std::shared_ptr<FenceTime>& gpuFence) {
     ATRACE_CALL();
     std::scoped_lock lock(mMutex);
     mCurrentDisplayFrame->setActualEndTime(sfPresentTime);
-    if (gpuComposition) {
-        mCurrentDisplayFrame->setGpuComposition();
-    }
+    mCurrentDisplayFrame->setGpuFence(gpuFence);
     mPendingPresentFences.emplace_back(std::make_pair(presentFence, mCurrentDisplayFrame));
     flushPendingPresentFences();
     finalizeCurrentDisplayFrame();
@@ -878,25 +869,32 @@
     mSurfaceFlingerActuals.endTime = actualEndTime;
 }
 
-void FrameTimeline::DisplayFrame::setGpuComposition() {
-    mGpuComposition = true;
+void FrameTimeline::DisplayFrame::setGpuFence(const std::shared_ptr<FenceTime>& gpuFence) {
+    mGpuFence = gpuFence;
 }
 
 void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync) {
     if (mPredictionState == PredictionState::Expired ||
         mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
-        // Cannot do jank classification with expired predictions or invalid signal times.
+        // Cannot do jank classification with expired predictions or invalid signal times. Set the
+        // deltas to 0 as both negative and positive deltas are used as real values.
         mJankType = JankType::Unknown;
-        deadlineDelta = -1;
-        deltaToVsync = -1;
+        deadlineDelta = 0;
+        deltaToVsync = 0;
         return;
     }
 
     // Delta between the expected present and the actual present
     const nsecs_t presentDelta =
             mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime;
-    deadlineDelta =
-            mSurfaceFlingerActuals.endTime - (mSurfaceFlingerPredictions.endTime - mHwcDuration);
+    // Sf actual end time represents the CPU end time. In case of HWC, SF's end time would have
+    // included the time for composition. However, for GPU composition, the final end time is max(sf
+    // end time, gpu fence time).
+    nsecs_t combinedEndTime = mSurfaceFlingerActuals.endTime;
+    if (mGpuFence != FenceTime::NO_FENCE) {
+        combinedEndTime = std::max(combinedEndTime, mGpuFence->getSignalTime());
+    }
+    deadlineDelta = combinedEndTime - mSurfaceFlingerPredictions.endTime;
 
     // How far off was the presentDelta when compared to the vsyncPeriod. Used in checking if there
     // was a prediction error or not.
@@ -911,9 +909,7 @@
         mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
     }
 
-    if (mSurfaceFlingerActuals.endTime > mSurfaceFlingerPredictions.endTime - mHwcDuration) {
-        // SF needs to have finished at least mHwcDuration ahead of the deadline for it to be
-        // on time.
+    if (combinedEndTime > mSurfaceFlingerPredictions.endTime) {
         mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
     } else {
         mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
@@ -970,7 +966,15 @@
                     mJankType = JankType::SurfaceFlingerScheduling;
                 } else {
                     // OnTime start, Finish late, Present late
-                    mJankType = JankType::SurfaceFlingerCpuDeadlineMissed;
+                    if (mGpuFence != FenceTime::NO_FENCE &&
+                        mSurfaceFlingerActuals.endTime - mSurfaceFlingerActuals.startTime <
+                                mRefreshRate.getPeriodNsecs()) {
+                        // If SF was in GPU composition and the CPU work finished before the vsync
+                        // period, classify it as GPU deadline missed.
+                        mJankType = JankType::SurfaceFlingerGpuDeadlineMissed;
+                    } else {
+                        mJankType = JankType::SurfaceFlingerCpuDeadlineMissed;
+                    }
                 }
             } else {
                 // Finish time unknown
@@ -1045,7 +1049,7 @@
         actualDisplayFrameStartEvent->set_present_type(toProto(mFramePresentMetadata));
         actualDisplayFrameStartEvent->set_on_time_finish(mFrameReadyMetadata ==
                                                          FrameReadyMetadata::OnTimeFinish);
-        actualDisplayFrameStartEvent->set_gpu_composition(mGpuComposition);
+        actualDisplayFrameStartEvent->set_gpu_composition(mGpuFence != FenceTime::NO_FENCE);
         actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
         actualDisplayFrameStartEvent->set_prediction_type(toProto(mPredictionState));
     });
@@ -1168,7 +1172,7 @@
     mDisplayFrames.push_back(mCurrentDisplayFrame);
     mCurrentDisplayFrame.reset();
     mCurrentDisplayFrame = std::make_shared<DisplayFrame>(mTimeStats, mJankClassificationThresholds,
-                                                          mHwcDuration, &mTraceCookieCounter);
+                                                          &mTraceCookieCounter);
 }
 
 nsecs_t FrameTimeline::DisplayFrame::getBaseTime() const {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 3f04592..41f4978 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -293,11 +293,12 @@
     // the token and sets the actualSfWakeTime for the current DisplayFrame.
     virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) = 0;
 
-    // Sets the sfPresentTime, gpuComposition and finalizes the current DisplayFrame. Tracks the
+    // Sets the sfPresentTime and finalizes the current DisplayFrame. Tracks the
     // given present fence until it's signaled, and updates the present timestamps of all presented
-    // SurfaceFrames in that vsync.
+    // SurfaceFrames in that vsync. If a gpuFence was also provided, its tracked in the
+    // corresponding DisplayFrame.
     virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
-                              bool gpuComposition) = 0;
+                              const std::shared_ptr<FenceTime>& gpuFence) = 0;
 
     // Args:
     // -jank : Dumps only the Display Frames that are either janky themselves
@@ -355,7 +356,7 @@
     class DisplayFrame {
     public:
         DisplayFrame(std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
-                     nsecs_t hwcDuration, TraceCookieCounter* traceCookieCounter);
+                     TraceCookieCounter* traceCookieCounter);
         virtual ~DisplayFrame() = default;
         // Dumpsys interface - dumps only if the DisplayFrame itself is janky or is at least one
         // SurfaceFrame is janky.
@@ -376,7 +377,7 @@
         void setPredictions(PredictionState predictionState, TimelineItem predictions);
         void setActualStartTime(nsecs_t actualStartTime);
         void setActualEndTime(nsecs_t actualEndTime);
-        void setGpuComposition();
+        void setGpuFence(const std::shared_ptr<FenceTime>& gpuFence);
 
         // BaseTime is the smallest timestamp in a DisplayFrame.
         // Used for dumping all timestamps relative to the oldest, making it easy to read.
@@ -410,7 +411,6 @@
         TimelineItem mSurfaceFlingerActuals;
         std::shared_ptr<TimeStats> mTimeStats;
         const JankClassificationThresholds mJankClassificationThresholds;
-        const nsecs_t mHwcDuration;
 
         // Collection of predictions and actual values sent over by Layers
         std::vector<std::shared_ptr<SurfaceFrame>> mSurfaceFrames;
@@ -418,8 +418,8 @@
         PredictionState mPredictionState = PredictionState::None;
         // Bitmask for the type of jank
         int32_t mJankType = JankType::None;
-        // Indicates if this frame was composited by the GPU or not
-        bool mGpuComposition = false;
+        // A valid gpu fence indicates that the DisplayFrame was composited by the GPU
+        std::shared_ptr<FenceTime> mGpuFence = FenceTime::NO_FENCE;
         // Enum for the type of present
         FramePresentMetadata mFramePresentMetadata = FramePresentMetadata::UnknownPresent;
         // Enum for the type of finish
@@ -436,8 +436,7 @@
     };
 
     FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
-                  JankClassificationThresholds thresholds = {},
-                  nsecs_t hwcDuration = kDefaultHwcDuration);
+                  JankClassificationThresholds thresholds = {});
     ~FrameTimeline() = default;
 
     frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
@@ -447,7 +446,7 @@
     void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
     void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override;
     void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
-                      bool gpuComposition = false) override;
+                      const std::shared_ptr<FenceTime>& gpuFence = FenceTime::NO_FENCE) override;
     void parseArgs(const Vector<String16>& args, std::string& result) override;
     void setMaxDisplayFrames(uint32_t size) override;
     float computeFps(const std::unordered_set<int32_t>& layerIds) override;
@@ -482,11 +481,6 @@
     std::shared_ptr<TimeStats> mTimeStats;
     const pid_t mSurfaceFlingerPid;
     const JankClassificationThresholds mJankClassificationThresholds;
-    // In SF Predictions, both end & present are the same. The predictions consider the time used by
-    // composer as well, but we have no way to estimate how much time the composer needs. We are
-    // assuming an arbitrary time for the composer work.
-    const nsecs_t mHwcDuration;
-    static constexpr nsecs_t kDefaultHwcDuration = std::chrono::nanoseconds(3ms).count();
     static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
     // The initial container size for the vector<SurfaceFrames> inside display frame. Although
     // this number doesn't represent any bounds on the number of surface frames that can go in a
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index 8ad805b..178c531 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -62,10 +62,9 @@
     mFrameRecords[mOffset].actualPresentTime = presentTime;
 }
 
-void FrameTracker::setActualPresentFence(
-        std::shared_ptr<FenceTime>&& readyFence) {
+void FrameTracker::setActualPresentFence(const std::shared_ptr<FenceTime>& readyFence) {
     Mutex::Autolock lock(mMutex);
-    mFrameRecords[mOffset].actualPresentFence = std::move(readyFence);
+    mFrameRecords[mOffset].actualPresentFence = readyFence;
     mNumFences++;
 }
 
diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h
index 35382be..bc412ae 100644
--- a/services/surfaceflinger/FrameTracker.h
+++ b/services/surfaceflinger/FrameTracker.h
@@ -66,7 +66,7 @@
 
     // setActualPresentFence sets the fence that is used to get the time
     // at which the current frame became visible to the user.
-    void setActualPresentFence(std::shared_ptr<FenceTime>&& fence);
+    void setActualPresentFence(const std::shared_ptr<FenceTime>& fence);
 
     // setDisplayRefreshPeriod sets the display refresh period in nanoseconds.
     // This is used to compute frame presentation duration statistics relative
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index cd3e8ad..cf215ad 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -116,7 +116,8 @@
     mCurrentState.bufferTransform = 0;
     mCurrentState.transformToDisplayInverse = false;
     mCurrentState.crop.makeInvalid();
-    mCurrentState.acquireFence = new Fence(-1);
+    mCurrentState.acquireFence = sp<Fence>::make(-1);
+    mCurrentState.acquireFenceTime = std::make_shared<FenceTime>(mCurrentState.acquireFence);
     mCurrentState.dataspace = ui::Dataspace::UNKNOWN;
     mCurrentState.hdrMetadata.validTypes = 0;
     mCurrentState.surfaceDamageRegion = Region::INVALID_REGION;
@@ -201,19 +202,6 @@
  */
 void Layer::onLayerDisplayed(const sp<Fence>& /*releaseFence*/) {}
 
-void Layer::removeRemoteSyncPoints() {
-    for (auto& point : mRemoteSyncPoints) {
-        point->setTransactionApplied();
-    }
-    mRemoteSyncPoints.clear();
-
-    {
-        for (State pendingState : mPendingStates) {
-            pendingState.barrierLayer_legacy = nullptr;
-        }
-    }
-}
-
 void Layer::removeRelativeZ(const std::vector<Layer*>& layersInTree) {
     if (mCurrentState.zOrderRelativeOf == nullptr) {
         return;
@@ -235,21 +223,6 @@
 void Layer::removeFromCurrentState() {
     mRemovedFromCurrentState = true;
 
-    // Since we are no longer reachable from CurrentState SurfaceFlinger
-    // will no longer invoke doTransaction for us, and so we will
-    // never finish applying transactions. We signal the sync point
-    // now so that another layer will not become indefinitely
-    // blocked.
-    removeRemoteSyncPoints();
-
-    {
-    Mutex::Autolock syncLock(mLocalSyncPointMutex);
-    for (auto& point : mLocalSyncPoints) {
-        point->setFrameAvailable();
-    }
-    mLocalSyncPoints.clear();
-    }
-
     mFlinger->markLayerPendingRemovalLocked(this);
 }
 
@@ -774,21 +747,6 @@
     }
 }
 
-bool Layer::addSyncPoint(const std::shared_ptr<SyncPoint>& point) {
-    if (point->getFrameNumber() <= mCurrentFrameNumber) {
-        // Don't bother with a SyncPoint, since we've already latched the
-        // relevant frame
-        return false;
-    }
-    if (isRemovedFromCurrentState()) {
-        return false;
-    }
-
-    Mutex::Autolock lock(mLocalSyncPointMutex);
-    mLocalSyncPoints.push_back(point);
-    return true;
-}
-
 // ----------------------------------------------------------------------------
 // local state
 // ----------------------------------------------------------------------------
@@ -807,132 +765,6 @@
 // transaction
 // ----------------------------------------------------------------------------
 
-void Layer::pushPendingState() {
-    if (!mCurrentState.modified) {
-        return;
-    }
-    ATRACE_CALL();
-
-    // If this transaction is waiting on the receipt of a frame, generate a sync
-    // point and send it to the remote layer.
-    // We don't allow installing sync points after we are removed from the current state
-    // as we won't be able to signal our end.
-    if (mCurrentState.barrierLayer_legacy != nullptr && !isRemovedFromCurrentState()) {
-        sp<Layer> barrierLayer = mCurrentState.barrierLayer_legacy.promote();
-        if (barrierLayer == nullptr) {
-            ALOGE("[%s] Unable to promote barrier Layer.", 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).
-            mCurrentState.barrierLayer_legacy = nullptr;
-        } else {
-            auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.barrierFrameNumber, this,
-                                                         barrierLayer);
-            if (barrierLayer->addSyncPoint(syncPoint)) {
-                std::stringstream ss;
-                ss << "Adding sync point " << mCurrentState.barrierFrameNumber;
-                ATRACE_NAME(ss.str().c_str());
-                mRemoteSyncPoints.push_back(std::move(syncPoint));
-            } else {
-                // We already missed the frame we're supposed to synchronize
-                // on, so go ahead and apply the state update
-                mCurrentState.barrierLayer_legacy = nullptr;
-            }
-        }
-
-        // Wake us up to check if the frame has been received
-        setTransactionFlags(eTransactionNeeded);
-        mFlinger->setTransactionFlags(eTraversalNeeded);
-    }
-    if (mCurrentState.bufferlessSurfaceFramesTX.size() >= State::kStateSurfaceFramesThreshold) {
-        // Ideally, the currentState would only contain one SurfaceFrame per transaction (assuming
-        // each Tx uses a different token). We don't expect the current state to hold a huge amount
-        // of SurfaceFrames. However, in the event it happens, this debug statement will leave a
-        // trail that can help in debugging.
-        ALOGW("Bufferless SurfaceFrames size on current state of layer %s is %" PRIu32 "",
-              mName.c_str(), static_cast<uint32_t>(mCurrentState.bufferlessSurfaceFramesTX.size()));
-    }
-    mPendingStates.push_back(mCurrentState);
-    // Since the current state along with the SurfaceFrames has been pushed into the pendingState,
-    // we no longer need to retain them. If multiple states are pushed and applied together, we have
-    // a merging logic to address the SurfaceFrames at mergeSurfaceFrames().
-    mCurrentState.bufferlessSurfaceFramesTX.clear();
-    ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
-}
-
-void Layer::mergeSurfaceFrames(State& source, State& target) {
-    // No need to merge BufferSurfaceFrame as the target's surfaceFrame, if it exists, will be used
-    // directly. Dropping of source's SurfaceFrame is taken care of at setBuffer().
-    target.bufferlessSurfaceFramesTX.merge(source.bufferlessSurfaceFramesTX);
-    source.bufferlessSurfaceFramesTX.clear();
-}
-
-void Layer::popPendingState(State* stateToCommit) {
-    ATRACE_CALL();
-
-    mergeSurfaceFrames(*stateToCommit, mPendingStates[0]);
-    *stateToCommit = mPendingStates[0];
-    mPendingStates.pop_front();
-    ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
-}
-
-bool Layer::applyPendingStates(State* stateToCommit) {
-    bool stateUpdateAvailable = false;
-    while (!mPendingStates.empty()) {
-        if (mPendingStates[0].barrierLayer_legacy != nullptr) {
-            if (mRemoteSyncPoints.empty()) {
-                // If we don't have a sync point for this, apply it anyway. It
-                // will be visually wrong, but it should keep us from getting
-                // into too much trouble.
-                ALOGV("[%s] No local sync point found", getDebugName());
-                popPendingState(stateToCommit);
-                stateUpdateAvailable = true;
-                continue;
-            }
-
-            if (mRemoteSyncPoints.front()->getFrameNumber() !=
-                mPendingStates[0].barrierFrameNumber) {
-                ALOGE("[%s] Unexpected sync point frame number found", getDebugName());
-
-                // Signal our end of the sync point and then dispose of it
-                mRemoteSyncPoints.front()->setTransactionApplied();
-                mRemoteSyncPoints.pop_front();
-                continue;
-            }
-
-            if (mRemoteSyncPoints.front()->frameIsAvailable()) {
-                ATRACE_NAME("frameIsAvailable");
-                // Apply the state update
-                popPendingState(stateToCommit);
-                stateUpdateAvailable = true;
-
-                // Signal our end of the sync point and then dispose of it
-                mRemoteSyncPoints.front()->setTransactionApplied();
-                mRemoteSyncPoints.pop_front();
-            } else {
-                ATRACE_NAME("!frameIsAvailable");
-                mRemoteSyncPoints.front()->checkTimeoutAndLog();
-                break;
-            }
-        } else {
-            popPendingState(stateToCommit);
-            stateUpdateAvailable = true;
-        }
-    }
-
-    // If we still have pending updates, we need to ensure SurfaceFlinger
-    // will keep calling doTransaction, and so we force a traversal.
-    // However, our pending states won't clear until a frame is available,
-    // and so there is no need to specifically trigger a wakeup.
-    if (!mPendingStates.empty()) {
-        setTransactionFlags(eTransactionNeeded);
-        mFlinger->setTraversalNeeded();
-    }
-
-    mCurrentState.modified = false;
-    return stateUpdateAvailable;
-}
-
 uint32_t Layer::doTransactionResize(uint32_t flags, State* stateToCommit) {
     const State& s(getDrawingState());
 
@@ -1013,15 +845,14 @@
         mChildrenChanged = false;
     }
 
-    pushPendingState();
-    State c = getCurrentState();
-    if (!applyPendingStates(&c)) {
-        return flags;
-    }
+    // TODO: This is unfortunate.
+    mCurrentStateModified = mCurrentState.modified;
+    mCurrentState.modified = false;
 
-    flags = doTransactionResize(flags, &c);
+    flags = doTransactionResize(flags, &mCurrentState);
 
     const State& s(getDrawingState());
+    State& c(getCurrentState());
 
     if (getActiveGeometry(c) != getActiveGeometry(s)) {
         // invalidate and recompute the visible regions if needed
@@ -1049,11 +880,10 @@
     }
 
     // Allow BufferStateLayer to release any unlatched buffers in drawing state.
-    bufferMayChange(c.buffer);
+    bufferMayChange(c.buffer->getBuffer());
 
     // Commit the transaction
     commitTransaction(c);
-    mPendingStatesSnapshot = mPendingStates;
     mCurrentState.callbackHandles = {};
 
     return flags;
@@ -1061,7 +891,11 @@
 
 void Layer::commitTransaction(State& stateToCommit) {
     if (auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX;
-        mDrawingState.buffer != stateToCommit.buffer && bufferSurfaceFrame != nullptr &&
+        ((mDrawingState.buffer && stateToCommit.buffer &&
+          mDrawingState.buffer->getBuffer() != stateToCommit.buffer->getBuffer()) ||
+         (mDrawingState.buffer && !stateToCommit.buffer) ||
+         (!mDrawingState.buffer && stateToCommit.buffer)) &&
+        bufferSurfaceFrame != nullptr &&
         bufferSurfaceFrame->getPresentState() != PresentState::Presented) {
         // If the previous buffer was committed but not latched (refreshPending - happens during
         // back to back invalidates), it gets silently dropped here. Mark the corresponding
@@ -1661,25 +1495,6 @@
     return frameRate;
 }
 
-void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
-    ATRACE_CALL();
-
-    mCurrentState.barrierLayer_legacy = barrierLayer;
-    mCurrentState.barrierFrameNumber = frameNumber;
-    // We don't set eTransactionNeeded, because just receiving a deferral
-    // request without any other state updates shouldn't actually induce a delay
-    mCurrentState.modified = true;
-    pushPendingState();
-    mCurrentState.barrierLayer_legacy = nullptr;
-    mCurrentState.barrierFrameNumber = 0;
-    mCurrentState.modified = false;
-}
-
-void Layer::deferTransactionUntil_legacy(const sp<IBinder>& barrierHandle, uint64_t frameNumber) {
-    sp<Handle> handle = static_cast<Handle*>(barrierHandle.get());
-    deferTransactionUntil_legacy(handle->owner.promote(), frameNumber);
-}
-
 // ----------------------------------------------------------------------------
 // pageflip handling...
 // ----------------------------------------------------------------------------
@@ -1948,32 +1763,6 @@
     return removeResult;
 }
 
-void Layer::reparentChildren(const sp<Layer>& newParent) {
-    for (const sp<Layer>& child : mCurrentChildren) {
-        newParent->addChild(child);
-    }
-    mCurrentChildren.clear();
-    updateTreeHasFrameRateVote();
-}
-
-bool Layer::reparentChildren(const sp<IBinder>& newParentHandle) {
-    sp<Handle> handle = nullptr;
-    sp<Layer> newParent = nullptr;
-    if (newParentHandle == nullptr) {
-        return false;
-    }
-    handle = static_cast<Handle*>(newParentHandle.get());
-    newParent = handle->owner.promote();
-    if (newParent == nullptr) {
-        ALOGE("Unable to promote Layer handle");
-        return false;
-    }
-
-    reparentChildren(newParent);
-
-    return true;
-}
-
 void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
     for (const sp<Layer>& child : mDrawingChildren) {
         child->mDrawingParent = newParent;
@@ -2289,8 +2078,13 @@
     return parentAlpha * getDrawingState().backgroundBlurRadius;
 }
 
-const std::vector<BlurRegion>& Layer::getBlurRegions() const {
-    return getDrawingState().blurRegions;
+const std::vector<BlurRegion> Layer::getBlurRegions() const {
+    auto regionsCopy(getDrawingState().blurRegions);
+    int layerAlpha = getAlpha();
+    for (auto& region : regionsCopy) {
+        region.alpha = region.alpha * layerAlpha;
+    }
+    return regionsCopy;
 }
 
 Layer::RoundedCornerState Layer::getRoundedCornerState() const {
@@ -2312,8 +2106,8 @@
         }
     }
     const float radius = getDrawingState().cornerRadius;
-    return radius > 0 && getCrop(getDrawingState()).isValid()
-            ? RoundedCornerState(getCrop(getDrawingState()).toFloatRect(), radius)
+    return radius > 0 && getCroppedBufferSize(getDrawingState()).isValid()
+            ? RoundedCornerState(getCroppedBufferSize(getDrawingState()).toFloatRect(), radius)
             : RoundedCornerState();
 }
 
@@ -2387,14 +2181,6 @@
     const ui::Transform transform = getTransform();
 
     if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
-        for (const auto& pendingState : mPendingStatesSnapshot) {
-            auto barrierLayer = pendingState.barrierLayer_legacy.promote();
-            if (barrierLayer != nullptr) {
-                BarrierLayerProto* barrierLayerProto = layerInfo->add_barrier_layer();
-                barrierLayerProto->set_id(barrierLayer->sequence);
-                barrierLayerProto->set_frame_number(pendingState.barrierFrameNumber);
-            }
-        }
 
         auto buffer = getBuffer();
         if (buffer != nullptr) {
@@ -2534,14 +2320,22 @@
     return mRemovedFromCurrentState;
 }
 
+ui::Transform Layer::getInputTransform() const {
+    return getTransform();
+}
+
+Rect Layer::getInputBounds() const {
+    return getCroppedBufferSize(getDrawingState());
+}
+
 void Layer::fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay) {
     // Transform layer size to screen space and inset it by surface insets.
     // If this is a portal window, set the touchableRegion to the layerBounds.
     Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE
-            ? getBufferSize(getDrawingState())
+            ? getInputBounds()
             : info.touchableRegion.getBounds();
     if (!layerBounds.isValid()) {
-        layerBounds = getCroppedBufferSize(getDrawingState());
+        layerBounds = getInputBounds();
     }
 
     if (!layerBounds.isValid()) {
@@ -2554,7 +2348,7 @@
         return;
     }
 
-    ui::Transform layerToDisplay = getTransform();
+    ui::Transform layerToDisplay = getInputTransform();
     // Transform that takes window coordinates to unrotated display coordinates
     ui::Transform t = toPhysicalDisplay * layerToDisplay;
     int32_t xSurfaceInset = info.surfaceInset;
@@ -2867,6 +2661,18 @@
     }
 }
 
+scheduler::Seamlessness Layer::FrameRate::convertChangeFrameRateStrategy(int8_t strategy) {
+    switch (strategy) {
+        case ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS:
+            return Seamlessness::OnlySeamless;
+        case ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS:
+            return Seamlessness::SeamedAndSeamless;
+        default:
+            LOG_ALWAYS_FATAL("Invalid change frame sate strategy value %d", strategy);
+            return Seamlessness::Default;
+    }
+}
+
 bool Layer::getPrimaryDisplayOnly() const {
     const State& s(mDrawingState);
     if (s.flags & layer_state_t::eLayerSkipScreenshot) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 1c5d6ec..9f3ea9a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -52,6 +52,7 @@
 #include "LayerVector.h"
 #include "MonitoredProducer.h"
 #include "RenderArea.h"
+#include "Scheduler/LayerInfo.h"
 #include "Scheduler/Seamlessness.h"
 #include "SurfaceFlinger.h"
 #include "SurfaceTracing.h"
@@ -141,60 +142,8 @@
         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
-
-        Exact, // Layer needs the exact frame rate.
-
-        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 {
-        using Seamlessness = scheduler::Seamlessness;
-
-        Fps rate;
-        FrameRateCompatibility type;
-        Seamlessness seamlessness;
-
-        FrameRate()
-              : rate(0),
-                type(FrameRateCompatibility::Default),
-                seamlessness(Seamlessness::Default) {}
-        FrameRate(Fps rate, FrameRateCompatibility type, bool shouldBeSeamless = true)
-              : rate(rate), type(type), seamlessness(getSeamlessness(rate, shouldBeSeamless)) {}
-
-        bool operator==(const FrameRate& other) const {
-            return rate.equalsWithMargin(other.rate) && type == other.type &&
-                    seamlessness == other.seamlessness;
-        }
-
-        bool operator!=(const FrameRate& other) const { return !(*this == other); }
-
-        // Convert an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value to a
-        // Layer::FrameRateCompatibility. Logs fatal if the compatibility value is invalid.
-        static FrameRateCompatibility convertCompatibility(int8_t compatibility);
-
-    private:
-        static Seamlessness getSeamlessness(Fps rate, bool shouldBeSeamless) {
-            if (!rate.isValid()) {
-                // Refresh rate of 0 is a special value which should reset the vote to
-                // its default value.
-                return Seamlessness::Default;
-            } else if (shouldBeSeamless) {
-                return Seamlessness::OnlySeamless;
-            } else {
-                return Seamlessness::SeamedAndSeamless;
-            }
-        }
-    };
+    using FrameRate = scheduler::LayerInfo::FrameRate;
+    using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
 
     struct State {
         Geometry active_legacy;
@@ -217,11 +166,6 @@
         Rect crop;
         Rect requestedCrop;
 
-        // If set, defers this state update until the identified Layer
-        // receives a frame with the given frameNumber
-        wp<Layer> barrierLayer_legacy;
-        uint64_t barrierFrameNumber;
-
         // the transparentRegion hint is a bit special, it's latched only
         // when we receive a buffer -- this is because it's "content"
         // dependent.
@@ -259,9 +203,10 @@
 
         Region transparentRegionHint;
 
-        sp<GraphicBuffer> buffer;
+        std::shared_ptr<renderengine::ExternalTexture> buffer;
         client_cache_t clientCacheId;
         sp<Fence> acquireFence;
+        std::shared_ptr<FenceTime> acquireFenceTime;
         HdrMetadata hdrMetadata;
         Region surfaceDamageRegion;
         int32_t api;
@@ -448,9 +393,6 @@
     virtual bool setFlags(uint32_t flags, uint32_t mask);
     virtual bool setLayerStack(uint32_t layerStack);
     virtual uint32_t getLayerStack() const;
-    virtual void deferTransactionUntil_legacy(const sp<IBinder>& barrierHandle,
-                                              uint64_t frameNumber);
-    virtual void deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber);
     virtual bool setMetadata(const LayerMetadata& data);
     virtual void setChildrenDrawingParent(const sp<Layer>&);
     virtual bool reparent(const sp<IBinder>& newParentHandle);
@@ -462,11 +404,11 @@
     // Used only to set BufferStateLayer state
     virtual bool setTransform(uint32_t /*transform*/) { return false; };
     virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
-    virtual bool setFrame(const Rect& /*frame*/) { return false; };
-    virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/,
-                           nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
-                           bool /*isAutoTimestamp*/, const client_cache_t& /*clientCacheId*/,
-                           uint64_t /* frameNumber */, std::optional<nsecs_t> /* dequeueTime */,
+    virtual bool setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& /*buffer*/,
+                           const sp<Fence>& /*acquireFence*/, nsecs_t /*postTime*/,
+                           nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
+                           const client_cache_t& /*clientCacheId*/, uint64_t /* frameNumber */,
+                           std::optional<nsecs_t> /* dequeueTime */,
                            const FrameTimelineInfo& /*info*/,
                            const sp<ITransactionCompletedListener>& /* releaseBufferListener */) {
         return false;
@@ -629,14 +571,6 @@
 
     virtual int32_t getQueuedFrameCount() const { return 0; }
 
-    virtual void pushPendingState();
-
-    /*
-     * Merges the BufferlessSurfaceFrames from source with the target. If the same token exists in
-     * both source and target, target's SurfaceFrame will be retained.
-     */
-    void mergeSurfaceFrames(State& source, State& target);
-
     /**
      * Returns active buffer size in the correct orientation. Buffer size is determined by undoing
      * any buffer transformations. If the layer has no buffer then return INVALID_RECT.
@@ -666,7 +600,6 @@
     // ignored.
     virtual RoundedCornerState getRoundedCornerState() const;
 
-    virtual void notifyAvailableFrames(nsecs_t /*expectedPresentTime*/) {}
     virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
     /**
      * Return whether this layer needs an input info. For most layer types
@@ -688,8 +621,6 @@
     void onLayerDisplayed(const sp<Fence>& releaseFence) override;
     const char* getDebugName() const override;
 
-    bool reparentChildren(const sp<IBinder>& newParentHandle);
-    void reparentChildren(const sp<Layer>& newParent);
     bool setShadowRadius(float shadowRadius);
 
     // Before color management is introduced, contents on Android have to be
@@ -779,7 +710,7 @@
      * Called before updating the drawing state buffer. Used by BufferStateLayer to release any
      * unlatched buffers in the drawing state.
      */
-    virtual void bufferMayChange(sp<GraphicBuffer>& /* newBuffer */){};
+    virtual void bufferMayChange(const sp<GraphicBuffer>& /* newBuffer */){};
 
     /*
      * Remove relative z for the layer if its relative parent is not part of the
@@ -956,65 +887,6 @@
     virtual std::string getPendingBufferCounterName() { return ""; }
 
 protected:
-    class SyncPoint {
-    public:
-        explicit SyncPoint(uint64_t frameNumber, wp<Layer> requestedSyncLayer,
-                           wp<Layer> barrierLayer_legacy)
-              : mFrameNumber(frameNumber),
-                mFrameIsAvailable(false),
-                mTransactionIsApplied(false),
-                mRequestedSyncLayer(requestedSyncLayer),
-                mBarrierLayer_legacy(barrierLayer_legacy) {}
-        uint64_t getFrameNumber() const { return mFrameNumber; }
-
-        bool frameIsAvailable() const { return mFrameIsAvailable; }
-
-        void setFrameAvailable() { mFrameIsAvailable = true; }
-
-        bool transactionIsApplied() const { return mTransactionIsApplied; }
-
-        void setTransactionApplied() { mTransactionIsApplied = true; }
-
-        sp<Layer> getRequestedSyncLayer() { return mRequestedSyncLayer.promote(); }
-
-        sp<Layer> getBarrierLayer() const { return mBarrierLayer_legacy.promote(); }
-
-        bool isTimeout() const {
-            using namespace std::chrono_literals;
-            static constexpr std::chrono::nanoseconds TIMEOUT_THRESHOLD = 1s;
-
-            return std::chrono::steady_clock::now() - mCreateTimeStamp > TIMEOUT_THRESHOLD;
-        }
-
-        void checkTimeoutAndLog() {
-            using namespace std::chrono_literals;
-            static constexpr std::chrono::nanoseconds LOG_PERIOD = 1s;
-
-            if (!frameIsAvailable() && isTimeout()) {
-                const auto now = std::chrono::steady_clock::now();
-                if (now - mLastLogTime > LOG_PERIOD) {
-                    mLastLogTime = now;
-                    sp<Layer> requestedSyncLayer = getRequestedSyncLayer();
-                    sp<Layer> barrierLayer = getBarrierLayer();
-                    ALOGW("[%s] sync point %" PRIu64 " wait timeout %lld for %s",
-                          requestedSyncLayer ? requestedSyncLayer->getDebugName() : "Removed",
-                          mFrameNumber, (now - mCreateTimeStamp).count(),
-                          barrierLayer ? barrierLayer->getDebugName() : "Removed");
-                }
-            }
-        }
-
-    private:
-        const uint64_t mFrameNumber;
-        std::atomic<bool> mFrameIsAvailable;
-        std::atomic<bool> mTransactionIsApplied;
-        wp<Layer> mRequestedSyncLayer;
-        wp<Layer> mBarrierLayer_legacy;
-        const std::chrono::time_point<std::chrono::steady_clock> mCreateTimeStamp =
-                std::chrono::steady_clock::now();
-        std::chrono::time_point<std::chrono::steady_clock> mLastLogTime;
-    };
-
     friend class impl::SurfaceInterceptor;
 
     // For unit tests
@@ -1033,7 +905,6 @@
             ui::Dataspace outputDataspace);
     virtual void preparePerFrameCompositionState();
     virtual void commitTransaction(State& stateToCommit);
-    virtual bool applyPendingStates(State* stateToCommit);
     virtual uint32_t doTransactionResize(uint32_t flags, Layer::State* stateToCommit);
     virtual void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&) {}
 
@@ -1076,20 +947,8 @@
     compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const;
     bool usingRelativeZ(LayerVector::StateSet) const;
 
-    // SyncPoints which will be signaled when the correct frame is at the head
-    // of the queue and dropped after the frame has been latched. Protected by
-    // mLocalSyncPointMutex.
-    Mutex mLocalSyncPointMutex;
-    std::list<std::shared_ptr<SyncPoint>> mLocalSyncPoints;
-
-    // SyncPoints which will be signaled and then dropped when the transaction
-    // is applied
-    std::list<std::shared_ptr<SyncPoint>> mRemoteSyncPoints;
-
-    // Returns false if the relevant frame has already been latched
-    bool addSyncPoint(const std::shared_ptr<SyncPoint>& point);
-
-    void popPendingState(State* stateToCommit);
+    virtual ui::Transform getInputTransform() const;
+    virtual Rect getInputBounds() const;
 
     // constant
     sp<SurfaceFlinger> mFlinger;
@@ -1100,14 +959,10 @@
 
     // These are only accessed by the main thread or the tracing thread.
     State mDrawingState;
-    // Store a copy of the pending state so that the drawing thread can access the
-    // states without a lock.
-    std::deque<State> mPendingStatesSnapshot;
 
     // these are protected by an external lock (mStateLock)
     State mCurrentState;
     std::atomic<uint32_t> mTransactionFlags{0};
-    std::deque<State> mPendingStates;
 
     // Timestamp history for UIAutomation. Thread safe.
     FrameTracker mFrameTracker;
@@ -1169,6 +1024,8 @@
     // Used in buffer stuffing analysis in FrameTimeline.
     nsecs_t mLastLatchTime = 0;
 
+    mutable bool mCurrentStateModified = false;
+
 private:
     virtual void setTransformHint(ui::Transform::RotationFlags) {}
 
@@ -1193,7 +1050,6 @@
 
     void updateTreeHasFrameRateVote();
     void setZOrderRelativeOf(const wp<Layer>& relativeOf);
-    void removeRemoteSyncPoints();
 
     // 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.
@@ -1241,7 +1097,7 @@
     float mEffectiveShadowRadius = 0.f;
 
     // A list of regions on this layer that should have blurs.
-    const std::vector<BlurRegion>& getBlurRegions() const;
+    const std::vector<BlurRegion> getBlurRegions() const;
 };
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 1d00cc3..a9fd16c 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -208,7 +208,8 @@
     return true;
 }
 
-const std::vector<sp<GraphicBuffer>>& RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) {
+const std::vector<std::shared_ptr<renderengine::ExternalTexture>>&
+RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) {
     if (mBufferCache.find(fps) == mBufferCache.end()) {
         // Ensure the range is > 0, so we don't divide by 0.
         const auto rangeLength = std::max(1u, mHighFps - mLowFps);
@@ -222,7 +223,17 @@
         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, mShowSpinner));
+        auto buffers = SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner);
+        std::vector<std::shared_ptr<renderengine::ExternalTexture>> textures;
+        std::transform(buffers.begin(), buffers.end(), std::back_inserter(textures),
+                       [&](const auto& buffer) -> std::shared_ptr<renderengine::ExternalTexture> {
+                           return std::make_shared<
+                                   renderengine::ExternalTexture>(buffer,
+                                                                  mFlinger.getRenderEngine(),
+                                                                  renderengine::ExternalTexture::
+                                                                          Usage::READABLE);
+                       });
+        mBufferCache.emplace(fps, textures);
     }
 
     return mBufferCache[fps];
@@ -231,8 +242,14 @@
 void RefreshRateOverlay::setViewport(ui::Size viewport) {
     Rect frame((3 * viewport.width) >> 4, viewport.height >> 5);
     frame.offsetBy(viewport.width >> 5, viewport.height >> 4);
-    mLayer->setFrame(frame);
 
+    layer_state_t::matrix22_t matrix;
+    matrix.dsdx = frame.getWidth() / static_cast<float>(SevenSegmentDrawer::getWidth());
+    matrix.dtdx = 0;
+    matrix.dtdy = 0;
+    matrix.dsdy = frame.getHeight() / static_cast<float>(SevenSegmentDrawer::getHeight());
+    mLayer->setMatrix(matrix, true);
+    mLayer->setPosition(frame.left, frame.top);
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
 
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index c16cfa0..aa8329c 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -16,13 +16,14 @@
 
 #pragma once
 
-#include <unordered_map>
-
 #include <math/vec4.h>
+#include <renderengine/RenderEngine.h>
 #include <ui/Rect.h>
 #include <ui/Size.h>
 #include <utils/StrongPointer.h>
 
+#include <unordered_map>
+
 #include "Fps.h"
 
 namespace android {
@@ -70,7 +71,8 @@
     };
 
     bool createLayer();
-    const std::vector<sp<GraphicBuffer>>& getOrCreateBuffers(uint32_t fps);
+    const std::vector<std::shared_ptr<renderengine::ExternalTexture>>& getOrCreateBuffers(
+            uint32_t fps);
 
     SurfaceFlinger& mFlinger;
     const sp<Client> mClient;
@@ -78,7 +80,8 @@
     sp<IBinder> mIBinder;
     sp<IGraphicBufferProducer> mGbp;
 
-    std::unordered_map<int, std::vector<sp<GraphicBuffer>>> mBufferCache;
+    std::unordered_map<int, std::vector<std::shared_ptr<renderengine::ExternalTexture>>>
+            mBufferCache;
     std::optional<int> mCurrentFps;
     int mFrame = 0;
     static constexpr float ALPHA = 0.8f;
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index d0032ac..00090d9 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -438,18 +438,22 @@
         mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor);
     };
 
-    sp<GraphicBuffer> buffer = nullptr;
-    if (mCachedBuffer && mCachedBuffer->getWidth() == sampledBounds.getWidth() &&
-        mCachedBuffer->getHeight() == sampledBounds.getHeight()) {
+    std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
+    if (mCachedBuffer && mCachedBuffer->getBuffer()->getWidth() == sampledBounds.getWidth() &&
+        mCachedBuffer->getBuffer()->getHeight() == sampledBounds.getHeight()) {
         buffer = mCachedBuffer;
     } else {
         const uint32_t usage =
                 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
-        buffer = new GraphicBuffer(sampledBounds.getWidth(), sampledBounds.getHeight(),
-                                   PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
-        const status_t bufferStatus = buffer->initCheck();
+        sp<GraphicBuffer> graphicBuffer =
+                new GraphicBuffer(sampledBounds.getWidth(), sampledBounds.getHeight(),
+                                  PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
+        const status_t bufferStatus = graphicBuffer->initCheck();
         LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureSample: Buffer failed to allocate: %d",
                             bufferStatus);
+        buffer = std::make_shared<
+                renderengine::ExternalTexture>(graphicBuffer, mFlinger.getRenderEngine(),
+                                               renderengine::ExternalTexture::Usage::WRITEABLE);
     }
 
     const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
@@ -465,8 +469,8 @@
     }
 
     ALOGV("Sampling %zu descriptors", activeDescriptors.size());
-    std::vector<float> lumas =
-            sampleBuffer(buffer, sampledBounds.leftTop(), activeDescriptors, orientation);
+    std::vector<float> lumas = sampleBuffer(buffer->getBuffer(), sampledBounds.leftTop(),
+                                            activeDescriptors, orientation);
     if (lumas.size() != activeDescriptors.size()) {
         ALOGW("collected %zu median luma values for %zu descriptors", lumas.size(),
               activeDescriptors.size());
@@ -477,16 +481,6 @@
         activeDescriptors[d].listener->onSampleCollected(lumas[d]);
     }
 
-    // Extend the lifetime of mCachedBuffer from the previous frame to here to ensure that:
-    // 1) The region sampling thread is the last owner of the buffer, and the freeing of the buffer
-    // happens in this thread, as opposed to the main thread.
-    // 2) The listener(s) receive their notifications prior to freeing the buffer.
-    if (mCachedBuffer != nullptr && mCachedBuffer != buffer) {
-        if (mFlinger.getRenderEngine().getRenderEngineType() ==
-            renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED) {
-            mFlinger.getRenderEngine().unbindExternalTextureBuffer(mCachedBuffer->getId());
-        }
-    }
     mCachedBuffer = buffer;
     ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::noWorkNeeded));
 }
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index 0defdb3..86632db 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -16,17 +16,19 @@
 
 #pragma once
 
+#include <android-base/thread_annotations.h>
+#include <binder/IBinder.h>
+#include <renderengine/ExternalTexture.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+#include <utils/StrongPointer.h>
+
 #include <chrono>
 #include <condition_variable>
 #include <mutex>
 #include <thread>
 #include <unordered_map>
 
-#include <android-base/thread_annotations.h>
-#include <binder/IBinder.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/Rect.h>
-#include <utils/StrongPointer.h>
 #include "Scheduler/OneShotTimer.h"
 
 namespace android {
@@ -122,7 +124,8 @@
 
     std::mutex mSamplingMutex;
     std::unordered_map<wp<IBinder>, Descriptor, WpHash> mDescriptors GUARDED_BY(mSamplingMutex);
-    sp<GraphicBuffer> mCachedBuffer GUARDED_BY(mSamplingMutex) = nullptr;
+    std::shared_ptr<renderengine::ExternalTexture> mCachedBuffer GUARDED_BY(mSamplingMutex) =
+            nullptr;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 6553efe..2321e2d 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -217,12 +217,18 @@
 EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,
                          android::frametimeline::TokenManager* tokenManager,
                          InterceptVSyncsCallback interceptVSyncsCallback,
-                         ThrottleVsyncCallback throttleVsyncCallback)
+                         ThrottleVsyncCallback throttleVsyncCallback,
+                         GetVsyncPeriodFunction getVsyncPeriodFunction)
       : mVSyncSource(std::move(vsyncSource)),
         mTokenManager(tokenManager),
         mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
         mThrottleVsyncCallback(std::move(throttleVsyncCallback)),
+        mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)),
         mThreadName(mVSyncSource->getName()) {
+
+    LOG_ALWAYS_FATAL_IF(getVsyncPeriodFunction == nullptr,
+            "getVsyncPeriodFunction must not be null");
+
     mVSyncSource->setCallback(this);
 
     mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
@@ -565,7 +571,11 @@
 void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
                                 const DisplayEventConsumers& consumers) {
     for (const auto& consumer : consumers) {
-        switch (consumer->postEvent(event)) {
+        DisplayEventReceiver::Event copy = event;
+        if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+            copy.vsync.frameInterval = mGetVsyncPeriodFunction(consumer->mOwnerUid);
+        }
+        switch (consumer->postEvent(copy)) {
             case NO_ERROR:
                 break;
 
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 3540604..1e6793f 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -152,9 +152,10 @@
 public:
     using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
     using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>;
+    using GetVsyncPeriodFunction = std::function<nsecs_t(uid_t)>;
 
     EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, InterceptVSyncsCallback,
-                ThrottleVsyncCallback);
+                ThrottleVsyncCallback, GetVsyncPeriodFunction);
     ~EventThread();
 
     sp<EventThreadConnection> createEventConnection(
@@ -210,6 +211,7 @@
 
     const InterceptVSyncsCallback mInterceptVSyncsCallback;
     const ThrottleVsyncCallback mThrottleVsyncCallback;
+    const GetVsyncPeriodFunction mGetVsyncPeriodFunction;
     const char* const mThreadName;
 
     std::thread mThread;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index ea92ad8..f4bc2a1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -39,13 +39,13 @@
 
 namespace {
 
-bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
+bool isLayerActive(const LayerInfo& info, nsecs_t threshold) {
     // Layers with an explicit vote are always kept active
-    if (layer.getFrameRateForLayerTree().rate.isValid()) {
+    if (info.getSetFrameRateVote().rate.isValid()) {
         return true;
     }
 
-    return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
+    return info.isVisible() && info.getLastUpdatedTime() >= threshold;
 }
 
 bool traceEnabled() {
@@ -58,11 +58,7 @@
     return atoi(value);
 }
 
-void trace(const wp<Layer>& weak, const LayerInfo& info, LayerHistory::LayerVoteType type,
-           int fps) {
-    const auto layer = weak.promote();
-    if (!layer) return;
-
+void trace(const LayerInfo& info, LayerHistory::LayerVoteType type, int fps) {
     const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) {
         ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0);
     };
@@ -75,7 +71,7 @@
     traceType(LayerHistory::LayerVoteType::Min, 1);
     traceType(LayerHistory::LayerVoteType::Max, 1);
 
-    ALOGD("%s: %s @ %d Hz", __FUNCTION__, layer->getName().c_str(), fps);
+    ALOGD("%s: %s @ %d Hz", __FUNCTION__, info.getName().c_str(), fps);
 }
 } // namespace
 
@@ -88,11 +84,27 @@
 LayerHistory::~LayerHistory() = default;
 
 void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
-    auto info = std::make_unique<LayerInfo>(layer->getName(), type);
+    auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
     std::lock_guard lock(mLock);
     mLayerInfos.emplace_back(layer, std::move(info));
 }
 
+void LayerHistory::deregisterLayer(Layer* layer) {
+    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 size_t i = static_cast<size_t>(it - mLayerInfos.begin());
+    if (i < mActiveLayersEnd) {
+        mActiveLayersEnd--;
+    }
+    const size_t last = mLayerInfos.size() - 1;
+    std::swap(mLayerInfos[i], mLayerInfos[last]);
+    mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(last));
+}
+
 void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
                           LayerUpdateType updateType) {
     std::lock_guard lock(mLock);
@@ -102,7 +114,15 @@
     LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
 
     const auto& info = it->second;
-    info->setLastPresentTime(presentTime, now, updateType, mModeChangePending);
+    const auto layerProps = LayerInfo::LayerProps{
+            .visible = layer->isVisible(),
+            .bounds = layer->getBounds(),
+            .transform = layer->getTransform(),
+            .setFrameRateVote = layer->getFrameRateForLayerTree(),
+            .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
+    };
+
+    info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
 
     // Activate layer if inactive.
     if (const auto end = activeLayers().end(); it >= end) {
@@ -119,15 +139,10 @@
     partitionLayers(now);
 
     for (const auto& [layer, info] : activeLayers()) {
-        const auto strong = layer.promote();
-        if (!strong) {
-            continue;
-        }
-
-        const auto frameRateSelectionPriority = strong->getFrameRateSelectionPriority();
+        const auto frameRateSelectionPriority = info->getFrameRateSelectionPriority();
         const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority);
-        ALOGV("%s has priority: %d %s focused", strong->getName().c_str(),
-              frameRateSelectionPriority, layerFocused ? "" : "not");
+        ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority,
+              layerFocused ? "" : "not");
 
         const auto vote = info->getRefreshRateVote(now);
         // Skip NoVote layer as those don't have any requirements
@@ -136,18 +151,18 @@
         }
 
         // Compute the layer's position on the screen
-        const Rect bounds = Rect(strong->getBounds());
-        const ui::Transform transform = strong->getTransform();
+        const Rect bounds = Rect(info->getBounds());
+        const ui::Transform transform = info->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(), strong->getOwnerUid(), vote.type, vote.fps,
+        summary.push_back({info->getName(), info->getOwnerUid(), vote.type, vote.fps,
                            vote.seamlessness, weight, layerFocused});
 
         if (CC_UNLIKELY(mTraceEnabled)) {
-            trace(layer, *info, vote.type, vote.fps.getIntValue());
+            trace(*info, vote.type, vote.fps.getIntValue());
         }
     }
 
@@ -160,11 +175,11 @@
     // 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)) {
+        auto& [layerUnsafe, info] = mLayerInfos[i];
+        if (isLayerActive(*info, threshold)) {
             i++;
             // Set layer vote if set
-            const auto frameRate = layer->getFrameRateForLayerTree();
+            const auto frameRate = info->getSetFrameRateVote();
             const auto voteType = [&]() {
                 switch (frameRate.type) {
                     case Layer::FrameRateCompatibility::Default:
@@ -179,7 +194,7 @@
             }();
 
             if (frameRate.rate.isValid() || voteType == LayerVoteType::NoVote) {
-                const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote;
+                const auto type = info->isVisible() ? voteType : LayerVoteType::NoVote;
                 info->setLayerVote({type, frameRate.rate, frameRate.seamlessness});
             } else {
                 info->resetLayerVote();
@@ -188,24 +203,12 @@
         }
 
         if (CC_UNLIKELY(mTraceEnabled)) {
-            trace(weak, *info, LayerHistory::LayerVoteType::NoVote, 0);
+            trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
         }
 
         info->onLayerInactive(now);
         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 LayerHistory::clear() {
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 05ecc70..82f6c39 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -70,13 +70,15 @@
     Summary summarize(nsecs_t now);
 
     void clear();
+
+    void deregisterLayer(Layer*);
     std::string dump() const;
 
 private:
     friend LayerHistoryTest;
     friend TestableScheduler;
 
-    using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfo>>;
+    using LayerPair = std::pair<Layer*, std::unique_ptr<LayerInfo>>;
     using LayerInfos = std::vector<LayerPair>;
 
     struct ActiveLayers {
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 4b4cdae..989bf4e 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -37,17 +37,20 @@
 const RefreshRateConfigs* LayerInfo::sRefreshRateConfigs = nullptr;
 bool LayerInfo::sTraceEnabled = false;
 
-LayerInfo::LayerInfo(const std::string& name, LayerHistory::LayerVoteType defaultVote)
+LayerInfo::LayerInfo(const std::string& name, uid_t ownerUid,
+                     LayerHistory::LayerVoteType defaultVote)
       : mName(name),
+        mOwnerUid(ownerUid),
         mDefaultVote(defaultVote),
         mLayerVote({defaultVote, Fps(0.0f)}),
         mRefreshRateHistory(name) {}
 
 void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
-                                   bool pendingModeChange) {
+                                   bool pendingModeChange, LayerProps props) {
     lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
 
     mLastUpdatedTime = std::max(lastPresentTime, now);
+    mLayerProps = props;
     switch (updateType) {
         case LayerUpdateType::AnimationTX:
             mLastAnimationTime = std::max(lastPresentTime, now);
@@ -232,6 +235,8 @@
     if (!isFrequent(now)) {
         ALOGV("%s is infrequent", mName.c_str());
         mLastRefreshRate.animatingOrInfrequent = true;
+        // Infrequent layers vote for mininal refresh rate for
+        // battery saving purposes and also to prevent b/135718869.
         return {LayerHistory::LayerVoteType::Min, Fps(0.0f)};
     }
 
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 40c0214..ba03c89 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <ui/Transform.h>
 #include <utils/Timers.h>
 
 #include <chrono>
@@ -65,22 +66,84 @@
         Seamlessness seamlessness = Seamlessness::Default;
     };
 
+    // 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
+
+        Exact, // Layer needs the exact frame rate.
+
+        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 {
+        using Seamlessness = scheduler::Seamlessness;
+
+        Fps rate;
+        FrameRateCompatibility type;
+        Seamlessness seamlessness;
+
+        FrameRate()
+              : rate(0),
+                type(FrameRateCompatibility::Default),
+                seamlessness(Seamlessness::Default) {}
+        FrameRate(Fps rate, FrameRateCompatibility type,
+                  Seamlessness seamlessness = Seamlessness::OnlySeamless)
+              : rate(rate), type(type), seamlessness(getSeamlessness(rate, seamlessness)) {}
+
+        bool operator==(const FrameRate& other) const {
+            return rate.equalsWithMargin(other.rate) && type == other.type &&
+                    seamlessness == other.seamlessness;
+        }
+
+        bool operator!=(const FrameRate& other) const { return !(*this == other); }
+
+        // Convert an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value to a
+        // Layer::FrameRateCompatibility. Logs fatal if the compatibility value is invalid.
+        static FrameRateCompatibility convertCompatibility(int8_t compatibility);
+        static scheduler::Seamlessness convertChangeFrameRateStrategy(int8_t strategy);
+
+    private:
+        static Seamlessness getSeamlessness(Fps rate, Seamlessness seamlessness) {
+            if (!rate.isValid()) {
+                // Refresh rate of 0 is a special value which should reset the vote to
+                // its default value.
+                return Seamlessness::Default;
+            }
+            return seamlessness;
+        }
+    };
+
     static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
 
     static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) {
         sRefreshRateConfigs = &refreshRateConfigs;
     }
 
-    LayerInfo(const std::string& name, LayerHistory::LayerVoteType defaultVote);
+    LayerInfo(const std::string& name, uid_t ownerUid, LayerHistory::LayerVoteType defaultVote);
 
     LayerInfo(const LayerInfo&) = delete;
     LayerInfo& operator=(const LayerInfo&) = delete;
 
+    struct LayerProps {
+        bool visible = false;
+        FloatRect bounds;
+        ui::Transform transform;
+        FrameRate setFrameRateVote;
+        int32_t frameRateSelectionPriority = -1;
+    };
+
     // 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, LayerUpdateType updateType,
-                            bool pendingModeChange);
+                            bool pendingModeChange, LayerProps props);
 
     // Sets an explicit layer vote. This usually comes directly from the application via
     // ANativeWindow_setFrameRate API
@@ -94,12 +157,24 @@
     // Resets the layer vote to its default.
     void resetLayerVote() { mLayerVote = {mDefaultVote, Fps(0.0f), Seamlessness::Default}; }
 
+    std::string getName() const { return mName; }
+
+    uid_t getOwnerUid() const { return mOwnerUid; }
+
     LayerVote getRefreshRateVote(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; }
 
+    FrameRate getSetFrameRateVote() const { return mLayerProps.setFrameRateVote; }
+    bool isVisible() const { return mLayerProps.visible; }
+    int32_t getFrameRateSelectionPriority() const { return mLayerProps.frameRateSelectionPriority; }
+
+    FloatRect getBounds() const { return mLayerProps.bounds; }
+
+    ui::Transform getTransform() const { return mLayerProps.transform; }
+
     // Returns a C string for tracing a vote
     const char* getTraceTag(LayerHistory::LayerVoteType type) const;
 
@@ -193,6 +268,7 @@
     bool isFrameTimeValid(const FrameTimeData&) const;
 
     const std::string mName;
+    const uid_t mOwnerUid;
 
     // Used for sanitizing the heuristic data. If two frames are less than
     // this period apart from each other they'll be considered as duplicates.
@@ -217,6 +293,8 @@
     static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
     static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
 
+    LayerProps mLayerProps;
+
     RefreshRateHistory mRefreshRateHistory;
 
     mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> mTraceTags;
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
index ce3b0c6..d659398 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -40,13 +40,21 @@
 namespace android {
 namespace scheduler {
 
+std::chrono::steady_clock::time_point OneShotTimer::Clock::now() const {
+    return std::chrono::steady_clock::now();
+}
+
 OneShotTimer::OneShotTimer(std::string name, const Interval& interval,
                            const ResetCallback& resetCallback,
-                           const TimeoutCallback& timeoutCallback)
-      : mName(std::move(name)),
+                           const TimeoutCallback& timeoutCallback,
+                           std::unique_ptr<OneShotTimer::Clock> clock)
+      : mClock(std::move(clock)),
+        mName(std::move(name)),
         mInterval(interval),
         mResetCallback(resetCallback),
-        mTimeoutCallback(timeoutCallback) {}
+        mTimeoutCallback(timeoutCallback) {
+    LOG_ALWAYS_FATAL_IF(!mClock, "Clock must not be provided");
+}
 
 OneShotTimer::~OneShotTimer() {
     stop();
@@ -112,7 +120,7 @@
             break;
         }
 
-        auto triggerTime = std::chrono::steady_clock::now() + mInterval;
+        auto triggerTime = mClock->now() + mInterval;
         state = TimerState::WAITING;
         while (state == TimerState::WAITING) {
             constexpr auto zero = std::chrono::steady_clock::duration::zero();
@@ -128,10 +136,9 @@
 
             state = checkForResetAndStop(state);
             if (state == TimerState::RESET) {
-                triggerTime = std::chrono::steady_clock::now() + mInterval;
+                triggerTime = mClock->now() + mInterval;
                 state = TimerState::WAITING;
-            } else if (state == TimerState::WAITING &&
-                       (triggerTime - std::chrono::steady_clock::now()) <= zero) {
+            } else if (state == TimerState::WAITING && (triggerTime - mClock->now()) <= zero) {
                 triggerTimeout = true;
                 state = TimerState::IDLE;
             }
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
index 3690ce7..7285427 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -36,8 +36,17 @@
     using ResetCallback = std::function<void()>;
     using TimeoutCallback = std::function<void()>;
 
+    class Clock {
+    public:
+        Clock() = default;
+        virtual ~Clock() = default;
+
+        virtual std::chrono::steady_clock::time_point now() const;
+    };
+
     OneShotTimer(std::string name, const Interval& interval, const ResetCallback& resetCallback,
-                 const TimeoutCallback& timeoutCallback);
+                 const TimeoutCallback& timeoutCallback,
+                 std::unique_ptr<OneShotTimer::Clock> = std::make_unique<OneShotTimer::Clock>());
     ~OneShotTimer();
 
     // Initializes and turns on the idle timer.
@@ -78,6 +87,9 @@
     // Thread waiting for timer to expire.
     std::thread mThread;
 
+    // Clock object for the timer. Mocked in unit tests.
+    std::unique_ptr<Clock> mClock;
+
     // Semaphore to keep mThread synchronized.
     sem_t mSemaphore;
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 91e8043..b062acd 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -215,7 +215,7 @@
     int explicitExactOrMultipleVoteLayers = 0;
     int explicitExact = 0;
     float maxExplicitWeight = 0;
-    int seamedLayers = 0;
+    int seamedFocusedLayers = 0;
     for (const auto& layer : layers) {
         switch (layer.vote) {
             case LayerVoteType::NoVote:
@@ -243,8 +243,8 @@
                 break;
         }
 
-        if (layer.seamlessness == Seamlessness::SeamedAndSeamless) {
-            seamedLayers++;
+        if (layer.seamlessness == Seamlessness::SeamedAndSeamless && layer.focused) {
+            seamedFocusedLayers++;
         }
     }
 
@@ -329,12 +329,11 @@
             // mode group otherwise. In second case, if the current mode group is different
             // from the default, this means a layer with seamlessness=SeamedAndSeamless has just
             // disappeared.
-            const bool isInPolicyForDefault = seamedLayers > 0
+            const bool isInPolicyForDefault = seamedFocusedLayers > 0
                     ? scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup()
                     : scores[i].refreshRate->getModeGroup() == defaultMode->getModeGroup();
 
-            if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault &&
-                !layer.focused) {
+            if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
                 ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
                       scores[i].refreshRate->toString().c_str(),
                       mCurrentRefreshRate->toString().c_str());
@@ -499,11 +498,7 @@
         // Now that we scored all the refresh rates we need to pick the one that got the highest
         // score.
         const RefreshRate* bestRefreshRate = getBestRefreshRate(scores.begin(), scores.end());
-
-        // If the nest refresh rate is the current one, we don't have an override
-        if (!bestRefreshRate->getFps().equalsWithMargin(displayFrameRate)) {
-            frameRateOverrides.emplace(uid, bestRefreshRate->getFps());
-        }
+        frameRateOverrides.emplace(uid, bestRefreshRate->getFps());
     }
 
     return frameRateOverrides;
@@ -839,11 +834,6 @@
     return static_cast<int>(numPeriodsRounded);
 }
 
-int RefreshRateConfigs::getRefreshRateDivider(Fps frameRate) const {
-    std::lock_guard lock(mLock);
-    return getFrameRateDivider(mCurrentRefreshRate->getFps(), frameRate);
-}
-
 void RefreshRateConfigs::dump(std::string& result) const {
     std::lock_guard lock(mLock);
     base::StringAppendF(&result, "DesiredDisplayModeSpecs (DisplayManager): %s\n\n",
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 2bc22b4..ee89149 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -324,8 +324,10 @@
 
     bool supportsFrameRateOverride() const { return mSupportsFrameRateOverride; }
 
-    // Returns a divider for the current refresh rate
-    int getRefreshRateDivider(Fps frameRate) const EXCLUDES(mLock);
+    // Return the display refresh rate divider to match the layer
+    // frame rate, or 0 if the display refresh rate is not a multiple of the
+    // layer refresh rate.
+    static int getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate);
 
     using UidToFrameRateOverride = std::map<uid_t, Fps>;
     // Returns the frame rate override for each uid.
@@ -373,11 +375,6 @@
     const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
     bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
 
-    // Return the display refresh rate divider to match the layer
-    // frame rate, or 0 if the display refresh rate is not a multiple of the
-    // layer refresh rate.
-    static int getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate);
-
     // calculates a score for a layer. Used to determine the display refresh rate
     // and the frame rate override for certains applications.
     float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 9813270..57bd045 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -235,12 +235,7 @@
         return true;
     }
 
-    const auto divider = mRefreshRateConfigs.getRefreshRateDivider(*frameRate);
-    if (divider <= 1) {
-        return true;
-    }
-
-    return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, divider);
+    return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, *frameRate);
 }
 
 impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
@@ -253,15 +248,34 @@
     };
 }
 
+impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
+    return [this](uid_t uid) {
+        nsecs_t basePeriod = mRefreshRateConfigs.getCurrentRefreshRate().getVsyncPeriod();
+        const auto frameRate = getFrameRateOverride(uid);
+        if (!frameRate.has_value()) {
+            return basePeriod;
+        }
+
+        const auto divider = scheduler::RefreshRateConfigs::getFrameRateDivider(
+            mRefreshRateConfigs.getCurrentRefreshRate().getFps(), *frameRate);
+        if (divider <= 1) {
+            return basePeriod;
+        }
+        return basePeriod * divider;
+    };
+}
+
 Scheduler::ConnectionHandle Scheduler::createConnection(
         const char* connectionName, frametimeline::TokenManager* tokenManager,
         std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
         impl::EventThread::InterceptVSyncsCallback interceptCallback) {
     auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration);
     auto throttleVsync = makeThrottleVsyncCallback();
+    auto getVsyncPeriod = makeGetVsyncPeriodFunction();
     auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager,
                                                            std::move(interceptCallback),
-                                                           std::move(throttleVsync));
+                                                           std::move(throttleVsync),
+                                                           std::move(getVsyncPeriod));
     return createConnection(std::move(eventThread));
 }
 
@@ -354,6 +368,10 @@
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
         // Cache the last reported modes for primary display.
         mFeatures.cachedModeChangedParams = {handle, displayId, modeId, vsyncPeriod};
+
+        // Invalidate content based refresh rate selection so it could be calculated
+        // again for the new refresh rate.
+        mFeatures.contentRequirements.clear();
     }
     onNonPrimaryDisplayModeChanged(handle, displayId, modeId, vsyncPeriod);
 }
@@ -445,7 +463,8 @@
                 std::make_unique<impl::EventThread>(std::move(vsyncSource),
                                                     /*tokenManager=*/nullptr,
                                                     impl::EventThread::InterceptVSyncsCallback(),
-                                                    impl::EventThread::ThrottleVsyncCallback());
+                                                    impl::EventThread::ThrottleVsyncCallback(),
+                                                    impl::EventThread::GetVsyncPeriodFunction());
 
         // EventThread does not dispatch VSYNC unless the display is connected and powered on.
         eventThread->onHotplugReceived(PhysicalDisplayId::fromPort(0), true);
@@ -581,6 +600,12 @@
     mLayerHistory->registerLayer(layer, voteType);
 }
 
+void Scheduler::deregisterLayer(Layer* layer) {
+    if (mLayerHistory) {
+        mLayerHistory->deregisterLayer(layer);
+    }
+}
+
 void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
                                    LayerHistory::LayerUpdateType updateType) {
     if (mLayerHistory) {
@@ -902,4 +927,11 @@
     }
 }
 
+std::chrono::steady_clock::time_point Scheduler::getPreviousVsyncFrom(
+        nsecs_t expectedPresentTime) const {
+    const auto presentTime = std::chrono::nanoseconds(expectedPresentTime);
+    const auto vsyncPeriod = std::chrono::nanoseconds(mVsyncSchedule.tracker->currentPeriod());
+    return std::chrono::steady_clock::time_point(presentTime - vsyncPeriod);
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 0e9eba7..49d3d93 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -129,6 +129,7 @@
     void registerLayer(Layer*);
     void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType);
     void setModeChangePending(bool pending);
+    void deregisterLayer(Layer*);
 
     // Detects content using layer history, and selects a matching refresh rate.
     void chooseRefreshRateForContent();
@@ -148,6 +149,8 @@
     bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const
             EXCLUDES(mFrameRateOverridesMutex);
 
+    std::chrono::steady_clock::time_point getPreviousVsyncFrom(nsecs_t expectedPresentTime) const;
+
     void dump(std::string&) const;
     void dump(ConnectionHandle, std::string&) const;
     void dumpVsync(std::string&) const;
@@ -239,6 +242,7 @@
             EXCLUDES(mFrameRateOverridesMutex);
 
     impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const;
+    impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
 
     // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
     struct Connection {
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 7cca206..028f7a6 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -30,6 +30,7 @@
 #include <algorithm>
 #include <chrono>
 #include <sstream>
+#include "RefreshRateConfigs.h"
 
 #undef LOG_TAG
 #define LOG_TAG "VSyncPredictor"
@@ -225,13 +226,14 @@
 }
 
 /*
- * Returns whether a given vsync timestamp is in phase with a vsync divider.
- * For example, if the vsync timestamps are (16,32,48,64):
- * isVSyncInPhase(16, 2) = true
- * isVSyncInPhase(32, 2) = false
- * isVSyncInPhase(48, 2) = true
+ * Returns whether a given vsync timestamp is in phase with a frame rate.
+ * If the frame rate is not a divider of the refresh rate, it is always considered in phase.
+ * For example, if the vsync timestamps are (16.6,33.3,50.0,66.6):
+ * isVSyncInPhase(16.6, 30) = true
+ * isVSyncInPhase(33.3, 30) = false
+ * isVSyncInPhase(50.0, 30) = true
  */
-bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, int divider) const {
+bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const {
     struct VsyncError {
         nsecs_t vsyncTimestamp;
         float error;
@@ -239,11 +241,13 @@
         bool operator<(const VsyncError& other) const { return error < other.error; }
     };
 
+    std::lock_guard lock(mMutex);
+    const auto divider =
+            RefreshRateConfigs::getFrameRateDivider(Fps::fromPeriodNsecs(mIdealPeriod), frameRate);
     if (divider <= 1 || timePoint == 0) {
         return true;
     }
 
-    std::lock_guard lock(mMutex);
     const nsecs_t period = mRateMap[mIdealPeriod].slope;
     const nsecs_t justBeforeTimePoint = timePoint - period / 2;
     const nsecs_t dividedPeriod = mIdealPeriod / divider;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 381cf81..40e6944 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -64,7 +64,7 @@
 
     VSyncPredictor::Model getVSyncPredictionModel() const EXCLUDES(mMutex);
 
-    bool isVSyncInPhase(nsecs_t timePoint, int divider) const final EXCLUDES(mMutex);
+    bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex);
 
     void dump(std::string& result) const final EXCLUDES(mMutex);
 
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 2cd9b3d..95750ad 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <utils/Timers.h>
+#include "Fps.h"
 #include "VSyncDispatch.h"
 
 namespace android::scheduler {
@@ -69,12 +70,12 @@
     virtual bool needsMoreSamples() const = 0;
 
     /*
-     * Checks if a vsync timestamp is in phase for a given divider.
+     * Checks if a vsync timestamp is in phase for a frame rate
      *
      * \param [in] timePoint  A vsync timestamp
-     * \param [in] divider  The divider to check for
+     * \param [in] frameRate  The frame rate to check for
      */
-    virtual bool isVSyncInPhase(nsecs_t timePoint, int divider) const = 0;
+    virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0;
 
     virtual void dump(std::string& result) const = 0;
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5be5734..b048682 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -64,7 +64,6 @@
 #include <private/android_filesystem_config.h>
 #include <private/gui/SyncFeatures.h>
 #include <renderengine/RenderEngine.h>
-#include <statslog.h>
 #include <sys/types.h>
 #include <ui/ColorSpace.h>
 #include <ui/DebugUtils.h>
@@ -308,20 +307,6 @@
 ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
 bool SurfaceFlinger::useFrameRateApi;
 
-std::string getHwcServiceName() {
-    char value[PROPERTY_VALUE_MAX] = {};
-    property_get("debug.sf.hwc_service_name", value, "default");
-    ALOGI("Using HWComposer service: '%s'", value);
-    return std::string(value);
-}
-
-bool useTrebleTestingOverride() {
-    char value[PROPERTY_VALUE_MAX] = {};
-    property_get("debug.sf.treble_testing_override", value, "false");
-    ALOGI("Treble testing override: '%s'", value);
-    return std::string(value) == "true";
-}
-
 std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
     switch(displayColorSetting) {
         case DisplayColorSetting::kManaged:
@@ -344,8 +329,6 @@
             PermissionCache::checkPermission(sRotateSurfaceFlinger, pid, uid);
 }
 
-SurfaceFlingerBE::SurfaceFlingerBE() : mHwcServiceName(getHwcServiceName()) {}
-
 SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
       : mFactory(factory),
         mInterceptor(mFactory.createSurfaceInterceptor()),
@@ -354,8 +337,11 @@
         mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, getpid())),
         mEventQueue(mFactory.createMessageQueue()),
         mCompositionEngine(mFactory.createCompositionEngine()),
+        mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)),
         mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
         mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) {
+    ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str());
+
     mSetInputWindowsListener = new SetInputWindowsListener([&]() { setInputWindowsFinished(); });
 }
 
@@ -469,12 +455,13 @@
     // comes online to attempt to read the property. The property is
     // instead read after the boot animation
 
-    if (useTrebleTestingOverride()) {
+    if (base::GetBoolProperty("debug.sf.treble_testing_override"s, false)) {
         // Without the override SurfaceFlinger cannot connect to HIDL
         // services that are not listed in the manifests.  Considered
         // deriving the setting from the set service name, but it
         // would be brittle if the name that's not 'default' is used
         // for production purposes later on.
+        ALOGI("Enabling Treble testing override");
         android::hardware::details::setTrebleTestingOverride(true);
     }
 
@@ -639,7 +626,6 @@
     ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
 
     mFrameTracer->initialize();
-    mTimeStats->onBootFinished();
     mFrameTimeline->onBootFinished();
 
     // wait patiently for the window manager death
@@ -693,12 +679,16 @@
 
     // The pool was empty, so we need to get a new texture name directly using a
     // blocking call to the main thread
-    return schedule([this] {
+    auto genTextures = [this] {
                uint32_t name = 0;
                getRenderEngine().genTextures(1, &name);
                return name;
-           })
-            .get();
+    };
+    if (std::this_thread::get_id() == mMainThreadId) {
+        return genTextures();
+    } else {
+        return schedule(genTextures).get();
+    }
 }
 
 void SurfaceFlinger::deleteTextureAsync(uint32_t texture) {
@@ -733,8 +723,9 @@
                                     : renderengine::RenderEngine::ContextPriority::MEDIUM)
                     .build()));
     mCompositionEngine->setTimeStats(mTimeStats);
-    mCompositionEngine->setHwComposer(getFactory().createHWComposer(getBE().mHwcServiceName));
+    mCompositionEngine->setHwComposer(getFactory().createHWComposer(mHwcServiceName));
     mCompositionEngine->getHwComposer().setConfiguration(this, getBE().mComposerSequenceId);
+    ClientCache::getInstance().setRenderEngine(&getRenderEngine());
     // Process any initial hotplug and resulting display changes.
     processDisplayHotplugEventsLocked();
     const auto display = getDefaultDisplayDeviceLocked();
@@ -755,6 +746,8 @@
         getRenderEngine().primeCache();
     }
 
+    getRenderEngine().onPrimaryDisplaySizeChanged(display->getSize());
+
     // Inform native graphics APIs whether the present timestamp is supported:
 
     const bool presentFenceReliable =
@@ -1304,6 +1297,11 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) {
+    *success = mTimeStats->onPullAtom(atomId, pulledData);
+    return NO_ERROR;
+}
+
 status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
                                                                ui::PixelFormat* outFormat,
                                                                ui::Dataspace* outDataspace,
@@ -1704,7 +1702,7 @@
     }));
 }
 
-sp<Fence> SurfaceFlinger::previousFrameFence() {
+SurfaceFlinger::FenceWithFenceTime SurfaceFlinger::previousFrameFence() {
     // 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
@@ -1714,9 +1712,9 @@
 
 bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
     ATRACE_CALL();
-    const sp<Fence>& fence = previousFrameFence();
+    const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime;
 
-    if (fence == Fence::NO_FENCE) {
+    if (fence == FenceTime::NO_FENCE) {
         return false;
     }
 
@@ -1727,9 +1725,9 @@
 }
 
 nsecs_t SurfaceFlinger::previousFramePresentTime() {
-    const sp<Fence>& fence = previousFrameFence();
+    const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime;
 
-    if (fence == Fence::NO_FENCE) {
+    if (fence == FenceTime::NO_FENCE) {
         return Fence::SIGNAL_TIME_INVALID;
     }
 
@@ -1812,10 +1810,6 @@
     if (frameMissed) {
         mFrameMissedCount++;
         mTimeStats->incrementMissedFrames();
-        if (mMissedFrameJankCount == 0) {
-            mMissedFrameJankStart = systemTime();
-        }
-        mMissedFrameJankCount++;
     }
 
     if (hwcFrameMissed) {
@@ -1847,37 +1841,6 @@
         }
     }
 
-    // Our jank window is always at least 100ms since we missed a
-    // frame...
-    static constexpr nsecs_t kMinJankyDuration =
-            std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
-    // ...but if it's larger than 1s then we missed the trace cutoff.
-    static constexpr nsecs_t kMaxJankyDuration =
-            std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
-    nsecs_t jankDurationToUpload = -1;
-    // If we're in a user build then don't push any atoms
-    if (!mIsUserBuild && mMissedFrameJankCount > 0) {
-        const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
-        // Only report jank when the display is on, as displays in DOZE
-        // power mode may operate at a different frame rate than is
-        // reported in their config, which causes noticeable (but less
-        // severe) jank.
-        if (display && display->getPowerMode() == hal::PowerMode::ON) {
-            const nsecs_t currentTime = systemTime();
-            const nsecs_t jankDuration = currentTime - mMissedFrameJankStart;
-            if (jankDuration > kMinJankyDuration && jankDuration < kMaxJankyDuration) {
-                jankDurationToUpload = jankDuration;
-            }
-
-            // We either reported a jank event or we missed the trace
-            // window, so clear counters here.
-            if (jankDuration > kMinJankyDuration) {
-                mMissedFrameJankCount = 0;
-                mMissedFrameJankStart = 0;
-            }
-        }
-    }
-
     if (mTracingEnabledChanged) {
         mTracingEnabled = mTracing.isEnabled();
         mTracingEnabledChanged = false;
@@ -1924,7 +1887,6 @@
 
     refreshNeeded |= mRepaintEverything;
     if (refreshNeeded && CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) {
-        mLastJankDuration = jankDurationToUpload;
         // Signal a refresh if a transaction modified the window state,
         // a new buffer was latched, or if HWC has requested a full
         // repaint
@@ -2010,6 +1972,8 @@
                 std::chrono::milliseconds(mDebugRegion > 1 ? mDebugRegion : 0);
     }
 
+    refreshArgs.earliestPresentTime = mScheduler->getPreviousVsyncFrom(mExpectedPresentTime);
+
     mGeometryInvalid = false;
 
     // Store the present time just before calling to the composition engine so we could notify
@@ -2070,6 +2034,9 @@
     ATRACE_CALL();
     bool refreshNeeded = handlePageFlip();
 
+    // Send on commit callbacks
+    mTransactionCallbackInvoker.sendCallbacks();
+
     if (mVisibleRegionsDirty) {
         computeLayerBounds();
     }
@@ -2160,17 +2127,18 @@
 
     getBE().mDisplayTimeline.updateSignalTimes();
     mPreviousPresentFences[1] = mPreviousPresentFences[0];
-    mPreviousPresentFences[0] =
+    mPreviousPresentFences[0].fence =
             display ? getHwComposer().getPresentFence(display->getPhysicalId()) : Fence::NO_FENCE;
-    auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFences[0]);
-    getBE().mDisplayTimeline.push(presentFenceTime);
+    mPreviousPresentFences[0].fenceTime =
+            std::make_shared<FenceTime>(mPreviousPresentFences[0].fence);
+
+    getBE().mDisplayTimeline.push(mPreviousPresentFences[0].fenceTime);
 
     // Set presentation information before calling Layer::releasePendingBuffer, such that jank
     // information from previous' frame classification is already available when sending jank info
     // to clients, so they get jank classification as early as possible.
-    mFrameTimeline->setSfPresent(systemTime(),
-                                 std::make_shared<FenceTime>(mPreviousPresentFences[0]),
-                                 glCompositionDoneFenceTime != FenceTime::NO_FENCE);
+    mFrameTimeline->setSfPresent(systemTime(), mPreviousPresentFences[0].fenceTime,
+                                 glCompositionDoneFenceTime);
 
     nsecs_t dequeueReadyTime = systemTime();
     for (const auto& layer : mLayersWithQueuedFrames) {
@@ -2183,7 +2151,7 @@
     // 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);
+                           mPreviousPresentFences[0].fenceTime);
     CompositorTiming compositorTiming;
     {
         std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
@@ -2191,8 +2159,9 @@
     }
 
     mDrawingState.traverse([&](Layer* layer) {
-        const bool frameLatched = layer->onPostComposition(display, glCompositionDoneFenceTime,
-                                                           presentFenceTime, compositorTiming);
+        const bool frameLatched =
+                layer->onPostComposition(display, glCompositionDoneFenceTime,
+                                         mPreviousPresentFences[0].fenceTime, compositorTiming);
         if (frameLatched) {
             recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false));
         }
@@ -2252,12 +2221,12 @@
         listener->dispatchHdrLayerInfo(info);
     }
 
-    mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0]);
+    mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0].fence);
     mTransactionCallbackInvoker.sendCallbacks();
 
     if (display && display->isPrimary() && display->getPowerMode() == hal::PowerMode::ON &&
-        presentFenceTime->isValid()) {
-        mScheduler->addPresentFence(presentFenceTime);
+        mPreviousPresentFences[0].fenceTime->isValid()) {
+        mScheduler->addPresentFence(mPreviousPresentFences[0].fenceTime);
     }
 
     const bool isDisplayConnected =
@@ -2272,9 +2241,8 @@
     if (mAnimCompositionPending) {
         mAnimCompositionPending = false;
 
-        if (presentFenceTime->isValid()) {
-            mAnimFrameTracker.setActualPresentFence(
-                    std::move(presentFenceTime));
+        if (mPreviousPresentFences[0].fenceTime->isValid()) {
+            mAnimFrameTracker.setActualPresentFence(mPreviousPresentFences[0].fenceTime);
         } else if (isDisplayConnected) {
             // The HWC doesn't support present fences, so use the refresh
             // timestamp instead.
@@ -2293,20 +2261,12 @@
         mTimeStats->incrementClientCompositionReusedFrames();
     }
 
-    mTimeStats->setPresentFenceGlobal(presentFenceTime);
+    mTimeStats->setPresentFenceGlobal(mPreviousPresentFences[0].fenceTime);
 
     const size_t sfConnections = mScheduler->getEventThreadConnectionCount(mSfConnectionHandle);
     const size_t appConnections = mScheduler->getEventThreadConnectionCount(mAppConnectionHandle);
     mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections);
 
-    if (mLastJankDuration > 0) {
-        ATRACE_NAME("Jank detected");
-        const int32_t jankyDurationMillis = mLastJankDuration / (1000 * 1000);
-        android::util::stats_write(android::util::DISPLAY_JANK_REPORTED, jankyDurationMillis,
-                                   mMissedFrameJankCount);
-        mLastJankDuration = -1;
-    }
-
     if (isDisplayConnected && !display->isPoweredOn()) {
         return;
     }
@@ -2724,6 +2684,7 @@
 
     if (display->isPrimary()) {
         mScheduler->onPrimaryDisplayAreaChanged(display->getWidth() * display->getHeight());
+        getRenderEngine().onPrimaryDisplaySizeChanged(display->getSize());
     }
 }
 
@@ -2798,6 +2759,7 @@
             (currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) {
             display->setProjection(currentState.orientation, currentState.layerStackSpaceRect,
                                    currentState.orientedDisplaySpaceRect);
+            mDefaultDisplayTransformHint = display->getTransformHint();
         }
         if (currentState.width != drawingState.width ||
             currentState.height != drawingState.height) {
@@ -2855,13 +2817,6 @@
 }
 
 void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) {
-    const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
-
-    // Notify all layers of available frames
-    mCurrentState.traverse([expectedPresentTime](Layer* layer) {
-        layer->notifyAvailableFrames(expectedPresentTime);
-    });
-
     /*
      * Traversal of the children
      * (perform the transaction for each of them if needed)
@@ -3311,66 +3266,38 @@
                                         const sp<IBinder>& parentHandle,
                                         const sp<Layer>& parentLayer, bool addToCurrentState,
                                         uint32_t* outTransformHint) {
-    // add this layer to the current state list
-    {
-        Mutex::Autolock _l(mStateLock);
-        sp<Layer> parent;
-        if (parentHandle != nullptr) {
-            parent = fromHandleLocked(parentHandle).promote();
-            if (parent == nullptr) {
-                return NAME_NOT_FOUND;
-            }
-        } else {
-            parent = parentLayer;
-        }
-
-        if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) {
-            ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
-                  ISurfaceComposer::MAX_LAYERS);
-            return NO_MEMORY;
-        }
-
-        mLayersByLocalBinderToken.emplace(handle->localBinder(), lbc);
-
-        if (parent == nullptr && addToCurrentState) {
-            mCurrentState.layersSortedByZ.add(lbc);
-        } else if (parent == nullptr) {
-            lbc->onRemovedFromCurrentState();
-        } else if (parent->isRemovedFromCurrentState()) {
-            parent->addChild(lbc);
-            lbc->onRemovedFromCurrentState();
-        } else {
-            parent->addChild(lbc);
-        }
-
-        if (gbc != nullptr) {
-            mGraphicBufferProducerList.insert(IInterface::asBinder(gbc).get());
-            LOG_ALWAYS_FATAL_IF(mGraphicBufferProducerList.size() >
-                                        mMaxGraphicBufferProducerListSize,
-                                "Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
-                                mGraphicBufferProducerList.size(),
-                                mMaxGraphicBufferProducerListSize, mNumLayers.load());
-            if (mGraphicBufferProducerList.size() > mGraphicBufferProducerListSizeLogThreshold) {
-                ALOGW("Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
-                      mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize,
-                      mNumLayers.load());
-            }
-        }
-
-        if (const auto display = getDefaultDisplayDeviceLocked()) {
-            lbc->updateTransformHint(display->getTransformHint());
-        }
-        if (outTransformHint) {
-            *outTransformHint = lbc->getTransformHint();
-        }
-
-        mLayersAdded = true;
+    if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) {
+        ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
+              ISurfaceComposer::MAX_LAYERS);
+        return NO_MEMORY;
     }
 
+    wp<IBinder> initialProducer;
+    if (gbc != nullptr) {
+        initialProducer = IInterface::asBinder(gbc);
+    }
+    setLayerCreatedState(handle, lbc, parentHandle, parentLayer, initialProducer);
+
+    // Create a transaction includes the initial parent and producer.
+    Vector<ComposerState> states;
+    Vector<DisplayState> displays;
+
+    ComposerState composerState;
+    composerState.state.what = layer_state_t::eLayerCreated;
+    composerState.state.surface = handle;
+    states.add(composerState);
+
+    lbc->updateTransformHint(mDefaultDisplayTransformHint);
+    if (outTransformHint) {
+        *outTransformHint = mDefaultDisplayTransformHint;
+    }
     // attach this layer to the client
     client->attachLayer(handle, lbc);
 
-    return NO_ERROR;
+    return setTransactionState(FrameTimelineInfo{}, states, displays, 0 /* flags */, nullptr,
+                               InputWindowCommands{}, -1 /* desiredPresentTime */,
+                               true /* isAutoTimestamp */, {}, false /* hasListenerCallbacks */, {},
+                               0 /* Undefined transactionId */);
 }
 
 void SurfaceFlinger::removeGraphicBufferProducerAsync(const wp<IBinder>& binder) {
@@ -3409,7 +3336,7 @@
     // states) around outside the scope of the lock
     std::vector<const TransactionState> transactions;
     // Layer handles that have transactions with buffers that are ready to be applied.
-    std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> pendingBuffers;
+    std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> bufferLayersReadyToPresent;
     {
         Mutex::Autolock _l(mStateLock);
         {
@@ -3425,10 +3352,13 @@
                                                        transaction.isAutoTimestamp,
                                                        transaction.desiredPresentTime,
                                                        transaction.originUid, transaction.states,
-                                                       pendingBuffers)) {
+                                                       bufferLayersReadyToPresent)) {
                         setTransactionFlags(eTransactionFlushNeeded);
                         break;
                     }
+                    transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
+                        bufferLayersReadyToPresent.insert(state.surface);
+                    });
                     transactions.emplace_back(std::move(transaction));
                     transactionQueue.pop();
                 }
@@ -3449,14 +3379,17 @@
                 auto& transaction = mTransactionQueue.front();
                 bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) !=
                         mPendingTransactionQueues.end();
-                if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
+                if (pendingTransactions ||
+                    !transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
                                                    transaction.isAutoTimestamp,
                                                    transaction.desiredPresentTime,
                                                    transaction.originUid, transaction.states,
-                                                   pendingBuffers) ||
-                    pendingTransactions) {
+                                                   bufferLayersReadyToPresent)) {
                     mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
                 } else {
+                    transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
+                        bufferLayersReadyToPresent.insert(state.surface);
+                    });
                     transactions.emplace_back(std::move(transaction));
                 }
                 mTransactionQueue.pop();
@@ -3511,28 +3444,28 @@
 bool SurfaceFlinger::transactionIsReadyToBeApplied(
         const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
         uid_t originUid, const Vector<ComposerState>& states,
-        std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers) {
+        const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
+                bufferLayersReadyToPresent) const {
     ATRACE_CALL();
     const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
-    bool ready = true;
     // 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 (!isAutoTimestamp && desiredPresentTime >= expectedPresentTime &&
         desiredPresentTime < expectedPresentTime + s2ns(1)) {
         ATRACE_NAME("not current");
-        ready = false;
+        return false;
     }
 
     if (!mScheduler->isVsyncValid(expectedPresentTime, originUid)) {
         ATRACE_NAME("!isVsyncValid");
-        ready = false;
+        return false;
     }
 
     // If the client didn't specify desiredPresentTime, use the vsyncId to determine the expected
     // present time of this transaction.
     if (isAutoTimestamp && frameIsEarly(expectedPresentTime, info.vsyncId)) {
         ATRACE_NAME("frameIsEarly");
-        ready = false;
+        return false;
     }
 
     for (const ComposerState& state : states) {
@@ -3540,13 +3473,14 @@
         const bool acquireFenceChanged = (s.what & layer_state_t::eAcquireFenceChanged);
         if (acquireFenceChanged && s.acquireFence &&
             s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
-            ready = false;
+            ATRACE_NAME("fence unsignaled");
+            return false;
         }
 
         sp<Layer> layer = nullptr;
         if (s.surface) {
             layer = fromHandleLocked(s.surface).promote();
-        } else if (acquireFenceChanged) {
+        } else if (s.hasBufferChanges()) {
             ALOGW("Transaction with buffer, but no Layer?");
             continue;
         }
@@ -3556,18 +3490,18 @@
 
         ATRACE_NAME(layer->getName().c_str());
 
-        if (acquireFenceChanged) {
+        if (s.hasBufferChanges()) {
             // If backpressure is enabled and we already have a buffer to commit, keep the
             // transaction in the queue.
-            const bool hasPendingBuffer = pendingBuffers.find(s.surface) != pendingBuffers.end();
+            const bool hasPendingBuffer =
+                    bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end();
             if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
                 ATRACE_NAME("hasPendingBuffer");
-                ready = false;
+                return false;
             }
-            pendingBuffers.insert(s.surface);
         }
     }
-    return ready;
+    return true;
 }
 
 void SurfaceFlinger::queueTransaction(TransactionState& state) {
@@ -3637,13 +3571,6 @@
         const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
     ATRACE_CALL();
 
-    // Check for incoming buffer updates and increment the pending buffer count.
-    for (const auto& state : states) {
-        if ((state.state.what & layer_state_t::eAcquireFenceChanged) && (state.state.surface)) {
-            mBufferCountTracker.increment(state.state.surface->localBinder());
-        }
-    }
-
     uint32_t permissions =
             callingThreadHasUnscopedSurfaceFlingerAccess() ? Permission::ACCESS_SURFACE_FLINGER : 0;
     // Avoid checking for rotation permissions if the caller already has ACCESS_SURFACE_FLINGER
@@ -3672,6 +3599,11 @@
                            permissions,        hasListenerCallbacks,
                            listenerCallbacks,  originPid,
                            originUid,          transactionId};
+
+    // Check for incoming buffer updates and increment the pending buffer count.
+    state.traverseStatesWithBuffers([&](const layer_state_t& state) {
+        mBufferCountTracker.increment(state.surface->localBinder());
+    });
     queueTransaction(state);
 
     // Check the pending state to make sure the transaction is synchronous.
@@ -3738,7 +3670,6 @@
 
     if (uncacheBuffer.isValid()) {
         ClientCache::getInstance().erase(uncacheBuffer);
-        getRenderEngine().unbindExternalTextureBuffer(uncacheBuffer.id);
     }
 
     // If a synchronous transaction is explicitly requested without any changes, force a transaction
@@ -3835,19 +3766,47 @@
 uint32_t SurfaceFlinger::setClientStateLocked(
         const FrameTimelineInfo& frameTimelineInfo, const ComposerState& composerState,
         int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, uint32_t permissions,
-        std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) {
+        std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& outListenerCallbacks) {
     const layer_state_t& s = composerState.state;
     const bool privileged = permissions & Permission::ACCESS_SURFACE_FLINGER;
+
+    std::vector<ListenerCallbacks> filteredListeners;
     for (auto& listener : s.listeners) {
+        // Starts a registration but separates the callback ids according to callback type. This
+        // allows the callback invoker to send on latch callbacks earlier.
         // note that startRegistration will not re-register if the listener has
         // already be registered for a prior surface control
-        mTransactionCallbackInvoker.startRegistration(listener);
-        listenerCallbacks.insert(listener);
+
+        ListenerCallbacks onCommitCallbacks = listener.filter(CallbackId::Type::ON_COMMIT);
+        if (!onCommitCallbacks.callbackIds.empty()) {
+            mTransactionCallbackInvoker.startRegistration(onCommitCallbacks);
+            filteredListeners.push_back(onCommitCallbacks);
+            outListenerCallbacks.insert(onCommitCallbacks);
+        }
+
+        ListenerCallbacks onCompleteCallbacks = listener.filter(CallbackId::Type::ON_COMPLETE);
+        if (!onCompleteCallbacks.callbackIds.empty()) {
+            mTransactionCallbackInvoker.startRegistration(onCompleteCallbacks);
+            filteredListeners.push_back(onCompleteCallbacks);
+            outListenerCallbacks.insert(onCompleteCallbacks);
+        }
     }
 
+    const uint64_t what = s.what;
+    uint32_t flags = 0;
     sp<Layer> layer = nullptr;
     if (s.surface) {
-        layer = fromHandleLocked(s.surface).promote();
+        if (what & layer_state_t::eLayerCreated) {
+            layer = handleLayerCreatedLocked(s.surface, privileged);
+            if (layer) {
+                // put the created layer into mLayersByLocalBinderToken.
+                mLayersByLocalBinderToken.emplace(s.surface->localBinder(), layer);
+                flags |= eTransactionNeeded | eTraversalNeeded;
+                mLayersAdded = true;
+            }
+        } else {
+            layer = fromHandleLocked(s.surface).promote();
+        }
     } 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");
@@ -3860,16 +3819,6 @@
         return 0;
     }
 
-    uint32_t flags = 0;
-
-    const uint64_t what = s.what;
-
-    // If we are deferring transaction, make sure to push the pending state, as otherwise the
-    // pending state will also be deferred.
-    if (what & layer_state_t::eDeferTransaction_legacy) {
-        layer->pushPendingState();
-    }
-
     // Only set by BLAST adapter layers
     if (what & layer_state_t::eProducerDisconnect) {
         layer->onDisconnect();
@@ -4004,17 +3953,6 @@
             flags |= eTransactionNeeded | eTraversalNeeded | eTransformHintUpdateNeeded;
         }
     }
-    if (what & layer_state_t::eDeferTransaction_legacy) {
-        layer->deferTransactionUntil_legacy(s.barrierSurfaceControl_legacy->getHandle(),
-                                            s.barrierFrameNumber);
-        // 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::eReparentChildren) {
-        if (layer->reparentChildren(s.reparentSurfaceControl->getHandle())) {
-            flags |= eTransactionNeeded|eTraversalNeeded;
-        }
-    }
     if (what & layer_state_t::eTransformChanged) {
         if (layer->setTransform(s.transform)) flags |= eTraversalNeeded;
     }
@@ -4025,9 +3963,6 @@
     if (what & layer_state_t::eCropChanged) {
         if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
     }
-    if (what & layer_state_t::eFrameChanged) {
-        if (layer->setFrame(s.orientedDisplaySpaceRect)) flags |= eTraversalNeeded;
-    }
     if (what & layer_state_t::eAcquireFenceChanged) {
         if (layer->setAcquireFence(s.acquireFence)) flags |= eTraversalNeeded;
     }
@@ -4073,13 +4008,16 @@
         }
     }
     if (what & layer_state_t::eFrameRateChanged) {
-        if (ValidateFrameRate(s.frameRate, s.frameRateCompatibility,
-                              "SurfaceFlinger::setClientStateLocked", privileged) &&
-            layer->setFrameRate(Layer::FrameRate(Fps(s.frameRate),
-                                                 Layer::FrameRate::convertCompatibility(
-                                                         s.frameRateCompatibility),
-                                                 s.shouldBeSeamless))) {
-            flags |= eTraversalNeeded;
+        if (ValidateFrameRate(s.frameRate, s.frameRateCompatibility, s.changeFrameRateStrategy,
+                              "SurfaceFlinger::setClientStateLocked", privileged)) {
+            const auto compatibility =
+                    Layer::FrameRate::convertCompatibility(s.frameRateCompatibility);
+            const auto strategy =
+                    Layer::FrameRate::convertChangeFrameRateStrategy(s.changeFrameRateStrategy);
+
+            if (layer->setFrameRate(Layer::FrameRate(Fps(s.frameRate), compatibility, strategy))) {
+                flags |= eTraversalNeeded;
+            }
         }
     }
     if (what & layer_state_t::eFixedTransformHintChanged) {
@@ -4112,30 +4050,23 @@
         }
     }
     std::vector<sp<CallbackHandle>> callbackHandles;
-    if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!s.listeners.empty())) {
-        for (auto& [listener, callbackIds] : s.listeners) {
+    if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) {
+        for (auto& [listener, callbackIds] : filteredListeners) {
             callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
         }
     }
     bool bufferChanged = what & layer_state_t::eBufferChanged;
     bool cacheIdChanged = what & layer_state_t::eCachedBufferChanged;
-    sp<GraphicBuffer> buffer;
+    std::shared_ptr<renderengine::ExternalTexture> buffer;
     if (bufferChanged && cacheIdChanged && s.buffer != nullptr) {
-        buffer = s.buffer;
-        bool success = ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
-        if (success) {
-            getRenderEngine().cacheExternalTextureBuffer(s.buffer);
-            success = ClientCache::getInstance()
-                              .registerErasedRecipient(s.cachedBuffer,
-                                                       wp<ClientCache::ErasedRecipient>(this));
-            if (!success) {
-                getRenderEngine().unbindExternalTextureBuffer(s.buffer->getId());
-            }
-        }
+        ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
+        buffer = ClientCache::getInstance().get(s.cachedBuffer);
     } else if (cacheIdChanged) {
         buffer = ClientCache::getInstance().get(s.cachedBuffer);
-    } else if (bufferChanged) {
-        buffer = s.buffer;
+    } else if (bufferChanged && s.buffer != nullptr) {
+        buffer = std::make_shared<
+                renderengine::ExternalTexture>(s.buffer, getRenderEngine(),
+                                               renderengine::ExternalTexture::Usage::READABLE);
     }
     if (buffer) {
         const bool frameNumberChanged = what & layer_state_t::eFrameNumberChanged;
@@ -4218,10 +4149,6 @@
 
     switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
         case ISurfaceComposerClient::eFXSurfaceBufferQueue:
-            result = createBufferQueueLayer(client, std::move(uniqueName), w, h, flags,
-                                            std::move(metadata), format, handle, gbp, &layer);
-
-            break;
         case ISurfaceComposerClient::eFXSurfaceBufferState: {
             result = createBufferStateLayer(client, std::move(uniqueName), w, h, flags,
                                             std::move(metadata), handle, &layer);
@@ -4345,14 +4272,7 @@
                                                 sp<Layer>* outLayer) {
     LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
     args.textureName = getNewTexture();
-    sp<BufferStateLayer> layer;
-    {
-        // TODO (b/173538294): Investigate why we need mStateLock here and above in
-        // createBufferQueue layer. Is it the renderengine::Image?
-        Mutex::Autolock lock(mStateLock);
-        layer = getFactory().createBufferStateLayer(args);
-
-    }
+    sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(args);
     *handle = layer->getHandle();
     *outLayer = layer;
 
@@ -4440,7 +4360,7 @@
     setPowerModeInternal(display, hal::PowerMode::ON);
     const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
     mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
-
+    mDefaultDisplayTransformHint = display->getTransformHint();
     // Use phase of 0 since phase is not known.
     // Use latency of 0, which will snap to the ideal latency.
     DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
@@ -5111,13 +5031,16 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic error "-Wswitch-enum"
     switch (static_cast<ISurfaceComposerTag>(code)) {
+        case ENABLE_VSYNC_INJECTIONS:
+        case INJECT_VSYNC:
+            if (!hasMockHwc()) return PERMISSION_DENIED;
+            [[fallthrough]];
         // These methods should at minimum make sure that the client requested
         // access to SF.
         case BOOT_FINISHED:
         case CLEAR_ANIMATION_FRAME_STATS:
         case CREATE_DISPLAY:
         case DESTROY_DISPLAY:
-        case ENABLE_VSYNC_INJECTIONS:
         case GET_ANIMATION_FRAME_STATS:
         case OVERRIDE_HDR_TYPES:
         case GET_HDR_CAPABILITIES:
@@ -5128,7 +5051,6 @@
         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:
@@ -5235,6 +5157,13 @@
             }
             return PERMISSION_DENIED;
         }
+        case ON_PULL_ATOM: {
+            const int uid = IPCThreadState::self()->getCallingUid();
+            if (uid == AID_SYSTEM) {
+                return OK;
+            }
+            return PERMISSION_DENIED;
+        }
     }
 
     // These codes are used for the IBinder protocol to either interrogate the recipient
@@ -5564,9 +5493,24 @@
                 const int modeId = data.readInt32();
                 mDebugDisplayModeSetByBackdoor = false;
 
-                const auto displayId = getInternalDisplayId();
+                const auto displayId = [&]() -> std::optional<PhysicalDisplayId> {
+                    uint64_t inputDisplayId = 0;
+                    if (data.readUint64(&inputDisplayId) == NO_ERROR) {
+                        const auto token = getPhysicalDisplayToken(
+                                static_cast<PhysicalDisplayId>(inputDisplayId));
+                        if (!token) {
+                            ALOGE("No display with id: %" PRIu64, inputDisplayId);
+                            return std::nullopt;
+                        }
+
+                        return std::make_optional<PhysicalDisplayId>(inputDisplayId);
+                    }
+
+                    return getInternalDisplayId();
+                }();
+
                 if (!displayId) {
-                    ALOGE("No internal display found.");
+                    ALOGE("No display found");
                     return NO_ERROR;
                 }
 
@@ -6037,15 +5981,17 @@
     const status_t bufferStatus = buffer->initCheck();
     LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureScreenCommon: Buffer failed to allocate: %d",
                         bufferStatus);
-    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
+    const auto texture = std::make_shared<
+            renderengine::ExternalTexture>(buffer, getRenderEngine(),
+                                           renderengine::ExternalTexture::Usage::WRITEABLE);
+    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, texture,
                                false /* regionSampling */, grayscale, captureListener);
 }
 
-status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
-                                             TraverseLayersFunction traverseLayers,
-                                             sp<GraphicBuffer>& buffer, bool regionSampling,
-                                             bool grayscale,
-                                             const sp<IScreenCaptureListener>& captureListener) {
+status_t SurfaceFlinger::captureScreenCommon(
+        RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers,
+        const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
+        bool grayscale, const sp<IScreenCaptureListener>& captureListener) {
     ATRACE_CALL();
 
     if (captureListener == nullptr) {
@@ -6078,15 +6024,6 @@
                                             regionSampling, grayscale, captureResults);
         });
 
-        // TODO(b/180767535): Remove this once we optimize buffer lifecycle for RenderEngine
-        // Only do this when we're not doing region sampling, to allow the region sampling thread to
-        // manage buffer lifecycle itself.
-        if (!regionSampling &&
-            getRenderEngine().getRenderEngineType() ==
-                    renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED) {
-            getRenderEngine().unbindExternalTextureBuffer(buffer->getId());
-        }
-
         captureResults.result = result;
         captureListener->onScreenCaptureCompleted(captureResults);
     }));
@@ -6094,11 +6031,10 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
-                                                TraverseLayersFunction traverseLayers,
-                                                const sp<GraphicBuffer>& buffer, bool forSystem,
-                                                bool regionSampling, bool grayscale,
-                                                ScreenCaptureResults& captureResults) {
+status_t SurfaceFlinger::renderScreenImplLocked(
+        const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
+        const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool forSystem,
+        bool regionSampling, bool grayscale, ScreenCaptureResults& captureResults) {
     ATRACE_CALL();
 
     traverseLayers([&](Layer* layer) {
@@ -6106,7 +6042,7 @@
                 captureResults.capturedSecureLayers || (layer->isVisible() && layer->isSecure());
     });
 
-    const bool useProtected = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
+    const bool useProtected = buffer->getBuffer()->getUsage() & GRALLOC_USAGE_PROTECTED;
 
     // We allow the system server to take screenshots of secure layers for
     // use in situations like the Screen-rotation animation and place
@@ -6116,7 +6052,7 @@
         return PERMISSION_DENIED;
     }
 
-    captureResults.buffer = buffer;
+    captureResults.buffer = buffer->getBuffer();
     captureResults.capturedDataspace = renderArea.getReqDataSpace();
 
     const auto reqWidth = renderArea.getReqWidth();
@@ -6207,11 +6143,9 @@
     base::unique_fd drawFence;
     getRenderEngine().useProtectedContext(useProtected);
 
-    // TODO(b/180767535): Remove this once we optimize buffer lifecycle for RenderEngine
-    const bool useFramebufferCache = getRenderEngine().getRenderEngineType() ==
-            renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED;
+    const constexpr bool kUseFramebufferCache = false;
     getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer,
-                                 useFramebufferCache, std::move(bufferFence), &drawFence);
+                                 kUseFramebufferCache, std::move(bufferFence), &drawFence);
 
     if (drawFence >= 0) {
         sp<Fence> releaseFence = new Fence(dup(drawFence));
@@ -6279,6 +6213,11 @@
                         "Can only set override policy on the primary display");
     LOG_ALWAYS_FATAL_IF(!policy && !overridePolicy, "Can only clear the override policy");
 
+    if (mDebugDisplayModeSetByBackdoor) {
+        // ignore this request as mode is overridden by backdoor
+        return NO_ERROR;
+    }
+
     if (!display->isPrimary()) {
         // TODO(b/144711714): For non-primary displays we should be able to set an active mode
         // as well. For now, just call directly to initiateModeChange but ideally
@@ -6305,11 +6244,6 @@
         return NO_ERROR;
     }
 
-    if (mDebugDisplayModeSetByBackdoor) {
-        // ignore this request as mode is overridden by backdoor
-        return NO_ERROR;
-    }
-
     status_t setPolicyResult = overridePolicy
             ? mRefreshRateConfigs->setOverridePolicy(policy)
             : mRefreshRateConfigs->setDisplayManagerPolicy(*policy);
@@ -6436,7 +6370,7 @@
     return fromHandleLocked(handle);
 }
 
-wp<Layer> SurfaceFlinger::fromHandleLocked(const sp<IBinder>& handle) {
+wp<Layer> SurfaceFlinger::fromHandleLocked(const sp<IBinder>& handle) const {
     BBinder* b = nullptr;
     if (handle) {
         b = handle->localBinder();
@@ -6457,6 +6391,7 @@
 }
 
 void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
+    mScheduler->deregisterLayer(layer);
     mNumLayers--;
     removeFromOffscreenLayers(layer);
 }
@@ -6475,10 +6410,6 @@
     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) {
@@ -6508,8 +6439,9 @@
 }
 
 status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
-                                      int8_t compatibility, bool shouldBeSeamless) {
-    if (!ValidateFrameRate(frameRate, compatibility, "SurfaceFlinger::setFrameRate")) {
+                                      int8_t compatibility, int8_t changeFrameRateStrategy) {
+    if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+                           "SurfaceFlinger::setFrameRate")) {
         return BAD_VALUE;
     }
 
@@ -6521,10 +6453,12 @@
                 ALOGE("Attempt to set frame rate on a layer that no longer exists");
                 return BAD_VALUE;
             }
+            const auto strategy =
+                    Layer::FrameRate::convertChangeFrameRateStrategy(changeFrameRateStrategy);
             if (layer->setFrameRate(
                         Layer::FrameRate(Fps{frameRate},
                                          Layer::FrameRate::convertCompatibility(compatibility),
-                                         shouldBeSeamless))) {
+                                         strategy))) {
                 setTransactionFlags(eTraversalNeeded);
             }
         } else {
@@ -6678,6 +6612,96 @@
     return NO_ERROR;
 }
 
+void SurfaceFlinger::TransactionState::traverseStatesWithBuffers(
+        std::function<void(const layer_state_t&)> visitor) {
+    for (const auto& state : states) {
+        if (state.state.hasBufferChanges() && (state.state.surface)) {
+            visitor(state.state);
+        }
+    }
+}
+
+void SurfaceFlinger::setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
+                                          const wp<IBinder>& parent, const wp<Layer> parentLayer,
+                                          const wp<IBinder>& producer) {
+    Mutex::Autolock lock(mCreatedLayersLock);
+    mCreatedLayers[handle->localBinder()] =
+            std::make_unique<LayerCreatedState>(layer, parent, parentLayer, producer);
+}
+
+auto SurfaceFlinger::getLayerCreatedState(const sp<IBinder>& handle) {
+    Mutex::Autolock lock(mCreatedLayersLock);
+    BBinder* b = nullptr;
+    if (handle) {
+        b = handle->localBinder();
+    }
+
+    if (b == nullptr) {
+        return std::unique_ptr<LayerCreatedState>(nullptr);
+    }
+
+    auto it = mCreatedLayers.find(b);
+    if (it == mCreatedLayers.end()) {
+        ALOGE("Can't find layer from handle %p", handle.get());
+        return std::unique_ptr<LayerCreatedState>(nullptr);
+    }
+
+    auto state = std::move(it->second);
+    mCreatedLayers.erase(it);
+    return state;
+}
+
+sp<Layer> SurfaceFlinger::handleLayerCreatedLocked(const sp<IBinder>& handle, bool privileged) {
+    const auto& state = getLayerCreatedState(handle);
+    if (!state) {
+        return nullptr;
+    }
+
+    sp<Layer> layer = state->layer.promote();
+    if (!layer) {
+        ALOGE("Invalid layer %p", state->layer.unsafe_get());
+        return nullptr;
+    }
+
+    sp<Layer> parent;
+    bool allowAddRoot = privileged;
+    if (state->initialParent != nullptr) {
+        parent = fromHandleLocked(state->initialParent.promote()).promote();
+        if (parent == nullptr) {
+            ALOGE("Invalid parent %p", state->initialParent.unsafe_get());
+            allowAddRoot = false;
+        }
+    } else if (state->initialParentLayer != nullptr) {
+        parent = state->initialParentLayer.promote();
+        allowAddRoot = false;
+    }
+
+    if (parent == nullptr && allowAddRoot) {
+        mCurrentState.layersSortedByZ.add(layer);
+    } else if (parent == nullptr) {
+        layer->onRemovedFromCurrentState();
+    } else if (parent->isRemovedFromCurrentState()) {
+        parent->addChild(layer);
+        layer->onRemovedFromCurrentState();
+    } else {
+        parent->addChild(layer);
+    }
+
+    if (state->initialProducer != nullptr) {
+        mGraphicBufferProducerList.insert(state->initialProducer);
+        LOG_ALWAYS_FATAL_IF(mGraphicBufferProducerList.size() > mMaxGraphicBufferProducerListSize,
+                            "Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
+                            mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize,
+                            mNumLayers.load());
+        if (mGraphicBufferProducerList.size() > mGraphicBufferProducerListSizeLogThreshold) {
+            ALOGW("Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
+                  mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize,
+                  mNumLayers.load());
+        }
+    }
+
+    return layer;
+}
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c6cbc51..cf1a545 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -134,13 +134,7 @@
 
 using DisplayColorSetting = compositionengine::OutputColorSetting;
 
-class SurfaceFlingerBE
-{
-public:
-    SurfaceFlingerBE();
-
-    const std::string mHwcServiceName; // "default" for real use, something else for testing.
-
+struct SurfaceFlingerBE {
     FenceTimeline mGlCompositionDoneTimeline;
     FenceTimeline mDisplayTimeline;
 
@@ -184,7 +178,6 @@
 
 class SurfaceFlinger : public BnSurfaceComposer,
                        public PriorityDumper,
-                       public ClientCache::ErasedRecipient,
                        private IBinder::DeathRecipient,
                        private HWC2::ComposerCallback,
                        private ISchedulerCallback {
@@ -330,10 +323,7 @@
     // Otherwise, returns a weak reference so that callers off the main-thread
     // won't accidentally hold onto the last strong reference.
     wp<Layer> fromHandle(const sp<IBinder>& handle);
-    wp<Layer> fromHandleLocked(const sp<IBinder>& handle) REQUIRES(mStateLock);
-
-    // Inherit from ClientCache::ErasedRecipient
-    void bufferErased(const client_cache_t& clientCacheId) override;
+    wp<Layer> fromHandleLocked(const sp<IBinder>& handle) const REQUIRES(mStateLock);
 
     // If set, disables reusing client composition buffers. This can be set by
     // debug.sf.disable_client_composition_cache
@@ -537,6 +527,8 @@
                 originUid(originUid),
                 id(transactionId) {}
 
+        void traverseStatesWithBuffers(std::function<void(const layer_state_t&)> visitor);
+
         FrameTimelineInfo frameTimelineInfo;
         Vector<ComposerState> states;
         Vector<DisplayState> displays;
@@ -645,6 +637,7 @@
     status_t getAnimationFrameStats(FrameStats* outStats) const override;
     status_t overrideHdrTypes(const sp<IBinder>& displayToken,
                               const std::vector<ui::Hdr>& hdrTypes) override;
+    status_t onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) override;
     status_t enableVSyncInjections(bool enable) override;
     status_t injectVSync(nsecs_t when) override;
     status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) override;
@@ -693,7 +686,7 @@
     status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
                                      float lightPosY, float lightPosZ, float lightRadius) override;
     status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
-                          int8_t compatibility, bool shouldBeSeamless) override;
+                          int8_t compatibility, int8_t changeFrameRateStrategy) override;
     status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override;
 
     status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
@@ -846,8 +839,8 @@
     bool transactionIsReadyToBeApplied(
             const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
             uid_t originUid, const Vector<ComposerState>& states,
-            std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers)
-            REQUIRES(mStateLock);
+            const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
+                    bufferLayersReadyToPresent) const REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
             REQUIRES(mStateLock);
@@ -905,12 +898,14 @@
     status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize,
                                  ui::PixelFormat, bool allowProtected, bool grayscale,
                                  const sp<IScreenCaptureListener>&);
-    status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, sp<GraphicBuffer>&,
+    status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction,
+                                 const std::shared_ptr<renderengine::ExternalTexture>&,
                                  bool regionSampling, bool grayscale,
                                  const sp<IScreenCaptureListener>&);
     status_t renderScreenImplLocked(const RenderArea&, TraverseLayersFunction,
-                                    const sp<GraphicBuffer>&, bool forSystem, bool regionSampling,
-                                    bool grayscale, ScreenCaptureResults&);
+                                    const std::shared_ptr<renderengine::ExternalTexture>&,
+                                    bool forSystem, bool regionSampling, bool grayscale,
+                                    ScreenCaptureResults&);
 
     sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
     sp<DisplayDevice> getDisplayById(DisplayId displayId) const REQUIRES(mStateLock);
@@ -1019,9 +1014,14 @@
 
     bool isDisplayModeAllowed(DisplayModeId) const REQUIRES(mStateLock);
 
+    struct FenceWithFenceTime {
+        sp<Fence> fence = Fence::NO_FENCE;
+        std::shared_ptr<FenceTime> fenceTime = FenceTime::NO_FENCE;
+    };
+
     // Gets the fence for the previous frame.
     // Must be called on the main thread.
-    sp<Fence> previousFrameFence();
+    FenceWithFenceTime previousFrameFence();
 
     // Whether the previous frame has not yet been presented to the display.
     // If graceTimeMs is positive, this method waits for at most the provided
@@ -1199,7 +1199,7 @@
     std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> mLayersWithQueuedFrames;
     // Tracks layers that need to update a display's dirty region.
     std::vector<sp<Layer>> mLayersPendingRefresh;
-    std::array<sp<Fence>, 2> mPreviousPresentFences = {Fence::NO_FENCE, Fence::NO_FENCE};
+    std::array<FenceWithFenceTime, 2> mPreviousPresentFences;
     // True if in the previous frame at least one layer was composed via the GPU.
     bool mHadClientComposition = false;
     // True if in the previous frame at least one layer was composed via HW Composer.
@@ -1316,6 +1316,10 @@
     SurfaceFlingerBE mBE;
     std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
 
+    const std::string mHwcServiceName;
+
+    bool hasMockHwc() const { return mHwcServiceName == "mock"; }
+
     /*
      * Scheduler
      */
@@ -1380,13 +1384,6 @@
     // be any issues with a raw pointer referencing an invalid object.
     std::unordered_set<Layer*> mOffscreenLayers;
 
-    // Fields tracking the current jank event: when it started and how many
-    // janky frames there are.
-    nsecs_t mMissedFrameJankStart = 0;
-    int32_t mMissedFrameJankCount = 0;
-    // Positive if jank should be uploaded in postComposition
-    nsecs_t mLastJankDuration = -1;
-
     int mFrameRateFlexibilityTokenCount = 0;
 
     sp<IBinder> mDebugFrameRateFlexibilityToken;
@@ -1395,6 +1392,35 @@
 
     std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
             GUARDED_BY(mStateLock);
+    mutable Mutex mCreatedLayersLock;
+    struct LayerCreatedState {
+        LayerCreatedState(const wp<Layer>& layer, const wp<IBinder>& parent,
+                          const wp<Layer> parentLayer, const wp<IBinder>& producer)
+              : layer(layer),
+                initialParent(parent),
+                initialParentLayer(parentLayer),
+                initialProducer(producer) {}
+        wp<Layer> layer;
+        // Indicates the initial parent of the created layer, only used for creating layer in
+        // SurfaceFlinger. If nullptr, it may add the created layer into the current root layers.
+        wp<IBinder> initialParent;
+        wp<Layer> initialParentLayer;
+        // Indicates the initial graphic buffer producer of the created layer, only used for
+        // creating layer in SurfaceFlinger.
+        wp<IBinder> initialProducer;
+    };
+
+    // A temporay pool that store the created layers and will be added to current state in main
+    // thread.
+    std::unordered_map<BBinder*, std::unique_ptr<LayerCreatedState>> mCreatedLayers;
+    void setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
+                              const wp<IBinder>& parent, const wp<Layer> parentLayer,
+                              const wp<IBinder>& producer);
+    auto getLayerCreatedState(const sp<IBinder>& handle);
+    sp<Layer> handleLayerCreatedLocked(const sp<IBinder>& handle, bool privileged)
+            REQUIRES(mStateLock);
+
+    std::atomic<ui::Transform::RotationFlags> mDefaultDisplayTransformHint;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 8a3be9f..113f463 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -141,11 +141,6 @@
     addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius);
     addBackgroundBlurRadiusLocked(transaction, layerId, layer->mCurrentState.backgroundBlurRadius);
     addBlurRegionsLocked(transaction, layerId, layer->mCurrentState.blurRegions);
-    if (layer->mCurrentState.barrierLayer_legacy != nullptr) {
-        addDeferTransactionLocked(transaction, layerId,
-                                  layer->mCurrentState.barrierLayer_legacy.promote(),
-                                  layer->mCurrentState.barrierFrameNumber);
-    }
     addFlagsLocked(transaction, layerId, layer->mCurrentState.flags,
                    layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque |
                            layer_state_t::eLayerSecure);
@@ -380,20 +375,6 @@
     }
 }
 
-void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
-        const sp<const Layer>& layer, uint64_t frameNumber)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    if (layer == nullptr) {
-        ALOGE("An existing layer could not be retrieved with the handle"
-                " for the deferred transaction");
-        return;
-    }
-    DeferredTransactionChange* deferTransaction(change->mutable_deferred_transaction());
-    deferTransaction->set_layer_id(getLayerId(layer));
-    deferTransaction->set_frame_number(frameNumber);
-}
-
 void SurfaceInterceptor::addReparentLocked(Transaction* transaction, int32_t layerId,
                                            int32_t parentId) {
     SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
@@ -401,13 +382,6 @@
     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::addRelativeParentLocked(Transaction* transaction, int32_t layerId,
                                                  int32_t parentId, int z) {
     SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
@@ -471,25 +445,12 @@
     if (state.what & layer_state_t::eBlurRegionsChanged) {
         addBlurRegionsLocked(transaction, layerId, state.blurRegions);
     }
-    if (state.what & layer_state_t::eDeferTransaction_legacy) {
-        sp<Layer> otherLayer = nullptr;
-        if (state.barrierSurfaceControl_legacy != nullptr) {
-            otherLayer = static_cast<Layer::Handle*>(
-                                 state.barrierSurfaceControl_legacy->getHandle().get())
-                                 ->owner.promote();
-        }
-        addDeferTransactionLocked(transaction, layerId, otherLayer, state.barrierFrameNumber);
-    }
     if (state.what & layer_state_t::eReparent) {
         auto parentHandle = (state.parentSurfaceControlForChild)
                 ? state.parentSurfaceControlForChild->getHandle()
                 : nullptr;
         addReparentLocked(transaction, layerId, getLayerIdFromHandle(parentHandle));
     }
-    if (state.what & layer_state_t::eReparentChildren) {
-        addReparentChildrenLocked(transaction, layerId,
-                                  getLayerIdFromHandle(state.reparentSurfaceControl->getHandle()));
-    }
     if (state.what & layer_state_t::eRelativeLayerChanged) {
         addRelativeParentLocked(transaction, layerId,
                                 getLayerIdFromHandle(
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 3e27e83..30aca83 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -167,8 +167,6 @@
                                        int32_t backgroundBlurRadius);
     void addBlurRegionsLocked(Transaction* transaction, int32_t layerId,
                               const std::vector<BlurRegion>& effectRegions);
-    void addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
-            const sp<const Layer>& layer, uint64_t frameNumber);
     void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state);
     void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates,
                               const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
@@ -176,7 +174,6 @@
                               uint32_t transactionFlags, int originPid, int originUid,
                               uint64_t transactionId);
     void addReparentLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
-    void addReparentChildrenLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
     void addRelativeParentLocked(Transaction* transaction, int32_t layerId, int32_t parentId,
                                  int z);
     void addShadowRadiusLocked(Transaction* transaction, int32_t layerId, float shadowRadius);
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index 62fddb4..bcc3e4e 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -18,20 +18,13 @@
         "libcutils",
         "liblog",
         "libprotobuf-cpp-lite",
-        "libprotoutil",
-        "libstatslog",
-        "libstatspull",
-        "libstatssocket",
+        "libtimestats_atoms_proto",
         "libtimestats_proto",
         "libui",
         "libutils",
     ],
     export_include_dirs: ["."],
     export_shared_lib_headers: [
-        "libprotoutil",
-        "libstatslog",
-        "libstatspull",
-        "libstatssocket",
         "libtimestats_proto",
     ],
     cppflags: [
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 974ae84..3d82afa 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -19,11 +19,9 @@
 #define LOG_TAG "TimeStats"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include "TimeStats.h"
-
 #include <android-base/stringprintf.h>
-#include <android/util/ProtoOutputStream.h>
 #include <log/log.h>
+#include <timestatsatomsproto/TimeStatsAtomsProtoHeader.h>
 #include <utils/String8.h>
 #include <utils/Timers.h>
 #include <utils/Trace.h>
@@ -31,144 +29,102 @@
 #include <algorithm>
 #include <chrono>
 
+#include "TimeStats.h"
 #include "timestatsproto/TimeStatsHelper.h"
 
 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) {
+FrameTimingHistogram histogramToProto(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;
+    FrameTimingHistogram histogramProto;
     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);
+        histogramProto.add_time_millis_buckets((int32_t)bucket.first);
+        histogramProto.add_frame_counts((int64_t)bucket.second);
     }
-
-    std::string byteString;
-    proto.serializeToString(&byteString);
-    return byteString;
+    return histogramProto;
 }
 
-std::string frameRateVoteToProtoByteString(float refreshRate, int frameRateCompatibility,
-                                           int seamlessness) {
-    util::ProtoOutputStream proto;
-    proto.write(android::util::FIELD_TYPE_FLOAT | 1 /* field id */, refreshRate);
-    proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */, frameRateCompatibility);
-    proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, seamlessness);
+SurfaceflingerStatsLayerInfo_SetFrameRateVote frameRateVoteToProto(
+        const TimeStats::SetFrameRateVote& setFrameRateVote) {
+    using FrameRateCompatibilityEnum =
+            SurfaceflingerStatsLayerInfo::SetFrameRateVote::FrameRateCompatibility;
+    using SeamlessnessEnum = SurfaceflingerStatsLayerInfo::SetFrameRateVote::Seamlessness;
 
-    std::string byteString;
-    proto.serializeToString(&byteString);
-    return byteString;
+    SurfaceflingerStatsLayerInfo_SetFrameRateVote proto;
+    proto.set_frame_rate(setFrameRateVote.frameRate);
+    proto.set_frame_rate_compatibility(
+            static_cast<FrameRateCompatibilityEnum>(setFrameRateVote.frameRateCompatibility));
+    proto.set_seamlessness(static_cast<SeamlessnessEnum>(setFrameRateVote.seamlessness));
+    return proto;
 }
 } // namespace
 
-AStatsManager_PullAtomCallbackReturn TimeStats::populateGlobalAtom(AStatsEventList* data) {
+bool TimeStats::populateGlobalAtom(std::string* pulledData) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     if (mTimeStats.statsStartLegacy == 0) {
-        return AStatsManager_PULL_SKIP;
+        return false;
     }
     flushPowerTimeLocked();
-
+    SurfaceflingerStatsGlobalInfoWrapper atomList;
     for (const auto& globalSlice : mTimeStats.stats) {
-        AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data);
-        mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
-        mStatsDelegate->statsEventWriteInt64(event, mTimeStats.totalFramesLegacy);
-        mStatsDelegate->statsEventWriteInt64(event, mTimeStats.missedFramesLegacy);
-        mStatsDelegate->statsEventWriteInt64(event, mTimeStats.clientCompositionFramesLegacy);
-        mStatsDelegate->statsEventWriteInt64(event, mTimeStats.displayOnTimeLegacy);
-        mStatsDelegate->statsEventWriteInt64(event, mTimeStats.presentToPresentLegacy.totalTime());
-        mStatsDelegate->statsEventWriteInt32(event, mTimeStats.displayEventConnectionsCountLegacy);
-        std::string frameDurationBytes =
-                histogramToProtoByteString(mTimeStats.frameDurationLegacy.hist,
-                                           mMaxPulledHistogramBuckets);
-        mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameDurationBytes.c_str(),
-                                                 frameDurationBytes.size());
-        std::string renderEngineTimingBytes =
-                histogramToProtoByteString(mTimeStats.renderEngineTimingLegacy.hist,
-                                           mMaxPulledHistogramBuckets);
-        mStatsDelegate->statsEventWriteByteArray(event,
-                                                 (const uint8_t*)renderEngineTimingBytes.c_str(),
-                                                 renderEngineTimingBytes.size());
-
-        mStatsDelegate->statsEventWriteInt32(event, globalSlice.second.jankPayload.totalFrames);
-        mStatsDelegate->statsEventWriteInt32(event,
-                                             globalSlice.second.jankPayload.totalJankyFrames);
-        mStatsDelegate->statsEventWriteInt32(event, globalSlice.second.jankPayload.totalSFLongCpu);
-        mStatsDelegate->statsEventWriteInt32(event, globalSlice.second.jankPayload.totalSFLongGpu);
-        mStatsDelegate->statsEventWriteInt32(event,
-                                             globalSlice.second.jankPayload.totalSFUnattributed);
-        mStatsDelegate->statsEventWriteInt32(event,
-                                             globalSlice.second.jankPayload.totalAppUnattributed);
-        mStatsDelegate->statsEventWriteInt32(event,
-                                             globalSlice.second.jankPayload.totalSFScheduling);
-        mStatsDelegate->statsEventWriteInt32(event,
-                                             globalSlice.second.jankPayload.totalSFPredictionError);
-        mStatsDelegate->statsEventWriteInt32(event,
-                                             globalSlice.second.jankPayload.totalAppBufferStuffing);
-        mStatsDelegate->statsEventWriteInt32(event, globalSlice.first.displayRefreshRateBucket);
-        std::string sfDeadlineMissedBytes =
-                histogramToProtoByteString(globalSlice.second.displayDeadlineDeltas.hist,
-                                           mMaxPulledHistogramBuckets);
-        mStatsDelegate->statsEventWriteByteArray(event,
-                                                 (const uint8_t*)sfDeadlineMissedBytes.c_str(),
-                                                 sfDeadlineMissedBytes.size());
-        std::string sfPredictionErrorBytes =
-                histogramToProtoByteString(globalSlice.second.displayPresentDeltas.hist,
-                                           mMaxPulledHistogramBuckets);
-        mStatsDelegate->statsEventWriteByteArray(event,
-                                                 (const uint8_t*)sfPredictionErrorBytes.c_str(),
-                                                 sfPredictionErrorBytes.size());
-        mStatsDelegate->statsEventWriteInt32(event, globalSlice.first.renderRateBucket);
-        mStatsDelegate->statsEventBuild(event);
+        SurfaceflingerStatsGlobalInfo* atom = atomList.add_atom();
+        atom->set_total_frames(mTimeStats.totalFramesLegacy);
+        atom->set_missed_frames(mTimeStats.missedFramesLegacy);
+        atom->set_client_composition_frames(mTimeStats.clientCompositionFramesLegacy);
+        atom->set_display_on_millis(mTimeStats.displayOnTimeLegacy);
+        atom->set_animation_millis(mTimeStats.presentToPresentLegacy.totalTime());
+        atom->set_event_connection_count(mTimeStats.displayEventConnectionsCountLegacy);
+        *atom->mutable_frame_duration() =
+                histogramToProto(mTimeStats.frameDurationLegacy.hist, mMaxPulledHistogramBuckets);
+        *atom->mutable_render_engine_timing() =
+                histogramToProto(mTimeStats.renderEngineTimingLegacy.hist,
+                                 mMaxPulledHistogramBuckets);
+        atom->set_total_timeline_frames(globalSlice.second.jankPayload.totalFrames);
+        atom->set_total_janky_frames(globalSlice.second.jankPayload.totalJankyFrames);
+        atom->set_total_janky_frames_with_long_cpu(globalSlice.second.jankPayload.totalSFLongCpu);
+        atom->set_total_janky_frames_with_long_gpu(globalSlice.second.jankPayload.totalSFLongGpu);
+        atom->set_total_janky_frames_sf_unattributed(
+                globalSlice.second.jankPayload.totalSFUnattributed);
+        atom->set_total_janky_frames_app_unattributed(
+                globalSlice.second.jankPayload.totalAppUnattributed);
+        atom->set_total_janky_frames_sf_scheduling(
+                globalSlice.second.jankPayload.totalSFScheduling);
+        atom->set_total_jank_frames_sf_prediction_error(
+                globalSlice.second.jankPayload.totalSFPredictionError);
+        atom->set_total_jank_frames_app_buffer_stuffing(
+                globalSlice.second.jankPayload.totalAppBufferStuffing);
+        atom->set_display_refresh_rate_bucket(globalSlice.first.displayRefreshRateBucket);
+        *atom->mutable_sf_deadline_misses() =
+                histogramToProto(globalSlice.second.displayDeadlineDeltas.hist,
+                                 mMaxPulledHistogramBuckets);
+        *atom->mutable_sf_prediction_errors() =
+                histogramToProto(globalSlice.second.displayPresentDeltas.hist,
+                                 mMaxPulledHistogramBuckets);
+        atom->set_render_rate_bucket(globalSlice.first.renderRateBucket);
     }
 
+    // Always clear data.
     clearGlobalLocked();
 
-    return AStatsManager_PULL_SUCCESS;
+    return atomList.SerializeToString(pulledData);
 }
 
-AStatsManager_PullAtomCallbackReturn TimeStats::populateLayerAtom(AStatsEventList* data) {
+bool TimeStats::populateLayerAtom(std::string* pulledData) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     std::vector<TimeStatsHelper::TimeStatsLayer*> dumpStats;
@@ -195,66 +151,73 @@
         dumpStats.resize(mMaxPulledLayers);
     }
 
+    SurfaceflingerStatsLayerInfoWrapper atomList;
     for (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());
-            }
+        SurfaceflingerStatsLayerInfo* atom = atomList.add_atom();
+        atom->set_layer_name(layer->layerName);
+        atom->set_total_frames(layer->totalFrames);
+        atom->set_dropped_frames(layer->droppedFrames);
+        const auto& present2PresentHist = layer->deltas.find("present2present");
+        if (present2PresentHist != layer->deltas.cend()) {
+            *atom->mutable_present_to_present() =
+                    histogramToProto(present2PresentHist->second.hist, mMaxPulledHistogramBuckets);
+        }
+        const auto& post2presentHist = layer->deltas.find("post2present");
+        if (post2presentHist != layer->deltas.cend()) {
+            *atom->mutable_post_to_present() =
+                    histogramToProto(post2presentHist->second.hist, mMaxPulledHistogramBuckets);
+        }
+        const auto& acquire2presentHist = layer->deltas.find("acquire2present");
+        if (acquire2presentHist != layer->deltas.cend()) {
+            *atom->mutable_acquire_to_present() =
+                    histogramToProto(acquire2presentHist->second.hist, mMaxPulledHistogramBuckets);
+        }
+        const auto& latch2presentHist = layer->deltas.find("latch2present");
+        if (latch2presentHist != layer->deltas.cend()) {
+            *atom->mutable_latch_to_present() =
+                    histogramToProto(latch2presentHist->second.hist, mMaxPulledHistogramBuckets);
+        }
+        const auto& desired2presentHist = layer->deltas.find("desired2present");
+        if (desired2presentHist != layer->deltas.cend()) {
+            *atom->mutable_desired_to_present() =
+                    histogramToProto(desired2presentHist->second.hist, mMaxPulledHistogramBuckets);
+        }
+        const auto& post2acquireHist = layer->deltas.find("post2acquire");
+        if (post2acquireHist != layer->deltas.cend()) {
+            *atom->mutable_post_to_acquire() =
+                    histogramToProto(post2acquireHist->second.hist, mMaxPulledHistogramBuckets);
         }
 
-        mStatsDelegate->statsEventWriteInt64(event, layer->lateAcquireFrames);
-        mStatsDelegate->statsEventWriteInt64(event, layer->badDesiredPresentFrames);
-        mStatsDelegate->statsEventWriteInt32(event, layer->uid);
-        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalFrames);
-        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalJankyFrames);
-        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFLongCpu);
-        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFLongGpu);
-        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFUnattributed);
-        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalAppUnattributed);
-        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFScheduling);
-        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFPredictionError);
-        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalAppBufferStuffing);
-        mStatsDelegate->statsEventWriteInt32(
-                event, layer->displayRefreshRateBucket); // display_refresh_rate_bucket
-        mStatsDelegate->statsEventWriteInt32(event, layer->renderRateBucket); // render_rate_bucket
-        std::string frameRateVoteBytes = frameRateVoteToProtoByteString(0.0, 0, 0);
-        mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameRateVoteBytes.c_str(),
-                                                 frameRateVoteBytes.size()); // set_frame_rate_vote
-        std::string appDeadlineMissedBytes =
-                histogramToProtoByteString(layer->deltas["appDeadlineDeltas"].hist,
-                                           mMaxPulledHistogramBuckets);
-        mStatsDelegate->statsEventWriteByteArray(event,
-                                                 (const uint8_t*)appDeadlineMissedBytes.c_str(),
-                                                 appDeadlineMissedBytes.size());
-
-        mStatsDelegate->statsEventBuild(event);
+        atom->set_late_acquire_frames(layer->lateAcquireFrames);
+        atom->set_bad_desired_present_frames(layer->badDesiredPresentFrames);
+        atom->set_uid(layer->uid);
+        atom->set_total_timeline_frames(layer->jankPayload.totalFrames);
+        atom->set_total_janky_frames(layer->jankPayload.totalJankyFrames);
+        atom->set_total_janky_frames_with_long_cpu(layer->jankPayload.totalSFLongCpu);
+        atom->set_total_janky_frames_with_long_gpu(layer->jankPayload.totalSFLongGpu);
+        atom->set_total_janky_frames_sf_unattributed(layer->jankPayload.totalSFUnattributed);
+        atom->set_total_janky_frames_app_unattributed(layer->jankPayload.totalAppUnattributed);
+        atom->set_total_janky_frames_sf_scheduling(layer->jankPayload.totalSFScheduling);
+        atom->set_total_jank_frames_sf_prediction_error(layer->jankPayload.totalSFPredictionError);
+        atom->set_total_jank_frames_app_buffer_stuffing(layer->jankPayload.totalAppBufferStuffing);
+        atom->set_display_refresh_rate_bucket(layer->displayRefreshRateBucket);
+        atom->set_render_rate_bucket(layer->renderRateBucket);
+        *atom->mutable_set_frame_rate_vote() = frameRateVoteToProto(layer->setFrameRateVote);
+        *atom->mutable_app_deadline_misses() =
+                histogramToProto(layer->deltas["appDeadlineDeltas"].hist,
+                                 mMaxPulledHistogramBuckets);
     }
+
+    // Always clear data.
     clearLayersLocked();
 
-    return AStatsManager_PULL_SUCCESS;
+    return atomList.SerializeToString(pulledData);
 }
 
-TimeStats::TimeStats() : TimeStats(nullptr, std::nullopt, std::nullopt) {}
+TimeStats::TimeStats() : TimeStats(std::nullopt, std::nullopt) {}
 
-TimeStats::TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate,
-                     std::optional<size_t> maxPulledLayers,
+TimeStats::TimeStats(std::optional<size_t> maxPulledLayers,
                      std::optional<size_t> maxPulledHistogramBuckets) {
-    if (statsDelegate != nullptr) {
-        mStatsDelegate = std::move(statsDelegate);
-    }
-
     if (maxPulledLayers) {
         mMaxPulledLayers = *maxPulledLayers;
     }
@@ -264,18 +227,19 @@
     }
 }
 
-TimeStats::~TimeStats() {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mStatsDelegate->clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
-    mStatsDelegate->clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
-}
+bool TimeStats::onPullAtom(const int atomId, std::string* pulledData) {
+    bool success = false;
+    if (atomId == 10062) { // SURFACEFLINGER_STATS_GLOBAL_INFO
+        success = populateGlobalAtom(pulledData);
+    } else if (atomId == 10063) { // SURFACEFLINGER_STATS_LAYER_INFO
+        success = populateLayerAtom(pulledData);
+    }
 
-void TimeStats::onBootFinished() {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mStatsDelegate->setStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
-                                             nullptr, TimeStats::pullAtomCallback, this);
-    mStatsDelegate->setStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
-                                             nullptr, TimeStats::pullAtomCallback, this);
+    // 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.
+    enable();
+    return success;
 }
 
 void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
@@ -468,8 +432,10 @@
 }
 
 void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
-                                                   std::optional<Fps> renderRate) {
+                                                   std::optional<Fps> renderRate,
+                                                   SetFrameRateVote frameRateVote) {
     ATRACE_CALL();
+    ALOGV("[%d]-flushAvailableRecordsToStatsLocked", layerId);
 
     LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
@@ -501,6 +467,9 @@
                 displayStats.stats[layerKey].uid = uid;
                 displayStats.stats[layerKey].layerName = layerName;
             }
+            if (frameRateVote.frameRate > 0.0f) {
+                displayStats.stats[layerKey].setFrameRateVote = frameRateVote;
+            }
             TimeStatsHelper::TimeStatsLayer& timeStatsLayer = displayStats.stats[layerKey];
             timeStatsLayer.totalFrames++;
             timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
@@ -724,7 +693,8 @@
 }
 
 void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
-                               Fps displayRefreshRate, std::optional<Fps> renderRate) {
+                               Fps displayRefreshRate, std::optional<Fps> renderRate,
+                               SetFrameRateVote frameRateVote) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
@@ -743,12 +713,13 @@
         layerRecord.waitData++;
     }
 
-    flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate);
+    flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote);
 }
 
 void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber,
                                 const std::shared_ptr<FenceTime>& presentFence,
-                                Fps displayRefreshRate, std::optional<Fps> renderRate) {
+                                Fps displayRefreshRate, std::optional<Fps> renderRate,
+                                SetFrameRateVote frameRateVote) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
@@ -768,13 +739,13 @@
         layerRecord.waitData++;
     }
 
-    flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate);
+    flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote);
 }
 
 static const constexpr int32_t kValidJankyReason = JankType::DisplayHAL |
         JankType::SurfaceFlingerCpuDeadlineMissed | JankType::SurfaceFlingerGpuDeadlineMissed |
         JankType::AppDeadlineMissed | JankType::PredictionError |
-        JankType::SurfaceFlingerScheduling | JankType::BufferStuffing;
+        JankType::SurfaceFlingerScheduling;
 
 template <class T>
 static void updateJankPayload(T& t, int32_t reasons) {
@@ -800,9 +771,11 @@
         if ((reasons & JankType::SurfaceFlingerScheduling) != 0) {
             t.jankPayload.totalSFScheduling++;
         }
-        if ((reasons & JankType::BufferStuffing) != 0) {
-            t.jankPayload.totalAppBufferStuffing++;
-        }
+    }
+
+    // We want to track BufferStuffing separately as it can provide info on latency issues
+    if (reasons & JankType::BufferStuffing) {
+        t.jankPayload.totalAppBufferStuffing++;
     }
 }
 
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index fd112b9..5b0f5bd 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -29,9 +29,6 @@
 
 #include <../Fps.h>
 #include <gui/JankInfo.h>
-#include <stats_event.h>
-#include <stats_pull_atom_callback.h>
-#include <statslog.h>
 #include <timestatsproto/TimeStatsHelper.h>
 #include <timestatsproto/TimeStatsProtoHeader.h>
 #include <ui/FenceTime.h>
@@ -50,11 +47,12 @@
 
 class TimeStats {
 public:
+    using SetFrameRateVote = TimeStatsHelper::SetFrameRateVote;
+
     virtual ~TimeStats() = default;
 
-    // Called once boot has been finished to perform additional capabilities,
-    // e.g. registration to statsd.
-    virtual void onBootFinished() = 0;
+    // Process a pull request from statsd.
+    virtual bool onPullAtom(const int atomId, std::string* pulledData);
 
     virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
     virtual bool isEnabled() = 0;
@@ -110,10 +108,12 @@
     // 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,
-                                Fps displayRefreshRate, std::optional<Fps> renderRate) = 0;
+                                Fps displayRefreshRate, std::optional<Fps> renderRate,
+                                SetFrameRateVote frameRateVote) = 0;
     virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
                                  const std::shared_ptr<FenceTime>& presentFence,
-                                 Fps displayRefreshRate, std::optional<Fps> renderRate) = 0;
+                                 Fps displayRefreshRate, std::optional<Fps> renderRate,
+                                 SetFrameRateVote frameRateVote) = 0;
 
     // Increments janky frames, blamed to the provided {refreshRate, renderRate, uid, layerName}
     // key, with JankMetadata as supplementary reasons for the jank. Because FrameTimeline is the
@@ -228,58 +228,11 @@
 
 public:
     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 setStatsPullAtomCallback(int32_t atom_tag,
-                                              AStatsManager_PullAtomMetadata* metadata,
-                                              AStatsManager_PullAtomCallback callback,
-                                              void* cookie) {
-            return AStatsManager_setPullAtomCallback(atom_tag, metadata, callback, cookie);
-        }
-
-        virtual void clearStatsPullAtomCallback(int32_t atom_tag) {
-            return AStatsManager_clearPullAtomCallback(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,
+    TimeStats(std::optional<size_t> maxPulledLayers,
               std::optional<size_t> maxPulledHistogramBuckets);
 
-    ~TimeStats() override;
-
-    void onBootFinished() override;
+    bool onPullAtom(const int atomId, std::string* pulledData) override;
     void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
     bool isEnabled() override;
     std::string miniDump() override;
@@ -307,10 +260,11 @@
     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,
-                        Fps displayRefreshRate, std::optional<Fps> renderRate) override;
+                        Fps displayRefreshRate, std::optional<Fps> renderRate,
+                        SetFrameRateVote frameRateVote) override;
     void setPresentFence(int32_t layerId, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate,
-                         std::optional<Fps> renderRate) override;
+                         std::optional<Fps> renderRate, SetFrameRateVote frameRateVote) override;
 
     void incrementJankyFrames(const JankyFramesInfo& info) override;
     // Clean up the layer record
@@ -327,14 +281,12 @@
     static const size_t MAX_NUM_TIME_RECORDS = 64;
 
 private:
-    static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atom_tag,
-                                                                 AStatsEventList* data,
-                                                                 void* cookie);
-    AStatsManager_PullAtomCallbackReturn populateGlobalAtom(AStatsEventList* data);
-    AStatsManager_PullAtomCallbackReturn populateLayerAtom(AStatsEventList* data);
+    bool populateGlobalAtom(std::string* pulledData);
+    bool populateLayerAtom(std::string* pulledData);
     bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
     void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
-                                            std::optional<Fps> renderRate);
+                                            std::optional<Fps> renderRate,
+                                            SetFrameRateVote frameRateVote);
     void flushPowerTimeLocked();
     void flushAvailableGlobalRecordsToStatsLocked();
     bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName);
@@ -360,7 +312,6 @@
     static const size_t RENDER_RATE_BUCKET_WIDTH = REFRESH_RATE_BUCKET_WIDTH;
     static const size_t MAX_NUM_LAYER_STATS = 200;
     static const size_t MAX_NUM_PULLED_LAYERS = MAX_NUM_LAYER_STATS;
-    std::unique_ptr<StatsEventDelegate> mStatsDelegate = std::make_unique<StatsEventDelegate>();
     size_t mMaxPulledLayers = MAX_NUM_PULLED_LAYERS;
     size_t mMaxPulledHistogramBuckets = 6;
 };
diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/Android.bp b/services/surfaceflinger/TimeStats/timestatsatomsproto/Android.bp
new file mode 100644
index 0000000..0cf086f
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/Android.bp
@@ -0,0 +1,36 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library {
+    name: "libtimestats_atoms_proto",
+    export_include_dirs: ["include"],
+
+    srcs: [
+        "timestats_atoms.proto",
+    ],
+
+    proto: {
+        type: "lite",
+        export_proto_headers: true,
+    },
+
+    cppflags: [
+        "-Werror",
+        "-Wno-c++98-compat-pedantic",
+        "-Wno-disabled-macro-expansion",
+        "-Wno-float-conversion",
+        "-Wno-float-equal",
+        "-Wno-format",
+        "-Wno-old-style-cast",
+        "-Wno-padded",
+        "-Wno-sign-conversion",
+        "-Wno-undef",
+        "-Wno-unused-parameter",
+    ],
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/include/timestatsatomsproto/TimeStatsAtomsProtoHeader.h b/services/surfaceflinger/TimeStats/timestatsatomsproto/include/timestatsatomsproto/TimeStatsAtomsProtoHeader.h
new file mode 100644
index 0000000..d305cb4
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/include/timestatsatomsproto/TimeStatsAtomsProtoHeader.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 is used here to disable the warnings emitted from the protobuf
+// headers. By adding #pragma before including layer.pb.h, it suppresses
+// protobuf warnings, but allows the rest of the files to continuing using
+// the current flags.
+// This file should be included instead of directly including timestats_atoms.b.h
+#pragma GCC system_header
+#include <timestats_atoms.pb.h>
diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
new file mode 100644
index 0000000..133a541
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+package android.surfaceflinger;
+
+// This is a copy of surfaceflinger's atoms from frameworks/proto_logging/stats/atoms.proto.
+// Pulled atoms for surfaceflinger must be routed through system server since surfaceflinger is
+// in the bootstrap namespace. This copy is used to pass the atoms as protos to system server using
+// proto lite to serialize/deserialize the atoms.
+
+// These wrappers are so that we can pass a List<Atom> as a single byte string.
+// They are not in atoms.proto
+message SurfaceflingerStatsGlobalInfoWrapper {
+    repeated SurfaceflingerStatsGlobalInfo atom = 1;
+}
+
+message SurfaceflingerStatsLayerInfoWrapper {
+    repeated SurfaceflingerStatsLayerInfo atom = 1;
+}
+
+/**
+ * Global display pipeline metrics reported by SurfaceFlinger.
+ * Metrics exist beginning in Android 11.
+ * Pulled from:
+ *    frameworks/native/services/surfaceflinger/TimeStats/TimeStats.cpp
+ */
+message SurfaceflingerStatsGlobalInfo {
+    // Aggregated refresh rate buckets that layers were presenting at. Buckets
+    // are defined in SurfaceFlinger and are tracked per device.
+    // Introduced in Android 12.
+    // This is intended to be used as a dimenstion in collecting per-refresh rate
+    // jank statistics.
+    optional int32 display_refresh_rate_bucket = 18;
+    // Aggregated render rate buckets that layers were overridden to run at.
+    // Buckets are defined in SurfaceFlinger and are tracked per device.
+    // Introduced in Android 12.
+    // This is intended to be used as a dimension in collecting per-render rate
+    // jank statistics.
+    optional int32 render_rate_bucket = 21;
+    // Total number of frames presented during the tracing period
+    // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+    // using render_rate_bucket as a dimension.
+    optional int64 total_frames = 1;
+    // Total number of frames missed
+    // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+    // using render_rate_bucket as a dimension.
+    optional int64 missed_frames = 2;
+    // Total number of frames that fell back to client composition
+    // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+    // using render_rate_bucket as a dimension.
+    optional int64 client_composition_frames = 3;
+    // Total time the display was turned on
+    // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+    // using render_rate_bucket as a dimension.
+    optional int64 display_on_millis = 4;
+    // Total time that was spent performing animations.
+    // This is derived from the present-to-present layer histogram.
+    // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+    // using render_rate_bucket as a dimension.
+    optional int64 animation_millis = 5;
+    // Total number of event connections tracked by SurfaceFlinger at the time
+    // of this pull. If this number grows prohibitively large, then this can
+    // cause jank due to resource contention.
+    // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+    // using render_rate_bucket as a dimension.
+    optional int32 event_connection_count = 6;
+    // Set of timings measured from when SurfaceFlinger began compositing a
+    // frame, until the frame was requested to be presented to the display. This
+    // measures SurfaceFlinger's total CPU walltime on the critical path per
+    // frame.
+    // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+    // using render_rate_bucket as a dimension.
+    optional FrameTimingHistogram frame_duration = 7;
+    // Set of timings measured from when SurfaceFlinger first began using the
+    // GPU to composite a frame, until the GPU has finished compositing that
+    // frame. This measures the total additional time SurfaceFlinger needed to
+    // perform due to falling back into GPU composition.
+    // Note: This stat is not sliced by dimension. It will be duplicated for metrics
+    // using render_rate_bucket as a dimension.
+    optional FrameTimingHistogram render_engine_timing = 8;
+    // Number of frames where SF saw a frame, based on its frame timeline.
+    // Frame timelines may include transactions without updating buffer contents.
+    // Introduced in Android 12.
+    optional int32 total_timeline_frames = 9;
+    // Number of frames where SF saw a janky frame.
+    // Introduced in Android 12.
+    optional int32 total_janky_frames = 10;
+    // Number of janky frames where SF spent a long time on the CPU.
+    // Introduced in Android 12.
+    optional int32 total_janky_frames_with_long_cpu = 11;
+    // Number of janky frames where SF spent a long time on the GPU.
+    // Introduced in Android 12.
+    optional int32 total_janky_frames_with_long_gpu = 12;
+    // Number of janky frames where SF missed the frame deadline, but there
+    // was not an attributed reason (e.g., maybe HWC missed?)
+    // Introduced in Android 12.
+    optional int32 total_janky_frames_sf_unattributed = 13;
+    // Number of janky frames where the app missed the frame deadline, but
+    // there was not an attributed reason
+    // Introduced in Android 12.
+    optional int32 total_janky_frames_app_unattributed = 14;
+    // Number of janky frames that were caused because of scheduling errors in
+    // SF that resulted in early present (e.g., SF sending a buffer to the
+    // composition engine earlier than expected, resulting in a present that is
+    // one vsync early)
+    // Introduced in Android 12.
+    optional int32 total_janky_frames_sf_scheduling = 15;
+    // Number of frames that were classified as jank because of possible drift in
+    // vsync predictions.
+    // Introduced in Android 12.
+    optional int32 total_jank_frames_sf_prediction_error = 16;
+    // Number of janky frames where the app was in a buffer stuffed state (more
+    // than one buffer ready to be presented at the same vsync). Usually caused
+    // when the first frame is unusually long, the following frames enter into a
+    // stuffed state.
+    // Introduced in Android 12.
+    optional int32 total_jank_frames_app_buffer_stuffing = 17;
+    // Buckets of timings in ms by which SurfaceFlinger's deadline was missed
+    // while latching and presenting frames.
+    // Introduced in Android 12.
+    optional FrameTimingHistogram sf_deadline_misses = 19;
+    // Buckets of timings in ms by which the Vsync prediction drifted, when
+    // compared to the actual hardware vsync.
+    // Introduced in Android 12.
+    optional FrameTimingHistogram sf_prediction_errors = 20;
+
+    // Next ID: 22
+}
+
+/**
+ * Per-layer display pipeline metrics reported by SurfaceFlinger.
+ * Metrics exist beginning in Android 11.
+ * The number of layers uploaded may be restricted due to size limitations.
+ * Pulled from:
+ *    frameworks/native/services/surfaceflinger/TimeStats/TimeStats.cpp
+ */
+message SurfaceflingerStatsLayerInfo {
+    // UID of the application who submitted this layer for presentation
+    // This is intended to be used as a dimension for surfacing rendering
+    // statistics to applications.
+    // Introduced in Android 12.
+    optional int32 uid = 12;
+    // Refresh rate bucket that the layer was presenting at. Buckets are
+    // defined in SurfaceFlinger and are tracked per device.
+    // Introduced in Android 12.
+    // This is intended to be used as a dimension in collecting per-refresh rate
+    // jank statistics
+    optional int32 display_refresh_rate_bucket = 22;
+    // Render rate bucket that the layer was submitting frames at. Buckets are
+    // defined in SurfaceFlinger and are tracked per device.
+    // Introduced in Android 12.
+    // This is intended to be used as a dimension in collecting per-render rate
+    // jank statistics.
+    optional int32 render_rate_bucket = 23;
+    // The layer for this set of metrics
+    // In many scenarios the package name is included in the layer name, e.g.,
+    // layers created by Window Manager. But this is not a guarantee - in the
+    // general case layer names are arbitrary debug names.
+    optional string layer_name = 1;
+    // Total number of frames presented
+    optional int64 total_frames = 2;
+    // Total number of dropped frames while latching a buffer for this layer.
+    optional int64 dropped_frames = 3;
+    // Set of timings measured between successive presentation timestamps.
+    optional FrameTimingHistogram present_to_present = 4;
+    // Set of timings measured from when an app queued a buffer for
+    // presentation, until the buffer was actually presented to the
+    // display.
+    optional FrameTimingHistogram post_to_present = 5;
+    // Set of timings measured from when a buffer is ready to be presented,
+    // until the buffer was actually presented to the display.
+    optional FrameTimingHistogram acquire_to_present = 6;
+    // Set of timings measured from when a buffer was latched by
+    // SurfaceFlinger, until the buffer was presented to the display
+    optional FrameTimingHistogram latch_to_present = 7;
+    // Set of timings measured from the desired presentation to the actual
+    // presentation time
+    optional FrameTimingHistogram desired_to_present = 8;
+    // Set of timings measured from when an app queued a buffer for
+    // presentation, until the buffer was ready to be presented.
+    optional FrameTimingHistogram post_to_acquire = 9;
+    // Frames missed latch because the acquire fence didn't fire
+    optional int64 late_acquire_frames = 10;
+    // Frames latched early because the desired present time was bad
+    optional int64 bad_desired_present_frames = 11;
+    // Number of frames where SF saw a frame, based on its frame timeline.
+    // Frame timelines may include transactions without updating buffer contents.
+    // Introduced in Android 12.
+    optional int32 total_timeline_frames = 13;
+    // Number of frames where SF saw a janky frame.
+    // Introduced in Android 12.
+    optional int32 total_janky_frames = 14;
+    // Number of janky frames where SF spent a long time on the CPU.
+    // Introduced in Android 12.
+    optional int32 total_janky_frames_with_long_cpu = 15;
+    // Number of janky frames where SF spent a long time on the GPU.
+    // Introduced in Android 12.
+    optional int32 total_janky_frames_with_long_gpu = 16;
+    // Number of janky frames where SF missed the frame deadline, but there
+    // was not an attributed reason (e.g., maybe HWC missed?)
+    // Introduced in Android 12.
+    optional int32 total_janky_frames_sf_unattributed = 17;
+    // Number of janky frames where the app missed the frame deadline, but
+    // there was not an attributed reason
+    // Introduced in Android 12.
+    optional int32 total_janky_frames_app_unattributed = 18;
+    // Number of janky frames that were caused because of scheduling errors in
+    // SF that resulted in early present (e.g., SF sending a buffer to the
+    // composition engine earlier than expected, resulting in a present that is
+    // one vsync early)
+    // Introduced in Android 12.
+    optional int32 total_janky_frames_sf_scheduling = 19;
+    // Number of frames that were classified as jank because of possible drift in
+    // vsync predictions.
+    // Introduced in Android 12.
+    optional int32 total_jank_frames_sf_prediction_error = 20;
+    // Number of janky frames where the app was in a buffer stuffed state (more
+    // than one buffer ready to be presented at the same vsync). Usually caused
+    // when the first frame is unusually long, the following frames enter into a
+    // stuffed state.
+    // Introduced in Android 12.
+    optional int32 total_jank_frames_app_buffer_stuffing = 21;
+
+    /**
+     * Encapsulates the FrameRateVote information sent by the application while
+     * calling setFrameRate.
+     * Metrics exist beginning in Android 12.
+     */
+    message SetFrameRateVote {
+        // The desired frame rate the application wishes to run on.
+        optional float frame_rate = 1;
+
+        enum FrameRateCompatibility {
+            FRAME_RATE_UNDEFINED = 0;
+            FRAME_RATE_DEFAULT = 1;
+            FRAME_RATE_EXACT_OR_MULTIPLE = 2;
+        }
+
+        // Specifies how to interpret the frame rate associated with the layer.
+        // Defined in Layer.h
+        optional FrameRateCompatibility frame_rate_compatibility = 2;
+
+        enum Seamlessness {
+            SEAMLESS_UNDEFINED = 0;
+            SEAMLESS_SHOULD_BE_SEAMLESS = 1;
+            SEAMLESS_NOT_REQUIRED = 2;
+        }
+        // Indicates whether seamless refresh rate switch is required or not.
+        optional Seamlessness seamlessness = 3;
+    }
+
+    // The last frame rate vote set by the application.
+    // Introduced in Android 12.
+    optional SetFrameRateVote set_frame_rate_vote = 24;
+    // Buckets of timings in ms by which the app deadline was missed while
+    // submitting work for a frame.
+    // Introduced in Android 12.
+    optional FrameTimingHistogram app_deadline_misses = 25;
+
+    // Next ID: 26
+}
+
+/**
+ * Histogram of frame counts bucketed by time in milliseconds.
+ * Because of size limitations, we hard-cap the number of buckets, with
+ * buckets for corresponding to larger milliseconds being less precise.
+ */
+message FrameTimingHistogram {
+    // Timings in milliseconds that describes a set of histogram buckets
+    repeated int32 time_millis_buckets = 1;
+    // Number of frames that match to each time_millis, i.e. the bucket
+    // contents
+    // It's required that len(time_millis) == len(frame_count)
+    repeated int64 frame_counts = 2;
+}
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index d116b02..a7e7db2 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -91,6 +91,37 @@
     return result;
 }
 
+std::string TimeStatsHelper::SetFrameRateVote::toString(FrameRateCompatibility compatibility) {
+    switch (compatibility) {
+        case FrameRateCompatibility::Undefined:
+            return "Undefined";
+        case FrameRateCompatibility::Default:
+            return "Default";
+        case FrameRateCompatibility::ExactOrMultiple:
+            return "ExactOrMultiple";
+    }
+}
+
+std::string TimeStatsHelper::SetFrameRateVote::toString(Seamlessness seamlessness) {
+    switch (seamlessness) {
+        case Seamlessness::Undefined:
+            return "Undefined";
+        case Seamlessness::ShouldBeSeamless:
+            return "ShouldBeSeamless";
+        case Seamlessness::NotRequired:
+            return "NotRequired";
+    }
+}
+
+std::string TimeStatsHelper::SetFrameRateVote::toString() const {
+    std::string result;
+    StringAppendF(&result, "frameRate = %.2f\n", frameRate);
+    StringAppendF(&result, "frameRateCompatibility = %s\n",
+                  toString(frameRateCompatibility).c_str());
+    StringAppendF(&result, "seamlessness = %s\n", toString(seamlessness).c_str());
+    return result;
+}
+
 std::string TimeStatsHelper::TimeStatsLayer::toString() const {
     std::string result = "\n";
     StringAppendF(&result, "displayRefreshRate = %d fps\n", displayRefreshRateBucket);
@@ -104,6 +135,8 @@
     StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
     result.append("Jank payload for this layer:\n");
     result.append(jankPayload.toString());
+    result.append("SetFrateRate vote for this layer:\n");
+    result.append(setFrameRateVote.toString());
     const auto iter = deltas.find("present2present");
     if (iter != deltas.end()) {
         const float averageTime = iter->second.averageTime();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 5ee28ce..2b37ffe 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -55,6 +55,28 @@
         std::string toString() const;
     };
 
+    struct SetFrameRateVote {
+        float frameRate = 0;
+
+        // Needs to be in sync with atoms.proto
+        enum class FrameRateCompatibility {
+            Undefined = 0,
+            Default = 1,
+            ExactOrMultiple = 2,
+        } frameRateCompatibility = FrameRateCompatibility::Undefined;
+
+        // Needs to be in sync with atoms.proto
+        enum class Seamlessness {
+            Undefined = 0,
+            ShouldBeSeamless = 1,
+            NotRequired = 2,
+        } seamlessness = Seamlessness::Undefined;
+
+        static std::string toString(FrameRateCompatibility);
+        static std::string toString(Seamlessness);
+        std::string toString() const;
+    };
+
     class TimeStatsLayer {
     public:
         uid_t uid;
@@ -67,6 +89,7 @@
         int32_t lateAcquireFrames = 0;
         int32_t badDesiredPresentFrames = 0;
         JankPayload jankPayload;
+        SetFrameRateVote setFrameRateVote;
         std::unordered_map<std::string, Histogram> deltas;
 
         std::string toString() const;
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index 3590e76..4f4c02b 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -36,13 +36,17 @@
 //         <0 if the first id that doesn't match is lower in c2 or all ids match but c2 is shorter
 //         >0 if the first id that doesn't match is greater in c2 or all ids match but c2 is longer
 //
-// See CallbackIdsHash for a explaniation of why this works
+// See CallbackIdsHash for a explanation of why this works
 static int compareCallbackIds(const std::vector<CallbackId>& c1,
                               const std::vector<CallbackId>& c2) {
     if (c1.empty()) {
         return !c2.empty();
     }
-    return c1.front() - c2.front();
+    return c1.front().id - c2.front().id;
+}
+
+static bool containsOnCommitCallbacks(const std::vector<CallbackId>& callbacks) {
+    return !callbacks.empty() && callbacks.front().type == CallbackId::Type::ON_COMMIT;
 }
 
 TransactionCallbackInvoker::~TransactionCallbackInvoker() {
@@ -114,39 +118,69 @@
     return NO_ERROR;
 }
 
+status_t TransactionCallbackInvoker::finalizeCallbackHandle(const sp<CallbackHandle>& handle,
+                                                            const std::vector<JankData>& jankData) {
+    auto listener = mPendingTransactions.find(handle->listener);
+    if (listener != mPendingTransactions.end()) {
+        auto& pendingCallbacks = listener->second;
+        auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
+
+        if (pendingCallback != pendingCallbacks.end()) {
+            auto& pendingCount = pendingCallback->second;
+
+            // Decrease the pending count for this listener
+            if (--pendingCount == 0) {
+                pendingCallbacks.erase(pendingCallback);
+            }
+        } else {
+            ALOGW("there are more latched callbacks than there were registered callbacks");
+        }
+        if (listener->second.size() == 0) {
+            mPendingTransactions.erase(listener);
+        }
+    } else {
+        ALOGW("cannot find listener in mPendingTransactions");
+    }
+
+    status_t err = addCallbackHandle(handle, jankData);
+    if (err != NO_ERROR) {
+        ALOGE("could not add callback handle");
+        return err;
+    }
+    return NO_ERROR;
+}
+
+status_t TransactionCallbackInvoker::finalizeOnCommitCallbackHandles(
+        const std::deque<sp<CallbackHandle>>& handles,
+        std::deque<sp<CallbackHandle>>& outRemainingHandles) {
+    if (handles.empty()) {
+        return NO_ERROR;
+    }
+    std::lock_guard lock(mMutex);
+    const std::vector<JankData>& jankData = std::vector<JankData>();
+    for (const auto& handle : handles) {
+        if (!containsOnCommitCallbacks(handle->callbackIds)) {
+            outRemainingHandles.push_back(handle);
+            continue;
+        }
+        status_t err = finalizeCallbackHandle(handle, jankData);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
+    return NO_ERROR;
+}
+
 status_t TransactionCallbackInvoker::finalizePendingCallbackHandles(
         const std::deque<sp<CallbackHandle>>& handles, const std::vector<JankData>& jankData) {
     if (handles.empty()) {
         return NO_ERROR;
     }
     std::lock_guard lock(mMutex);
-
     for (const auto& handle : handles) {
-        auto listener = mPendingTransactions.find(handle->listener);
-        if (listener != mPendingTransactions.end()) {
-            auto& pendingCallbacks = listener->second;
-            auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
-
-            if (pendingCallback != pendingCallbacks.end()) {
-                auto& pendingCount = pendingCallback->second;
-
-                // Decrease the pending count for this listener
-                if (--pendingCount == 0) {
-                    pendingCallbacks.erase(pendingCallback);
-                }
-            } else {
-                ALOGW("there are more latched callbacks than there were registered callbacks");
-            }
-            if (listener->second.size() == 0) {
-                mPendingTransactions.erase(listener);
-            }
-        } else {
-            ALOGW("cannot find listener in mPendingTransactions");
-        }
-
-        status_t err = addCallbackHandle(handle, jankData);
+        status_t err = finalizeCallbackHandle(handle, jankData);
         if (err != NO_ERROR) {
-            ALOGE("could not add callback handle");
             return err;
         }
     }
@@ -243,7 +277,8 @@
             }
 
             // If the transaction has been latched
-            if (transactionStats.latchTime >= 0) {
+            if (transactionStats.latchTime >= 0 &&
+                !containsOnCommitCallbacks(transactionStats.callbackIds)) {
                 if (!mPresentFence) {
                     break;
                 }
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index caa8a4f..184b151 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -74,6 +74,8 @@
     // Notifies the TransactionCallbackInvoker that a pending CallbackHandle has been presented.
     status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
                                             const std::vector<JankData>& jankData);
+    status_t finalizeOnCommitCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
+                                             std::deque<sp<CallbackHandle>>& outRemainingHandles);
 
     // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
     // presented this frame.
@@ -95,6 +97,9 @@
     status_t addCallbackHandle(const sp<CallbackHandle>& handle,
                                const std::vector<JankData>& jankData) REQUIRES(mMutex);
 
+    status_t finalizeCallbackHandle(const sp<CallbackHandle>& handle,
+                                    const std::vector<JankData>& jankData) REQUIRES(mMutex);
+
     class CallbackDeathRecipient : public IBinder::DeathRecipient {
     public:
         // This function is a no-op. isBinderAlive needs a linked DeathRecipient to work.
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 2b8424c..9686523 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -89,13 +89,47 @@
     // binder threads to 4.
     ProcessState::self()->setThreadPoolMaxThreadCount(4);
 
+    // The binder threadpool we start will inherit sched policy and priority
+    // of (this) creating thread. We want the binder thread pool to have
+    // SCHED_FIFO policy and priority 1 (lowest RT priority)
+    // Once the pool is created we reset this thread's priority back to
+    // original.
+    int newPriority = 0;
+    int origPolicy = sched_getscheduler(0);
+    struct sched_param origSchedParam;
+
+    int errorInPriorityModification = sched_getparam(0, &origSchedParam);
+    if (errorInPriorityModification == 0) {
+        int policy = SCHED_FIFO;
+        newPriority = sched_get_priority_min(policy);
+
+        struct sched_param param;
+        param.sched_priority = newPriority;
+
+        errorInPriorityModification = sched_setscheduler(0, policy, &param);
+    }
+
     // start the thread pool
     sp<ProcessState> ps(ProcessState::self());
     ps->startThreadPool();
 
+    // Reset current thread's policy and priority
+    if (errorInPriorityModification == 0) {
+        errorInPriorityModification = sched_setscheduler(0, origPolicy, &origSchedParam);
+    } else {
+        ALOGE("Failed to set SurfaceFlinger binder threadpool priority to SCHED_FIFO");
+    }
+
     // instantiate surfaceflinger
     sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger();
 
+    // Set the minimum policy of surfaceflinger node to be SCHED_FIFO.
+    // So any thread with policy/priority lower than {SCHED_FIFO, 1}, will run
+    // at least with SCHED_FIFO policy and priority 1.
+    if (errorInPriorityModification == 0) {
+        flinger->setMinSchedulerPolicy(SCHED_FIFO, newPriority);
+    }
+
     setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);
 
     set_sched_policy(0, SP_FOREGROUND);
diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp
index 03f8e1a..47a150d 100644
--- a/services/surfaceflinger/tests/BufferGenerator.cpp
+++ b/services/surfaceflinger/tests/BufferGenerator.cpp
@@ -296,12 +296,12 @@
 
 BufferGenerator::BufferGenerator()
       : mSurfaceManager(new SurfaceManager), mEglManager(new EglManager), mProgram(new Program) {
-    const float width = 1000.0;
-    const float height = 1000.0;
+    mBufferSize.set(1000.0, 1000.0);
 
     auto setBufferWithContext =
             std::bind(setBuffer, std::placeholders::_1, std::placeholders::_2, this);
-    mSurfaceManager->initialize(width, height, HAL_PIXEL_FORMAT_RGBA_8888, setBufferWithContext);
+    mSurfaceManager->initialize(mBufferSize.width, mBufferSize.height, HAL_PIXEL_FORMAT_RGBA_8888,
+                                setBufferWithContext);
 
     if (!mEglManager->initialize(mSurfaceManager->getSurface())) return;
 
@@ -309,7 +309,9 @@
 
     if (!mProgram->initialize(VERTEX_SHADER, FRAGMENT_SHADER)) return;
     mProgram->use();
-    mProgram->bindVec4(0, vec4{width, height, 1.0f / width, 1.0f / height});
+    mProgram->bindVec4(0,
+                       vec4{mBufferSize.width, mBufferSize.height, 1.0f / mBufferSize.width,
+                            1.0f / mBufferSize.height});
     mProgram->bindVec3(2, &SPHERICAL_HARMONICS[0], 4);
 
     glEnableVertexAttribArray(0);
@@ -372,6 +374,10 @@
     return NO_ERROR;
 }
 
+ui::Size BufferGenerator::getSize() {
+    return mBufferSize;
+}
+
 // static
 void BufferGenerator::setBuffer(const sp<GraphicBuffer>& buffer, int32_t fence,
                                 void* bufferGenerator) {
diff --git a/services/surfaceflinger/tests/BufferGenerator.h b/services/surfaceflinger/tests/BufferGenerator.h
index a3ffe86..f7d548b 100644
--- a/services/surfaceflinger/tests/BufferGenerator.h
+++ b/services/surfaceflinger/tests/BufferGenerator.h
@@ -37,6 +37,7 @@
 
     /* Static callback that sets the fence on a particular instance */
     static void setBuffer(const sp<GraphicBuffer>& buffer, int32_t fence, void* fenceGenerator);
+    ui::Size getSize();
 
 private:
     bool mInitialized = false;
@@ -53,6 +54,7 @@
 
     using Epoch = std::chrono::time_point<std::chrono::steady_clock>;
     Epoch mEpoch = std::chrono::steady_clock::now();
+    ui::Size mBufferSize;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 6246321..fa3f0e7 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -305,25 +305,6 @@
 /**
  * The following tests are for methods accessible directly through SurfaceFlinger.
  */
-
-/**
- * An app can pass a buffer queue to the media server and ask the media server to decode a DRM video
- * to that buffer queue. The media server is the buffer producer in this case. Because the app may create
- * its own buffer queue and act as the buffer consumer, the media server wants to be careful to avoid
- * sending decoded video frames to the app. This is where authenticateSurfaceTexture call comes in, to check
- * the consumer of a buffer queue is SurfaceFlinger.
- */
-TEST_F(CredentialsTest, AuthenticateSurfaceTextureTest) {
-    setupBackgroundSurface();
-    sp<IGraphicBufferProducer> producer =
-            mBGSurfaceControl->getSurface()->getIGraphicBufferProducer();
-    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-
-    std::function<bool()> condition = [=]() { return sf->authenticateSurfaceTexture(producer); };
-    // Anyone should be able to check if the consumer of the buffer queue is SF.
-    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true));
-}
-
 TEST_F(CredentialsTest, GetLayerDebugInfo) {
     setupBackgroundSurface();
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index f470eda..af00ec7 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -149,7 +149,6 @@
         t.reparent(blurLayer, mParentLayer);
         t.setBackgroundBlurRadius(blurLayer, blurRadius);
         t.setCrop(blurLayer, blurRect);
-        t.setFrame(blurLayer, blurRect);
         t.setAlpha(blurLayer, 0.0f);
         t.show(blurLayer);
     });
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index a8647c3..9fa3d4c 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -161,7 +161,6 @@
                                                  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)
diff --git a/services/surfaceflinger/tests/InvalidHandles_test.cpp b/services/surfaceflinger/tests/InvalidHandles_test.cpp
index 58b039e..9cf7c09 100644
--- a/services/surfaceflinger/tests/InvalidHandles_test.cpp
+++ b/services/surfaceflinger/tests/InvalidHandles_test.cpp
@@ -52,12 +52,15 @@
     }
 };
 
-TEST_F(InvalidHandleTest, createSurfaceInvalidHandle) {
-    auto notSc = makeNotSurfaceControl();
-    ASSERT_EQ(nullptr,
-              mScc->createSurface(String8("lolcats"), 19, 47, PIXEL_FORMAT_RGBA_8888, 0,
-                                  notSc->getHandle())
-                      .get());
+TEST_F(InvalidHandleTest, createSurfaceInvalidParentHandle) {
+    // The createSurface is scheduled now, we could still get a created surface from createSurface.
+    // Should verify if it actually added into current state by checking the screenshot.
+    auto notSc = mScc->createSurface(String8("lolcats"), 19, 47, PIXEL_FORMAT_RGBA_8888, 0,
+                                     mNotSc->getHandle());
+    LayerCaptureArgs args;
+    args.layerHandle = notSc->getHandle();
+    ScreenCaptureResults captureResults;
+    ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
 }
 
 TEST_F(InvalidHandleTest, captureLayersInvalidHandle) {
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 158801a..011ff70 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -164,7 +164,10 @@
         return;
     }
 
-    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+    ui::Size bufferSize = getBufferSize();
+    TransactionUtils::setFrame(transaction, layer, Rect(0, 0, bufferSize.width, bufferSize.height),
+                               Rect(0, 0, 32, 32));
+    transaction.apply();
 
     ExpectedResult expected;
     expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
@@ -184,7 +187,10 @@
         return;
     }
 
-    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+    ui::Size bufferSize = getBufferSize();
+    TransactionUtils::setFrame(transaction, layer, Rect(0, 0, bufferSize.width, bufferSize.height),
+                               Rect(0, 0, 32, 32));
+    transaction.apply();
 
     ExpectedResult expected;
     expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
@@ -203,7 +209,10 @@
         return;
     }
 
-    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+    ui::Size bufferSize = getBufferSize();
+    TransactionUtils::setFrame(transaction, layer, Rect(0, 0, bufferSize.width, bufferSize.height),
+                               Rect(0, 0, 32, 32));
+    transaction.apply();
 
     ExpectedResult expected;
     expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
@@ -238,7 +247,10 @@
         return;
     }
 
-    transaction.setFrame(layer, Rect(-100, -100, 100, 100)).apply();
+    ui::Size bufferSize = getBufferSize();
+    TransactionUtils::setFrame(transaction, layer, Rect(0, 0, bufferSize.width, bufferSize.height),
+                               Rect(-100, -100, 100, 100));
+    transaction.apply();
 
     ExpectedResult expected;
     expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
@@ -263,8 +275,15 @@
         return;
     }
 
-    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
-    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+    ui::Size bufferSize = getBufferSize();
+
+    TransactionUtils::setFrame(transaction1, layer1,
+                               Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+    TransactionUtils::setFrame(transaction2, layer2,
+                               Rect(0, 0, bufferSize.width, bufferSize.height),
+                               Rect(32, 32, 64, 64));
+
+    transaction2.merge(std::move(transaction1)).apply();
 
     ExpectedResult expected;
     expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
@@ -290,8 +309,15 @@
         return;
     }
 
-    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
-    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+    ui::Size bufferSize = getBufferSize();
+
+    TransactionUtils::setFrame(transaction1, layer1,
+                               Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+    TransactionUtils::setFrame(transaction2, layer2,
+                               Rect(0, 0, bufferSize.width, bufferSize.height),
+                               Rect(32, 32, 64, 64));
+
+    transaction2.merge(std::move(transaction1)).apply();
 
     ExpectedResult expected;
     expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
@@ -318,8 +344,15 @@
         return;
     }
 
-    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
-    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+    ui::Size bufferSize = getBufferSize();
+
+    TransactionUtils::setFrame(transaction1, layer1,
+                               Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+    TransactionUtils::setFrame(transaction2, layer2,
+                               Rect(0, 0, bufferSize.width, bufferSize.height),
+                               Rect(32, 32, 64, 64));
+
+    transaction2.merge(std::move(transaction1)).apply();
 
     ExpectedResult expected;
     expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer1);
@@ -405,8 +438,15 @@
         return;
     }
 
-    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
-    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+    ui::Size bufferSize = getBufferSize();
+
+    TransactionUtils::setFrame(transaction1, layer1,
+                               Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+    TransactionUtils::setFrame(transaction2, layer2,
+                               Rect(0, 0, bufferSize.width, bufferSize.height),
+                               Rect(32, 32, 64, 64));
+
+    transaction2.merge(std::move(transaction1)).apply();
 
     ExpectedResult expected;
     expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
@@ -491,7 +531,11 @@
             }
         }
 
-        transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+        ui::Size bufferSize = getBufferSize();
+        TransactionUtils::setFrame(transaction, layer,
+                                   Rect(0, 0, bufferSize.width, bufferSize.height),
+                                   Rect(0, 0, 32, 32));
+        transaction.apply();
 
         ExpectedResult expected;
         expected.addSurface((i == 0) ? ExpectedResult::Transaction::PRESENTED
@@ -523,8 +567,16 @@
             return;
         }
 
-        transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
-        transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+        ui::Size bufferSize = getBufferSize();
+
+        TransactionUtils::setFrame(transaction1, layer1,
+                                   Rect(0, 0, bufferSize.width, bufferSize.height),
+                                   Rect(0, 0, 32, 32));
+        TransactionUtils::setFrame(transaction2, layer2,
+                                   Rect(0, 0, bufferSize.width, bufferSize.height),
+                                   Rect(32, 32, 64, 64));
+
+        transaction2.merge(std::move(transaction1)).apply();
 
         ExpectedResult expected;
         expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
@@ -564,8 +616,16 @@
             return;
         }
 
-        transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
-        transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+        ui::Size bufferSize = getBufferSize();
+
+        TransactionUtils::setFrame(transaction1, layer1,
+                                   Rect(0, 0, bufferSize.width, bufferSize.height),
+                                   Rect(0, 0, 32, 32));
+        TransactionUtils::setFrame(transaction2, layer2,
+                                   Rect(0, 0, bufferSize.width, bufferSize.height),
+                                   Rect(32, 32, 64, 64));
+
+        transaction2.merge(std::move(transaction1)).apply();
 
         ExpectedResult expected;
         expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
@@ -606,8 +666,15 @@
         return;
     }
 
-    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
-    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+    ui::Size bufferSize = getBufferSize();
+
+    TransactionUtils::setFrame(transaction1, layer1,
+                               Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+    TransactionUtils::setFrame(transaction2, layer2,
+                               Rect(0, 0, bufferSize.width, bufferSize.height),
+                               Rect(32, 32, 64, 64));
+
+    transaction2.merge(std::move(transaction1)).apply();
 
     ExpectedResult expected;
     expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
@@ -661,8 +728,15 @@
         return;
     }
 
-    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
-    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+    ui::Size bufferSize = getBufferSize();
+
+    TransactionUtils::setFrame(transaction1, layer1,
+                               Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+    TransactionUtils::setFrame(transaction2, layer2,
+                               Rect(0, 0, bufferSize.width, bufferSize.height),
+                               Rect(32, 32, 64, 64));
+
+    transaction2.merge(std::move(transaction1)).apply();
 
     ExpectedResult expected;
     expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
@@ -682,7 +756,10 @@
         return;
     }
 
-    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+    TransactionUtils::setFrame(transaction2, layer2,
+                               Rect(0, 0, bufferSize.width, bufferSize.height),
+                               Rect(32, 32, 64, 64));
+    transaction2.merge(std::move(transaction1)).apply();
 
     expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2,
                         ExpectedResult::Buffer::NOT_ACQUIRED);
@@ -762,7 +839,10 @@
         return;
     }
 
-    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+    ui::Size bufferSize = getBufferSize();
+    TransactionUtils::setFrame(transaction, layer, Rect(0, 0, bufferSize.width, bufferSize.height),
+                               Rect(0, 0, 32, 32));
+    transaction.apply();
 
     ExpectedResult expectedResult;
     expectedResult.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
@@ -781,7 +861,10 @@
             return;
         }
 
-        transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+        TransactionUtils::setFrame(transaction, layer,
+                                   Rect(0, 0, bufferSize.width, bufferSize.height),
+                                   Rect(0, 0, 32, 32));
+        transaction.apply();
     }
     EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
 }
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 7505e6e..7581cd3 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -135,53 +135,6 @@
     }
 }
 
-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, CreateLayer_BufferState) {
     uint32_t transformHint = ui::Transform::ROT_INVALID;
     sp<SurfaceControl> layer;
@@ -204,11 +157,7 @@
             Transaction().setPosition(layerG, 16, 16).setRelativeLayer(layerG, layerR, 1).apply();
             break;
         case ISurfaceComposerClient::eFXSurfaceBufferState:
-            Transaction()
-                    .setFrame(layerR, Rect(0, 0, 32, 32))
-                    .setFrame(layerG, Rect(16, 16, 48, 48))
-                    .setRelativeLayer(layerG, layerR, 1)
-                    .apply();
+            Transaction().setPosition(layerG, 16, 16).setRelativeLayer(layerG, layerR, 1).apply();
             break;
         default:
             ASSERT_FALSE(true) << "Unsupported layer type";
@@ -260,10 +209,9 @@
             break;
         case ISurfaceComposerClient::eFXSurfaceBufferState:
             Transaction()
-                    .setFrame(layerR, Rect(0, 0, 32, 32))
-                    .setFrame(layerG, Rect(8, 8, 40, 40))
+                    .setPosition(layerG, 8, 8)
                     .setRelativeLayer(layerG, layerR, 3)
-                    .setFrame(layerB, Rect(16, 16, 48, 48))
+                    .setPosition(layerB, 16, 16)
                     .setLayer(layerB, mLayerZBase + 2)
                     .apply();
             break;
@@ -388,7 +336,6 @@
     Transaction()
             .setTransparentRegionHint(layer, Region(top))
             .setBuffer(layer, buffer)
-            .setFrame(layer, Rect(0, 0, 32, 32))
             .apply();
     {
         SCOPED_TRACE("top transparent");
@@ -447,7 +394,7 @@
     // check that transparent region hint is bound by the layer size
     Transaction()
             .setTransparentRegionHint(layerTransparent, Region(mDisplayRect))
-            .setFrame(layerR, Rect(16, 16, 48, 48))
+            .setPosition(layerR, 16, 16)
             .setLayer(layerR, mLayerZBase + 1)
             .apply();
     ASSERT_NO_FATAL_FAILURE(
@@ -477,8 +424,7 @@
             Transaction()
                     .setAlpha(layer1, 0.25f)
                     .setAlpha(layer2, 0.75f)
-                    .setFrame(layer1, Rect(0, 0, 32, 32))
-                    .setFrame(layer2, Rect(16, 0, 48, 32))
+                    .setPosition(layer2, 16, 0)
                     .setLayer(layer2, mLayerZBase + 1)
                     .apply();
             break;
@@ -573,7 +519,7 @@
                 ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, fillColor, width, height));
                 expectedColor = fillColor;
             }
-            Transaction().setFrame(layer, Rect(0, 0, width, height)).apply();
+            Transaction().setCrop(layer, Rect(0, 0, width, height)).apply();
             break;
         default:
             GTEST_FAIL() << "Unknown layer type in setBackgroundColorHelper";
@@ -614,44 +560,6 @@
 }
 
 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;
@@ -849,42 +757,39 @@
     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();
+    Transaction().setPosition(layer, 32, 32).setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f).apply();
     {
         SCOPED_TRACE("IDENTITY");
-        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+        getScreenCapture()->expectQuadrant(Rect(32, 32, 64, 64), 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);
+        getScreenCapture()->expectQuadrant(Rect(0, 32, 32, 64), Color::GREEN, Color::RED,
+                                           Color::WHITE, Color::BLUE);
     }
 
     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);
+        getScreenCapture()->expectQuadrant(Rect(32, 0, 64, 32), Color::BLUE, Color::WHITE,
+                                           Color::RED, Color::GREEN);
     }
 
     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);
+        getScreenCapture()->expectQuadrant(Rect(0, 32, 32, 64), Color::BLUE, Color::RED,
+                                           Color::WHITE, Color::GREEN);
     }
 
     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);
+        getScreenCapture()->expectQuadrant(Rect(32, 32, 96, 96), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE, 1 /* tolerance */);
     }
 }
 
@@ -911,29 +816,6 @@
     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, SetCropBasic_BufferQueue) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
@@ -955,8 +837,8 @@
 
     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);
+    shot->expectColor(crop, Color::RED);
+    shot->expectBorder(crop, Color::BLACK);
 }
 
 TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferQueue) {
@@ -986,13 +868,13 @@
     {
         SCOPED_TRACE("empty rect");
         Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply();
-        getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     {
         SCOPED_TRACE("negative rect");
         Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply();
-        getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 }
 
@@ -1016,8 +898,6 @@
     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
@@ -1025,8 +905,8 @@
     {
         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);
+        shot->expectColor(Rect(0, 0, 32, 16), Color::BLUE);
+        shot->expectBorder(Rect(0, 0, 32, 16), Color::BLACK);
     }
 
     // Partially out of bounds in the positive (lower right) direction
@@ -1034,8 +914,8 @@
     {
         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);
+        shot->expectColor(Rect(0, 16, 32, 64), Color::RED);
+        shot->expectBorder(Rect(0, 16, 32, 64), Color::BLACK);
     }
 
     // Fully out of buffer space bounds
@@ -1043,9 +923,7 @@
     {
         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);
+        shot->expectColor(Rect(0, 0, 64, 64), Color::BLACK);
     }
 }
 
@@ -1068,12 +946,11 @@
             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();
+    Transaction().setPosition(layer, 32, 32).setCrop(layer, crop).apply();
     auto shot = getScreenCapture();
-    shot->expectColor(frame, Color::RED);
-    shot->expectBorder(frame, Color::BLACK);
+    shot->expectColor(Rect(40, 40, 56, 56), Color::RED);
+    shot->expectBorder(Rect(40, 40, 56, 56), Color::BLACK);
 }
 
 TEST_P(LayerRenderTypeTransactionTest, SetCropWithScale_BufferQueue) {
@@ -1091,29 +968,6 @@
     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 is applied immediately by default, with or without resize pending
-    Transaction().setCrop(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(
@@ -1121,7 +975,10 @@
     ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
     const Rect frame(8, 8, 24, 24);
 
-    Transaction().setFrame(layer, frame).apply();
+    Transaction t;
+    TransactionUtils::setFrame(t, layer, Rect(0, 0, 32, 32), frame);
+    t.apply();
+
     auto shot = getScreenCapture();
     shot->expectColor(frame, Color::RED);
     shot->expectBorder(frame, Color::BLACK);
@@ -1133,16 +990,23 @@
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
     ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
 
+    Transaction t;
     {
         SCOPED_TRACE("empty rect");
-        Transaction().setFrame(layer, Rect(8, 8, 8, 8)).apply();
+        TransactionUtils::setFrame(t, layer, Rect(0, 0, 32, 32), Rect(8, 8, 8, 8));
+        t.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);
+        TransactionUtils::setFrame(t, layer, Rect(0, 0, 32, 32), Rect(8, 8, 0, 0));
+        t.apply();
+
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
+        shot->expectBorder(Rect(0, 0, 8, 8), Color::BLACK);
     }
 }
 
@@ -1152,10 +1016,10 @@
             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
+    // A layer with a buffer will have a computed size that matches the buffer size.
     auto shot = getScreenCapture();
-    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+    shot->expectColor(Rect(0, 0, 10, 10), Color::RED);
+    shot->expectBorder(Rect(0, 0, 10, 10), Color::BLACK);
 }
 
 TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBSParent_BufferState) {
@@ -1163,17 +1027,16 @@
     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));
+            child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
     ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
 
     Transaction().reparent(child, parent).apply();
 
-    // A layer will default to the frame of its parent
+    // A layer with a buffer will have a computed size that matches the buffer size.
     auto shot = getScreenCapture();
-    shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+    shot->expectColor(Rect(0, 0, 10, 10), Color::BLUE);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
@@ -1183,14 +1046,14 @@
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parent, Color::RED, 32, 32));
 
     ASSERT_NO_FATAL_FAILURE(
-            child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+            child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
     ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
 
     Transaction().reparent(child, parent).apply();
 
-    // A layer will default to the frame of its parent
+    // A layer with a buffer will have a computed size that matches the buffer size.
     auto shot = getScreenCapture();
-    shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+    shot->expectColor(Rect(0, 0, 10, 10), Color::BLUE);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
@@ -1199,11 +1062,10 @@
     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();
+    Transaction().setPosition(layer, 16, 16).apply();
 
     auto shot = getScreenCapture();
     shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
@@ -1215,18 +1077,20 @@
     ASSERT_NO_FATAL_FAILURE(
             parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
     ASSERT_NO_FATAL_FAILURE(
-            child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+            child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
     Transaction().reparent(child, parent).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();
+    Rect childDst(0, 16, 32, 32);
+    Transaction t;
+    TransactionUtils::setFrame(t, child, Rect(0, 0, 10, 10), childDst);
+    t.apply();
 
     auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 16), Color::RED);
-    shot->expectColor(Rect(0, 16, 32, 32), Color::BLUE);
+    shot->expectColor(childDst, Color::BLUE);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
@@ -1238,8 +1102,8 @@
     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);
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
 TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) {
@@ -1252,8 +1116,8 @@
     {
         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);
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
     ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32));
@@ -1261,8 +1125,8 @@
     {
         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);
+        shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
     ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
@@ -1270,8 +1134,8 @@
     {
         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);
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
     }
 }
 
@@ -1286,7 +1150,6 @@
 
     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();
@@ -1295,7 +1158,6 @@
 
     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();
@@ -1350,8 +1212,8 @@
 
             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);
+            shot->expectColor(Rect(0, 0, 32, 32), color);
+            shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
         }
         idx++;
     }
@@ -1383,8 +1245,8 @@
 
             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);
+            shot->expectColor(Rect(0, 0, 32, 32), color);
+            shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
         }
         idx++;
     }
@@ -1416,8 +1278,8 @@
 
             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);
+            shot->expectColor(Rect(0, 0, 32, 32), color);
+            shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
         }
         if (idx == 0) {
             buffers[0].clear();
@@ -1435,7 +1297,6 @@
                                                          Color::BLUE, Color::WHITE));
 
     Transaction()
-            .setFrame(layer, Rect(0, 0, 32, 32))
             .setTransform(layer, NATIVE_WINDOW_TRANSFORM_ROT_90)
             .apply();
 
@@ -1452,7 +1313,6 @@
                                                          Color::BLUE, Color::WHITE));
 
     Transaction()
-            .setFrame(layer, Rect(0, 0, 32, 32))
             .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_H)
             .apply();
 
@@ -1469,7 +1329,6 @@
                                                          Color::BLUE, Color::WHITE));
 
     Transaction()
-            .setFrame(layer, Rect(0, 0, 32, 32))
             .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_V)
             .apply();
 
@@ -1518,8 +1377,8 @@
     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);
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
 TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) {
@@ -1534,8 +1393,8 @@
     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);
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
 TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) {
@@ -1552,8 +1411,8 @@
     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);
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
 TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) {
@@ -1570,8 +1429,8 @@
     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);
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
 TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) {
@@ -1586,8 +1445,8 @@
     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);
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
 }
 
 TEST_P(LayerRenderTypeTransactionTest, SetColorTransformBasic) {
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 87c7b7d..0bc8fe7 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -244,6 +244,11 @@
         return bufferGenerator.get(outBuffer, outFence);
     }
 
+    static ui::Size getBufferSize() {
+        static BufferGenerator bufferGenerator;
+        return bufferGenerator.getSize();
+    }
+
     sp<SurfaceComposerClient> mClient;
 
     bool deviceSupportsBlurs() {
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index ac5e297..2828d61 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -51,14 +51,9 @@
         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;
@@ -196,17 +191,7 @@
     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(layer, Rect(0, 0, size, size))
-                .apply();
-    } else {
-        Transaction()
-                .setCornerRadius(layer, cornerRadius)
-                .setFrame(layer, Rect(0, 0, size, size))
-                .apply();
-    }
+    Transaction().setCornerRadius(layer, cornerRadius).apply();
     {
         const uint8_t bottom = size - 1;
         const uint8_t right = size - 1;
@@ -234,19 +219,13 @@
     ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
     ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size));
 
-    auto transaction = Transaction()
-                               .setCornerRadius(parent, cornerRadius)
-                               .setCrop(parent, Rect(0, 0, size, size))
-                               .reparent(child, parent)
-                               .setPosition(child, 0, size)
-                               // Rotate by half PI
-                               .setMatrix(child, 0.0f, -1.0f, 1.0f, 0.0f);
-    if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
-        transaction.setCrop(parent, Rect(0, 0, size, size));
-    } else {
-        transaction.setFrame(parent, Rect(0, 0, size, size));
-    }
-    transaction.apply();
+    Transaction()
+            .setCornerRadius(parent, cornerRadius)
+            .reparent(child, parent)
+            .setPosition(child, 0, size)
+            // Rotate by half PI
+            .setMatrix(child, 0.0f, -1.0f, 1.0f, 0.0f)
+            .apply();
 
     {
         const uint8_t bottom = size - 1;
@@ -275,21 +254,12 @@
     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(parent, Rect(0, 0, size, size))
-                .reparent(child, parent)
-                .setPosition(child, 0, size / 2)
-                .apply();
-    } else {
-        Transaction()
-                .setCornerRadius(parent, cornerRadius)
-                .setFrame(parent, Rect(0, 0, size, size))
-                .reparent(child, parent)
-                .setFrame(child, Rect(0, size / 2, size, size))
-                .apply();
-    }
+    Transaction()
+            .setCornerRadius(parent, cornerRadius)
+            .reparent(child, parent)
+            .setPosition(child, 0, size / 2)
+            .apply();
+
     {
         const uint8_t bottom = size - 1;
         const uint8_t right = size - 1;
@@ -331,12 +301,9 @@
 
     Transaction()
             .setLayer(greenLayer, mLayerZBase)
-            .setFrame(leftLayer, {0, 0, canvasSize * 2, canvasSize * 2})
             .setLayer(leftLayer, mLayerZBase + 1)
-            .setFrame(leftLayer, leftRect)
             .setLayer(rightLayer, mLayerZBase + 2)
             .setPosition(rightLayer, rightRect.left, rightRect.top)
-            .setFrame(rightLayer, rightRect)
             .apply();
 
     {
@@ -352,8 +319,6 @@
             .setLayer(blurLayer, mLayerZBase + 3)
             .setBackgroundBlurRadius(blurLayer, blurRadius)
             .setCrop(blurLayer, blurRect)
-            .setFrame(blurLayer, blurRect)
-            .setSize(blurLayer, blurRect.getWidth(), blurRect.getHeight())
             .setAlpha(blurLayer, 0.0f)
             .apply();
 
@@ -435,10 +400,8 @@
 
     Transaction()
             .setLayer(left, mLayerZBase + 1)
-            .setFrame(left, {0, 0, size, size})
             .setLayer(right, mLayerZBase + 2)
             .setPosition(right, size, 0)
-            .setFrame(right, {size, 0, size * 2, size})
             .apply();
 
     {
@@ -457,7 +420,6 @@
             .setAlpha(blurParent, 0.5)
             .setLayer(blur, mLayerZBase + 4)
             .setBackgroundBlurRadius(blur, size) // set the blur radius to the size of one rect
-            .setFrame(blur, {0, 0, size * 2, size})
             .reparent(blur, blurParent)
             .apply();
 
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index e4a1f66..ee4d367 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -127,15 +127,7 @@
         TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
         waitForPostedBuffers();
     }
-    void restoreInitialState() {
-        asTransaction([&](Transaction& t) {
-            t.setSize(mFGSurfaceControl, 64, 64);
-            t.setPosition(mFGSurfaceControl, 64, 64);
-            t.setCrop(mFGSurfaceControl, Rect(0, 0, 64, 64));
-        });
 
-        EXPECT_INITIAL_STATE("After restoring initial state");
-    }
     std::unique_ptr<ScreenCapture> sc;
 };
 
@@ -160,61 +152,6 @@
     }
 };
 
-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,
-                                       mSyncSurfaceControl->getSurface()->getNextFrameNumber());
-    });
-
-    asTransaction([&](Transaction& t) {
-        t.setPosition(mFGSurfaceControl, 128, 128);
-        t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl,
-                                       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;
 
@@ -449,48 +386,16 @@
 
     {
         mCapture = screenshot();
-        // Child and BG blended.
-        mCapture->checkPixel(0, 0, 127, 127, 0);
+        // Child and BG blended. See b/175352694 for tolerance.
+        mCapture->expectColor(Rect(0, 0, 1, 1), Color{127, 127, 0, 255}, 1);
     }
 
     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); });
-
-    {
-        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);
+        // Child and BG blended. See b/175352694 for tolerance.
+        mCapture->expectColor(Rect(0, 0, 1, 1), Color{95, 64, 95, 255}, 1);
     }
 }
 
@@ -539,7 +444,7 @@
 
     asTransaction([&](Transaction& t) {
         t.reparent(mChild, nullptr);
-        t.reparentChildren(mChild, mFGSurfaceControl);
+        t.reparent(mGrandChild, mFGSurfaceControl);
     });
 
     {
@@ -549,221 +454,6 @@
     }
 }
 
-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) {
-        mFGSurfaceControl->getSurface()->setScalingMode(
-            NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-        // Resubmit buffer with new scaling mode
-        TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
-        // 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) {
-        mFGSurfaceControl->getSurface()->setScalingMode(
-            NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-        // Resubmit buffer with new scaling mode
-        TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
-        // 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) {
-        mFGSurfaceControl->getSurface()->setScalingMode(
-            NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-        // Resubmit buffer with new scaling mode
-        TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
-        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,
-                                       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);
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index 613b21e..ccf434d 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -195,7 +195,7 @@
             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();
+    Transaction().show(bufferStateLayer).apply();
 
     {
         SCOPED_TRACE("Initial Mirror BufferStateLayer");
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index b0753c8..2e9c10c 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -350,7 +350,10 @@
 
 TEST_F(ScreenCaptureTest, CaptureBufferLayerWithoutBufferFails) {
     sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+                                             PIXEL_FORMAT_RGBA_8888,
+                                             ISurfaceComposerClient::eFXSurfaceBufferState,
+                                             mFGSurfaceControl.get());
+
     SurfaceComposerClient::Transaction().show(child).apply(true);
     sp<GraphicBuffer> outBuffer;
 
@@ -361,7 +364,7 @@
     ScreenCaptureResults captureResults;
     ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(args, captureResults));
 
-    TransactionUtils::fillSurfaceRGBA8(child, Color::RED);
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::RED, 32, 32));
     SurfaceComposerClient::Transaction().apply(true);
     ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(args, captureResults));
     ScreenCapture sc(captureResults.buffer);
@@ -432,12 +435,15 @@
 }
 
 TEST_F(ScreenCaptureTest, CaptureCrop) {
-    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60,
+                                              ISurfaceComposerClient::eFXSurfaceBufferState);
     sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
-                                                 PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
+                                                 PIXEL_FORMAT_RGBA_8888,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                 redLayer.get());
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(blueLayer, Color::BLUE, 30, 30));
 
     SurfaceComposerClient::Transaction()
             .setLayer(redLayer, INT32_MAX - 1)
@@ -464,12 +470,15 @@
 }
 
 TEST_F(ScreenCaptureTest, CaptureSize) {
-    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+  sp<SurfaceControl> redLayer =
+      createLayer(String8("Red surface"), 60, 60, ISurfaceComposerClient::eFXSurfaceBufferState);
     sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
-                                                 PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
+                                                 PIXEL_FORMAT_RGBA_8888,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                 redLayer.get());
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(blueLayer, Color::BLUE, 30, 30));
 
     SurfaceComposerClient::Transaction()
             .setLayer(redLayer, INT32_MAX - 1)
@@ -489,6 +498,7 @@
 
     captureArgs.frameScaleX = 0.5f;
     captureArgs.frameScaleY = 0.5f;
+    sleep(1);
 
     ScreenCapture::captureLayers(&mCapture, captureArgs);
     // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area.
@@ -519,14 +529,15 @@
 }
 
 TEST_F(ScreenCaptureTest, CaputureSecureLayer) {
-    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60,
+                                              ISurfaceComposerClient::eFXSurfaceBufferState);
     sp<SurfaceControl> secureLayer =
             createLayer(String8("Secure surface"), 30, 30,
                         ISurfaceComposerClient::eSecure |
-                                ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                ISurfaceComposerClient::eFXSurfaceBufferState,
                         redLayer.get());
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(secureLayer, Color::BLUE, 30, 30));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(secureLayer, Color::BLUE, 30, 30));
 
     auto redLayerHandle = redLayer->getHandle();
     Transaction()
@@ -874,4 +885,4 @@
 } // 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
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index d9cab42..ee4e863 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -44,7 +44,6 @@
 constexpr uint32_t LAYER_UPDATE = INT_MAX - 2;
 constexpr uint32_t SIZE_UPDATE = 134;
 constexpr uint32_t STACK_UPDATE = 1;
-constexpr uint64_t DEFERRED_UPDATE = 0;
 constexpr int32_t RELATIVE_Z = 42;
 constexpr float ALPHA_UPDATE = 0.29f;
 constexpr float CORNER_RADIUS_UPDATE = 0.2f;
@@ -191,10 +190,8 @@
     bool hiddenFlagUpdateFound(const SurfaceChange& change, bool foundHiddenFlag);
     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 reparentChildrenUpdateFound(const SurfaceChange& change, bool found);
     bool shadowRadiusUpdateFound(const SurfaceChange& change, bool found);
     bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase);
 
@@ -228,10 +225,8 @@
     void hiddenFlagUpdate(Transaction&);
     void opaqueFlagUpdate(Transaction&);
     void secureFlagUpdate(Transaction&);
-    void deferredTransactionUpdate(Transaction&);
     void reparentUpdate(Transaction&);
     void relativeParentUpdate(Transaction&);
-    void reparentChildrenUpdate(Transaction&);
     void shadowRadiusUpdate(Transaction&);
     void surfaceCreation(Transaction&);
     void displayCreation(Transaction&);
@@ -398,10 +393,6 @@
     t.setFlags(mBGSurfaceControl, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
 }
 
-void SurfaceInterceptorTest::deferredTransactionUpdate(Transaction& t) {
-    t.deferTransactionUntil_legacy(mBGSurfaceControl, mBGSurfaceControl, DEFERRED_UPDATE);
-}
-
 void SurfaceInterceptorTest::reparentUpdate(Transaction& t) {
     t.reparent(mBGSurfaceControl, mFGSurfaceControl);
 }
@@ -410,10 +401,6 @@
     t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl, RELATIVE_Z);
 }
 
-void SurfaceInterceptorTest::reparentChildrenUpdate(Transaction& t) {
-    t.reparentChildren(mBGSurfaceControl, mFGSurfaceControl);
-}
-
 void SurfaceInterceptorTest::shadowRadiusUpdate(Transaction& t) {
     t.setShadowRadius(mBGSurfaceControl, SHADOW_RADIUS_UPDATE);
 }
@@ -443,9 +430,7 @@
     runInTransaction(&SurfaceInterceptorTest::hiddenFlagUpdate);
     runInTransaction(&SurfaceInterceptorTest::opaqueFlagUpdate);
     runInTransaction(&SurfaceInterceptorTest::secureFlagUpdate);
-    runInTransaction(&SurfaceInterceptorTest::deferredTransactionUpdate);
     runInTransaction(&SurfaceInterceptorTest::reparentUpdate);
-    runInTransaction(&SurfaceInterceptorTest::reparentChildrenUpdate);
     runInTransaction(&SurfaceInterceptorTest::relativeParentUpdate);
     runInTransaction(&SurfaceInterceptorTest::shadowRadiusUpdate);
 }
@@ -628,18 +613,6 @@
     return foundSecureFlag;
 }
 
-bool SurfaceInterceptorTest::deferredTransactionUpdateFound(const SurfaceChange& change,
-        bool foundDeferred) {
-    bool hasId(change.deferred_transaction().layer_id() == mBGLayerId);
-    bool hasFrameNumber(change.deferred_transaction().frame_number() == DEFERRED_UPDATE);
-    if (hasId && hasFrameNumber && !foundDeferred) {
-        foundDeferred = true;
-    } else if (hasId && hasFrameNumber && foundDeferred) {
-        [] () { FAIL(); }();
-    }
-    return foundDeferred;
-}
-
 bool SurfaceInterceptorTest::reparentUpdateFound(const SurfaceChange& change, bool found) {
     bool hasId(change.reparent().parent_id() == mFGLayerId);
     if (hasId && !found) {
@@ -660,16 +633,6 @@
     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);
@@ -732,15 +695,9 @@
                         case SurfaceChange::SurfaceChangeCase::kSecureFlag:
                             foundUpdate = secureFlagUpdateFound(change, foundUpdate);
                             break;
-                        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;
@@ -769,9 +726,7 @@
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kHiddenFlag));
     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));
 }
 
@@ -927,21 +882,11 @@
             SurfaceChange::SurfaceChangeCase::kSecureFlag);
 }
 
-TEST_F(SurfaceInterceptorTest, InterceptDeferredTransactionUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::deferredTransactionUpdate,
-            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);
@@ -976,11 +921,6 @@
     ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kDisplayDeletion));
 }
 
-TEST_F(SurfaceInterceptorTest, InterceptBufferUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::nBufferUpdates,
-            &SurfaceInterceptorTest::bufferUpdatesFound);
-}
-
 // If the interceptor is enabled while buffer updates are being pushed, the interceptor should
 // first create a snapshot of the existing displays and surfaces and then start capturing
 // the buffer updates
@@ -996,26 +936,6 @@
     const auto& firstIncrement = capturedTrace.mutable_increment(0);
     ASSERT_EQ(firstIncrement->increment_case(), Increment::IncrementCase::kDisplayCreation);
 }
-
-TEST_F(SurfaceInterceptorTest, InterceptSimultaneousUpdatesWorks) {
-    enableInterceptor();
-    setupBackgroundSurface();
-    std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
-    std::thread surfaceUpdates(&SurfaceInterceptorTest::runAllUpdates, this);
-    runInTransaction(&SurfaceInterceptorTest::surfaceCreation);
-    bufferUpdates.join();
-    surfaceUpdates.join();
-    disableInterceptor();
-
-    Trace capturedTrace;
-    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
-    preProcessTrace(capturedTrace);
-
-    assertAllUpdatesFound(capturedTrace);
-    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 -Wextra"
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 820f248..162711d 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -91,7 +91,6 @@
 constexpr static TestColor LIGHT_RED = {255, 177, 177, 255};
 constexpr static TestColor GREEN = {63, 195, 63, 255};
 constexpr static TestColor BLUE = {63, 63, 195, 255};
-constexpr static TestColor DARK_GRAY = {63, 63, 63, 255};
 constexpr static TestColor LIGHT_GRAY = {200, 200, 200, 255};
 
 // Fill an RGBA_8888 formatted surface with a single color.
@@ -1294,31 +1293,6 @@
         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?
         {
@@ -1469,77 +1443,6 @@
         }
     }
 
-    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,
-                                            syncSurfaceControl->getSurface()->getNextFrameNumber());
-        }
-        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setPosition(mFGSurfaceControl, 128, 128);
-            ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl,
-                                            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,
@@ -1589,10 +1492,6 @@
     Test_LayerMove();
 }
 
-TEST_F(TransactionTest_2_1, DISABLED_LayerResize) {
-    Test_LayerResize();
-}
-
 TEST_F(TransactionTest_2_1, DISABLED_LayerCrop) {
     Test_LayerCrop();
 }
@@ -1625,10 +1524,6 @@
     Test_LayerSetMatrix();
 }
 
-TEST_F(TransactionTest_2_1, DISABLED_DeferredTransaction) {
-    Test_DeferredTransaction();
-}
-
 TEST_F(TransactionTest_2_1, DISABLED_SetRelativeLayer) {
     Test_SetRelativeLayer();
 }
@@ -1766,101 +1661,6 @@
         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);
-        }
-
-        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()));
-    }
-
-    // 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->getHandle());
-
-        // Show the child layer in a deferred transaction
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.deferTransactionUntil_legacy(mChild, Base::mFGSurfaceControl,
-                                            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;
 };
 
@@ -1886,19 +1686,6 @@
     Test_LayerAlpha();
 }
 
-TEST_F(ChildLayerTest_2_1, DISABLED_ReparentChildren) {
-    Test_ReparentChildren();
-}
-
-// Regression test for b/37673612
-TEST_F(ChildLayerTest_2_1, DISABLED_ChildrenWithParentBufferTransform) {
-    Test_ChildrenWithParentBufferTransform();
-}
-
-TEST_F(ChildLayerTest_2_1, DISABLED_Bug36858924) {
-    Test_Bug36858924();
-}
-
 template <typename FakeComposerService>
 class ChildColorLayerTest : public ChildLayerTest<FakeComposerService> {
     using Base = ChildLayerTest<FakeComposerService>;
@@ -1988,91 +1775,6 @@
 TEST_F(ChildColorLayerTest_2_1, DISABLED_LayerZeroAlpha) {
     Test_LayerZeroAlpha();
 }
-
-template <typename FakeComposerService>
-class LatchingTest : public TransactionTest<FakeComposerService> {
-    using Base = TransactionTest<FakeComposerService>;
-
-protected:
-    void lockAndFillFGBuffer() { fillSurfaceRGBA8(Base::mFGSurfaceControl, RED, false); }
-
-    void unlockFGBuffer() {
-        sp<Surface> s = Base::mFGSurfaceControl->getSurface();
-        ASSERT_EQ(NO_ERROR, s->unlockAndPost());
-        Base::sFakeComposer->runVSyncAndWait();
-    }
-
-    void completeFGResize() {
-        fillSurfaceRGBA8(Base::mFGSurfaceControl, RED);
-        Base::sFakeComposer->runVSyncAndWait();
-    }
-    void restoreInitialState() {
-        TransactionScope ts(*Base::sFakeComposer);
-        ts.setSize(Base::mFGSurfaceControl, 64, 64);
-        ts.setPosition(Base::mFGSurfaceControl, 64, 64);
-        ts.setCrop(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(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()));
-    }
-};
-
-using LatchingTest_2_1 = LatchingTest<FakeComposerService_2_1>;
-
-TEST_F(LatchingTest_2_1, DISABLED_SurfacePositionLatching) {
-    Test_SurfacePositionLatching();
-}
-
-TEST_F(LatchingTest_2_1, DISABLED_CropLatching) {
-    Test_CropLatching();
-}
-
 } // namespace
 
 int main(int argc, char** argv) {
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 3c1b9d8..88fb811 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -55,6 +55,7 @@
         "EventThreadTest.cpp",
         "FpsReporterTest.cpp",
         "FpsTest.cpp",
+        "FramebufferSurfaceTest.cpp",
         "FrameTimelineTest.cpp",
         "HWComposerTest.cpp",
         "OneShotTimerTest.cpp",
@@ -114,6 +115,7 @@
         "android.hardware.power@1.1",
         "android.hardware.power@1.2",
         "android.hardware.power@1.3",
+        "android.hardware.power-V1-cpp",
         "libcompositionengine_mocks",
         "libcompositionengine",
         "libframetimeline",
@@ -125,6 +127,7 @@
         "librenderengine",
         "libserviceutils",
         "libtimestats",
+        "libtimestats_atoms_proto",
         "libtimestats_proto",
         "libtrace_proto",
         "perfetto_trace_protos",
@@ -136,7 +139,6 @@
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.common@1.2",
-        "android.hardware.power-V1-cpp",
         "libbase",
         "libbinder",
         "libcutils",
@@ -151,14 +153,10 @@
         "libnativewindow",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
-        "libprotoutil",
-        "libstatslog",
-        "libstatssocket",
         "libSurfaceFlingerProp",
         "libsync",
         "libui",
         "libutils",
-        "libstatspull",
     ],
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 4e1c0c7..3042450 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -15,6 +15,7 @@
  */
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include "renderengine/ExternalTexture.h"
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 #pragma clang diagnostic ignored "-Wextra"
@@ -194,7 +195,7 @@
 
     sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
 
-    sp<GraphicBuffer> mCaptureScreenBuffer;
+    std::shared_ptr<renderengine::ExternalTexture> mCaptureScreenBuffer;
 };
 
 template <typename LayerCase>
@@ -243,11 +244,15 @@
     // TODO: Eliminate expensive/real allocation if possible.
     const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
             GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
-    mCaptureScreenBuffer = new GraphicBuffer(renderArea->getReqWidth(), renderArea->getReqHeight(),
-                                             HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot");
+    mCaptureScreenBuffer = std::make_shared<
+            renderengine::ExternalTexture>(new GraphicBuffer(renderArea->getReqWidth(),
+                                                             renderArea->getReqHeight(),
+                                                             HAL_PIXEL_FORMAT_RGBA_8888, 1, usage,
+                                                             "screenshot"),
+                                           *mRenderEngine, true);
 
     status_t result =
-            mFlinger.renderScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer.get(),
+            mFlinger.renderScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer,
                                             forSystem, regionSampling);
     EXPECT_EQ(NO_ERROR, result);
 
@@ -340,8 +345,8 @@
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
                                    const std::vector<const renderengine::LayerSettings*>&,
-                                   const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
-                                   base::unique_fd*) -> status_t {
+                                   const std::shared_ptr<renderengine::ExternalTexture>&,
+                                   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);
@@ -389,8 +394,8 @@
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
                                    const std::vector<const renderengine::LayerSettings*>&,
-                                   const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
-                                   base::unique_fd*) -> status_t {
+                                   const std::shared_ptr<renderengine::ExternalTexture>&,
+                                   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);
@@ -625,8 +630,8 @@
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillOnce([](const renderengine::DisplaySettings& displaySettings,
                              const std::vector<const renderengine::LayerSettings*>& layerSettings,
-                             const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
-                             base::unique_fd*) -> status_t {
+                             const std::shared_ptr<renderengine::ExternalTexture>&, 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);
@@ -674,8 +679,8 @@
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillOnce([](const renderengine::DisplaySettings& displaySettings,
                              const std::vector<const renderengine::LayerSettings*>& layerSettings,
-                             const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
-                             base::unique_fd*) -> status_t {
+                             const std::shared_ptr<renderengine::ExternalTexture>&, 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);
@@ -751,8 +756,8 @@
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillOnce([](const renderengine::DisplaySettings& displaySettings,
                              const std::vector<const renderengine::LayerSettings*>& layerSettings,
-                             const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
-                             base::unique_fd*) -> status_t {
+                             const std::shared_ptr<renderengine::ExternalTexture>&, 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);
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 7cc0032..b4a1481 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -44,7 +44,7 @@
 constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID(111);
 constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID(222);
 constexpr PhysicalDisplayId DISPLAY_ID_64BIT(0xabcd12349876fedcULL);
-
+constexpr std::chrono::duration VSYNC_PERIOD(16ms);
 class MockVSyncSource : public VSyncSource {
 public:
     const char* getName() const override { return "test"; }
@@ -166,11 +166,14 @@
         mThrottleVsyncCallRecorder.getInvocable()(expectedVsyncTimestamp, uid);
         return (uid == mThrottledConnectionUid);
     };
+    const auto getVsyncPeriod = [](uid_t uid) {
+        return VSYNC_PERIOD.count();
+    };
 
     mThread = std::make_unique<impl::EventThread>(std::move(source),
                                                   /*tokenManager=*/nullptr,
                                                   mInterceptVSyncCallRecorder.getInvocable(),
-                                                  throttleVsync);
+                                                  throttleVsync, getVsyncPeriod);
 
     // EventThread should register itself as VSyncSource callback.
     mCallback = expectVSyncSetCallbackCallReceived();
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index d1385c0..7727052 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -68,7 +68,7 @@
     void SetUp() override {
         mTimeStats = std::make_shared<mock::TimeStats>();
         mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, kSurfaceFlingerPid,
-                                                               kTestThresholds, kHwcDuration);
+                                                               kTestThresholds);
         mFrameTimeline->registerDataSource();
         mTokenManager = &mFrameTimeline->mTokenManager;
         mTraceCookieCounter = &mFrameTimeline->mTraceCookieCounter;
@@ -163,7 +163,6 @@
     static constexpr JankClassificationThresholds kTestThresholds{kPresentThreshold,
                                                                   kDeadlineThreshold,
                                                                   kStartThreshold};
-    static constexpr nsecs_t kHwcDuration = std::chrono::nanoseconds(3ns).count();
 };
 
 static const std::string sLayerNameOne = "layer1";
@@ -483,7 +482,6 @@
 
 TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) {
     Fps refreshRate = Fps::fromPeriodNsecs(11);
-    // Deadline delta is 2ms because, sf's adjusted deadline is 60 - composerTime(3) = 57ms.
     EXPECT_CALL(*mTimeStats,
                 incrementJankyFrames(
                         TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
@@ -504,7 +502,34 @@
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     presentFence1->signalForTest(70);
 
-    mFrameTimeline->setSfPresent(59, presentFence1);
+    mFrameTimeline->setSfPresent(62, presentFence1);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfGpu) {
+    Fps refreshRate = Fps::fromPeriodNsecs(11);
+    EXPECT_CALL(*mTimeStats,
+                incrementJankyFrames(
+                        TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+                                                   sLayerNameOne,
+                                                   JankType::SurfaceFlingerGpuDeadlineMissed, 4, 10,
+                                                   0}));
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto gpuFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne, /*isBuffer*/ true);
+    mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+    surfaceFrame1->setAcquireFenceTime(20);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    gpuFence1->signalForTest(64);
+    presentFence1->signalForTest(70);
+
+    mFrameTimeline->setSfPresent(59, presentFence1, gpuFence1);
 }
 
 TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) {
@@ -512,7 +537,7 @@
     EXPECT_CALL(*mTimeStats,
                 incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
                                                                 sLayerNameOne, JankType::DisplayHAL,
-                                                                -1, 0, 0}));
+                                                                -4, 0, 0}));
 
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
@@ -536,7 +561,7 @@
     EXPECT_CALL(*mTimeStats,
                 incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
                                                                 sLayerNameOne,
-                                                                JankType::AppDeadlineMissed, -1, 0,
+                                                                JankType::AppDeadlineMissed, -4, 0,
                                                                 25}));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
@@ -563,7 +588,7 @@
                 incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
                                                                 sLayerNameOne,
                                                                 JankType::SurfaceFlingerScheduling,
-                                                                -1, 0, -10}));
+                                                                -4, 0, -10}));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({40, 60, 92});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
@@ -588,7 +613,7 @@
     EXPECT_CALL(*mTimeStats,
                 incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
                                                                 sLayerNameOne,
-                                                                JankType::PredictionError, -1, 5,
+                                                                JankType::PredictionError, -4, 5,
                                                                 0}));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({30, 40, 60});
@@ -614,7 +639,7 @@
     EXPECT_CALL(*mTimeStats,
                 incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
                                                                 sLayerNameOne,
-                                                                JankType::BufferStuffing, -1, 0,
+                                                                JankType::BufferStuffing, -4, 0,
                                                                 0}));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({30, 40, 58});
@@ -642,7 +667,7 @@
     EXPECT_CALL(*mTimeStats,
                 incrementJankyFrames(
                         TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne, sLayerNameOne,
-                                                   JankType::AppDeadlineMissed, -1, 0, 25}));
+                                                   JankType::AppDeadlineMissed, -4, 0, 25}));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90});
@@ -668,9 +693,10 @@
     Fps renderRate = Fps::fromPeriodNsecs(30);
 
     EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne,
-                                                                sLayerNameOne, JankType::Unknown,
-                                                                -1, -1, 25}));
+                incrementJankyFrames(
+                        TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne, sLayerNameOne,
+                                                   JankType::Unknown | JankType::AppDeadlineMissed,
+                                                   0, 0, 25}));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90});
@@ -697,7 +723,7 @@
     EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
 
     EXPECT_EQ(surfaceFrame1->getActuals().presentTime, 90);
-    EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown);
+    EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown | JankType::AppDeadlineMissed);
 }
 
 /*
@@ -1457,24 +1483,47 @@
 }
 
 TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) {
+    /*
+     * Case 1 - cpu time > vsync period but combined time > deadline > deadline -> cpudeadlinemissed
+     * Case 2 - cpu time < vsync period but combined time > deadline -> gpudeadlinemissed
+     */
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto gpuFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto gpuFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+    // case 1 - cpu time = 33 - 12 = 21, vsync period = 11
     mFrameTimeline->setSfWakeUp(sfToken1, 12, Fps::fromPeriodNsecs(11));
-    mFrameTimeline->setSfPresent(36, presentFence1);
-    auto displayFrame = getDisplayFrame(0);
+    mFrameTimeline->setSfPresent(33, presentFence1, gpuFence1);
+    auto displayFrame0 = getDisplayFrame(0);
+    gpuFence1->signalForTest(36);
     presentFence1->signalForTest(52);
 
     // Fences haven't been flushed yet, so it should be 0
-    EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+    EXPECT_EQ(displayFrame0->getActuals().presentTime, 0);
+
+    // case 2 - cpu time = 56 - 52 = 4, vsync period = 11
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
+    mFrameTimeline->setSfPresent(56, presentFence2, gpuFence2);
+    auto displayFrame1 = getDisplayFrame(1);
+    gpuFence2->signalForTest(66);
+    presentFence2->signalForTest(71);
+
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+    // Fences have flushed for first displayFrame, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame0->getActuals().presentTime, 52);
+    EXPECT_EQ(displayFrame0->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame0->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(displayFrame0->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
 
     addEmptyDisplayFrame();
-    displayFrame = getDisplayFrame(0);
 
-    // Fences have flushed, so the present timestamps should be updated
-    EXPECT_EQ(displayFrame->getActuals().presentTime, 52);
-    EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
-    EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
-    EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+    // Fences have flushed for second displayFrame, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 71);
+    EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
 }
 
 TEST_F(FrameTimelineTest, jankClassification_displayFrameLateStartLateFinishLatePresent) {
@@ -1561,7 +1610,7 @@
     EXPECT_CALL(*mTimeStats,
                 incrementJankyFrames(
                         TimeStats::JankyFramesInfo{Fps::fromPeriodNsecs(11), std::nullopt, sUidOne,
-                                                   sLayerNameOne, JankType::PredictionError, 0, 5,
+                                                   sLayerNameOne, JankType::PredictionError, -3, 5,
                                                    0}));
 
     addEmptyDisplayFrame();
@@ -1641,7 +1690,7 @@
     EXPECT_CALL(*mTimeStats,
                 incrementJankyFrames(
                         TimeStats::JankyFramesInfo{Fps::fromPeriodNsecs(11), std::nullopt, sUidOne,
-                                                   sLayerNameOne, JankType::PredictionError, 0, 5,
+                                                   sLayerNameOne, JankType::PredictionError, -3, 5,
                                                    0}));
 
     addEmptyDisplayFrame();
@@ -1699,9 +1748,9 @@
 }
 
 TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) {
-    // First frame - DisplayFrame is not janky. This should classify the SurfaceFrame as
+    // First frame - DisplayFrame is not janky. This should classify the SurfaceFrame as only
     // AppDeadlineMissed. Second frame - DisplayFrame is janky. This should propagate DisplayFrame's
-    // jank to the SurfaceFrame.
+    // jank to the SurfaceFrame along with AppDeadlineMissed.
 
     EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -1771,7 +1820,8 @@
     EXPECT_EQ(actuals2.presentTime, 60);
     EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
     EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
-    EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+    EXPECT_EQ(presentedSurfaceFrame2.getJankType(),
+              JankType::SurfaceFlingerCpuDeadlineMissed | JankType::AppDeadlineMissed);
 }
 
 TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadlineMissed) {
diff --git a/services/surfaceflinger/tests/unittests/FramebufferSurfaceTest.cpp b/services/surfaceflinger/tests/unittests/FramebufferSurfaceTest.cpp
new file mode 100644
index 0000000..b8df640
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FramebufferSurfaceTest.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "DisplayHardware/FramebufferSurface.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+class FramebufferSurfaceTest : public testing::Test {
+public:
+    ui::Size limitSize(const ui::Size& size, const ui::Size maxSize) {
+        return FramebufferSurface::limitSizeInternal(size, maxSize);
+    }
+};
+
+TEST_F(FramebufferSurfaceTest, limitSize) {
+    const ui::Size kMaxSize(1920, 1080);
+    EXPECT_EQ(ui::Size(1920, 1080), limitSize({3840, 2160}, kMaxSize));
+    EXPECT_EQ(ui::Size(1920, 1080), limitSize({1920, 1080}, kMaxSize));
+    EXPECT_EQ(ui::Size(1920, 1012), limitSize({4096, 2160}, kMaxSize));
+    EXPECT_EQ(ui::Size(1080, 1080), limitSize({3840, 3840}, kMaxSize));
+    EXPECT_EQ(ui::Size(1280, 720), limitSize({1280, 720}, kMaxSize));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index fec590e..b67ebca 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -81,8 +81,8 @@
 
     void setDefaultLayerVote(Layer* layer,
                              LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
-        for (auto& [weak, info] : history().mLayerInfos) {
-            if (auto strong = weak.promote(); strong && strong.get() == layer) {
+        for (auto& [layerUnsafe, info] : history().mLayerInfos) {
+            if (layerUnsafe == layer) {
                 info->setDefaultLayerVote(vote);
                 return;
             }
@@ -180,6 +180,7 @@
     EXPECT_EQ(1, activeLayerCount());
 
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
+    history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
 
     summary = history().summarize(time);
     EXPECT_TRUE(history().summarize(time).empty());
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
index be76e8f..325fb8f 100644
--- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -40,7 +40,7 @@
 
     auto calculateAverageFrameTime() { return layerInfo.calculateAverageFrameTime(); }
 
-    LayerInfo layerInfo{"TestLayerInfo", LayerHistory::LayerVoteType::Heuristic};
+    LayerInfo layerInfo{"TestLayerInfo", 0, LayerHistory::LayerVoteType::Heuristic};
 };
 
 namespace {
diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
index cfbb3f5..a1f0588 100644
--- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
@@ -19,6 +19,7 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <utils/Log.h>
+#include <utils/Timers.h>
 
 #include "AsyncCallRecorder.h"
 #include "Scheduler/OneShotTimer.h"
@@ -28,21 +29,22 @@
 namespace android {
 namespace scheduler {
 
+class FakeClock : public OneShotTimer::Clock {
+public:
+    virtual ~FakeClock() = default;
+    std::chrono::steady_clock::time_point now() const override { return mNow; }
+
+    void advanceTime(std::chrono::nanoseconds delta) { mNow += delta; }
+
+private:
+    std::chrono::steady_clock::time_point mNow;
+};
+
 class OneShotTimerTest : public testing::Test {
 protected:
     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
-    // does not always cooperate, at it can take significantly longer (observed
-    // 30ms).
-    static constexpr auto waitTimeForExpected3msCallback = 100ms;
-
-    // This timeout should be used when an 3ms callback is not expected.
-    // Note that there can be false-negatives if the callback happens later.
-    static constexpr auto waitTimeForUnexpected3msCallback = 6ms;
-
     AsyncCallRecorder<void (*)()> mResetTimerCallback;
     AsyncCallRecorder<void (*)()> mExpiredTimerCallback;
 
@@ -56,162 +58,179 @@
 
 namespace {
 TEST_F(OneShotTimerTest, createAndDestroyTest) {
+    FakeClock* clock = new FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>(
-            "TestTimer", 3ms, [] {}, [] {});
+            "TestTimer", 3ms, [] {}, [] {}, std::unique_ptr<FakeClock>(clock));
 }
 
 TEST_F(OneShotTimerTest, startStopTest) {
-    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 30ms,
+    FakeClock* clock = new FakeClock();
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
-                                                           mExpiredTimerCallback.getInvocable());
-    auto startTime = std::chrono::steady_clock::now();
+                                                           mExpiredTimerCallback.getInvocable(),
+                                                           std::unique_ptr<FakeClock>(clock));
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
-    // The idle timer fires after 30ms, so there should be no callback within
-    // 25ms (waiting for a callback for the full 30ms would be problematic).
-    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 OneShotTimer
-    // thread was started as a sanity check to not have a flakey test.
-    EXPECT_FALSE(callbackCalled && std::chrono::steady_clock::now() - startTime < 30ms);
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
 
-    std::this_thread::sleep_for(std::chrono::milliseconds(25));
-    EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
+    clock->advanceTime(2ms);
+    EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
+
+    clock->advanceTime(2ms);
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
     mIdleTimer->stop();
 }
 
 TEST_F(OneShotTimerTest, resetTest) {
-    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 20ms,
+    FakeClock* clock = new FakeClock();
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
-                                                           mExpiredTimerCallback.getInvocable());
+                                                           mExpiredTimerCallback.getInvocable(),
+                                                           std::unique_ptr<FakeClock>(clock));
+
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
-    // Observe any event that happens in about 25ms. We don't care if one was
-    // observed or not.
-    mExpiredTimerCallback.waitForCall(25ms).has_value();
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+    clock->advanceTime(2ms);
+    EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
     mIdleTimer->reset();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
-    // There may have been a race with the reset. Clear any callbacks we
-    // received right afterwards.
-    clearPendingCallbacks();
-    // A single callback should be generated after 30ms
-    EXPECT_TRUE(
-            mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback + 30ms).has_value());
-    // After one event, it should be idle, and not generate another.
-    EXPECT_FALSE(
-            mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback * 10).has_value());
-    mIdleTimer->stop();
-    // Final quick check that no more callback were observed.
-    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
-    EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+    clock->advanceTime(2ms);
+    EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
+
+    clock->advanceTime(2ms);
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
 }
 
 TEST_F(OneShotTimerTest, resetBackToBackTest) {
-    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 20ms,
+    FakeClock* clock = new FakeClock();
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
-                                                           mExpiredTimerCallback.getInvocable());
+                                                           mExpiredTimerCallback.getInvocable(),
+                                                           std::unique_ptr<FakeClock>(clock));
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
 
     mIdleTimer->reset();
-    EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
-    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
 
     mIdleTimer->reset();
-    EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
-    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
 
     mIdleTimer->reset();
-    EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
-    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
 
     mIdleTimer->reset();
-    EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
-    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
 
-    // A single callback should be generated after 30ms
-    EXPECT_TRUE(
-            mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback + 30ms).has_value());
+    clock->advanceTime(2ms);
+    EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
+
     mIdleTimer->stop();
+    clock->advanceTime(2ms);
     // Final quick check that no more callback were observed.
-    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
-    EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
 }
 
 TEST_F(OneShotTimerTest, startNotCalledTest) {
-    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 3ms,
+    FakeClock* clock = new FakeClock();
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
-                                                           mExpiredTimerCallback.getInvocable());
+                                                           mExpiredTimerCallback.getInvocable(),
+                                                           std::unique_ptr<FakeClock>(clock));
     // The start hasn't happened, so the callback does not happen.
-    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
-    EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
     mIdleTimer->stop();
+    clock->advanceTime(2ms);
     // Final quick check that no more callback were observed.
-    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
-    EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
 }
 
 TEST_F(OneShotTimerTest, idleTimerIdlesTest) {
-    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 3ms,
+    FakeClock* clock = new FakeClock();
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
-                                                           mExpiredTimerCallback.getInvocable());
+                                                           mExpiredTimerCallback.getInvocable(),
+                                                           std::unique_ptr<FakeClock>(clock));
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+    clock->advanceTime(2ms);
+    EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
 
-    // A callback should be generated after 3ms
-    EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
-    // After one event, it should be idle, and not generate another.
-    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
-    // Once reset, it should generate another
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
+
     mIdleTimer->reset();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
-    EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
+    clock->advanceTime(2ms);
+    EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
     mIdleTimer->stop();
+    clock->advanceTime(2ms);
     // Final quick check that no more callback were observed.
-    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value());
-    EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
 }
 
 TEST_F(OneShotTimerTest, timeoutCallbackExecutionTest) {
-    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 3ms,
+    FakeClock* clock = new FakeClock();
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
-                                                           mExpiredTimerCallback.getInvocable());
+                                                           mExpiredTimerCallback.getInvocable(),
+                                                           std::unique_ptr<FakeClock>(clock));
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
-    EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
+
+    clock->advanceTime(2ms);
+    EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
     mIdleTimer->stop();
+    clock->advanceTime(2ms);
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
 }
 
 TEST_F(OneShotTimerTest, noCallbacksAfterStopAndResetTest) {
-    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 3ms,
+    FakeClock* clock = new FakeClock();
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
-                                                           mExpiredTimerCallback.getInvocable());
+                                                           mExpiredTimerCallback.getInvocable(),
+                                                           std::unique_ptr<FakeClock>(clock));
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
-    EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
+    clock->advanceTime(2ms);
+    EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
 
     mIdleTimer->stop();
-    clearPendingCallbacks();
     mIdleTimer->reset();
-    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
-    EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
+    clock->advanceTime(2ms);
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
 }
 
 TEST_F(OneShotTimerTest, noCallbacksAfterStopTest) {
-    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 3ms,
+    FakeClock* clock = new FakeClock();
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
-                                                           mExpiredTimerCallback.getInvocable());
+                                                           mExpiredTimerCallback.getInvocable(),
+                                                           std::unique_ptr<FakeClock>(clock));
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
 
     mIdleTimer->stop();
-    clearPendingCallbacks();
     mIdleTimer->reset();
 
+    clock->advanceTime(2ms);
     // No more idle events should be observed
-    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
-    EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
+    EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 06f2036..0b70f27 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -1182,11 +1182,13 @@
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
-TEST_F(RefreshRateConfigsTest, groupSwitching) {
+TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
+    // The default policy doesn't allow group switching. Verify that no
+    // group switches are performed.
     auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
     auto& layer = layers[0];
     layer.vote = LayerVoteType::ExplicitDefault;
@@ -1198,64 +1200,203 @@
     ASSERT_EQ(HWC_CONFIG_ID_60,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getModeId());
+}
 
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
     RefreshRateConfigs::Policy policy;
     policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::ExplicitDefault;
+    layer.desiredRefreshRate = Fps(90.0f);
+    layer.seamlessness = Seamlessness::SeamedAndSeamless;
+    layer.name = "90Hz ExplicitDefault";
+    layer.focused = true;
     ASSERT_EQ(HWC_CONFIG_ID_90,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    RefreshRateConfigs::Policy policy;
+    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
 
     // Verify that we won't change the group if seamless switch is required.
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::ExplicitDefault;
+    layer.desiredRefreshRate = Fps(90.0f);
     layer.seamlessness = Seamlessness::OnlySeamless;
+    layer.name = "90Hz ExplicitDefault";
+    layer.focused = true;
     ASSERT_EQ(HWC_CONFIG_ID_60,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    RefreshRateConfigs::Policy policy;
+    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
 
     // Verify that we won't do a seamless switch if we request the same mode as the default
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::ExplicitDefault;
     layer.desiredRefreshRate = Fps(60.0f);
-    layer.name = "60Hz ExplicitDefault";
     layer.seamlessness = Seamlessness::OnlySeamless;
+    layer.name = "60Hz ExplicitDefault";
+    layer.focused = true;
     ASSERT_EQ(HWC_CONFIG_ID_90,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    RefreshRateConfigs::Policy policy;
+    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
 
     // Verify that if the current config is in another group and there are no layers with
     // seamlessness=SeamedAndSeamless we'll go back to the default group.
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::ExplicitDefault;
     layer.desiredRefreshRate = Fps(60.0f);
-    layer.name = "60Hz ExplicitDefault";
     layer.seamlessness = Seamlessness::Default;
+    layer.name = "60Hz ExplicitDefault";
+    layer.focused = true;
+
     ASSERT_EQ(HWC_CONFIG_ID_60,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    RefreshRateConfigs::Policy policy;
+    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
 
     // If there's a layer with seamlessness=SeamedAndSeamless, another layer with
     // seamlessness=OnlySeamless can't change the mode group.
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
-    layer.seamlessness = Seamlessness::OnlySeamless;
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+    layers[0].desiredRefreshRate = Fps(60.0f);
+    layers[0].seamlessness = Seamlessness::OnlySeamless;
+    layers[0].name = "60Hz ExplicitDefault";
+    layers[0].focused = true;
 
     layers.push_back(LayerRequirement{.weight = 0.5f});
-    auto& layer2 = layers[layers.size() - 1];
-    layer2.vote = LayerVoteType::ExplicitDefault;
-    layer2.desiredRefreshRate = Fps(90.0f);
-    layer2.name = "90Hz ExplicitDefault";
-    layer2.seamlessness = Seamlessness::SeamedAndSeamless;
-    layer2.focused = false;
+    layers[1].vote = LayerVoteType::ExplicitDefault;
+    layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+    layers[1].desiredRefreshRate = Fps(90.0f);
+    layers[1].name = "90Hz ExplicitDefault";
+    layers[1].focused = false;
 
     ASSERT_EQ(HWC_CONFIG_ID_90,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getModeId());
+}
 
-    // If there's a layer with seamlessness=SeamedAndSeamless, another layer with
-    // seamlessness=Default can't change the mode group.
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    RefreshRateConfigs::Policy policy;
+    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+
+    // If there's a focused layer with seamlessness=SeamedAndSeamless, another layer with
+    // seamlessness=Default can't change the mode group back to the group of the default
+    // mode.
+    // For example, this may happen when a video playback requests and gets a seamed switch,
+    // but another layer (with default seamlessness) starts animating. The animating layer
+    // should not cause a seamed switch.
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
     layers[0].seamlessness = Seamlessness::Default;
+    layers[0].desiredRefreshRate = Fps(60.0f);
+    layers[0].focused = true;
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+    layers[0].name = "60Hz ExplicitDefault";
+
+    layers.push_back(LayerRequirement{.weight = 0.1f});
+    layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+    layers[1].desiredRefreshRate = Fps(90.0f);
+    layers[1].focused = true;
+    layers[1].vote = LayerVoteType::ExplicitDefault;
+    layers[1].name = "90Hz ExplicitDefault";
+
     ASSERT_EQ(HWC_CONFIG_ID_90,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getModeId());
 }
 
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    RefreshRateConfigs::Policy policy;
+    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+
+    // Layer with seamlessness=Default can change the mode group if there's a not
+    // focused layer with seamlessness=SeamedAndSeamless. This happens for example,
+    // when in split screen mode the user switches between the two visible applications.
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    layers[0].seamlessness = Seamlessness::Default;
+    layers[0].desiredRefreshRate = Fps(60.0f);
+    layers[0].focused = true;
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+    layers[0].name = "60Hz ExplicitDefault";
+
+    layers.push_back(LayerRequirement{.weight = 0.7f});
+    layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+    layers[1].desiredRefreshRate = Fps(90.0f);
+    layers[1].focused = false;
+    layers[1].vote = LayerVoteType::ExplicitDefault;
+    layers[1].name = "90Hz ExplicitDefault";
+
+    ASSERT_EQ(HWC_CONFIG_ID_60,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+                      .getModeId());
+}
+
 TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60Device,
@@ -1621,29 +1762,35 @@
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
 }
 
-TEST_F(RefreshRateConfigsTest, getRefreshRateDivider) {
+TEST_F(RefreshRateConfigsTest, getFrameRateDivider) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_30);
 
     const auto frameRate = Fps(30.f);
-    EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDivider(frameRate));
+    Fps displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+    EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
 
     refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_60);
-    EXPECT_EQ(2, refreshRateConfigs->getRefreshRateDivider(frameRate));
+    displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+    EXPECT_EQ(2, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
 
     refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_72);
-    EXPECT_EQ(0, refreshRateConfigs->getRefreshRateDivider(frameRate));
+    displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
 
     refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
-    EXPECT_EQ(3, refreshRateConfigs->getRefreshRateDivider(frameRate));
+    displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+    EXPECT_EQ(3, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
 
     refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_120);
-    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(frameRate));
+    displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+    EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
 
     refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
-    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(Fps(22.5f)));
-    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(Fps(22.6f)));
+    displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+    EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.5f)));
+    EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.6f)));
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) {
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index abecd4b..9c6ad06 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -118,11 +118,8 @@
 }
 
 void RefreshRateSelectionTest::commitTransaction(Layer* layer) {
-    layer->pushPendingState();
     auto c = layer->getCurrentState();
-    if (layer->applyPendingStates(&c)) {
-        layer->commitTransaction(c);
-    }
+    layer->commitTransaction(c);
 }
 
 void RefreshRateSelectionTest::setupScheduler() {
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 5c8c2d8..c088ddc 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -122,7 +122,6 @@
 
     void addChild(sp<Layer> layer, sp<Layer> child);
     void removeChild(sp<Layer> layer, sp<Layer> child);
-    void reparentChildren(sp<Layer> layer, sp<Layer> child);
     void commitTransaction();
 
     TestableSurfaceFlinger mFlinger;
@@ -152,17 +151,10 @@
     layer.get()->removeChild(child.get());
 }
 
-void SetFrameRateTest::reparentChildren(sp<Layer> parent, sp<Layer> newParent) {
-    parent.get()->reparentChildren(newParent);
-}
-
 void SetFrameRateTest::commitTransaction() {
     for (auto layer : mLayers) {
-        layer->pushPendingState();
         auto c = layer->getCurrentState();
-        if (layer->applyPendingStates(&c)) {
-            layer->commitTransaction(c);
-        }
+        layer->commitTransaction(c);
     }
 }
 
@@ -433,41 +425,6 @@
     EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
 }
 
-TEST_P(SetFrameRateTest, SetAndGetReparentChildren) {
-    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
-    const auto& layerFactory = GetParam();
-
-    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto parent2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
-    addChild(parent, child1);
-    addChild(child1, child2);
-
-    child2->setFrameRate(FRAME_RATE_VOTE1);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent2->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
-    reparentChildren(parent, parent2);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_TREE, parent2->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
-    child2->setFrameRate(FRAME_RATE_NO_VOTE);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent2->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
 INSTANTIATE_TEST_SUITE_P(PerLayerType, SetFrameRateTest,
                          testing::Values(std::make_shared<BufferQueueLayerFactory>(),
                                          std::make_shared<BufferStateLayerFactory>(),
@@ -475,18 +432,36 @@
                          PrintToStringParamName);
 
 TEST_F(SetFrameRateTest, ValidateFrameRate) {
-    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
-    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
-    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, ""));
-    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, "", /*privileged=*/true));
+    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                                  ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                                  ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                                  ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS, ""));
+    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+                                  ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT,
+                                  ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "",
+                                  /*privileged=*/true));
 
-    EXPECT_FALSE(ValidateFrameRate(-1, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
-    EXPECT_FALSE(
-            ValidateFrameRate(1.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
-    EXPECT_FALSE(
-            ValidateFrameRate(0.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
+    EXPECT_FALSE(ValidateFrameRate(-1, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                                   ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+    EXPECT_FALSE(ValidateFrameRate(1.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                                   ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+    EXPECT_FALSE(ValidateFrameRate(0.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                                   ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
 
-    EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, ""));
+    EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT,
+                                   ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+
+    // Invalid compatibility
+    EXPECT_FALSE(
+            ValidateFrameRate(60.0f, -1, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+    EXPECT_FALSE(ValidateFrameRate(60.0f, 2, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+
+    // Invalid change frame rate strategy
+    EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, -1, ""));
+    EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, 2, ""));
 }
 
 TEST_P(SetFrameRateTest, SetOnParentActivatesTree) {
@@ -505,6 +480,13 @@
     parent->setFrameRate(FRAME_RATE_VOTE1);
     commitTransaction();
 
+    mFlinger.mutableScheduler()
+            .mutableLayerHistory()
+            ->record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+    mFlinger.mutableScheduler()
+            .mutableLayerHistory()
+            ->record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+
     const auto layerHistorySummary =
             mFlinger.mutableScheduler().mutableLayerHistory()->summarize(0);
     ASSERT_EQ(2u, layerHistorySummary.size());
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 63baf7d..d004b9d 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -352,8 +352,8 @@
 
     auto renderScreenImplLocked(const RenderArea& renderArea,
                                 SurfaceFlinger::TraverseLayersFunction traverseLayers,
-                                const sp<GraphicBuffer>& buffer, bool forSystem,
-                                bool regionSampling) {
+                                const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+                                bool forSystem, bool regionSampling) {
         ScreenCaptureResults captureResults;
         return mFlinger->renderScreenImplLocked(renderArea, traverseLayers, buffer, forSystem,
                                                 regionSampling, false /* grayscale */,
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index a72ab42..188ea75 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -23,10 +23,10 @@
 #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 <timestatsatomsproto/TimeStatsAtomsProtoHeader.h>
 #include <utils/String16.h>
 #include <utils/Vector.h>
 
@@ -142,57 +142,22 @@
 
     std::string inputCommand(InputCommand cmd, bool useProto);
 
-    void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts);
+    void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
+                      TimeStats::SetFrameRateVote frameRateVote);
 
     int32_t genRandomInt32(int32_t begin, int32_t end);
 
     template <size_t N>
     void insertTimeRecord(const TimeStamp (&sequence)[N], int32_t id, uint64_t frameNumber,
-                          nsecs_t ts) {
+                          nsecs_t ts, TimeStats::SetFrameRateVote frameRateVote = {}) {
         for (size_t i = 0; i < N; i++, ts += 1000000) {
-            setTimeStamp(sequence[i], id, frameNumber, ts);
+            setTimeStamp(sequence[i], id, frameNumber, ts, frameRateVote);
         }
     }
 
     std::mt19937 mRandomEngine = std::mt19937(std::random_device()());
-
-    class FakeStatsEventDelegate : public impl::TimeStats::StatsEventDelegate {
-    public:
-        FakeStatsEventDelegate() = default;
-        ~FakeStatsEventDelegate() override = default;
-
-        struct AStatsEvent* addStatsEventToPullData(AStatsEventList*) override {
-            return mEvent;
-        }
-        void setStatsPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata*,
-                                      AStatsManager_PullAtomCallback callback,
-                                      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(clearStatsPullAtomCallback, 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::make_unique<impl::TimeStats>(std::nullopt, std::nullopt);
 };
 
 std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
@@ -234,7 +199,8 @@
     return (layerId < 0 ? "PopupWindow:b54fcd1#0" : "com.example.fake#") + std::to_string(layerId);
 }
 
-void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
+void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
+                                 TimeStats::SetFrameRateVote frameRateVote) {
     switch (type) {
         case TimeStamp::POST:
             ASSERT_NO_FATAL_FAILURE(
@@ -254,13 +220,13 @@
             ASSERT_NO_FATAL_FAILURE(mTimeStats->setDesiredTime(id, frameNumber, ts));
             break;
         case TimeStamp::PRESENT:
-            ASSERT_NO_FATAL_FAILURE(
-                    mTimeStats->setPresentTime(id, frameNumber, ts, kRefreshRate0, kRenderRate0));
+            ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentTime(id, frameNumber, ts, kRefreshRate0,
+                                                               kRenderRate0, frameRateVote));
             break;
         case TimeStamp::PRESENT_FENCE:
-            ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentFence(id, frameNumber,
-                                                                std::make_shared<FenceTime>(ts),
-                                                                kRefreshRate0, kRenderRate0));
+            ASSERT_NO_FATAL_FAILURE(
+                    mTimeStats->setPresentFence(id, frameNumber, std::make_shared<FenceTime>(ts),
+                                                kRefreshRate0, kRenderRate0, frameRateVote));
             break;
         default:
             ALOGD("Invalid timestamp type");
@@ -276,21 +242,6 @@
     ASSERT_FALSE(mTimeStats->isEnabled());
 }
 
-TEST_F(TimeStatsTest, setsCallbacksAfterBoot) {
-    mTimeStats->onBootFinished();
-    EXPECT_THAT(mDelegate->mAtomTags,
-                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
-                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
-}
-
-TEST_F(TimeStatsTest, clearsCallbacksOnDestruction) {
-    EXPECT_CALL(*mDelegate,
-                clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
-    EXPECT_CALL(*mDelegate,
-                clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
-    mTimeStats.reset();
-}
-
 TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
     ASSERT_TRUE(mTimeStats->isEnabled());
@@ -411,6 +362,96 @@
     EXPECT_THAT(result, HasSubstr(expectedResult));
 }
 
+TEST_F(TimeStatsTest, canCaptureSetFrameRateVote) {
+    // this stat is not in the proto so verify by checking the string dump
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+
+    const auto frameRate60 = TimeStats::SetFrameRateVote{
+            .frameRate = 60.0f,
+            .frameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility::Default,
+            .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::ShouldBeSeamless,
+    };
+    const auto frameRate90 = TimeStats::SetFrameRateVote{
+            .frameRate = 90.0f,
+            .frameRateCompatibility =
+                    TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+            .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+    };
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60);
+    std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    std::string expectedResult = "frameRate = 60.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = Default";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = ShouldBeSeamless";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, frameRate90);
+    result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+    expectedResult = "frameRate = 90.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = ExactOrMultiple";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = NotRequired";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+
+    insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 4, 4000000, frameRate60);
+    result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+    expectedResult = "frameRate = 60.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = Default";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = ShouldBeSeamless";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canCaptureSetFrameRateVoteAfterZeroForLayer) {
+    // this stat is not in the proto so verify by checking the string dump
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+
+    const auto frameRate90 = TimeStats::SetFrameRateVote{
+            .frameRate = 90.0f,
+            .frameRateCompatibility =
+                    TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+            .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+    };
+    const auto frameRateDefault = TimeStats::SetFrameRateVote{
+            .frameRate = 0.0f,
+            .frameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility::Default,
+            .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::ShouldBeSeamless,
+    };
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate90);
+    std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    std::string expectedResult = "frameRate = 90.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = ExactOrMultiple";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = NotRequired";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, frameRateDefault);
+    result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+    expectedResult = "frameRate = 90.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = ExactOrMultiple";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = NotRequired";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+
+    insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 4, 4000000, frameRateDefault);
+    result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+    expectedResult = "frameRate = 90.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = ExactOrMultiple";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = NotRequired";
+    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;
@@ -920,58 +961,49 @@
 }
 
 namespace {
-std::string buildExpectedHistogramBytestring(const std::vector<int32_t>& times,
-                                             const std::vector<int32_t>& frameCounts) {
-    util::ProtoOutputStream proto;
+FrameTimingHistogram buildExpectedHistogram(const std::vector<int32_t>& times,
+                                            const std::vector<int32_t>& frameCounts) {
+    FrameTimingHistogram histogram;
     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]);
+        histogram.add_time_millis_buckets(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]);
+        histogram.add_frame_counts((int64_t)frameCounts[i]);
     }
-    std::string byteString;
-    proto.serializeToString(&byteString);
-    return byteString;
+    return histogram;
 }
-
-std::string frameRateVoteToProtoByteString(float refreshRate, int frameRateCompatibility,
-                                           int seamlessness) {
-    util::ProtoOutputStream proto;
-    proto.write(android::util::FIELD_TYPE_FLOAT | 1 /* field id */, refreshRate);
-    proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */, frameRateCompatibility);
-    proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, seamlessness);
-
-    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);
+MATCHER_P(HistogramEq, expected, "") {
+    *result_listener << "Histograms are not equal! \n";
 
-    *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";
+    if (arg.time_millis_buckets_size() != expected.time_millis_buckets_size()) {
+        *result_listener << "Time millis bucket are different sizes. Expected: "
+                         << expected.time_millis_buckets_size() << ". Actual "
+                         << arg.time_millis_buckets_size();
+        return false;
+    }
+    if (arg.frame_counts_size() != expected.frame_counts_size()) {
+        *result_listener << "Frame counts are different sizes. Expected: "
+                         << expected.frame_counts_size() << ". Actual " << arg.frame_counts_size();
+        return false;
+    }
 
-    return expected == actual;
+    for (int i = 0; i < expected.time_millis_buckets_size(); i++) {
+        if (arg.time_millis_buckets(i) != expected.time_millis_buckets(i)) {
+            *result_listener << "time_millis_bucket[" << i
+                             << "] is different. Expected: " << expected.time_millis_buckets(i)
+                             << ". Actual: " << arg.time_millis_buckets(i);
+            return false;
+        }
+        if (arg.frame_counts(i) != expected.frame_counts(i)) {
+            *result_listener << "frame_counts[" << i
+                             << "] is different. Expected: " << expected.frame_counts(i)
+                             << ". Actual: " << arg.frame_counts(i);
+            return false;
+        }
+    }
+    return true;
 }
 
 TEST_F(TimeStatsTest, globalStatsCallback) {
@@ -980,7 +1012,6 @@
     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++) {
@@ -1020,71 +1051,39 @@
                                       JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2,
                                       3});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::BufferStuffing, 1, 2, 3});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
                                       JankType::None, 1, 2, 3});
 
-    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 pulledData;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledData));
 
-    std::string expectedFrameDuration = buildExpectedHistogramBytestring({2}, {1});
-    std::string expectedRenderEngineTiming = buildExpectedHistogramBytestring({1, 2}, {1, 1});
-    std::string expectedEmptyHistogram = buildExpectedHistogramBytestring({}, {});
-    std::string expectedSfDeadlineMissed = buildExpectedHistogramBytestring({1}, {7});
-    std::string expectedSfPredictionErrors = buildExpectedHistogramBytestring({2}, {7});
+    android::surfaceflinger::SurfaceflingerStatsGlobalInfoWrapper atomList;
+    ASSERT_TRUE(atomList.ParseFromString(pulledData));
+    ASSERT_EQ(atomList.atom_size(), 1);
+    const android::surfaceflinger::SurfaceflingerStatsGlobalInfo& atom = atomList.atom(0);
 
-    {
-        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, statsEventWriteInt32(mDelegate->mEvent, 8));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 7));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 2));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, REFRESH_RATE_BUCKET_0));
-        EXPECT_CALL(*mDelegate,
-                    statsEventWriteByteArray(mDelegate->mEvent,
-                                             BytesEq((const uint8_t*)
-                                                             expectedSfDeadlineMissed.c_str(),
-                                                     expectedSfDeadlineMissed.size()),
-                                             expectedSfDeadlineMissed.size()));
-        EXPECT_CALL(*mDelegate,
-                    statsEventWriteByteArray(mDelegate->mEvent,
-                                             BytesEq((const uint8_t*)
-                                                             expectedSfPredictionErrors.c_str(),
-                                                     expectedSfPredictionErrors.size()),
-                                             expectedSfPredictionErrors.size()));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, RENDER_RATE_BUCKET_0));
-
-        EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
-    }
-    EXPECT_EQ(AStatsManager_PULL_SUCCESS,
-              mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
-                                              mDelegate->mCookie));
+    EXPECT_EQ(atom.total_frames(), TOTAL_FRAMES);
+    EXPECT_EQ(atom.missed_frames(), MISSED_FRAMES);
+    EXPECT_EQ(atom.client_composition_frames(), CLIENT_COMPOSITION_FRAMES);
+    // Display on millis is not checked.
+    EXPECT_EQ(atom.animation_millis(), 2);
+    EXPECT_EQ(atom.event_connection_count(), DISPLAY_EVENT_CONNECTIONS);
+    EXPECT_THAT(atom.frame_duration(), HistogramEq(buildExpectedHistogram({2}, {1})));
+    EXPECT_THAT(atom.render_engine_timing(), HistogramEq(buildExpectedHistogram({1, 2}, {1, 1})));
+    EXPECT_EQ(atom.total_timeline_frames(), 9);
+    EXPECT_EQ(atom.total_janky_frames(), 7);
+    EXPECT_EQ(atom.total_janky_frames_with_long_cpu(), 1);
+    EXPECT_EQ(atom.total_janky_frames_with_long_gpu(), 1);
+    EXPECT_EQ(atom.total_janky_frames_sf_unattributed(), 1);
+    EXPECT_EQ(atom.total_janky_frames_app_unattributed(), 2);
+    EXPECT_EQ(atom.total_janky_frames_sf_scheduling(), 1);
+    EXPECT_EQ(atom.total_jank_frames_sf_prediction_error(), 1);
+    EXPECT_EQ(atom.total_jank_frames_app_buffer_stuffing(), 2);
+    EXPECT_EQ(atom.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
+    EXPECT_THAT(atom.sf_deadline_misses(), HistogramEq(buildExpectedHistogram({1}, {7})));
+    EXPECT_THAT(atom.sf_prediction_errors(), HistogramEq(buildExpectedHistogram({2}, {7})));
+    EXPECT_EQ(atom.render_rate_bucket(), RENDER_RATE_BUCKET_0);
 
     SFTimeStatsGlobalProto globalProto;
     ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
@@ -1099,7 +1098,7 @@
     const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
     std::string expectedResult = "totalTimelineFrames = " + std::to_string(0);
     EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "totalTimelineFrames = " + std::to_string(8);
+    expectedResult = "totalTimelineFrames = " + std::to_string(9);
     EXPECT_THAT(result, HasSubstr(expectedResult));
     expectedResult = "jankyFrames = " + std::to_string(0);
     EXPECT_THAT(result, HasSubstr(expectedResult));
@@ -1131,7 +1130,7 @@
     EXPECT_THAT(result, HasSubstr(expectedResult));
     expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(0);
     EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(1);
+    expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(2);
     EXPECT_THAT(result, HasSubstr(expectedResult));
 }
 
@@ -1140,8 +1139,6 @@
     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);
@@ -1149,7 +1146,13 @@
     for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
         mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
     }
-    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    const auto frameRate60 = TimeStats::SetFrameRateVote{
+            .frameRate = 60.0f,
+            .frameRateCompatibility =
+                    TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+            .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+    };
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60);
 
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
                                       JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
@@ -1169,96 +1172,42 @@
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
                                       JankType::None, 1, 2, 3});
 
-    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 pulledData;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
 
-    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});
-    std::string expectedFrameRateOverride = frameRateVoteToProtoByteString(0.0, 0, 0);
-    std::string expectedAppDeadlineMissed = buildExpectedHistogramBytestring({3, 2}, {4, 3});
-    {
-        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, statsEventWriteInt32(mDelegate->mEvent, UID_0));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 8));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 7));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 2));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, REFRESH_RATE_BUCKET_0));
-        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, RENDER_RATE_BUCKET_0));
-        EXPECT_CALL(*mDelegate,
-                    statsEventWriteByteArray(mDelegate->mEvent,
-                                             BytesEq((const uint8_t*)
-                                                             expectedFrameRateOverride.c_str(),
-                                                     expectedFrameRateOverride.size()),
-                                             expectedFrameRateOverride.size()));
-        EXPECT_CALL(*mDelegate,
-                    statsEventWriteByteArray(mDelegate->mEvent,
-                                             BytesEq((const uint8_t*)
-                                                             expectedAppDeadlineMissed.c_str(),
-                                                     expectedAppDeadlineMissed.size()),
-                                             expectedAppDeadlineMissed.size()));
+    android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+    ASSERT_TRUE(atomList.ParseFromString(pulledData));
+    ASSERT_EQ(atomList.atom_size(), 1);
+    const android::surfaceflinger::SurfaceflingerStatsLayerInfo& atom = atomList.atom(0);
 
-        EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
-    }
-    EXPECT_EQ(AStatsManager_PULL_SUCCESS,
-              mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
-                                              mDelegate->mCookie));
+    EXPECT_EQ(atom.layer_name(), genLayerName(LAYER_ID_0));
+    EXPECT_EQ(atom.total_frames(), 1);
+    EXPECT_EQ(atom.dropped_frames(), 0);
+    EXPECT_THAT(atom.present_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
+    EXPECT_THAT(atom.post_to_present(), HistogramEq(buildExpectedHistogram({4}, {1})));
+    EXPECT_THAT(atom.acquire_to_present(), HistogramEq(buildExpectedHistogram({3}, {1})));
+    EXPECT_THAT(atom.latch_to_present(), HistogramEq(buildExpectedHistogram({2}, {1})));
+    EXPECT_THAT(atom.desired_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
+    EXPECT_THAT(atom.post_to_acquire(), HistogramEq(buildExpectedHistogram({1}, {1})));
+    EXPECT_EQ(atom.late_acquire_frames(), LATE_ACQUIRE_FRAMES);
+    EXPECT_EQ(atom.bad_desired_present_frames(), BAD_DESIRED_PRESENT_FRAMES);
+    EXPECT_EQ(atom.uid(), UID_0);
+    EXPECT_EQ(atom.total_timeline_frames(), 8);
+    EXPECT_EQ(atom.total_janky_frames(), 7);
+    EXPECT_EQ(atom.total_janky_frames_with_long_cpu(), 1);
+    EXPECT_EQ(atom.total_janky_frames_with_long_gpu(), 1);
+    EXPECT_EQ(atom.total_janky_frames_sf_unattributed(), 1);
+    EXPECT_EQ(atom.total_janky_frames_app_unattributed(), 2);
+    EXPECT_EQ(atom.total_janky_frames_sf_scheduling(), 1);
+    EXPECT_EQ(atom.total_jank_frames_sf_prediction_error(), 1);
+    EXPECT_EQ(atom.total_jank_frames_app_buffer_stuffing(), 1);
+    EXPECT_EQ(atom.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
+    EXPECT_EQ(atom.render_rate_bucket(), RENDER_RATE_BUCKET_0);
+    EXPECT_THAT(atom.set_frame_rate_vote().frame_rate(), testing::FloatEq(frameRate60.frameRate));
+    EXPECT_EQ((int)atom.set_frame_rate_vote().frame_rate_compatibility(),
+              (int)frameRate60.frameRateCompatibility);
+    EXPECT_EQ((int)atom.set_frame_rate_vote().seamlessness(), (int)frameRate60.seamlessness);
+    EXPECT_THAT(atom.app_deadline_misses(), HistogramEq(buildExpectedHistogram({3, 2}, {4, 3})));
 
     SFTimeStatsGlobalProto globalProto;
     ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
@@ -1294,37 +1243,26 @@
 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);
+    std::string pulledData;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
 
-    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));
+    android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+    ASSERT_TRUE(atomList.ParseFromString(pulledData));
+    ASSERT_EQ(atomList.atom_size(), 2);
+    std::vector<std::string> actualLayerNames = {atomList.atom(0).layer_name(),
+                                                 atomList.atom(1).layer_name()};
+    EXPECT_THAT(actualLayerNames,
+                UnorderedElementsAre(genLayerName(LAYER_ID_0), genLayerName(LAYER_ID_1)));
 }
 
 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);
@@ -1334,102 +1272,53 @@
     mTimeStats->setPowerMode(PowerMode::ON);
     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));
+    std::string pulledData;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+
+    android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+    ASSERT_TRUE(atomList.ParseFromString(pulledData));
+    ASSERT_EQ(atomList.atom_size(), 1);
+    const android::surfaceflinger::SurfaceflingerStatsLayerInfo& atom = atomList.atom(0);
+    EXPECT_THAT(atom.present_to_present(), HistogramEq(buildExpectedHistogram({1, 2}, {2, 1})));
 }
 
 TEST_F(TimeStatsTest, layerStatsCallback_limitsHistogramBuckets) {
-    mDelegate = new FakeStatsEventDelegate;
-    mTimeStats =
-            std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate),
-                                              std::nullopt, 1);
+    mTimeStats = std::make_unique<impl::TimeStats>(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);
+    std::string pulledData;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
 
-    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));
+    android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+    ASSERT_TRUE(atomList.ParseFromString(pulledData));
+    ASSERT_EQ(atomList.atom_size(), 1);
+    const android::surfaceflinger::SurfaceflingerStatsLayerInfo& atom = atomList.atom(0);
+    EXPECT_THAT(atom.present_to_present(), HistogramEq(buildExpectedHistogram({1}, {2})));
 }
 
 TEST_F(TimeStatsTest, layerStatsCallback_limitsLayers) {
-    mDelegate = new FakeStatsEventDelegate;
-    mTimeStats =
-            std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate), 1,
-                                              std::nullopt);
+    mTimeStats = std::make_unique<impl::TimeStats>(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);
+    std::string pulledData;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
 
-    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));
+    android::surfaceflinger::SurfaceflingerStatsLayerInfoWrapper atomList;
+    ASSERT_TRUE(atomList.ParseFromString(pulledData));
+    ASSERT_EQ(atomList.atom_size(), 1);
+    EXPECT_EQ(atomList.atom(0).layer_name(), genLayerName(LAYER_ID_1));
 }
 
 TEST_F(TimeStatsTest, canSurviveMonkey) {
@@ -1455,7 +1344,7 @@
         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);
+        setTimeStamp(type, layerId, frameNumber, ts, {});
     }
 }
 
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index b5ef0a1..25001d3 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -21,6 +21,8 @@
 #include <gtest/gtest.h>
 #include <gui/SurfaceComposerClient.h>
 #include <log/log.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/mock/RenderEngine.h>
 #include <utils/String8.h>
 
 #include "TestableSurfaceFlinger.h"
@@ -60,11 +62,8 @@
     }
 
     void commitTransaction(Layer* layer) {
-        layer->pushPendingState();
         auto c = layer->getCurrentState();
-        if (layer->applyPendingStates(&c)) {
-            layer->commitTransaction(c);
-        }
+        layer->commitTransaction(c);
     }
 
     void setupScheduler() {
@@ -102,6 +101,7 @@
 
     TestableSurfaceFlinger mFlinger;
     Hwc2::mock::Composer* mComposer = nullptr;
+    renderengine::mock::RenderEngine mRenderEngine;
     FenceToFenceTimeMap fenceFactory;
     client_cache_t mClientCache;
 
@@ -109,9 +109,12 @@
         sp<BufferStateLayer> layer = createBufferStateLayer();
 
         sp<Fence> fence(new Fence());
-        sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         int32_t layerId = layer->getSequence();
-        uint64_t bufferId = buffer->getId();
+        uint64_t bufferId = buffer->getBuffer()->getId();
         uint64_t frameNumber = 5;
         nsecs_t dequeueTime = 10;
         nsecs_t postTime = 20;
@@ -151,4 +154,4 @@
     BLASTTransactionSendsFrameTracerEvents();
 }
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index c75538f..b7917aa 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -21,6 +21,8 @@
 #include <gtest/gtest.h>
 #include <gui/SurfaceComposerClient.h>
 #include <log/log.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/mock/RenderEngine.h>
 #include <utils/String8.h>
 
 #include "TestableSurfaceFlinger.h"
@@ -60,13 +62,8 @@
     }
 
     void commitTransaction(Layer* layer) {
-        layer->pushPendingState();
-        // After pushing the state, the currentState should not store any BufferlessSurfaceFrames
-        EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
         auto c = layer->getCurrentState();
-        if (layer->applyPendingStates(&c)) {
-            layer->commitTransaction(c);
-        }
+        layer->commitTransaction(c);
     }
 
     void setupScheduler() {
@@ -104,6 +101,7 @@
 
     TestableSurfaceFlinger mFlinger;
     Hwc2::mock::Composer* mComposer = nullptr;
+    renderengine::mock::RenderEngine mRenderEngine;
     FenceToFenceTimeMap fenceFactory;
     client_cache_t mClientCache;
 
@@ -124,7 +122,10 @@
         sp<BufferStateLayer> layer = createBufferStateLayer();
         sp<Fence> fence(new Fence());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
-        sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         acquireFence->signalForTest(12);
@@ -149,7 +150,10 @@
 
         sp<Fence> fence1(new Fence());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
-        sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer1 = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
@@ -158,7 +162,10 @@
 
         sp<Fence> fence2(new Fence());
         auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
-        sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer2 = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         nsecs_t start = systemTime();
         layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
@@ -196,8 +203,10 @@
 
         sp<Fence> fence(new Fence());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
-        sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
-
+        const auto buffer = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         acquireFence->signalForTest(12);
@@ -222,8 +231,10 @@
         sp<BufferStateLayer> layer = createBufferStateLayer();
         sp<Fence> fence(new Fence());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
-        sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
-
+        const auto buffer = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
@@ -252,8 +263,10 @@
 
         sp<Fence> fence(new Fence());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
-        sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
-
+        const auto buffer = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 3, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         EXPECT_EQ(2u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
@@ -283,75 +296,15 @@
         EXPECT_EQ(PresentState::Presented, bufferSurfaceFrameTX->getPresentState());
     }
 
-    void MergePendingStates_BufferlessSurfaceFramesWithoutOverlappingToken() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
-        layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 1, /*inputEventId*/ 0},
-                                                             10);
-        EXPECT_EQ(1u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
-        ASSERT_EQ(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
-        const auto bufferlessSurfaceFrame1 =
-                layer->mCurrentState.bufferlessSurfaceFramesTX.at(/*token*/ 1);
-
-        layer->pushPendingState();
-        EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
-
-        layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 2, /*inputEventId*/ 0},
-                                                             12);
-        EXPECT_EQ(1u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
-        ASSERT_EQ(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
-        const auto bufferlessSurfaceFrame2 =
-                layer->mCurrentState.bufferlessSurfaceFramesTX.at(/*token*/ 2);
-
-        commitTransaction(layer.get());
-
-        EXPECT_EQ(1, bufferlessSurfaceFrame1->getToken());
-        EXPECT_EQ(false, bufferlessSurfaceFrame1->getIsBuffer());
-        EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame1->getPresentState());
-        EXPECT_EQ(10, bufferlessSurfaceFrame1->getActuals().endTime);
-
-        EXPECT_EQ(2, bufferlessSurfaceFrame2->getToken());
-        EXPECT_EQ(false, bufferlessSurfaceFrame2->getIsBuffer());
-        EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState());
-        EXPECT_EQ(12, bufferlessSurfaceFrame2->getActuals().endTime);
-    }
-
-    void MergePendingStates_BufferlessSurfaceFramesWithOverlappingToken() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
-        layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 1, /*inputEventId*/ 0},
-                                                             10);
-        EXPECT_EQ(1u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
-        ASSERT_EQ(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
-        const auto bufferlessSurfaceFrame1 =
-                layer->mCurrentState.bufferlessSurfaceFramesTX.at(/*token*/ 1);
-
-        layer->pushPendingState();
-        EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
-
-        layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 1, /*inputEventId*/ 0},
-                                                             12);
-        EXPECT_EQ(1u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
-        ASSERT_EQ(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
-        const auto bufferlessSurfaceFrame2 =
-                layer->mCurrentState.bufferlessSurfaceFramesTX.at(/*token*/ 1);
-
-        commitTransaction(layer.get());
-
-        EXPECT_EQ(1, bufferlessSurfaceFrame1->getToken());
-        EXPECT_EQ(false, bufferlessSurfaceFrame1->getIsBuffer());
-        EXPECT_EQ(PresentState::Unknown, bufferlessSurfaceFrame1->getPresentState());
-
-        EXPECT_EQ(1, bufferlessSurfaceFrame2->getToken());
-        EXPECT_EQ(false, bufferlessSurfaceFrame2->getIsBuffer());
-        EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState());
-        EXPECT_EQ(12, bufferlessSurfaceFrame2->getActuals().endTime);
-    }
-
     void PendingSurfaceFramesRemovedAfterClassification() {
         sp<BufferStateLayer> layer = createBufferStateLayer();
 
         sp<Fence> fence1(new Fence());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
-        sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer1 = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
@@ -359,7 +312,10 @@
 
         sp<Fence> fence2(new Fence());
         auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
-        sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer2 = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         acquireFence2->signalForTest(12);
@@ -386,7 +342,10 @@
 
         sp<Fence> fence1(new Fence());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
-        sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer1 = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
@@ -395,7 +354,10 @@
 
         sp<Fence> fence2(new Fence());
         auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
-        sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer2 = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         auto dropStartTime1 = systemTime();
         layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0},
@@ -407,7 +369,10 @@
 
         sp<Fence> fence3(new Fence());
         auto acquireFence3 = fenceFactory.createFenceTimeForTest(fence3);
-        sp<GraphicBuffer> buffer3{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        const auto buffer3 = std::make_shared<
+                renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                 1, 0),
+                                               mRenderEngine, false);
         auto dropStartTime2 = systemTime();
         layer->setBuffer(buffer3, fence3, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ 2, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
@@ -447,7 +412,11 @@
         std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> bufferlessSurfaceFrames;
         for (int i = 0; i < 10; i += 2) {
             sp<Fence> fence1(new Fence());
-            sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+            const auto buffer1 = std::make_shared<
+                    renderengine::ExternalTexture>(new GraphicBuffer(1, 1,
+                                                                     HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                                                     0),
+                                                   mRenderEngine, false);
             layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
                              {/*vsyncId*/ 1, /*inputEventId*/ 0},
                              nullptr /* releaseBufferCallback */);
@@ -529,16 +498,6 @@
     MultipleSurfaceFramesPresentedTogether();
 }
 
-TEST_F(TransactionSurfaceFrameTest,
-       MergePendingStates_BufferlessSurfaceFramesWithoutOverlappingToken) {
-    MergePendingStates_BufferlessSurfaceFramesWithoutOverlappingToken();
-}
-
-TEST_F(TransactionSurfaceFrameTest,
-       MergePendingStates_BufferlessSurfaceFramesWithOverlappingToken) {
-    MergePendingStates_BufferlessSurfaceFramesWithOverlappingToken();
-}
-
 TEST_F(TransactionSurfaceFrameTest, PendingSurfaceFramesRemovedAfterClassification) {
     PendingSurfaceFramesRemovedAfterClassification();
 }
@@ -552,4 +511,4 @@
     MultipleCommitsBeforeLatch();
 }
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 0af5f30..42b1993 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -52,7 +52,7 @@
     void setPeriod(nsecs_t) final {}
     void resetModel() final {}
     bool needsMoreSamples() const final { return false; }
-    bool isVSyncInPhase(nsecs_t, int) const final { return false; }
+    bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
     void dump(std::string&) const final {}
 
 private:
@@ -89,7 +89,7 @@
     void setPeriod(nsecs_t) final {}
     void resetModel() final {}
     bool needsMoreSamples() const final { return false; }
-    bool isVSyncInPhase(nsecs_t, int) const final { return false; }
+    bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
     void dump(std::string&) const final {}
 
 private:
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 00cf574..b64cce9 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -49,7 +49,7 @@
     MOCK_METHOD1(setPeriod, void(nsecs_t));
     MOCK_METHOD0(resetModel, void());
     MOCK_CONST_METHOD0(needsMoreSamples, bool());
-    MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
+    MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 
     nsecs_t nextVSyncTime(nsecs_t timePoint) const {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index a4ddbf4..2a658dd 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -469,7 +469,9 @@
     for (int divider = 1; divider < maxDivider; divider++) {
         for (int i = 0; i < maxPeriods; i++) {
             const bool expectedInPhase = (i % divider) == 0;
-            EXPECT_THAT(expectedInPhase, tracker.isVSyncInPhase(mNow + i * mPeriod - bias, divider))
+            EXPECT_THAT(expectedInPhase,
+                        tracker.isVSyncInPhase(mNow + i * mPeriod - bias,
+                                               Fps::fromPeriodNsecs(divider * mPeriod)))
                     << "vsync at " << mNow + (i + 1) * mPeriod - bias << " is "
                     << (expectedInPhase ? "not " : "") << "in phase for divider " << divider;
         }
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index b9651ea..5826a9b 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -46,7 +46,7 @@
     MOCK_METHOD1(setPeriod, void(nsecs_t));
     MOCK_METHOD0(resetModel, void());
     MOCK_CONST_METHOD0(needsMoreSamples, bool());
-    MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
+    MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
index 5707978..5dc48c3 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
@@ -32,7 +32,9 @@
     MOCK_METHOD0(onBootFinished, void());
     MOCK_METHOD1(addSurfaceFrame, void(std::shared_ptr<frametimeline::SurfaceFrame>));
     MOCK_METHOD3(setSfWakeUp, void(int64_t, nsecs_t, Fps));
-    MOCK_METHOD3(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&, bool));
+    MOCK_METHOD3(setSfPresent,
+                 void(nsecs_t, const std::shared_ptr<FenceTime>&,
+                      const std::shared_ptr<FenceTime>&));
     MOCK_METHOD1(computeFps, float(const std::unordered_set<int32_t>&));
 };
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 3e4a0b8..526a847 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -27,7 +27,7 @@
     TimeStats();
     ~TimeStats() override;
 
-    MOCK_METHOD0(onBootFinished, void());
+    MOCK_METHOD2(onPullAtom, bool(const int, std::string*));
     MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&));
     MOCK_METHOD0(isEnabled, bool());
     MOCK_METHOD0(miniDump, std::string());
@@ -48,10 +48,11 @@
     MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
     MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t));
     MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
-    MOCK_METHOD5(setPresentTime, void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>));
-    MOCK_METHOD5(setPresentFence,
-                 void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps,
-                      std::optional<Fps>));
+    MOCK_METHOD6(setPresentTime,
+                 void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>, SetFrameRateVote));
+    MOCK_METHOD6(setPresentFence,
+                 void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, std::optional<Fps>,
+                      SetFrameRateVote));
     MOCK_METHOD1(incrementJankyFrames, void(const JankyFramesInfo&));
     MOCK_METHOD1(onDestroy, void(int32_t));
     MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index de98025..5b0c1f3 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -33,7 +33,7 @@
     MOCK_METHOD1(setPeriod, void(nsecs_t));
     MOCK_METHOD0(resetModel, void());
     MOCK_CONST_METHOD0(needsMoreSamples, bool());
-    MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
+    MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
diff --git a/services/surfaceflinger/tests/utils/TransactionUtils.h b/services/surfaceflinger/tests/utils/TransactionUtils.h
index 3cbfed9..8c448e2 100644
--- a/services/surfaceflinger/tests/utils/TransactionUtils.h
+++ b/services/surfaceflinger/tests/utils/TransactionUtils.h
@@ -126,7 +126,7 @@
             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))
+                ASSERT_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]} << ")";
@@ -161,6 +161,22 @@
             ASSERT_EQ(NO_ERROR, s->unlockAndPost());
         }
     }
+
+    static void setFrame(Transaction& t, const sp<SurfaceControl>& sc, Rect source, Rect dest,
+                         int32_t transform = 0) {
+        uint32_t sourceWidth = source.getWidth();
+        uint32_t sourceHeight = source.getHeight();
+
+        if (transform & ui::Transform::ROT_90) {
+            std::swap(sourceWidth, sourceHeight);
+        }
+
+        float dsdx = dest.getWidth() / static_cast<float>(sourceWidth);
+        float dsdy = dest.getHeight() / static_cast<float>(sourceHeight);
+
+        t.setMatrix(sc, dsdx, 0, 0, dsdy);
+        t.setPosition(sc, dest.left, dest.top);
+    }
 };
 
 enum class RenderPath { SCREENSHOT, VIRTUAL_DISPLAY };
diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp
index a9d499d..6e660e7 100644
--- a/services/vibratorservice/VibratorManagerHalWrapper.cpp
+++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp
@@ -30,7 +30,7 @@
 const constexpr char* MISSING_VIBRATOR_MESSAGE_PREFIX = "No vibrator with id=";
 
 HalResult<void> LegacyManagerHalWrapper::ping() {
-    auto pingFn = [](std::shared_ptr<HalWrapper> hal) { return hal->ping(); };
+    auto pingFn = [](HalWrapper* hal) { return hal->ping(); };
     return mController->doWithRetry<void>(pingFn, "ping");
 }
 
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index 354e56c..6c31e2b 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -30,7 +30,7 @@
 std::shared_ptr<HalWrapper> connectHal(std::shared_ptr<CallbackScheduler> scheduler);
 
 template <typename T>
-using HalFunction = std::function<T(std::shared_ptr<HalWrapper>)>;
+using HalFunction = std::function<T(HalWrapper*)>;
 
 // Controller for Vibrator HAL handle.
 // This relies on a given Connector to connect to the underlying Vibrator HAL service and reconnects
@@ -64,8 +64,7 @@
      */
     Info getInfo() {
         static Info sDefaultInfo = InfoCache().get();
-        return apply<Info>([](std::shared_ptr<HalWrapper> hal) { return hal->getInfo(); },
-                           sDefaultInfo, "getInfo");
+        return apply<Info>([](HalWrapper* hal) { return hal->getInfo(); }, sDefaultInfo, "getInfo");
     }
 
     /* Calls given HAL function, applying automatic retries to reconnect with the HAL when the
@@ -103,7 +102,7 @@
         }
 
         for (int i = 0; i < MAX_RETRIES; i++) {
-            T result = halFn(hal);
+            T result = halFn(hal.get());
             if (result.checkAndLogFailure(functionName)) {
                 tryReconnect();
             } else {
@@ -111,7 +110,7 @@
             }
         }
 
-        return halFn(hal);
+        return halFn(hal.get());
     }
 };
 
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index 279496a..8e77bc5 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -40,11 +40,9 @@
 using namespace std::chrono_literals;
 using namespace testing;
 
-static const auto ON_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) {
-    return hal->on(10ms, []() {});
-};
-static const auto OFF_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->off(); };
-static const auto PING_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->ping(); };
+static const auto ON_FN = [](vibrator::HalWrapper* hal) { return hal->on(10ms, []() {}); };
+static const auto OFF_FN = [](vibrator::HalWrapper* hal) { return hal->off(); };
+static const auto PING_FN = [](vibrator::HalWrapper* hal) { return hal->ping(); };
 
 // -------------------------------------------------------------------------------------------------
 
@@ -233,7 +231,7 @@
     std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
     auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
 
-    auto onFn = [&](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->on(10ms, callback); };
+    auto onFn = [&](vibrator::HalWrapper* hal) { return hal->on(10ms, callback); };
     ASSERT_TRUE(mController->doWithRetry<void>(onFn, "on").isOk());
     ASSERT_TRUE(mController->doWithRetry<void>(PING_FN, "ping").isFailed());
     mMockHal.reset();
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index d1db82b..af0cdb8 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -644,7 +644,6 @@
                 .Times(Exactly(2))
                 .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
                 .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
-        ;
     }
 
     std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
index 3de1576..548d028 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
@@ -40,7 +40,7 @@
 using namespace android;
 using namespace testing;
 
-static const auto OFF_FN = [](std::shared_ptr<vibrator::HalWrapper> hal) { return hal->off(); };
+static const auto OFF_FN = [](vibrator::HalWrapper* hal) { return hal->off(); };
 
 class MockBinder : public BBinder {
 public:
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index 866007e..eb24a22 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -108,6 +108,7 @@
 
 cc_binary {
     name: "vr_hwc",
+    enabled: false,
     system_ext_specific: true,
     vintf_fragments: ["manifest_vr_hwc.xml"],
     srcs: [
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 020b520..8d6681c 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1086,7 +1086,8 @@
     ALOGW_IF(err != android::OK, "native_window_api_connect failed: %s (%d)",
              strerror(-err), err);
 
-    err = window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, -1);
+    err =
+        window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, nsecs_t{-1});
     if (err != android::OK) {
         ALOGE("window->perform(SET_DEQUEUE_TIMEOUT) failed: %s (%d)",
               strerror(-err), err);
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index eb0fcc3..5872495 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -363,6 +363,10 @@
   VkJsonDeviceGroup device_group;
   std::vector<VkPhysicalDeviceGroupProperties> group_properties;
   group_properties.resize(count);
+  for (auto& properties : group_properties) {
+    properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES;
+    properties.pNext = nullptr;
+  }
   result = vkEnumeratePhysicalDeviceGroups(vkinstance, &count,
                                            group_properties.data());
   if (result != VK_SUCCESS) {