Merge "SF: Add unit test for RefreshRateConfigs" 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/TEST_MAPPING b/TEST_MAPPING
index 307e21c..4b64203 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -60,9 +60,6 @@
       ]
     },
     {
-      "name": "libsurfaceflinger_unittest"
-    },
-    {
       "name": "CtsGraphicsTestCases",
       "options": [
         {
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index cedff0b..79419d3 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -323,9 +323,6 @@
 static const char* k_funcgraphProcPath =
     "options/funcgraph-proc";
 
-static const char* k_funcgraphFlatPath =
-    "options/funcgraph-flat";
-
 static const char* k_ftraceFilterPath =
     "set_ftrace_filter";
 
@@ -703,7 +700,6 @@
         ok &= setKernelOptionEnable(k_funcgraphAbsTimePath, true);
         ok &= setKernelOptionEnable(k_funcgraphCpuPath, true);
         ok &= setKernelOptionEnable(k_funcgraphProcPath, true);
-        ok &= setKernelOptionEnable(k_funcgraphFlatPath, true);
 
         // Set the requested filter functions.
         ok &= truncateFile(k_ftraceFilterPath);
diff --git a/cmds/bugreport/OWNERS b/cmds/bugreport/OWNERS
index 2a9b681..5f56531 100644
--- a/cmds/bugreport/OWNERS
+++ b/cmds/bugreport/OWNERS
@@ -1,4 +1,5 @@
 set noparent
 
+gavincorkery@google.com
 nandana@google.com
 jsharkey@android.com
diff --git a/cmds/bugreportz/OWNERS b/cmds/bugreportz/OWNERS
index 2a9b681..5f56531 100644
--- a/cmds/bugreportz/OWNERS
+++ b/cmds/bugreportz/OWNERS
@@ -1,4 +1,5 @@
 set noparent
 
+gavincorkery@google.com
 nandana@google.com
 jsharkey@android.com
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/OWNERS b/cmds/dumpstate/OWNERS
index 2a9b681..5f56531 100644
--- a/cmds/dumpstate/OWNERS
+++ b/cmds/dumpstate/OWNERS
@@ -1,4 +1,5 @@
 set noparent
 
+gavincorkery@google.com
 nandana@google.com
 jsharkey@android.com
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 7ab2a8d..25e6dc9 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");
@@ -1638,6 +1641,10 @@
         MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n");
     } else {
         RunCommand("LSMOD", {"lsmod"});
+        RunCommand("MODULES INFO",
+                   {"sh", "-c", "cat /proc/modules | cut -d' ' -f1 | "
+                    "    while read MOD ; do echo modinfo:$MOD ; modinfo $MOD ; "
+                    "done"}, CommandOptions::AS_ROOT);
     }
 
     if (android::base::GetBoolProperty("ro.logd.kernel", false)) {
@@ -1689,6 +1696,12 @@
 
     RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysHigh);
 
+    // The dump mechanism in connectivity is refactored due to modularization work. Connectivity can
+    // only register with a default priority(NORMAL priority). Dumpstate has to call connectivity
+    // dump with priority parameters to dump high priority information.
+    RunDumpsys("SERVICE HIGH connectivity", {"connectivity", "--dump-priority", "HIGH"},
+                   CommandOptions::WithTimeout(10).Build());
+
     RunCommand("SYSTEM PROPERTIES", {"getprop"});
 
     RunCommand("STORAGED IO INFO", {"storaged", "-u", "-p"});
@@ -1957,6 +1970,8 @@
 
     RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(),
                SEC_TO_MSEC(10));
+    RunDumpsys("DUMPSYS", {"vcn_management"}, CommandOptions::WithTimeout(90).Build(),
+               SEC_TO_MSEC(10));
     if (include_sensitive_info) {
         // Carrier apps' services will be dumped below in dumpsys activity service all-non-platform.
         RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(),
@@ -2175,14 +2190,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 +2304,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());
@@ -3178,6 +3195,11 @@
         // Since we do not have user consent to share the bugreport it does not get
         // copied over to the calling app but remains in the internal directory from
         // where the user can manually pull it.
+        std::string final_path = GetPath(".zip");
+        bool copy_succeeded = android::os::CopyFileToFile(path_, final_path);
+        if (copy_succeeded) {
+            android::os::UnlinkAndLogOnError(path_);
+        }
         return Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT;
     }
     // Unknown result; must be a programming error.
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..97a63ca 100644
--- a/cmds/dumpsys/OWNERS
+++ b/cmds/dumpsys/OWNERS
@@ -1,4 +1,8 @@
 set noparent
 
+gavincorkery@google.com
 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/Android.bp b/cmds/installd/Android.bp
index a546236..3f180d9 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -47,6 +47,9 @@
         "libutils",
         "server_configurable_flags",
     ],
+    static_libs: [
+        "libasync_safe",
+    ],
     export_shared_lib_headers: [
         "libbinder",
     ],
@@ -250,6 +253,7 @@
     ],
 
     static_libs: [
+        "libasync_safe",
         "libdiskusage",
         "libotapreoptparameters",
     ],
diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS
index fc745d0..d6807ff 100644
--- a/cmds/installd/OWNERS
+++ b/cmds/installd/OWNERS
@@ -9,3 +9,4 @@
 ngeoffray@google.com
 rpl@google.com
 toddke@google.com
+patb@google.com
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 0cf50a3..204953c 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -36,6 +36,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <async_safe/log.h>
 #include <cutils/fs.h>
 #include <cutils/properties.h>
 #include <cutils/sched_policy.h>
@@ -727,7 +728,8 @@
 
         if (flock(out_fd.get(), LOCK_EX | LOCK_NB) != 0) {
             if (errno != EWOULDBLOCK) {
-                PLOG(WARNING) << "Error locking profile " << package_name;
+                async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Error locking profile %s: %d",
+                        package_name.c_str(), errno);
             }
             // This implies that the app owning this profile is running
             // (and has acquired the lock).
@@ -735,13 +737,15 @@
             // The app never acquires the lock for the reference profiles of primary apks.
             // Only dex2oat from installd will do that. Since installd is single threaded
             // we should not see this case. Nevertheless be prepared for it.
-            PLOG(WARNING) << "Failed to flock " << package_name;
+            async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Failed to flock %s: %d",
+                    package_name.c_str(), errno);
             return false;
         }
 
         bool truncated = ftruncate(out_fd.get(), 0) == 0;
         if (!truncated) {
-            PLOG(WARNING) << "Could not truncate " << package_name;
+            async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Could not truncate %s: %d",
+                    package_name.c_str(), errno);
         }
 
         // Copy over data.
@@ -755,7 +759,8 @@
             write(out_fd.get(), buffer, bytes);
         }
         if (flock(out_fd.get(), LOCK_UN) != 0) {
-            PLOG(WARNING) << "Error unlocking profile " << package_name;
+            async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Error unlocking profile %s: %d",
+                    package_name.c_str(), errno);
         }
         // Use _exit since we don't want to run the global destructors in the child.
         // b/62597429
@@ -1513,7 +1518,8 @@
 
         // Validate the path structure.
         if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) {
-            LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+            async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                    "Could not validate secondary dex path %s", dex_path.c_str());
             _exit(kSecondaryDexDexoptAnalyzerSkippedValidatePath);
         }
 
@@ -1809,7 +1815,8 @@
         drop_capabilities(uid);
 
         if (flock(out_oat.fd(), LOCK_EX | LOCK_NB) != 0) {
-            PLOG(ERROR) << "flock(" << out_oat.path() << ") failed";
+            async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "flock(%s) failed",
+                    out_oat.path().c_str());
             _exit(DexoptReturnCodes::kFlock);
         }
 
@@ -1904,7 +1911,8 @@
         const char* volume_uuid_cstr = volume_uuid ? volume_uuid->c_str() : nullptr;
         if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr,
                 uid, storage_flag)) {
-            LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+            async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                    "Could not validate secondary dex path %s", dex_path.c_str());
             _exit(kReconcileSecondaryDexValidationError);
         }
 
@@ -1917,7 +1925,8 @@
             case kSecondaryDexAccessIOError: _exit(kReconcileSecondaryDexAccessIOError);
             case kSecondaryDexAccessPermissionError: _exit(kReconcileSecondaryDexValidationError);
             default:
-                LOG(ERROR) << "Unexpected result from check_secondary_dex_access: " << access_check;
+                async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                        "Unexpected result from check_secondary_dex_access: %d", access_check);
                 _exit(kReconcileSecondaryDexValidationError);
         }
 
@@ -1930,7 +1939,7 @@
             std::string error_msg;
             if (!create_secondary_dex_oat_layout(
                     dex_path,isas[i], oat_dir, oat_isa_dir, oat_path, &error_msg)) {
-                LOG(ERROR) << error_msg;
+                async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "%s", error_msg.c_str());
                 _exit(kReconcileSecondaryDexValidationError);
             }
 
@@ -1957,7 +1966,8 @@
             result = rmdir_if_empty(oat_dir) && result;
         }
         if (!result) {
-            PLOG(ERROR) << "Failed to clean secondary dex artifacts for location " << dex_path;
+            async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                    "Could not validate secondary dex path %s", dex_path.c_str());
         }
         _exit(result ? kReconcileSecondaryDexCleanedUp : kReconcileSecondaryDexAccessIOError);
     }
@@ -2030,7 +2040,8 @@
         pipe_read.reset();
 
         if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr, uid, storage_flag)) {
-            LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+            async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                    "Could not validate secondary dex path %s", dex_path.c_str());
             _exit(DexoptReturnCodes::kHashValidatePath);
         }
 
@@ -2041,6 +2052,8 @@
                 _exit(0);
             }
             PLOG(ERROR) << "Failed to open secondary dex " << dex_path;
+            async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                    "Failed to open secondary dex %s: %d", dex_path.c_str(), errno);
             _exit(DexoptReturnCodes::kHashOpenPath);
         }
 
@@ -2053,7 +2066,8 @@
             if (bytes_read == 0) {
                 break;
             } else if (bytes_read == -1) {
-                PLOG(ERROR) << "Failed to read secondary dex " << dex_path;
+                async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                        "Failed to read secondary dex %s: %d", dex_path.c_str(), errno);
                 _exit(DexoptReturnCodes::kHashReadDex);
             }
 
diff --git a/cmds/installd/file_parsing.h b/cmds/installd/file_parsing.h
index 3e2f815..88801ca 100644
--- a/cmds/installd/file_parsing.h
+++ b/cmds/installd/file_parsing.h
@@ -19,18 +19,14 @@
 
 #include <fstream>
 #include <functional>
-#include <string>
+#include <string_view>
+#include "android-base/unique_fd.h"
 
 namespace android {
 namespace installd {
 
-bool ParseFile(const std::string& strFile, std::function<bool (const std::string&)> parse) {
-    std::ifstream input_stream(strFile);
-
-    if (!input_stream.is_open()) {
-        return false;
-    }
-
+template<typename Func>
+bool ParseFile(std::istream& input_stream, Func parse) {
     while (!input_stream.eof()) {
         // Read the next line.
         std::string line;
@@ -54,6 +50,15 @@
     return true;
 }
 
+template<typename Func>
+bool ParseFile(std::string_view str_file, Func parse) {
+  std::ifstream ifs(str_file);
+  if (!ifs.is_open()) {
+    return false;
+  }
+  return ParseFile(ifs, parse);
+}
+
 }  // namespace installd
 }  // namespace android
 
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index ed31ad9..6aa32b8 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -26,6 +26,7 @@
 #include <sys/capability.h>
 #include <sys/prctl.h>
 #include <sys/stat.h>
+#include <sys/mman.h>
 
 #include <android-base/logging.h>
 #include <android-base/macros.h>
@@ -36,6 +37,7 @@
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
 
+#include "android-base/file.h"
 #include "dexopt.h"
 #include "file_parsing.h"
 #include "globals.h"
@@ -195,38 +197,63 @@
         //   export NAME VALUE
         // For simplicity, don't respect string quotation. The values we are interested in can be
         // encoded without them.
-        // init.environ.rc and etc/classpath have the same format for
-        // environment variable exports and can be matched by the same regex.
+        //
+        // init.environ.rc and derive_classpath all have the same format for
+        // environment variable exports (since they are all meant to be read by
+        // init) and can be matched by the same regex.
+
+        std::regex export_regex("\\s*export\\s+(\\S+)\\s+(\\S+)");
+        auto parse_results = [&](auto& input) {
+          ParseFile(input, [&](const std::string& line) {
+              std::smatch export_match;
+              if (!std::regex_match(line, export_match, export_regex)) {
+                  return true;
+              }
+
+              if (export_match.size() != 3) {
+                  return true;
+              }
+
+              std::string name = export_match[1].str();
+              std::string value = export_match[2].str();
+
+              system_properties_.SetProperty(name, value);
+
+              return true;
+          });
+        };
+
         // TODO Just like with the system-properties above we really should have
         // common code between init and otapreopt to deal with reading these
         // things. See b/181182967
+        // There have been a variety of places the various env-vars have been
+        // over the years.  Expand or reduce this list as needed.
         static constexpr const char* kEnvironmentVariableSources[] = {
-                "/init.environ.rc", "/etc/classpath"
+                "/init.environ.rc",
         };
-
-        std::regex export_regex("\\s*export\\s+(\\S+)\\s+(\\S+)");
+        // First get everything from the static files.
         for (const char* env_vars_file : kEnvironmentVariableSources) {
-            bool parse_result = ParseFile(env_vars_file, [&](const std::string& line) {
-                std::smatch export_match;
-                if (!std::regex_match(line, export_match, export_regex)) {
-                    return true;
-                }
-
-                if (export_match.size() != 3) {
-                    return true;
-                }
-
-                std::string name = export_match[1].str();
-                std::string value = export_match[2].str();
-
-                system_properties_.SetProperty(name, value);
-
-                return true;
-            });
-            if (!parse_result) {
-                return false;
-            }
+          parse_results(env_vars_file);
         }
+
+        // Next get everything from derive_classpath, since we're already in the
+        // chroot it will get the new versions of any dependencies.
+        {
+          android::base::unique_fd fd(memfd_create("derive_classpath_temp", MFD_CLOEXEC));
+          if (!fd.ok()) {
+            LOG(ERROR) << "Unable to create fd for derive_classpath";
+            return false;
+          }
+          std::string memfd_file = StringPrintf("/proc/%d/fd/%d", getpid(), fd.get());
+          std::string error_msg;
+          if (!Exec({"/apex/com.android.sdkext/bin/derive_classpath", memfd_file}, &error_msg)) {
+            PLOG(ERROR) << "Running derive_classpath failed: " << error_msg;
+            return false;
+          }
+          std::ifstream ifs(memfd_file);
+          parse_results(ifs);
+        }
+
         if (system_properties_.GetProperty(kAndroidDataPathPropertyName) == nullptr) {
             return false;
         }
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 3a87776..c62734a 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -179,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) {
@@ -270,6 +275,7 @@
     static constexpr const std::string_view kRequiredApexs[] = {
       "com.android.art",
       "com.android.runtime",
+      "com.android.sdkext",  // For derive_classpath
     };
     std::array<bool, arraysize(kRequiredApexs)> found_apexs{ false, false };
     DIR* apex_dir = opendir("/apex");
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index f67ab81..7082017 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -20,6 +20,7 @@
         "libcutils",
     ],
     static_libs: [
+        "libasync_safe",
         "libdiskusage",
         "libinstalld",
         "liblog",
@@ -44,6 +45,7 @@
         "server_configurable_flags",
     ],
     static_libs: [
+        "libasync_safe",
         "libdiskusage",
         "libinstalld",
         "liblog",
@@ -84,6 +86,7 @@
         "server_configurable_flags",
     ],
     static_libs: [
+        "libasync_safe",
         "libdiskusage",
         "libinstalld",
         "liblog",
@@ -124,6 +127,7 @@
         "server_configurable_flags",
     ],
     static_libs: [
+        "libasync_safe",
         "libdiskusage",
         "libinstalld",
         "liblog",
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 7c1ca91..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;
@@ -510,7 +508,7 @@
 
     std::string error;
     vintf::HalManifest m;
-    EXPECT_EQ(true, vintf::gHalManifestConverter(&m, out.str(), &error))
+    EXPECT_EQ(true, vintf::fromXml(&m, out.str(), &error))
         << "--init-vintf does not emit valid HAL manifest: " << error;
 }
 
@@ -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/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 2f55249..90db509 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) {
@@ -388,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) {
@@ -432,7 +481,12 @@
           name.c_str());
 
     std::thread([=] {
-        (void)base::SetProperty("ctl.interface_start", "aidl/" + name);
+        if (!base::SetProperty("ctl.interface_start", "aidl/" + name)) {
+            LOG(INFO) << "Tried to start aidl service " << name
+                      << " as a lazy service, but was unable to. Usually this happens when a "
+                         "service is not installed, but if the service is intended to be used as a "
+                         "lazy service, then it may be configured incorrectly.";
+        }
     }).detach();
 }
 
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/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..fb7d09c 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,10 @@
  * skipping frames in an image with such frames may not produce the correct
  * results.
  *
+ * Only supported by {@link ANDROID_BITMAP_FORMAT_RGBA_8888} and
+ * {@link ANDROID_BITMAP_FORMAT_RGBA_F16}.
+ *
+ * @param decoder an {@link AImageDecoder} object.
  * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
  *         indicating the reason for the failure.
  *
@@ -732,6 +750,8 @@
  * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The AImageDecoder
  *   represents an image that is not animated (see
  *   {@link AImageDecoder_isAnimated}) or the AImageDecoder is null.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_STATE): The requested
+ *   {@link AndroidBitmapFormat} does not support animation.
  * - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The input appears
  *   to be truncated. The client must call {@link AImageDecoder_rewind}
  *   before calling {@link AImageDecoder_decodeImage} again.
@@ -755,6 +775,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.
  *
@@ -816,9 +837,11 @@
  * is the current frame.
  *
  * If the image only has one frame, this will fill the {@link
- * AImageDecoderFrameInfo} with the encoded info, if any, or reasonable
+ * AImageDecoderFrameInfo} with the encoded info and reasonable
  * defaults.
  *
+ * If {@link AImageDecoder_advanceFrame} succeeded, this will succeed as well.
+ *
  * @param decoder Opaque object representing the decoder.
  * @param info Opaque object to hold frame information. On success, will be
  *             filled with information regarding the current frame.
@@ -840,14 +863,14 @@
  * Introduced in API 31.
  *
  * Errors:
- * - returns 0 if |info| is null.
+ * - returns {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if |info| is null.
  */
 int64_t AImageDecoderFrameInfo_getDuration(
         const AImageDecoderFrameInfo* _Nonnull info) __INTRODUCED_IN(31);
 
 /**
  * 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.
@@ -875,19 +898,25 @@
  *
  * Introduced in API 31.
  *
- * Note that this may differ from whether the composed frame has
- * alpha. If this frame does not fill the entire image dimensions
- * (see {@link AImageDecoderFrameInfo_getFrameRect}) or it blends
- * with an opaque frame, for example, the composed frame’s alpha
- * may not match. It is also conservative; for example, if a color
- * index-based frame has a color with alpha but does not use it,
- * this will still return true.
+ * Unless this frame is independent (see {@link AImageDecoder_decodeImage}),
+ * a single call to {@link AImageDecoder_decodeImage} will decode an updated
+ * rectangle of pixels and then blend it with the existing pixels in the
+ * |pixels| buffer according to {@link AImageDecoderFrameInfo_getBlendOp}. This
+ * method returns whether the updated rectangle has alpha, prior to blending.
+ * The return value is conservative; for example, if a color-index-based frame
+ * has a color with alpha but does not use it, this will still return true.
  *
  * This, along with other information in AImageDecoderFrameInfo,
  * can be useful for determining whether a frame is independent, but
  * the decoder handles blending frames, so a simple
  * sequential client does not need this.
  *
+ * Note that this may differ from whether the composed frame (that is, the
+ * resulting image after blending) has alpha. If this frame does not fill the
+ * entire image dimensions (see {@link AImageDecoderFrameInfo_getFrameRect})
+ * or it blends with an opaque frame, for example, the composed frame’s alpha
+ * may not match.
+ *
  * Errors:
  * - returns false if |info| is null.
  */
@@ -905,15 +934,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 +977,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 +1031,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 c5e3587..059bc41 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -133,6 +133,9 @@
  * ASurfaceTransaction_OnComplete callback can be used to be notified when a frame
  * including the updates in a transaction was presented.
  *
+ * Buffers which are replaced or removed from the scene in the transaction invoking
+ * this callback may be reused after this point.
+ *
  * \param context Optional context provided by the client that is passed into
  * the callback.
  *
@@ -147,6 +150,35 @@
 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.
+ *
+ * This callback does not mean buffers have been released! It simply means that any new
+ * transactions applied will not overwrite the transaction for which we are receiving
+ * a callback and instead will be included in the next frame. If you are trying to avoid
+ * dropping frames (overwriting transactions), and unable to use timestamps (Which provide
+ * a more efficient solution), then this method provides a method to pace your transaction
+ * application.
+ *
+ * \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 +193,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 +252,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 +272,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.
  *
@@ -322,6 +368,11 @@
  * enum.
  *
  * Available since API level 29.
+ *
+ * @deprecated Use setCrop, setPosition, setBufferTransform, and setScale instead. Those functions
+ * provide well defined behavior and allows for more control by the apps. It also allows the caller
+ * to set different properties at different times, instead of having to specify all the desired
+ * properties at once.
  */
 void ASurfaceTransaction_setGeometry(ASurfaceTransaction* transaction,
                                      ASurfaceControl* surface_control, const ARect& source,
@@ -329,39 +380,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 {
@@ -476,6 +541,9 @@
  * callback timings, and changes to the time interval at which the system releases buffers back to
  * the application.
  *
+ * You can register for changes in the refresh rate using
+ * \a AChoreographer_registerRefreshRateCallback.
+ *
  * \param frameRate is the intended frame rate of this surface, in frames per second. 0 is a special
  * value that indicates the app will accept the system's choice for the display frame rate, which is
  * the default behavior if this function isn't called. The frameRate param does <em>not</em> need to
@@ -484,11 +552,12 @@
  *
  * \param compatibility The frame rate compatibility of this surface. The compatibility value may
  * influence the system's choice of display frame rate. To specify a compatibility use the
- * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum.
+ * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum. This parameter is ignored when frameRate is 0.
  *
- * \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.
+ * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this
+ * surface 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. This parameter is ignored when frameRate is 0.
  *
  * Available since API level 31.
  */
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 bb5ca0e..d4defa8 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -70,6 +70,14 @@
      */
     AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE = 0x8,
 
+    /**
+     * This flag indicates that the event will not cause a focus change if it is directed to an
+     * unfocused window, even if it an ACTION_DOWN. This is typically used with pointer
+     * gestures to allow the user to direct gestures to an unfocused window without bringing it
+     * into focus.
+     */
+    AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40,
+
     /* Motion event is inconsistent with previously sent motion events. */
     AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
 };
@@ -318,6 +326,12 @@
  */
 constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits<float>::quiet_NaN();
 
+/**
+ * Invalid value for display size. Used when display size isn't available for an event or doesn't
+ * matter. This is just a constant 0 so that it has no effect if unused.
+ */
+constexpr int32_t AMOTION_EVENT_INVALID_DISPLAY_SIZE = 0;
+
 /*
  * Pointer coordinate data.
  */
@@ -360,6 +374,8 @@
         return getAxisValue(AMOTION_EVENT_AXIS_Y);
     }
 
+    vec2 getXYValue() const { return vec2(getX(), getY()); }
+
 #ifdef __linux__
     status_t readFromParcel(Parcel* parcel);
     status_t writeToParcel(Parcel* parcel) const;
@@ -548,6 +564,8 @@
 
     void setCursorPosition(float x, float y);
 
+    int2 getDisplaySize() const { return {mDisplayWidth, mDisplayHeight}; }
+
     static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); }
 
     inline nsecs_t getDownTime() const { return mDownTime; }
@@ -570,8 +588,17 @@
 
     inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; }
 
+    /**
+     * The actual raw pointer coords: whatever comes from the input device without any external
+     * transforms applied.
+     */
     const PointerCoords* getRawPointerCoords(size_t pointerIndex) const;
 
+    /**
+     * This is the raw axis value. However, for X/Y axes, this currently applies a "compat-raw"
+     * transform because many apps (incorrectly) assumed that raw == oriented-screen-space.
+     * "compat raw" is raw coordinates with screen rotation applied.
+     */
     float getRawAxisValue(int32_t axis, size_t pointerIndex) const;
 
     inline float getRawX(size_t pointerIndex) const {
@@ -634,9 +661,18 @@
         return mSampleEventTimes[historicalIndex];
     }
 
+    /**
+     * The actual raw pointer coords: whatever comes from the input device without any external
+     * transforms applied.
+     */
     const PointerCoords* getHistoricalRawPointerCoords(
             size_t pointerIndex, size_t historicalIndex) const;
 
+    /**
+     * This is the raw axis value. However, for X/Y axes, this currently applies a "compat-raw"
+     * transform because many apps (incorrectly) assumed that raw == oriented-screen-space.
+     * "compat raw" is raw coordinates with screen rotation applied.
+     */
     float getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
             size_t historicalIndex) const;
 
@@ -704,9 +740,9 @@
                     int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
                     MotionClassification classification, const ui::Transform& transform,
                     float xPrecision, float yPrecision, float rawXCursorPosition,
-                    float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime,
-                    size_t pointerCount, const PointerProperties* pointerProperties,
-                    const PointerCoords* pointerCoords);
+                    float rawYCursorPosition, int32_t displayWidth, int32_t displayHeight,
+                    nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
+                    const PointerProperties* pointerProperties, const PointerCoords* pointerCoords);
 
     void copyFrom(const MotionEvent* other, bool keepHistory);
 
@@ -718,10 +754,14 @@
 
     void scale(float globalScaleFactor);
 
-    // Apply 3x3 perspective matrix transformation.
+    // Set 3x3 perspective matrix transformation.
     // Matrix is in row-major form and compatible with SkMatrix.
     void transform(const std::array<float, 9>& matrix);
 
+    // Apply 3x3 perspective matrix transformation only to content (do not modify mTransform).
+    // Matrix is in row-major form and compatible with SkMatrix.
+    void applyTransform(const std::array<float, 9>& matrix);
+
 #ifdef __linux__
     status_t readFromParcel(Parcel* parcel);
     status_t writeToParcel(Parcel* parcel) const;
@@ -759,6 +799,8 @@
     float mYPrecision;
     float mRawXCursorPosition;
     float mRawYCursorPosition;
+    int32_t mDisplayWidth;
+    int32_t mDisplayHeight;
     nsecs_t mDownTime;
     Vector<PointerProperties> mPointerProperties;
     std::vector<nsecs_t> mSampleEventTimes;
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 898d1a9..360dfbf 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -136,6 +136,8 @@
             float yPrecision;
             float xCursorPosition;
             float yCursorPosition;
+            int32_t displayWidth;
+            int32_t displayHeight;
             uint32_t pointerCount;
             uint32_t empty3;
             /**
@@ -227,7 +229,7 @@
     InputChannel(const InputChannel& other)
           : mName(other.mName), mFd(::dup(other.mFd)), mToken(other.mToken){};
     InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token);
-    virtual ~InputChannel();
+    ~InputChannel() override;
     /**
      * Create a pair of input channels.
      * The two returned input channels are equivalent, and are labeled as "server" and "client"
@@ -353,8 +355,9 @@
                                 int32_t metaState, int32_t buttonState,
                                 MotionClassification classification, const ui::Transform& transform,
                                 float xPrecision, float yPrecision, float xCursorPosition,
-                                float yCursorPosition, nsecs_t downTime, nsecs_t eventTime,
-                                uint32_t pointerCount, const PointerProperties* pointerProperties,
+                                float yCursorPosition, int32_t displayWidth, int32_t displayHeight,
+                                nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
+                                const PointerProperties* pointerProperties,
                                 const PointerCoords* pointerCoords);
 
     /* Publishes a focus event to the input channel.
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 36097d6..121be6d 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -168,6 +168,10 @@
     // Transform applied to individual windows.
     ui::Transform transform;
 
+    // Display size in its natural rotation. Used to rotate raw coordinates for compatibility.
+    int32_t displayWidth = AMOTION_EVENT_INVALID_DISPLAY_SIZE;
+    int32_t displayHeight = AMOTION_EVENT_INVALID_DISPLAY_SIZE;
+
     /*
      * This is filled in by the WM relative to the frame and then translated
      * to absolute coordinates by SurfaceFlinger once the frame is computed.
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 cdd8006..be260e8 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -60,16 +60,9 @@
 // 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",
     "IPermissionController.cpp",
-    "IUidObserver.cpp",
     "PermissionCache.cpp",
     "PermissionController.cpp",
-    ":activity_manager_procstate_aidl",
 ]
 
 cc_library {
@@ -119,7 +112,7 @@
         "PersistableBundle.cpp",
         "ProcessState.cpp",
         "RpcAddress.cpp",
-        "RpcConnection.cpp",
+        "RpcSession.cpp",
         "RpcServer.cpp",
         "RpcState.cpp",
         "Static.cpp",
@@ -163,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: {
@@ -195,6 +189,9 @@
         // Only check our headers
         "--header-filter=^.*frameworks/native/libs/binder/.*.h$",
     ],
+    tidy_checks: [
+        "-performance-no-int-to-ptr",
+    ],
     tidy_checks_as_errors: [
         // Explicitly list the checks that should not occur in this module.
         "abseil-*",
@@ -202,20 +199,9 @@
         "bugprone-*",
         "cert-*",
         "clang-analyzer-*",
-        "-clang-analyzer-core.CallAndMessage",
-        "-clang-analyzer-core.uninitialized.Assign",
-        "-clang-analyzer-unix.Malloc",
-        "-clang-analyzer-deadcode.DeadStores",
-        "-clang-analyzer-optin.cplusplus.UninitializedObject",
         "google-*",
-        "-google-readability-*",
-        "-google-runtime-references",
         "misc-*",
-        "-misc-no-recursion",
-        "-misc-redundant-expression",
-        "-misc-unused-using-decls",
         "performance*",
-        "-performance-no-int-to-ptr",
         "portability*",
     ],
 
@@ -294,13 +280,21 @@
     ],
 }
 
-// TODO(b/183654927): initially empty lib to work around some merge conflicts
 cc_library {
     name: "libactivitymanager_aidl",
-    srcs: [],
+    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/Binder.cpp b/libs/binder/Binder.cpp
index d964d25..d5bdd1c 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -29,6 +29,16 @@
 
 namespace android {
 
+// Service implementations inherit from BBinder and IBinder, and this is frozen
+// in prebuilts.
+#ifdef __LP64__
+static_assert(sizeof(IBinder) == 24);
+static_assert(sizeof(BBinder) == 40);
+#else
+static_assert(sizeof(IBinder) == 12);
+static_assert(sizeof(BBinder) == 20);
+#endif
+
 // ---------------------------------------------------------------------------
 
 IBinder::IBinder()
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 53b36ff..1dcb94c 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -21,7 +21,7 @@
 
 #include <binder/IPCThreadState.h>
 #include <binder/IResultReceiver.h>
-#include <binder/RpcConnection.h>
+#include <binder/RpcSession.h>
 #include <binder/Stability.h>
 #include <cutils/compiler.h>
 #include <utils/Log.h>
@@ -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,18 +133,18 @@
         }
         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) {
-    LOG_ALWAYS_FATAL_IF(connection == nullptr, "BpBinder::create null connection");
+sp<BpBinder> BpBinder::create(const sp<RpcSession>& session, const RpcAddress& address) {
+    LOG_ALWAYS_FATAL_IF(session == nullptr, "BpBinder::create null session");
 
     // These are not currently tracked, since there is no UID or other
     // identifier to track them with. However, if similar functionality is
-    // needed, connection objects keep track of all BpBinder objects on a
-    // per-connection basis.
+    // needed, session objects keep track of all BpBinder objects on a
+    // per-session basis.
 
-    return new BpBinder(SocketHandle{connection, address});
+    return sp<BpBinder>::make(RpcHandle{session, address});
 }
 
 BpBinder::BpBinder(Handle&& handle)
@@ -166,20 +165,20 @@
     IPCThreadState::self()->incWeakHandle(this->binderHandle(), this);
 }
 
-BpBinder::BpBinder(SocketHandle&& handle) : BpBinder(Handle(handle)) {
-    LOG_ALWAYS_FATAL_IF(rpcConnection() == nullptr, "BpBinder created w/o connection object");
+BpBinder::BpBinder(RpcHandle&& handle) : BpBinder(Handle(handle)) {
+    LOG_ALWAYS_FATAL_IF(rpcSession() == nullptr, "BpBinder created w/o session object");
 }
 
 bool BpBinder::isRpcBinder() const {
-    return std::holds_alternative<SocketHandle>(mHandle);
+    return std::holds_alternative<RpcHandle>(mHandle);
 }
 
 const RpcAddress& BpBinder::rpcAddress() const {
-    return std::get<SocketHandle>(mHandle).address;
+    return std::get<RpcHandle>(mHandle).address;
 }
 
-const sp<RpcConnection>& BpBinder::rpcConnection() const {
-    return std::get<SocketHandle>(mHandle).connection;
+const sp<RpcSession>& BpBinder::rpcSession() const {
+    return std::get<RpcHandle>(mHandle).session;
 }
 
 int32_t BpBinder::binderHandle() const {
@@ -194,7 +193,7 @@
 const String16& BpBinder::getInterfaceDescriptor() const
 {
     if (isDescriptorCached() == false) {
-        sp<BpBinder> thiz = const_cast<BpBinder*>(this);
+        sp<BpBinder> thiz = sp<BpBinder>::fromExisting(const_cast<BpBinder*>(this));
 
         Parcel data;
         data.markForBinder(thiz);
@@ -226,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);
 }
@@ -274,7 +273,7 @@
 
         status_t status;
         if (CC_UNLIKELY(isRpcBinder())) {
-            status = rpcConnection()->transact(rpcAddress(), code, data, reply, flags);
+            status = rpcSession()->transact(rpcAddress(), code, data, reply, flags);
         } else {
             status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
         }
@@ -403,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));
 }
 
 
@@ -480,7 +479,7 @@
 {
     ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle());
     if (CC_UNLIKELY(isRpcBinder())) {
-        (void)rpcConnection()->sendDecStrong(rpcAddress());
+        (void)rpcSession()->sendDecStrong(rpcAddress());
         return;
     }
     IF_ALOGV() {
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/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/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..ef7fd44 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -29,8 +29,6 @@
 #include <utils/SystemClock.h>
 #include <utils/threads.h>
 
-#include <private/binder/binder_module.h>
-
 #include <atomic>
 #include <errno.h>
 #include <inttypes.h>
@@ -43,6 +41,7 @@
 #include <unistd.h>
 
 #include "Static.h"
+#include "binder_module.h"
 
 #if LOG_NDEBUG
 
@@ -90,6 +89,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 +895,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/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/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index f96b6bb..6165911 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -123,14 +123,20 @@
     std::string regStr = (reRegister) ? "Re-registering" : "Registering";
     ALOGI("%s service %s", regStr.c_str(), name.c_str());
 
-    if (!manager->addService(name.c_str(), service, allowIsolated, dumpFlags).isOk()) {
-        ALOGE("Failed to register service %s", name.c_str());
+    if (Status status = manager->addService(name.c_str(), service, allowIsolated, dumpFlags);
+        !status.isOk()) {
+        ALOGE("Failed to register service %s (%s)", name.c_str(), status.toString8().c_str());
         return false;
     }
 
     if (!reRegister) {
-        if(!manager->registerClientCallback(name, service, this).isOk()) {
-            ALOGE("Failed to add client callback for service %s", name.c_str());
+        if (Status status =
+                    manager->registerClientCallback(name, service,
+                                                    sp<android::os::IClientCallback>::fromExisting(
+                                                            this));
+            !status.isOk()) {
+            ALOGE("Failed to add client callback for service %s (%s)", name.c_str(),
+                  status.toString8().c_str());
             return false;
         }
 
@@ -169,10 +175,10 @@
     auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
 
     for (auto& [name, entry] : mRegisteredServices) {
-        bool success = manager->tryUnregisterService(name, entry.service).isOk();
+        Status status = manager->tryUnregisterService(name, entry.service);
 
-        if (!success) {
-            ALOGI("Failed to unregister service %s", name.c_str());
+        if (!status.isOk()) {
+            ALOGI("Failed to unregister service %s (%s)", name.c_str(), status.toString8().c_str());
             return false;
         }
         entry.registered = 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 0425159..ee834ea 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -48,10 +48,10 @@
 #include <utils/String8.h>
 #include <utils/misc.h>
 
-#include <private/binder/binder_module.h>
 #include "RpcState.h"
 #include "Static.h"
 #include "Utils.h"
+#include "binder_module.h"
 
 #define LOG_REFS(...)
 //#define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
@@ -78,7 +78,11 @@
 namespace android {
 
 // many things compile this into prebuilts on the stack
-static_assert(sizeof(Parcel) == 60 || sizeof(Parcel) == 120);
+#ifdef __LP64__
+static_assert(sizeof(Parcel) == 120);
+#else
+static_assert(sizeof(Parcel) == 60);
+#endif
 
 static std::atomic<size_t> gParcelGlobalAllocCount;
 static std::atomic<size_t> gParcelGlobalAllocSize;
@@ -198,7 +202,7 @@
             status_t status = writeInt32(1); // non-null
             if (status != OK) return status;
             RpcAddress address = RpcAddress::zero();
-            status = mConnection->state()->onBinderLeaving(mConnection, binder, &address);
+            status = mSession->state()->onBinderLeaving(mSession, binder, &address);
             if (status != OK) return status;
             status = address.writeToParcel(this);
             if (status != OK) return status;
@@ -269,8 +273,7 @@
 status_t Parcel::unflattenBinder(sp<IBinder>* out) const
 {
     if (isForRpc()) {
-        LOG_ALWAYS_FATAL_IF(mConnection == nullptr,
-                            "RpcConnection required to read from remote parcel");
+        LOG_ALWAYS_FATAL_IF(mSession == nullptr, "RpcSession required to read from remote parcel");
 
         int32_t isNull;
         status_t status = readInt32(&isNull);
@@ -282,7 +285,7 @@
             auto addr = RpcAddress::zero();
             status_t status = addr.readFromParcel(*this);
             if (status != OK) return status;
-            binder = mConnection->state()->onBinderEntering(mConnection, addr);
+            binder = mSession->state()->onBinderEntering(mSession, addr);
         }
 
         return finishUnflattenBinder(binder, out);
@@ -293,7 +296,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: {
@@ -563,20 +567,20 @@
     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());
+        markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcSession());
     }
 }
 
-void Parcel::markForRpc(const sp<RpcConnection>& connection) {
+void Parcel::markForRpc(const sp<RpcSession>& session) {
     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;
+    LOG_ALWAYS_FATAL_IF(session == nullptr, "markForRpc requires session");
+    mSession = session;
 }
 
 bool Parcel::isForRpc() const {
-    return mConnection != nullptr;
+    return mSession != nullptr;
 }
 
 void Parcel::updateWorkSourceRequestHeaderPosition() const {
@@ -2494,7 +2498,7 @@
     mDataPos = 0;
     ALOGV("initState Setting data size of %p to %zu", this, mDataSize);
     ALOGV("initState Setting data pos of %p to %zu", this, mDataPos);
-    mConnection = nullptr;
+    mSession = nullptr;
     mObjects = nullptr;
     mObjectsSize = 0;
     mObjectsCapacity = 0;
diff --git a/libs/binder/PermissionCache.cpp b/libs/binder/PermissionCache.cpp
index 6eae5ef..670fd55 100644
--- a/libs/binder/PermissionCache.cpp
+++ b/libs/binder/PermissionCache.cpp
@@ -109,5 +109,10 @@
     return granted;
 }
 
+void PermissionCache::purgeCache() {
+    PermissionCache& pc(PermissionCache::getInstance());
+    pc.purge();
+}
+
 // ---------------------------------------------------------------------------
 } // namespace android
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index abb792e..4fd0dc7 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -27,8 +27,8 @@
 #include <utils/String8.h>
 #include <utils/threads.h>
 
-#include <private/binder/binder_module.h>
 #include "Static.h"
+#include "binder_module.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -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) {
+        ALOGI("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) {
+            ALOGI("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
deleted file mode 100644
index dab3246..0000000
--- a/libs/binder/RpcConnection.cpp
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "RpcConnection"
-
-#include <binder/RpcConnection.h>
-
-#include <binder/Parcel.h>
-#include <binder/Stability.h>
-#include <utils/String8.h>
-
-#include "RpcState.h"
-#include "RpcWireFormat.h"
-
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#ifdef __GLIBC__
-extern "C" pid_t gettid();
-#endif
-
-#ifdef __BIONIC__
-#include <linux/vm_sockets.h>
-#endif
-
-namespace android {
-
-using base::unique_fd;
-
-RpcConnection::SocketAddress::~SocketAddress() {}
-
-RpcConnection::RpcConnection() {
-    LOG_RPC_DETAIL("RpcConnection created %p", this);
-
-    mState = std::make_unique<RpcState>();
-}
-RpcConnection::~RpcConnection() {
-    LOG_RPC_DETAIL("RpcConnection destroyed %p", this);
-}
-
-sp<RpcConnection> RpcConnection::make() {
-    return new RpcConnection;
-}
-
-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), "%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) {
-    return addServer(UnixSocketAddress(path));
-}
-
-bool RpcConnection::addUnixDomainClient(const char* path) {
-    return addClient(UnixSocketAddress(path));
-}
-
-#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 %du port %du", 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 addServer(VsockSocketAddress(kAnyCid, port));
-}
-
-bool RpcConnection::addVsockClient(unsigned int cid, unsigned int port) {
-    return addClient(VsockSocketAddress(cid, port));
-}
-
-#endif // __BIONIC__
-
-sp<IBinder> RpcConnection::getRootObject() {
-    ExclusiveSocket socket(this, SocketUse::CLIENT);
-    return state()->getRootObject(socket.fd(), this);
-}
-
-status_t RpcConnection::transact(const RpcAddress& address, uint32_t code, const Parcel& data,
-                                 Parcel* reply, uint32_t flags) {
-    ExclusiveSocket socket(this,
-                           (flags & IBinder::FLAG_ONEWAY) ? SocketUse::CLIENT_ASYNC
-                                                          : SocketUse::CLIENT);
-    return state()->transact(socket.fd(), address, code, data, this, reply, flags);
-}
-
-status_t RpcConnection::sendDecStrong(const RpcAddress& address) {
-    ExclusiveSocket socket(this, SocketUse::CLIENT_REFCOUNT);
-    return state()->sendDecStrong(socket.fd(), address);
-}
-
-void RpcConnection::join() {
-    // establish a connection
-    {
-        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;
-        }
-
-        LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
-
-        assignServerToThisThread(std::move(clientFd));
-    }
-
-    // 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);
-
-    while (true) {
-        status_t error = state()->getAndExecuteCommand(socket.fd(), this);
-
-        if (error != OK) {
-            ALOGI("Binder socket thread closing w/ status %s", statusToString(error).c_str());
-            return;
-        }
-    }
-}
-
-void RpcConnection::setForServer(const wp<RpcServer>& server) {
-    mForServer = server;
-}
-
-wp<RpcServer> RpcConnection::server() {
-    return mForServer;
-}
-
-bool RpcConnection::addServer(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::addClient(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());
-
-    std::lock_guard<std::mutex> _l(mSocketMutex);
-    sp<ConnectionSocket> connection = new ConnectionSocket();
-    connection->fd = std::move(serverFd);
-    mClients.push_back(connection);
-    return true;
-}
-
-void RpcConnection::assignServerToThisThread(base::unique_fd&& fd) {
-    std::lock_guard<std::mutex> _l(mSocketMutex);
-    sp<ConnectionSocket> connection = new ConnectionSocket();
-    connection->fd = std::move(fd);
-    mServers.push_back(connection);
-}
-
-RpcConnection::ExclusiveSocket::ExclusiveSocket(const sp<RpcConnection>& connection, SocketUse use)
-      : mConnection(connection) {
-    pid_t tid = gettid();
-    std::unique_lock<std::mutex> _l(mConnection->mSocketMutex);
-
-    mConnection->mWaitingThreads++;
-    while (true) {
-        sp<ConnectionSocket> exclusive;
-        sp<ConnectionSocket> available;
-
-        // 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);
-
-            // 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)
-        //
-        // 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 */);
-        }
-
-        // if our thread is already using a connection, prioritize using that
-        if (exclusive != nullptr) {
-            mSocket = exclusive;
-            mReentrant = true;
-            break;
-        } else if (available != nullptr) {
-            mSocket = available;
-            mSocket->exclusiveTid = tid;
-            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 "
-                            "RPC server to make any non-nested (e.g. oneway or on another thread) "
-                            "calls.");
-
-        LOG_RPC_DETAIL("No available connection (have %zu clients and %zu servers). Waiting...",
-                       mConnection->mClients.size(), mConnection->mServers.size());
-        mConnection->mSocketCv.wait(_l);
-    }
-    mConnection->mWaitingThreads--;
-}
-
-void RpcConnection::ExclusiveSocket::findSocket(pid_t tid, sp<ConnectionSocket>* exclusive,
-                                                sp<ConnectionSocket>* available,
-                                                std::vector<sp<ConnectionSocket>>& sockets,
-                                                size_t socketsIndexHint) {
-    LOG_ALWAYS_FATAL_IF(sockets.size() > 0 && socketsIndexHint >= sockets.size(),
-                        "Bad index %zu >= %zu", socketsIndexHint, sockets.size());
-
-    if (*exclusive != nullptr) return; // consistent with break below
-
-    for (size_t i = 0; i < sockets.size(); i++) {
-        sp<ConnectionSocket>& socket = sockets[(i + socketsIndexHint) % sockets.size()];
-
-        // take first available connection (intuition = caching)
-        if (available && *available == nullptr && socket->exclusiveTid == std::nullopt) {
-            *available = socket;
-            continue;
-        }
-
-        // though, prefer to take connection which is already inuse by this thread
-        // (nested transactions)
-        if (exclusive && socket->exclusiveTid == tid) {
-            *exclusive = socket;
-            break; // consistent with return above
-        }
-    }
-}
-
-RpcConnection::ExclusiveSocket::~ExclusiveSocket() {
-    // reentrant use of a connection means something less deep in the call stack
-    // is using this fd, and it retains the right to it. So, we don't give up
-    // exclusive ownership, and no thread is freed.
-    if (!mReentrant) {
-        std::unique_lock<std::mutex> _l(mConnection->mSocketMutex);
-        mSocket->exclusiveTid = std::nullopt;
-        if (mConnection->mWaitingThreads > 0) {
-            _l.unlock();
-            mConnection->mSocketCv.notify_one();
-        }
-    }
-}
-
-} // namespace android
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index df07916..9cc6e7f 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -19,46 +19,279 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 
+#include <thread>
 #include <vector>
 
+#include <android-base/scopeguard.h>
 #include <binder/Parcel.h>
 #include <binder/RpcServer.h>
 #include <log/log.h>
 #include "RpcState.h"
 
+#include "RpcSocketAddress.h"
 #include "RpcWireFormat.h"
 
 namespace android {
 
+using base::ScopeGuard;
 using base::unique_fd;
 
 RpcServer::RpcServer() {}
 RpcServer::~RpcServer() {}
 
 sp<RpcServer> RpcServer::make() {
-    return new RpcServer;
+    return sp<RpcServer>::make();
 }
 
 void RpcServer::iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction() {
     mAgreedExperimental = true;
 }
 
-sp<RpcConnection> RpcServer::addClientConnection() {
-    LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+bool RpcServer::setupUnixDomainServer(const char* path) {
+    return setupSocketServer(UnixSocketAddress(path));
+}
 
-    auto connection = RpcConnection::make();
-    connection->setForServer(this);
-    mConnections.push_back(connection);
-    return connection;
+bool RpcServer::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 RpcServer::setupInetServer(unsigned int port, unsigned int* assignedPort) {
+    const char* kAddr = "127.0.0.1";
+
+    if (assignedPort != nullptr) *assignedPort = 0;
+    auto aiStart = InetSocketAddress::getAddrInfo(kAddr, port);
+    if (aiStart == nullptr) return false;
+    for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
+        InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, kAddr, port);
+        if (!setupSocketServer(socketAddress)) {
+            continue;
+        }
+
+        LOG_ALWAYS_FATAL_IF(socketAddress.addr()->sa_family != AF_INET, "expecting inet");
+        sockaddr_in addr{};
+        socklen_t len = sizeof(addr);
+        if (0 != getsockname(mServer.get(), reinterpret_cast<sockaddr*>(&addr), &len)) {
+            int savedErrno = errno;
+            ALOGE("Could not getsockname at %s: %s", socketAddress.toString().c_str(),
+                  strerror(savedErrno));
+            return false;
+        }
+        LOG_ALWAYS_FATAL_IF(len != sizeof(addr), "Wrong socket type: len %zu vs len %zu",
+                            static_cast<size_t>(len), sizeof(addr));
+        unsigned int realPort = ntohs(addr.sin_port);
+        LOG_ALWAYS_FATAL_IF(port != 0 && realPort != port,
+                            "Requesting inet server on %s but it is set up on %u.",
+                            socketAddress.toString().c_str(), realPort);
+
+        if (assignedPort != nullptr) {
+            *assignedPort = realPort;
+        }
+
+        return true;
+    }
+    ALOGE("None of the socket address resolved for %s:%u can be set up as inet server.", kAddr,
+          port);
+    return false;
+}
+
+void RpcServer::setMaxThreads(size_t threads) {
+    LOG_ALWAYS_FATAL_IF(threads <= 0, "RpcServer is useless without threads");
+    LOG_ALWAYS_FATAL_IF(mStarted, "must be called before started");
+    mMaxThreads = threads;
+}
+
+size_t RpcServer::getMaxThreads() {
+    return mMaxThreads;
 }
 
 void RpcServer::setRootObject(const sp<IBinder>& binder) {
-    LOG_ALWAYS_FATAL_IF(mRootObject != nullptr, "There can only be one root object");
-    mRootObject = binder;
+    std::lock_guard<std::mutex> _l(mLock);
+    mRootObjectWeak = mRootObject = binder;
+}
+
+void RpcServer::setRootObjectWeak(const wp<IBinder>& binder) {
+    std::lock_guard<std::mutex> _l(mLock);
+    mRootObject.clear();
+    mRootObjectWeak = binder;
 }
 
 sp<IBinder> RpcServer::getRootObject() {
-    return mRootObject;
+    std::lock_guard<std::mutex> _l(mLock);
+    bool hasWeak = mRootObjectWeak.unsafe_get();
+    sp<IBinder> ret = mRootObjectWeak.promote();
+    ALOGW_IF(hasWeak && ret == nullptr, "RpcServer root object is freed, returning nullptr");
+    return ret;
+}
+
+void RpcServer::join() {
+    while (true) {
+        (void)acceptOne();
+    }
+}
+
+bool RpcServer::acceptOne() {
+    LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+    LOG_ALWAYS_FATAL_IF(!hasServer(), "RpcServer must be setup to join.");
+
+    unique_fd clientFd(
+            TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, nullptr /*length*/, SOCK_CLOEXEC)));
+
+    if (clientFd < 0) {
+        ALOGE("Could not accept4 socket: %s", strerror(errno));
+        return false;
+    }
+    LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
+
+    {
+        std::lock_guard<std::mutex> _l(mLock);
+        std::thread thread =
+                std::thread(&RpcServer::establishConnection, this,
+                            std::move(sp<RpcServer>::fromExisting(this)), std::move(clientFd));
+        mConnectingThreads[thread.get_id()] = std::move(thread);
+    }
+
+    return true;
+}
+
+std::vector<sp<RpcSession>> RpcServer::listSessions() {
+    std::lock_guard<std::mutex> _l(mLock);
+    std::vector<sp<RpcSession>> sessions;
+    for (auto& [id, session] : mSessions) {
+        (void)id;
+        sessions.push_back(session);
+    }
+    return sessions;
+}
+
+size_t RpcServer::numUninitializedSessions() {
+    std::lock_guard<std::mutex> _l(mLock);
+    return mConnectingThreads.size();
+}
+
+void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd) {
+    LOG_ALWAYS_FATAL_IF(this != server.get(), "Must pass same ownership object");
+
+    // TODO(b/183988761): cannot trust this simple ID
+    LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+    bool idValid = true;
+    int32_t id;
+    if (sizeof(id) != read(clientFd.get(), &id, sizeof(id))) {
+        ALOGE("Could not read ID from fd %d", clientFd.get());
+        idValid = false;
+    }
+
+    std::thread thisThread;
+    sp<RpcSession> session;
+    {
+        std::lock_guard<std::mutex> _l(mLock);
+
+        auto threadId = mConnectingThreads.find(std::this_thread::get_id());
+        LOG_ALWAYS_FATAL_IF(threadId == mConnectingThreads.end(),
+                            "Must establish connection on owned thread");
+        thisThread = std::move(threadId->second);
+        ScopeGuard detachGuard = [&]() { thisThread.detach(); };
+        mConnectingThreads.erase(threadId);
+
+        if (!idValid) {
+            return;
+        }
+
+        if (id == RPC_SESSION_ID_NEW) {
+            LOG_ALWAYS_FATAL_IF(mSessionIdCounter >= INT32_MAX, "Out of session IDs");
+            mSessionIdCounter++;
+
+            session = RpcSession::make();
+            session->setForServer(wp<RpcServer>::fromExisting(this), mSessionIdCounter);
+
+            mSessions[mSessionIdCounter] = session;
+        } else {
+            auto it = mSessions.find(id);
+            if (it == mSessions.end()) {
+                ALOGE("Cannot add thread, no record of session with ID %d", id);
+                return;
+            }
+            session = it->second;
+        }
+
+        detachGuard.Disable();
+        session->preJoin(std::move(thisThread));
+    }
+
+    // avoid strong cycle
+    server = nullptr;
+    //
+    //
+    // DO NOT ACCESS MEMBER VARIABLES BELOW
+    //
+
+    session->join(std::move(clientFd));
+}
+
+bool RpcServer::setupSocketServer(const RpcSocketAddress& addr) {
+    LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str());
+    LOG_ALWAYS_FATAL_IF(hasServer(), "Each RpcServer 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;
+    }
+
+    LOG_RPC_DETAIL("Successfully setup socket server %s", addr.toString().c_str());
+
+    mServer = std::move(serverFd);
+    return true;
+}
+
+void RpcServer::onSessionTerminating(const sp<RpcSession>& session) {
+    auto id = session->mId;
+    LOG_ALWAYS_FATAL_IF(id == std::nullopt, "Server sessions must be initialized with ID");
+    LOG_RPC_DETAIL("Dropping session %d", *id);
+
+    std::lock_guard<std::mutex> _l(mLock);
+    auto it = mSessions.find(*id);
+    LOG_ALWAYS_FATAL_IF(it == mSessions.end(), "Bad state, unknown session id %d", *id);
+    LOG_ALWAYS_FATAL_IF(it->second != session, "Bad state, session has id mismatch %d", *id);
+    (void)mSessions.erase(it);
+}
+
+bool RpcServer::hasServer() {
+    LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+    std::lock_guard<std::mutex> _l(mLock);
+    return mServer.ok();
+}
+
+unique_fd RpcServer::releaseServer() {
+    LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+    std::lock_guard<std::mutex> _l(mLock);
+    return std::move(mServer);
+}
+
+bool RpcServer::setupExternalServer(base::unique_fd serverFd) {
+    LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+    std::lock_guard<std::mutex> _l(mLock);
+    if (mServer.ok()) {
+        ALOGE("Each RpcServer can only have one server.");
+        return false;
+    }
+    mServer = std::move(serverFd);
+    return true;
 }
 
 } // namespace android
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
new file mode 100644
index 0000000..05fa49e
--- /dev/null
+++ b/libs/binder/RpcSession.cpp
@@ -0,0 +1,407 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "RpcSession"
+
+#include <binder/RpcSession.h>
+
+#include <inttypes.h>
+#include <unistd.h>
+
+#include <string_view>
+
+#include <binder/Parcel.h>
+#include <binder/RpcServer.h>
+#include <binder/Stability.h>
+#include <utils/String8.h>
+
+#include "RpcSocketAddress.h"
+#include "RpcState.h"
+#include "RpcWireFormat.h"
+
+#ifdef __GLIBC__
+extern "C" pid_t gettid();
+#endif
+
+namespace android {
+
+using base::unique_fd;
+
+RpcSession::RpcSession() {
+    LOG_RPC_DETAIL("RpcSession created %p", this);
+
+    mState = std::make_unique<RpcState>();
+}
+RpcSession::~RpcSession() {
+    LOG_RPC_DETAIL("RpcSession destroyed %p", this);
+
+    std::lock_guard<std::mutex> _l(mMutex);
+    LOG_ALWAYS_FATAL_IF(mServerConnections.size() != 0,
+                        "Should not be able to destroy a session with servers in use.");
+}
+
+sp<RpcSession> RpcSession::make() {
+    return sp<RpcSession>::make();
+}
+
+bool RpcSession::setupUnixDomainClient(const char* path) {
+    return setupSocketClient(UnixSocketAddress(path));
+}
+
+bool RpcSession::setupVsockClient(unsigned int cid, unsigned int port) {
+    return setupSocketClient(VsockSocketAddress(cid, port));
+}
+
+bool RpcSession::setupInetClient(const char* addr, unsigned int port) {
+    auto aiStart = InetSocketAddress::getAddrInfo(addr, port);
+    if (aiStart == nullptr) return false;
+    for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
+        InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, addr, port);
+        if (setupSocketClient(socketAddress)) return true;
+    }
+    ALOGE("None of the socket address resolved for %s:%u can be added as inet client.", addr, port);
+    return false;
+}
+
+bool RpcSession::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;
+    }
+
+    addClientConnection(std::move(serverFd));
+    return true;
+}
+
+sp<IBinder> RpcSession::getRootObject() {
+    ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), ConnectionUse::CLIENT);
+    return state()->getRootObject(connection.fd(), sp<RpcSession>::fromExisting(this));
+}
+
+status_t RpcSession::getRemoteMaxThreads(size_t* maxThreads) {
+    ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), ConnectionUse::CLIENT);
+    return state()->getMaxThreads(connection.fd(), sp<RpcSession>::fromExisting(this), maxThreads);
+}
+
+status_t RpcSession::transact(const RpcAddress& address, uint32_t code, const Parcel& data,
+                              Parcel* reply, uint32_t flags) {
+    ExclusiveConnection connection(sp<RpcSession>::fromExisting(this),
+                                   (flags & IBinder::FLAG_ONEWAY) ? ConnectionUse::CLIENT_ASYNC
+                                                                  : ConnectionUse::CLIENT);
+    return state()->transact(connection.fd(), address, code, data,
+                             sp<RpcSession>::fromExisting(this), reply, flags);
+}
+
+status_t RpcSession::sendDecStrong(const RpcAddress& address) {
+    ExclusiveConnection connection(sp<RpcSession>::fromExisting(this),
+                                   ConnectionUse::CLIENT_REFCOUNT);
+    return state()->sendDecStrong(connection.fd(), address);
+}
+
+status_t RpcSession::readId() {
+    {
+        std::lock_guard<std::mutex> _l(mMutex);
+        LOG_ALWAYS_FATAL_IF(mForServer != nullptr, "Can only update ID for client.");
+    }
+
+    int32_t id;
+
+    ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), ConnectionUse::CLIENT);
+    status_t status =
+            state()->getSessionId(connection.fd(), sp<RpcSession>::fromExisting(this), &id);
+    if (status != OK) return status;
+
+    LOG_RPC_DETAIL("RpcSession %p has id %d", this, id);
+    mId = id;
+    return OK;
+}
+
+void RpcSession::preJoin(std::thread thread) {
+    LOG_ALWAYS_FATAL_IF(thread.get_id() != std::this_thread::get_id(), "Must own this thread");
+
+    {
+        std::lock_guard<std::mutex> _l(mMutex);
+        mThreads[thread.get_id()] = std::move(thread);
+    }
+}
+
+void RpcSession::join(unique_fd client) {
+    // 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<RpcConnection> connection = assignServerToThisThread(std::move(client));
+
+    while (true) {
+        status_t error =
+                state()->getAndExecuteCommand(connection->fd, sp<RpcSession>::fromExisting(this));
+
+        if (error != OK) {
+            ALOGI("Binder connection thread closing w/ status %s", statusToString(error).c_str());
+            break;
+        }
+    }
+
+    LOG_ALWAYS_FATAL_IF(!removeServerConnection(connection),
+                        "bad state: connection object guaranteed to be in list");
+
+    {
+        std::lock_guard<std::mutex> _l(mMutex);
+        auto it = mThreads.find(std::this_thread::get_id());
+        LOG_ALWAYS_FATAL_IF(it == mThreads.end());
+        it->second.detach();
+        mThreads.erase(it);
+    }
+}
+
+void RpcSession::terminateLocked() {
+    // TODO(b/185167543):
+    // - kindly notify other side of the connection of termination (can't be
+    // locked)
+    // - prevent new client/servers from being added
+    // - stop all threads which are currently reading/writing
+    // - terminate RpcState?
+
+    if (mTerminated) return;
+
+    sp<RpcServer> server = mForServer.promote();
+    if (server) {
+        server->onSessionTerminating(sp<RpcSession>::fromExisting(this));
+    }
+}
+
+wp<RpcServer> RpcSession::server() {
+    return mForServer;
+}
+
+bool RpcSession::setupSocketClient(const RpcSocketAddress& addr) {
+    {
+        std::lock_guard<std::mutex> _l(mMutex);
+        LOG_ALWAYS_FATAL_IF(mClientConnections.size() != 0,
+                            "Must only setup session once, but already has %zu clients",
+                            mClientConnections.size());
+    }
+
+    if (!setupOneSocketClient(addr, RPC_SESSION_ID_NEW)) return false;
+
+    // TODO(b/185167543): we should add additional sessions dynamically
+    // instead of all at once.
+    // TODO(b/186470974): first risk of blocking
+    size_t numThreadsAvailable;
+    if (status_t status = getRemoteMaxThreads(&numThreadsAvailable); status != OK) {
+        ALOGE("Could not get max threads after initial session to %s: %s", addr.toString().c_str(),
+              statusToString(status).c_str());
+        return false;
+    }
+
+    if (status_t status = readId(); status != OK) {
+        ALOGE("Could not get session id after initial session to %s; %s", addr.toString().c_str(),
+              statusToString(status).c_str());
+        return false;
+    }
+
+    // we've already setup one client
+    for (size_t i = 0; i + 1 < numThreadsAvailable; i++) {
+        // TODO(b/185167543): shutdown existing connections?
+        if (!setupOneSocketClient(addr, mId.value())) return false;
+    }
+
+    return true;
+}
+
+bool RpcSession::setupOneSocketClient(const RpcSocketAddress& addr, int32_t id) {
+    for (size_t tries = 0; tries < 5; tries++) {
+        if (tries > 0) usleep(10000);
+
+        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()))) {
+            if (errno == ECONNRESET) {
+                ALOGW("Connection reset on %s", addr.toString().c_str());
+                continue;
+            }
+            int savedErrno = errno;
+            ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(),
+                  strerror(savedErrno));
+            return false;
+        }
+
+        if (sizeof(id) != TEMP_FAILURE_RETRY(write(serverFd.get(), &id, sizeof(id)))) {
+            int savedErrno = errno;
+            ALOGE("Could not write id to 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());
+
+        addClientConnection(std::move(serverFd));
+        return true;
+    }
+
+    ALOGE("Ran out of retries to connect to %s", addr.toString().c_str());
+    return false;
+}
+
+void RpcSession::addClientConnection(unique_fd fd) {
+    std::lock_guard<std::mutex> _l(mMutex);
+    sp<RpcConnection> session = sp<RpcConnection>::make();
+    session->fd = std::move(fd);
+    mClientConnections.push_back(session);
+}
+
+void RpcSession::setForServer(const wp<RpcServer>& server, int32_t sessionId) {
+    mId = sessionId;
+    mForServer = server;
+}
+
+sp<RpcSession::RpcConnection> RpcSession::assignServerToThisThread(unique_fd fd) {
+    std::lock_guard<std::mutex> _l(mMutex);
+    sp<RpcConnection> session = sp<RpcConnection>::make();
+    session->fd = std::move(fd);
+    session->exclusiveTid = gettid();
+    mServerConnections.push_back(session);
+
+    return session;
+}
+
+bool RpcSession::removeServerConnection(const sp<RpcConnection>& connection) {
+    std::lock_guard<std::mutex> _l(mMutex);
+    if (auto it = std::find(mServerConnections.begin(), mServerConnections.end(), connection);
+        it != mServerConnections.end()) {
+        mServerConnections.erase(it);
+        if (mServerConnections.size() == 0) {
+            terminateLocked();
+        }
+        return true;
+    }
+    return false;
+}
+
+RpcSession::ExclusiveConnection::ExclusiveConnection(const sp<RpcSession>& session,
+                                                     ConnectionUse use)
+      : mSession(session) {
+    pid_t tid = gettid();
+    std::unique_lock<std::mutex> _l(mSession->mMutex);
+
+    mSession->mWaitingThreads++;
+    while (true) {
+        sp<RpcConnection> exclusive;
+        sp<RpcConnection> available;
+
+        // CHECK FOR DEDICATED CLIENT SOCKET
+        //
+        // A server/looper should always use a dedicated session if available
+        findConnection(tid, &exclusive, &available, mSession->mClientConnections,
+                       mSession->mClientConnectionsOffset);
+
+        // WARNING: this assumes a server cannot request its client to send
+        // a transaction, as mServerConnections 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 connection. Then, if
+        // we naively send a synchronous command to that same connection, 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 == ConnectionUse::CLIENT_ASYNC && (exclusive != nullptr || available != nullptr)) {
+            mSession->mClientConnectionsOffset =
+                    (mSession->mClientConnectionsOffset + 1) % mSession->mClientConnections.size();
+        }
+
+        // USE SERVING SOCKET (for nested transaction)
+        //
+        // asynchronous calls cannot be nested
+        if (use != ConnectionUse::CLIENT_ASYNC) {
+            // server connections are always assigned to a thread
+            findConnection(tid, &exclusive, nullptr /*available*/, mSession->mServerConnections,
+                           0 /* index hint */);
+        }
+
+        // if our thread is already using a session, prioritize using that
+        if (exclusive != nullptr) {
+            mConnection = exclusive;
+            mReentrant = true;
+            break;
+        } else if (available != nullptr) {
+            mConnection = available;
+            mConnection->exclusiveTid = tid;
+            break;
+        }
+
+        // in regular binder, this would usually be a deadlock :)
+        LOG_ALWAYS_FATAL_IF(mSession->mClientConnections.size() == 0,
+                            "Not a client of any session. You must create a session to an "
+                            "RPC server to make any non-nested (e.g. oneway or on another thread) "
+                            "calls.");
+
+        LOG_RPC_DETAIL("No available session (have %zu clients and %zu servers). Waiting...",
+                       mSession->mClientConnections.size(), mSession->mServerConnections.size());
+        mSession->mAvailableConnectionCv.wait(_l);
+    }
+    mSession->mWaitingThreads--;
+}
+
+void RpcSession::ExclusiveConnection::findConnection(pid_t tid, sp<RpcConnection>* exclusive,
+                                                     sp<RpcConnection>* available,
+                                                     std::vector<sp<RpcConnection>>& sockets,
+                                                     size_t socketsIndexHint) {
+    LOG_ALWAYS_FATAL_IF(sockets.size() > 0 && socketsIndexHint >= sockets.size(),
+                        "Bad index %zu >= %zu", socketsIndexHint, sockets.size());
+
+    if (*exclusive != nullptr) return; // consistent with break below
+
+    for (size_t i = 0; i < sockets.size(); i++) {
+        sp<RpcConnection>& socket = sockets[(i + socketsIndexHint) % sockets.size()];
+
+        // take first available session (intuition = caching)
+        if (available && *available == nullptr && socket->exclusiveTid == std::nullopt) {
+            *available = socket;
+            continue;
+        }
+
+        // though, prefer to take session which is already inuse by this thread
+        // (nested transactions)
+        if (exclusive && socket->exclusiveTid == tid) {
+            *exclusive = socket;
+            break; // consistent with return above
+        }
+    }
+}
+
+RpcSession::ExclusiveConnection::~ExclusiveConnection() {
+    // reentrant use of a session means something less deep in the call stack
+    // is using this fd, and it retains the right to it. So, we don't give up
+    // exclusive ownership, and no thread is freed.
+    if (!mReentrant) {
+        std::unique_lock<std::mutex> _l(mSession->mMutex);
+        mConnection->exclusiveTid = std::nullopt;
+        if (mSession->mWaitingThreads > 0) {
+            _l.unlock();
+            mSession->mAvailableConnectionCv.notify_one();
+        }
+    }
+}
+
+} // namespace android
diff --git a/libs/binder/RpcSocketAddress.h b/libs/binder/RpcSocketAddress.h
new file mode 100644
index 0000000..c7ba5d9
--- /dev/null
+++ b/libs/binder/RpcSocketAddress.h
@@ -0,0 +1,116 @@
+/*
+ * 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 <string>
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include "vm_sockets.h"
+
+namespace android {
+
+class RpcSocketAddress {
+public:
+    virtual ~RpcSocketAddress() {}
+    virtual std::string toString() const = 0;
+    virtual const sockaddr* addr() const = 0;
+    virtual size_t addrSize() const = 0;
+};
+
+class UnixSocketAddress : public RpcSocketAddress {
+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;
+};
+
+class VsockSocketAddress : public RpcSocketAddress {
+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;
+};
+
+class InetSocketAddress : public RpcSocketAddress {
+public:
+    InetSocketAddress(const sockaddr* sockAddr, size_t size, const char* addr, unsigned int port)
+          : mSockAddr(sockAddr), mSize(size), mAddr(addr), mPort(port) {}
+    [[nodiscard]] std::string toString() const override {
+        return String8::format("%s:%u", mAddr, mPort).c_str();
+    }
+    [[nodiscard]] const sockaddr* addr() const override { return mSockAddr; }
+    [[nodiscard]] size_t addrSize() const override { return mSize; }
+
+    using AddrInfo = std::unique_ptr<addrinfo, decltype(&freeaddrinfo)>;
+    static 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);
+    }
+
+private:
+    const sockaddr* mSockAddr;
+    size_t mSize;
+    const char* mAddr;
+    unsigned int mPort;
+};
+
+} // namespace android
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 755ff35..2ba9fa2 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -31,16 +31,16 @@
 RpcState::RpcState() {}
 RpcState::~RpcState() {}
 
-status_t RpcState::onBinderLeaving(const sp<RpcConnection>& connection, const sp<IBinder>& binder,
+status_t RpcState::onBinderLeaving(const sp<RpcSession>& session, const sp<IBinder>& binder,
                                    RpcAddress* outAddress) {
     bool isRemote = binder->remoteBinder();
     bool isRpc = isRemote && binder->remoteBinder()->isRpcBinder();
 
-    if (isRpc && binder->remoteBinder()->getPrivateAccessorForId().rpcConnection() != connection) {
+    if (isRpc && binder->remoteBinder()->getPrivateAccessorForId().rpcSession() != session) {
         // 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 session.");
         return INVALID_OPERATION;
     }
 
@@ -91,8 +91,7 @@
     return OK;
 }
 
-sp<IBinder> RpcState::onBinderEntering(const sp<RpcConnection>& connection,
-                                       const RpcAddress& address) {
+sp<IBinder> RpcState::onBinderEntering(const sp<RpcSession>& session, const RpcAddress& address) {
     std::unique_lock<std::mutex> _l(mNodeMutex);
 
     if (auto it = mNodeForAddress.find(address); it != mNodeForAddress.end()) {
@@ -106,7 +105,7 @@
         // We have timesRecd RPC refcounts, but we only need to hold on to one
         // when we keep the object. All additional dec strongs are sent
         // immediately, we wait to send the last one in BpBinder::onLastDecStrong.
-        (void)connection->sendDecStrong(address);
+        (void)session->sendDecStrong(address);
 
         return binder;
     }
@@ -114,9 +113,9 @@
     auto&& [it, inserted] = mNodeForAddress.insert({address, BinderNode{}});
     LOG_ALWAYS_FATAL_IF(!inserted, "Failed to insert binder when creating proxy");
 
-    // Currently, all binders are assumed to be part of the same connection (no
+    // Currently, all binders are assumed to be part of the same session (no
     // device global binders in the RPC world).
-    sp<IBinder> binder = BpBinder::create(connection, it->first);
+    sp<IBinder> binder = BpBinder::create(session, it->first);
     it->second.binder = binder;
     it->second.timesRecd = 1;
     return binder;
@@ -183,6 +182,27 @@
     }
 }
 
+RpcState::CommandData::CommandData(size_t size) : mSize(size) {
+    // The maximum size for regular binder is 1MB for all concurrent
+    // transactions. A very small proportion of transactions are even
+    // larger than a page, but we need to avoid allocating too much
+    // data on behalf of an arbitrary client, or we could risk being in
+    // a position where a single additional allocation could run out of
+    // memory.
+    //
+    // Note, this limit may not reflect the total amount of data allocated for a
+    // transaction (in some cases, additional fixed size amounts are added),
+    // though for rough consistency, we should avoid cases where this data type
+    // is used for multiple dynamic allocations for a single transaction.
+    constexpr size_t kMaxTransactionAllocation = 100 * 1000;
+    if (size == 0) return;
+    if (size > kMaxTransactionAllocation) {
+        ALOGW("Transaction requested too much data allocation %zu", size);
+        return;
+    }
+    mData.reset(new (std::nothrow) uint8_t[size]);
+}
+
 bool RpcState::rpcSend(const base::unique_fd& fd, const char* what, const void* data, size_t size) {
     LOG_RPC_DETAIL("Sending %s on fd %d: %s", what, fd.get(), hexString(data, size).c_str());
 
@@ -232,14 +252,13 @@
     return true;
 }
 
-sp<IBinder> RpcState::getRootObject(const base::unique_fd& fd,
-                                    const sp<RpcConnection>& connection) {
+sp<IBinder> RpcState::getRootObject(const base::unique_fd& fd, const sp<RpcSession>& session) {
     Parcel data;
-    data.markForRpc(connection);
+    data.markForRpc(session);
     Parcel reply;
 
-    status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT, data,
-                               connection, &reply, 0);
+    status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT, data, session,
+                               &reply, 0);
     if (status != OK) {
         ALOGE("Error getting root object: %s", statusToString(status).c_str());
         return nullptr;
@@ -248,8 +267,54 @@
     return reply.readStrongBinder();
 }
 
+status_t RpcState::getMaxThreads(const base::unique_fd& fd, const sp<RpcSession>& session,
+                                 size_t* maxThreadsOut) {
+    Parcel data;
+    data.markForRpc(session);
+    Parcel reply;
+
+    status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_MAX_THREADS, data,
+                               session, &reply, 0);
+    if (status != OK) {
+        ALOGE("Error getting max threads: %s", statusToString(status).c_str());
+        return status;
+    }
+
+    int32_t maxThreads;
+    status = reply.readInt32(&maxThreads);
+    if (status != OK) return status;
+    if (maxThreads <= 0) {
+        ALOGE("Error invalid max maxThreads: %d", maxThreads);
+        return BAD_VALUE;
+    }
+
+    *maxThreadsOut = maxThreads;
+    return OK;
+}
+
+status_t RpcState::getSessionId(const base::unique_fd& fd, const sp<RpcSession>& session,
+                                int32_t* sessionIdOut) {
+    Parcel data;
+    data.markForRpc(session);
+    Parcel reply;
+
+    status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_SESSION_ID, data,
+                               session, &reply, 0);
+    if (status != OK) {
+        ALOGE("Error getting session ID: %s", statusToString(status).c_str());
+        return status;
+    }
+
+    int32_t sessionId;
+    status = reply.readInt32(&sessionId);
+    if (status != OK) return status;
+
+    *sessionIdOut = sessionId;
+    return OK;
+}
+
 status_t RpcState::transact(const base::unique_fd& fd, const RpcAddress& address, uint32_t code,
-                            const Parcel& data, const sp<RpcConnection>& connection, Parcel* reply,
+                            const Parcel& data, const sp<RpcSession>& session, Parcel* reply,
                             uint32_t flags) {
     uint64_t asyncNumber = 0;
 
@@ -282,7 +347,11 @@
             .asyncNumber = asyncNumber,
     };
 
-    std::vector<uint8_t> transactionData(sizeof(RpcWireTransaction) + data.dataSize());
+    CommandData transactionData(sizeof(RpcWireTransaction) + data.dataSize());
+    if (!transactionData.valid()) {
+        return NO_MEMORY;
+    }
+
     memcpy(transactionData.data() + 0, &transaction, sizeof(RpcWireTransaction));
     memcpy(transactionData.data() + sizeof(RpcWireTransaction), data.data(), data.dataSize());
 
@@ -309,7 +378,7 @@
 
     LOG_ALWAYS_FATAL_IF(reply == nullptr, "Reply parcel must be used for synchronous transaction.");
 
-    return waitForReply(fd, connection, reply);
+    return waitForReply(fd, session, reply);
 }
 
 static void cleanup_reply_data(Parcel* p, const uint8_t* data, size_t dataSize,
@@ -321,7 +390,7 @@
     LOG_ALWAYS_FATAL_IF(objectsCount, 0);
 }
 
-status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcConnection>& connection,
+status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcSession>& session,
                                 Parcel* reply) {
     RpcWireHeader command;
     while (true) {
@@ -331,13 +400,16 @@
 
         if (command.command == RPC_COMMAND_REPLY) break;
 
-        status_t status = processServerCommand(fd, connection, command);
+        status_t status = processServerCommand(fd, session, command);
         if (status != OK) return status;
     }
 
-    uint8_t* data = new uint8_t[command.bodySize];
+    CommandData data(command.bodySize);
+    if (!data.valid()) {
+        return NO_MEMORY;
+    }
 
-    if (!rpcRec(fd, "reply body", data, command.bodySize)) {
+    if (!rpcRec(fd, "reply body", data.data(), command.bodySize)) {
         return DEAD_OBJECT;
     }
 
@@ -347,13 +419,14 @@
         terminate();
         return BAD_VALUE;
     }
-    RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data);
+    RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data.data());
     if (rpcReply->status != OK) return rpcReply->status;
 
+    data.release();
     reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data),
                                nullptr, 0, cleanup_reply_data);
 
-    reply->markForRpc(connection);
+    reply->markForRpc(session);
 
     return OK;
 }
@@ -384,8 +457,7 @@
     return OK;
 }
 
-status_t RpcState::getAndExecuteCommand(const base::unique_fd& fd,
-                                        const sp<RpcConnection>& connection) {
+status_t RpcState::getAndExecuteCommand(const base::unique_fd& fd, const sp<RpcSession>& session) {
     LOG_RPC_DETAIL("getAndExecuteCommand on fd %d", fd.get());
 
     RpcWireHeader command;
@@ -393,15 +465,14 @@
         return DEAD_OBJECT;
     }
 
-    return processServerCommand(fd, connection, command);
+    return processServerCommand(fd, session, command);
 }
 
-status_t RpcState::processServerCommand(const base::unique_fd& fd,
-                                        const sp<RpcConnection>& connection,
+status_t RpcState::processServerCommand(const base::unique_fd& fd, const sp<RpcSession>& session,
                                         const RpcWireHeader& command) {
     switch (command.command) {
         case RPC_COMMAND_TRANSACT:
-            return processTransact(fd, connection, command);
+            return processTransact(fd, session, command);
         case RPC_COMMAND_DEC_STRONG:
             return processDecStrong(fd, command);
     }
@@ -410,21 +481,24 @@
     // RPC-binder-level wire protocol is not self synchronizing, we have no way
     // to understand where the current command ends and the next one begins. We
     // also can't consider it a fatal error because this would allow any client
-    // to kill us, so ending the connection for misbehaving client.
-    ALOGE("Unknown RPC command %d - terminating connection", command.command);
+    // to kill us, so ending the session for misbehaving client.
+    ALOGE("Unknown RPC command %d - terminating session", command.command);
     terminate();
     return DEAD_OBJECT;
 }
-status_t RpcState::processTransact(const base::unique_fd& fd, const sp<RpcConnection>& connection,
+status_t RpcState::processTransact(const base::unique_fd& fd, const sp<RpcSession>& session,
                                    const RpcWireHeader& command) {
     LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command);
 
-    std::vector<uint8_t> transactionData(command.bodySize);
+    CommandData transactionData(command.bodySize);
+    if (!transactionData.valid()) {
+        return NO_MEMORY;
+    }
     if (!rpcRec(fd, "transaction body", transactionData.data(), transactionData.size())) {
         return DEAD_OBJECT;
     }
 
-    return processTransactInternal(fd, connection, std::move(transactionData));
+    return processTransactInternal(fd, session, std::move(transactionData));
 }
 
 static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t dataSize,
@@ -436,9 +510,8 @@
     (void)objectsCount;
 }
 
-status_t RpcState::processTransactInternal(const base::unique_fd& fd,
-                                           const sp<RpcConnection>& connection,
-                                           std::vector<uint8_t>&& transactionData) {
+status_t RpcState::processTransactInternal(const base::unique_fd& fd, const sp<RpcSession>& session,
+                                           CommandData transactionData) {
     if (transactionData.size() < sizeof(RpcWireTransaction)) {
         ALOGE("Expecting %zu but got %zu bytes for RpcWireTransaction. Terminating!",
               sizeof(RpcWireTransaction), transactionData.size());
@@ -459,7 +532,6 @@
         auto it = mNodeForAddress.find(addr);
         if (it == mNodeForAddress.end()) {
             ALOGE("Unknown binder address %s.", addr.toString().c_str());
-            dump();
             replyStatus = BAD_VALUE;
         } else {
             target = it->second.binder.promote();
@@ -469,7 +541,7 @@
                 // However, for local binders, it indicates a misbehaving client
                 // (any binder which is being transacted on should be holding a
                 // strong ref count), so in either case, terminating the
-                // connection.
+                // session.
                 ALOGE("While transacting, binder has been deleted at address %s. Terminating!",
                       addr.toString().c_str());
                 terminate();
@@ -498,40 +570,53 @@
         }
     }
 
-    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);
-
     Parcel reply;
-    reply.markForRpc(connection);
+    reply.markForRpc(session);
 
     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(session);
+
         if (target) {
             replyStatus = target->transact(transaction->code, data, &reply, transaction->flags);
         } else {
             LOG_RPC_DETAIL("Got special transaction %u", transaction->code);
-            // special case for 'zero' address (special server commands)
-            switch (transaction->code) {
-                case RPC_SPECIAL_TRANSACT_GET_ROOT: {
-                    sp<IBinder> root;
-                    sp<RpcServer> server = connection->server().promote();
-                    if (server) {
-                        root = server->getRootObject();
-                    } else {
-                        ALOGE("Root object requested, but no server attached.");
-                    }
 
-                    replyStatus = reply.writeStrongBinder(root);
-                    break;
+            sp<RpcServer> server = session->server().promote();
+            if (server) {
+                // special case for 'zero' address (special server commands)
+                switch (transaction->code) {
+                    case RPC_SPECIAL_TRANSACT_GET_ROOT: {
+                        replyStatus = reply.writeStrongBinder(server->getRootObject());
+                        break;
+                    }
+                    case RPC_SPECIAL_TRANSACT_GET_MAX_THREADS: {
+                        replyStatus = reply.writeInt32(server->getMaxThreads());
+                        break;
+                    }
+                    case RPC_SPECIAL_TRANSACT_GET_SESSION_ID: {
+                        // only sessions w/ services can be the source of a
+                        // session ID (so still guarded by non-null server)
+                        //
+                        // sessions associated with servers must have an ID
+                        // (hence abort)
+                        int32_t id = session->getPrivateAccessorForId().get().value();
+                        replyStatus = reply.writeInt32(id);
+                        break;
+                    }
+                    default: {
+                        replyStatus = UNKNOWN_TRANSACTION;
+                    }
                 }
-                default: {
-                    replyStatus = UNKNOWN_TRANSACTION;
-                }
+            } else {
+                ALOGE("Special command sent, but no server object attached.");
             }
         }
     }
@@ -576,11 +661,11 @@
                 // justification for const_cast (consider avoiding priority_queue):
                 // - AsyncTodo operator< doesn't depend on 'data' object
                 // - gotta go fast
-                std::vector<uint8_t> data = std::move(
+                CommandData data = std::move(
                         const_cast<BinderNode::AsyncTodo&>(it->second.asyncTodo.top()).data);
                 it->second.asyncTodo.pop();
                 _l.unlock();
-                return processTransactInternal(fd, connection, std::move(data));
+                return processTransactInternal(fd, session, std::move(data));
             }
         }
         return OK;
@@ -590,7 +675,10 @@
             .status = replyStatus,
     };
 
-    std::vector<uint8_t> replyData(sizeof(RpcWireReply) + reply.dataSize());
+    CommandData replyData(sizeof(RpcWireReply) + reply.dataSize());
+    if (!replyData.valid()) {
+        return NO_MEMORY;
+    }
     memcpy(replyData.data() + 0, &rpcReply, sizeof(RpcWireReply));
     memcpy(replyData.data() + sizeof(RpcWireReply), reply.data(), reply.dataSize());
 
@@ -617,7 +705,10 @@
 status_t RpcState::processDecStrong(const base::unique_fd& fd, const RpcWireHeader& command) {
     LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_DEC_STRONG, "command: %d", command.command);
 
-    std::vector<uint8_t> commandData(command.bodySize);
+    CommandData commandData(command.bodySize);
+    if (!commandData.valid()) {
+        return NO_MEMORY;
+    }
     if (!rpcRec(fd, "dec ref body", commandData.data(), commandData.size())) {
         return DEAD_OBJECT;
     }
@@ -636,7 +727,6 @@
     auto it = mNodeForAddress.find(addr);
     if (it == mNodeForAddress.end()) {
         ALOGE("Unknown binder address %s for dec strong.", addr.toString().c_str());
-        dump();
         return OK;
     }
 
@@ -669,7 +759,7 @@
     }
 
     _l.unlock();
-    tempHold = nullptr; // destructor may make binder calls on this connection
+    tempHold = nullptr; // destructor may make binder calls on this session
 
     return OK;
 }
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index f4f5151..31f8a22 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -18,9 +18,10 @@
 #include <android-base/unique_fd.h>
 #include <binder/IBinder.h>
 #include <binder/Parcel.h>
-#include <binder/RpcConnection.h>
+#include <binder/RpcSession.h>
 
 #include <map>
+#include <optional>
 #include <queue>
 
 namespace android {
@@ -43,74 +44,91 @@
 
 /**
  * Abstracts away management of ref counts and the wire format from
- * RpcConnection
+ * RpcSession
  */
 class RpcState {
 public:
     RpcState();
     ~RpcState();
 
-    sp<IBinder> getRootObject(const base::unique_fd& fd, const sp<RpcConnection>& connection);
+    // TODO(b/182940634): combine some special transactions into one "getServerInfo" call?
+    sp<IBinder> getRootObject(const base::unique_fd& fd, const sp<RpcSession>& session);
+    status_t getMaxThreads(const base::unique_fd& fd, const sp<RpcSession>& session,
+                           size_t* maxThreadsOut);
+    status_t getSessionId(const base::unique_fd& fd, const sp<RpcSession>& session,
+                          int32_t* sessionIdOut);
 
     [[nodiscard]] status_t transact(const base::unique_fd& fd, const RpcAddress& address,
                                     uint32_t code, const Parcel& data,
-                                    const sp<RpcConnection>& connection, Parcel* reply,
-                                    uint32_t flags);
+                                    const sp<RpcSession>& session, Parcel* reply, uint32_t flags);
     [[nodiscard]] status_t sendDecStrong(const base::unique_fd& fd, const RpcAddress& address);
     [[nodiscard]] status_t getAndExecuteCommand(const base::unique_fd& fd,
-                                                const sp<RpcConnection>& connection);
+                                                const sp<RpcSession>& session);
 
     /**
      * Called by Parcel for outgoing binders. This implies one refcount of
      * ownership to the outgoing binder.
      */
-    [[nodiscard]] status_t onBinderLeaving(const sp<RpcConnection>& connection,
-                                           const sp<IBinder>& binder, RpcAddress* outAddress);
+    [[nodiscard]] status_t onBinderLeaving(const sp<RpcSession>& session, const sp<IBinder>& binder,
+                                           RpcAddress* outAddress);
 
     /**
      * Called by Parcel for incoming binders. This either returns the refcount
      * to the process, if this process already has one, or it takes ownership of
      * that refcount
      */
-    sp<IBinder> onBinderEntering(const sp<RpcConnection>& connection, const RpcAddress& address);
+    sp<IBinder> onBinderEntering(const sp<RpcSession>& session, const RpcAddress& address);
 
     size_t countBinders();
     void dump();
 
 private:
     /**
-     * Called when reading or writing data to a connection fails to clean up
-     * data associated with the connection in order to cleanup binders.
+     * Called when reading or writing data to a session fails to clean up
+     * data associated with the session in order to cleanup binders.
      * Specifically, we have a strong dependency cycle, since BpBinder is
      * OBJECT_LIFETIME_WEAK (so that onAttemptIncStrong may return true).
      *
-     *     BpBinder -> RpcConnection -> RpcState
+     *     BpBinder -> RpcSession -> RpcState
      *      ^-----------------------------/
      *
      * In the success case, eventually all refcounts should be propagated over
-     * the connection, though this could also be called to eagerly cleanup
-     * the connection.
+     * the session, though this could also be called to eagerly cleanup
+     * the session.
      *
-     * WARNING: RpcState is responsible for calling this when the connection is
+     * WARNING: RpcState is responsible for calling this when the session is
      * no longer recoverable.
      */
     void terminate();
 
+    // Alternative to std::vector<uint8_t> that doesn't abort on allocation failure and caps
+    // large allocations to avoid being requested from allocating too much data.
+    struct CommandData {
+        explicit CommandData(size_t size);
+        bool valid() { return mSize == 0 || mData != nullptr; }
+        size_t size() { return mSize; }
+        uint8_t* data() { return mData.get(); }
+        uint8_t* release() { return mData.release(); }
+
+    private:
+        std::unique_ptr<uint8_t[]> mData;
+        size_t mSize;
+    };
+
     [[nodiscard]] bool rpcSend(const base::unique_fd& fd, const char* what, const void* data,
                                size_t size);
     [[nodiscard]] bool rpcRec(const base::unique_fd& fd, const char* what, void* data, size_t size);
 
-    [[nodiscard]] status_t waitForReply(const base::unique_fd& fd,
-                                        const sp<RpcConnection>& connection, Parcel* reply);
+    [[nodiscard]] status_t waitForReply(const base::unique_fd& fd, const sp<RpcSession>& session,
+                                        Parcel* reply);
     [[nodiscard]] status_t processServerCommand(const base::unique_fd& fd,
-                                                const sp<RpcConnection>& connection,
+                                                const sp<RpcSession>& session,
                                                 const RpcWireHeader& command);
-    [[nodiscard]] status_t processTransact(const base::unique_fd& fd,
-                                           const sp<RpcConnection>& connection,
+    [[nodiscard]] status_t processTransact(const base::unique_fd& fd, const sp<RpcSession>& session,
                                            const RpcWireHeader& command);
     [[nodiscard]] status_t processTransactInternal(const base::unique_fd& fd,
-                                                   const sp<RpcConnection>& connection,
-                                                   std::vector<uint8_t>&& transactionData);
+                                                   const sp<RpcSession>& session,
+                                                   CommandData transactionData);
     [[nodiscard]] status_t processDecStrong(const base::unique_fd& fd,
                                             const RpcWireHeader& command);
 
@@ -145,7 +163,7 @@
 
         // async transaction queue, _only_ for local binder
         struct AsyncTodo {
-            std::vector<uint8_t> data; // most convenient format, to move it here
+            CommandData data;
             uint64_t asyncNumber = 0;
 
             bool operator<(const AsyncTodo& o) const {
@@ -163,7 +181,7 @@
 
     std::mutex mNodeMutex;
     bool mTerminated = false;
-    // binders known by both sides of a connection
+    // binders known by both sides of a session
     std::map<RpcAddress, BinderNode> mNodeForAddress;
 };
 
diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h
index 60ec6c9..c5fa008 100644
--- a/libs/binder/RpcWireFormat.h
+++ b/libs/binder/RpcWireFormat.h
@@ -47,8 +47,12 @@
  */
 enum : uint32_t {
     RPC_SPECIAL_TRANSACT_GET_ROOT = 0,
+    RPC_SPECIAL_TRANSACT_GET_MAX_THREADS = 1,
+    RPC_SPECIAL_TRANSACT_GET_SESSION_ID = 2,
 };
 
+constexpr int32_t RPC_SESSION_ID_NEW = -1;
+
 // serialization is like:
 // |RpcWireHeader|struct desginated by 'command'| (over and over again)
 
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/private/binder/binder_module.h b/libs/binder/binder_module.h
similarity index 72%
rename from libs/binder/include/private/binder/binder_module.h
rename to libs/binder/binder_module.h
index 1579199..9dea3b4 100644
--- a/libs/binder/include/private/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -29,18 +29,14 @@
 #undef B_PACK_CHARS
 #endif
 
-#include <sys/ioctl.h>
 #include <linux/android/binder.h>
-
-#ifdef __cplusplus
-namespace android {
-#endif
+#include <sys/ioctl.h>
 
 #ifndef BR_FROZEN_REPLY
 // Temporary definition of BR_FROZEN_REPLY. For production
 // this will come from UAPI binder.h
 #define BR_FROZEN_REPLY _IO('r', 18)
-#endif //BR_FROZEN_REPLY
+#endif // BR_FROZEN_REPLY
 
 #ifndef BINDER_FREEZE
 /*
@@ -53,43 +49,53 @@
     //
     // Group-leader PID of process to be frozen
     //
-    uint32_t            pid;
+    uint32_t pid;
     //
     // Enable(1) / Disable(0) freeze for given PID
     //
-    uint32_t            enable;
+    uint32_t enable;
     //
     // Timeout to wait for transactions to drain.
     // 0: don't wait (ioctl will return EAGAIN if not drained)
     // N: number of ms to wait
-    uint32_t            timeout_ms;
+    uint32_t timeout_ms;
 };
-#endif //BINDER_FREEZE
+#endif // BINDER_FREEZE
 
 #ifndef BINDER_GET_FROZEN_INFO
 
-#define BINDER_GET_FROZEN_INFO          _IOWR('b', 15, struct binder_frozen_status_info)
+#define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info)
 
 struct binder_frozen_status_info {
     //
     // Group-leader PID of process to be queried
     //
-    __u32            pid;
+    __u32 pid;
     //
     // Indicates whether the process has received any sync calls since last
     // freeze (cleared at freeze/unfreeze)
     //
-    __u32            sync_recv;
+    __u32 sync_recv;
     //
     // Indicates whether the process has received any async calls since last
     // freeze (cleared at freeze/unfreeze)
     //
-    __u32            async_recv;
+    __u32 async_recv;
 };
-#endif //BINDER_GET_FROZEN_INFO
+#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/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..61bf018 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -28,7 +28,7 @@
 // ---------------------------------------------------------------------------
 namespace android {
 
-class RpcConnection;
+class RpcSession;
 class RpcState;
 namespace internal {
 class Stability;
@@ -40,12 +40,12 @@
 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<RpcSession>& session, const RpcAddress& address);
 
     /**
      * Return value:
-     * true - this is associated with a socket RpcConnection
+     * true - this is associated with a socket RpcSession
      * false - (usual) binder over e.g. /dev/binder
      */
     bool isRpcBinder() const;
@@ -133,7 +133,7 @@
 
         // valid if isRpcBinder
         const RpcAddress& rpcAddress() const { return mBinder->rpcAddress(); }
-        const sp<RpcConnection>& rpcConnection() const { return mBinder->rpcConnection(); }
+        const sp<RpcSession>& rpcSession() const { return mBinder->rpcSession(); }
 
         const BpBinder* mBinder;
     };
@@ -143,23 +143,24 @@
 
 private:
     friend PrivateAccessorForId;
+    friend class sp<BpBinder>;
 
     struct BinderHandle {
         int32_t handle;
     };
-    struct SocketHandle {
-        sp<RpcConnection> connection;
+    struct RpcHandle {
+        sp<RpcSession> session;
         RpcAddress address;
     };
-    using Handle = std::variant<BinderHandle, SocketHandle>;
+    using Handle = std::variant<BinderHandle, RpcHandle>;
 
     int32_t binderHandle() const;
     const RpcAddress& rpcAddress() const;
-    const sp<RpcConnection>& rpcConnection() const;
+    const sp<RpcSession>& rpcSession() const;
 
     explicit BpBinder(Handle&& handle);
     BpBinder(BinderHandle&& handle, int32_t trackedUid);
-    explicit BpBinder(SocketHandle&& handle);
+    explicit BpBinder(RpcHandle&& handle);
 
     virtual             ~BpBinder();
     virtual void        onFirstRef();
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 31f63c8..97c826c 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <android-base/unique_fd.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 #include <utils/String16.h>
@@ -49,39 +50,39 @@
 {
 public:
     enum {
-        FIRST_CALL_TRANSACTION  = 0x00000001,
-        LAST_CALL_TRANSACTION   = 0x00ffffff,
+        FIRST_CALL_TRANSACTION = 0x00000001,
+        LAST_CALL_TRANSACTION = 0x00ffffff,
 
-        PING_TRANSACTION        = B_PACK_CHARS('_','P','N','G'),
-        DUMP_TRANSACTION        = B_PACK_CHARS('_','D','M','P'),
-        SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_','C','M','D'),
-        INTERFACE_TRANSACTION   = B_PACK_CHARS('_', 'N', 'T', 'F'),
-        SYSPROPS_TRANSACTION    = B_PACK_CHARS('_', 'S', 'P', 'R'),
-        EXTENSION_TRANSACTION   = B_PACK_CHARS('_', 'E', 'X', 'T'),
-        DEBUG_PID_TRANSACTION   = B_PACK_CHARS('_', 'P', 'I', 'D'),
+        PING_TRANSACTION = B_PACK_CHARS('_', 'P', 'N', 'G'),
+        DUMP_TRANSACTION = B_PACK_CHARS('_', 'D', 'M', 'P'),
+        SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_', 'C', 'M', 'D'),
+        INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'),
+        SYSPROPS_TRANSACTION = B_PACK_CHARS('_', 'S', 'P', 'R'),
+        EXTENSION_TRANSACTION = B_PACK_CHARS('_', 'E', 'X', 'T'),
+        DEBUG_PID_TRANSACTION = B_PACK_CHARS('_', 'P', 'I', 'D'),
 
         // See android.os.IBinder.TWEET_TRANSACTION
         // Most importantly, messages can be anything not exceeding 130 UTF-8
         // characters, and callees should exclaim "jolly good message old boy!"
-        TWEET_TRANSACTION       = B_PACK_CHARS('_', 'T', 'W', 'T'),
+        TWEET_TRANSACTION = B_PACK_CHARS('_', 'T', 'W', 'T'),
 
         // See android.os.IBinder.LIKE_TRANSACTION
         // Improve binder self-esteem.
-        LIKE_TRANSACTION        = B_PACK_CHARS('_', 'L', 'I', 'K'),
+        LIKE_TRANSACTION = B_PACK_CHARS('_', 'L', 'I', 'K'),
 
         // Corresponds to TF_ONE_WAY -- an asynchronous call.
-        FLAG_ONEWAY             = 0x00000001,
+        FLAG_ONEWAY = 0x00000001,
 
         // Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call
         // is made
-        FLAG_CLEAR_BUF          = 0x00000020,
+        FLAG_CLEAR_BUF = 0x00000020,
 
         // Private userspace flag for transaction which is being requested from
         // a vendor context.
-        FLAG_PRIVATE_VENDOR     = 0x10000000,
+        FLAG_PRIVATE_VENDOR = 0x10000000,
     };
 
-                          IBinder();
+    IBinder();
 
     /**
      * Check if this IBinder implements the interface named by
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index b86fc0b..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;                                                    \
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/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index 9659732..f3ba830 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -50,8 +50,12 @@
                               int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
      /**
       * Force the service to persist, even when it has 0 clients.
-      * If setting this flag from the server side, make sure to do so before calling registerService,
-      * or there may be a race with the default dynamic shutdown.
+      * If setting this flag from the server side, make sure to do so before calling
+      * registerService, or there may be a race with the default dynamic shutdown.
+      *
+      * This should only be used if it is every eventually set to false. If a
+      * service needs to persist but doesn't need to dynamically shut down,
+      * prefer to control it with another mechanism such as ctl.start.
       */
      void forcePersist(bool persist);
 
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 211790d..5aaaa0c 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -50,7 +50,7 @@
 class IBinder;
 class IPCThreadState;
 class ProcessState;
-class RpcConnection;
+class RpcSession;
 class String8;
 class TextOutput;
 
@@ -101,6 +101,10 @@
     // is for an RPC transaction).
     void markForBinder(const sp<IBinder>& binder);
 
+    // Whenever possible, markForBinder should be preferred. This method is
+    // called automatically on reply Parcels for RPC transactions.
+    void markForRpc(const sp<RpcSession>& session);
+
     // Whether this Parcel is written for RPC transactions (after calls to
     // markForBinder or markForRpc).
     bool isForRpc() const;
@@ -536,10 +540,6 @@
                                             const binder_size_t* objects, size_t objectsCount,
                                             release_func relFunc);
 
-    // Whenever possible, markForBinder should be preferred. This method is
-    // called automatically on reply Parcels for RPC transactions.
-    void markForRpc(const sp<RpcConnection>& connection);
-
     status_t            finishWrite(size_t len);
     void                releaseObjects();
     void                acquireObjects();
@@ -1136,7 +1136,7 @@
 
     release_func        mOwner;
 
-    sp<RpcConnection> mConnection;
+    sp<RpcSession> mSession;
 
     class Blob {
     public:
diff --git a/libs/binder/include/binder/PermissionCache.h b/libs/binder/include/binder/PermissionCache.h
index 835a3a8..21aa705 100644
--- a/libs/binder/include/binder/PermissionCache.h
+++ b/libs/binder/include/binder/PermissionCache.h
@@ -73,6 +73,8 @@
 
     static bool checkPermission(const String16& permission,
             pid_t pid, uid_t uid);
+
+    static void purgeCache();
 };
 
 // ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index ca29440..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();
 
diff --git a/libs/binder/include/binder/RpcConnection.h b/libs/binder/include/binder/RpcConnection.h
deleted file mode 100644
index efa922d..0000000
--- a/libs/binder/include/binder/RpcConnection.h
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <android-base/unique_fd.h>
-#include <binder/IBinder.h>
-#include <binder/RpcAddress.h>
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-
-#include <optional>
-#include <vector>
-
-// WARNING: This is a feature which is still in development, and it is subject
-// to radical change. Any production use of this may subject your code to any
-// number of problems.
-
-namespace android {
-
-class Parcel;
-class RpcServer;
-class RpcState;
-
-/**
- * This represents a multi-threaded/multi-socket connection between a client
- * and a server.
- */
-class RpcConnection final : public virtual RefBase {
-public:
-    static sp<RpcConnection> make();
-
-    /**
-     * This represents a connection for responses, e.g.:
-     *
-     *     process A serves binder a
-     *     process B opens a connection to process A
-     *     process B makes binder b and sends it to A
-     *     A uses this 'back connection' to send things back to B
-     *
-     * This should be called once, and then a call should be made to join per
-     * connection thread.
-     */
-    [[nodiscard]] bool setupUnixDomainServer(const char* path);
-
-    /**
-     * This should be called once per thread, matching 'join' in the remote
-     * process.
-     */
-    [[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__
-
-    /**
-     * Query the other side of the connection for the root object hosted by that
-     * process's RpcServer (if one exists)
-     */
-    sp<IBinder> getRootObject();
-
-    [[nodiscard]] status_t transact(const RpcAddress& address, uint32_t code, const Parcel& data,
-                                    Parcel* reply, uint32_t flags);
-    [[nodiscard]] status_t sendDecStrong(const RpcAddress& address);
-
-    /**
-     * Adds a server thread accepting connections. Must be called after
-     * setup*Server.
-     */
-    void join();
-
-    ~RpcConnection();
-
-    void setForServer(const wp<RpcServer>& server);
-    wp<RpcServer> server();
-
-    // internal only
-    const std::unique_ptr<RpcState>& state() { return mState; }
-
-    class SocketAddress {
-    public:
-        virtual ~SocketAddress();
-        virtual std::string toString() const = 0;
-        virtual const sockaddr* addr() const = 0;
-        virtual size_t addrSize() const = 0;
-    };
-
-private:
-    friend sp<RpcConnection>;
-    RpcConnection();
-
-    bool addServer(const SocketAddress& address);
-    bool addClient(const SocketAddress& address);
-    void assignServerToThisThread(base::unique_fd&& fd);
-
-    struct ConnectionSocket : public RefBase {
-        base::unique_fd fd;
-
-        // whether this or another thread is currently using this fd to make
-        // or receive transactions.
-        std::optional<pid_t> exclusiveTid;
-    };
-
-    enum class SocketUse {
-        CLIENT,
-        CLIENT_ASYNC,
-        CLIENT_REFCOUNT,
-        SERVER,
-    };
-
-    // RAII object for connection socket
-    class ExclusiveSocket {
-    public:
-        explicit ExclusiveSocket(const sp<RpcConnection>& connection, SocketUse use);
-        ~ExclusiveSocket();
-        const base::unique_fd& fd() { return mSocket->fd; }
-
-    private:
-        static void findSocket(pid_t tid, sp<ConnectionSocket>* exclusive,
-                               sp<ConnectionSocket>* available,
-                               std::vector<sp<ConnectionSocket>>& sockets, size_t socketsIndexHint);
-
-        sp<RpcConnection> mConnection; // avoid deallocation
-        sp<ConnectionSocket> mSocket;
-
-        // whether this is being used for a nested transaction (being on the same
-        // thread guarantees we won't write in the middle of a message, the way
-        // the wire protocol is constructed guarantees this is safe).
-        bool mReentrant = false;
-    };
-
-    // On the other side of a connection, for each of mClients here, there should
-    // be one of mServers on the other side (and vice versa).
-    //
-    // For the simplest connection, a single server with one client, you would
-    // have:
-    //  - the server has a single 'mServers' and a thread listening on this
-    //  - the client has a single 'mClients' and makes calls to this
-    //  - here, when the client makes a call, the server can call back into it
-    //    (nested calls), but outside of this, the client will only ever read
-    //    calls from the server when it makes a call itself.
-    //
-    // For a more complicated case, the client might itself open up a thread to
-    // serve calls to the server at all times (e.g. if it hosts a callback)
-
-    wp<RpcServer> mForServer; // maybe null, for client connections
-
-    std::unique_ptr<RpcState> mState;
-
-    base::unique_fd mServer; // socket we are accepting connections on
-
-    std::mutex mSocketMutex;           // for all below
-    std::condition_variable mSocketCv; // for mWaitingThreads
-    size_t mWaitingThreads = 0;
-    size_t mClientsOffset = 0; // hint index into clients, ++ when sending an async transaction
-    std::vector<sp<ConnectionSocket>> mClients;
-    std::vector<sp<ConnectionSocket>> mServers;
-};
-
-} // namespace android
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index a2c2aee..8f0c6fd 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -17,68 +17,148 @@
 
 #include <android-base/unique_fd.h>
 #include <binder/IBinder.h>
-#include <binder/RpcConnection.h>
+#include <binder/RpcSession.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 
+#include <mutex>
+#include <thread>
+
 // WARNING: This is a feature which is still in development, and it is subject
 // to radical change. Any production use of this may subject your code to any
 // number of problems.
 
 namespace android {
 
+class RpcSocketAddress;
+
 /**
  * This represents a server of an interface, which may be connected to by any
  * number of clients over sockets.
  *
- * This object is not (currently) thread safe. All calls to it are expected to
- * happen at process startup.
+ * Usage:
+ *     auto server = RpcServer::make();
+ *     // only supports one now
+ *     if (!server->setup*Server(...)) {
+ *         :(
+ *     }
+ *     server->join();
  */
 class RpcServer final : public virtual RefBase {
 public:
     static sp<RpcServer> make();
 
+    /**
+     * This represents a session for responses, e.g.:
+     *
+     *     process A serves binder a
+     *     process B opens a session to process A
+     *     process B makes binder b and sends it to A
+     *     A uses this 'back session' to send things back to B
+     */
+    [[nodiscard]] bool setupUnixDomainServer(const char* path);
+
+    /**
+     * Creates an RPC server at the current port.
+     */
+    [[nodiscard]] bool setupVsockServer(unsigned int port);
+
+    /**
+     * Creates an RPC server at the current port using IPv4.
+     *
+     * TODO(b/182914638): IPv6 support
+     *
+     * Set |port| to 0 to pick an ephemeral port; see discussion of
+     * /proc/sys/net/ipv4/ip_local_port_range in ip(7). In this case, |assignedPort|
+     * will be set to the picked port number, if it is not null.
+     */
+    [[nodiscard]] bool setupInetServer(unsigned int port, unsigned int* assignedPort);
+
+    /**
+     * If setup*Server has been successful, return true. Otherwise return false.
+     */
+    [[nodiscard]] bool hasServer();
+
+    /**
+     * If hasServer(), return the server FD. Otherwise return invalid FD.
+     */
+    [[nodiscard]] base::unique_fd releaseServer();
+
+    /**
+     * Set up server using an external FD previously set up by releaseServer().
+     * Return false if there's already a server.
+     */
+    bool setupExternalServer(base::unique_fd serverFd);
+
     void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
 
     /**
-     * Setup a static connection, when the number of clients are known.
+     * This must be called before adding a client session.
      *
-     * Each call to this function corresponds to a different client, and clients
-     * each have their own threadpools.
+     * If this is not specified, this will be a single-threaded server.
      *
-     * TODO(b/167966510): support dynamic creation of connections/threads
+     * TODO(b/185167543): these are currently created per client, but these
+     * should be shared.
      */
-    sp<RpcConnection> addClientConnection();
-
-    /**
-     * Allowing a server to explicitly drop clients would be easy to add here,
-     * but it is not currently implemented, since users of this functionality
-     * could not use similar functionality if they are running under real
-     * binder.
-     */
-    // void drop(const sp<RpcConnection>& connection);
+    void setMaxThreads(size_t threads);
+    size_t getMaxThreads();
 
     /**
      * The root object can be retrieved by any client, without any
-     * authentication.
+     * authentication. TODO(b/183988761)
+     *
+     * Holds a strong reference to the root object.
      */
     void setRootObject(const sp<IBinder>& binder);
+    /**
+     * Holds a weak reference to the root object.
+     */
+    void setRootObjectWeak(const wp<IBinder>& binder);
+    sp<IBinder> getRootObject();
 
     /**
-     * Root object set with setRootObject
+     * You must have at least one client session before calling this.
+     *
+     * TODO(b/185167543): way to shut down?
      */
-    sp<IBinder> getRootObject();
+    void join();
+
+    /**
+     * Accept one connection on this server. You must have at least one client
+     * session before calling this.
+     */
+    [[nodiscard]] bool acceptOne();
+
+    /**
+     * For debugging!
+     */
+    std::vector<sp<RpcSession>> listSessions();
+    size_t numUninitializedSessions();
 
     ~RpcServer();
 
+    // internal use only
+
+    void onSessionTerminating(const sp<RpcSession>& session);
+
 private:
+    friend sp<RpcServer>;
     RpcServer();
 
+    void establishConnection(sp<RpcServer>&& session, base::unique_fd clientFd);
+    bool setupSocketServer(const RpcSocketAddress& address);
+
     bool mAgreedExperimental = false;
+    bool mStarted = false; // TODO(b/185167543): support dynamically added clients
+    size_t mMaxThreads = 1;
+    base::unique_fd mServer; // socket we are accepting sessions on
 
+    std::mutex mLock; // for below
+    std::map<std::thread::id, std::thread> mConnectingThreads;
     sp<IBinder> mRootObject;
-
-    std::vector<sp<RpcConnection>> mConnections; // per-client
+    wp<IBinder> mRootObjectWeak;
+    std::map<int32_t, sp<RpcSession>> mSessions;
+    int32_t mSessionIdCounter = 0;
 };
 
 } // namespace android
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
new file mode 100644
index 0000000..bcc213c
--- /dev/null
+++ b/libs/binder/include/binder/RpcSession.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <android-base/unique_fd.h>
+#include <binder/IBinder.h>
+#include <binder/RpcAddress.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <map>
+#include <optional>
+#include <thread>
+#include <vector>
+
+// WARNING: This is a feature which is still in development, and it is subject
+// to radical change. Any production use of this may subject your code to any
+// number of problems.
+
+namespace android {
+
+class Parcel;
+class RpcServer;
+class RpcSocketAddress;
+class RpcState;
+
+/**
+ * This represents a session (group of connections) between a client
+ * and a server. Multiple connections are needed for multiple parallel "binder"
+ * calls which may also have nested calls.
+ */
+class RpcSession final : public virtual RefBase {
+public:
+    static sp<RpcSession> make();
+
+    /**
+     * This should be called once per thread, matching 'join' in the remote
+     * process.
+     */
+    [[nodiscard]] bool setupUnixDomainClient(const char* path);
+
+    /**
+     * Connects to an RPC server at the CVD & port.
+     */
+    [[nodiscard]] bool setupVsockClient(unsigned int cvd, unsigned int port);
+
+    /**
+     * Connects to an RPC server at the given address and port.
+     */
+    [[nodiscard]] bool setupInetClient(const char* addr, unsigned int port);
+
+    /**
+     * For debugging!
+     *
+     * Sets up an empty connection. All queries to this connection 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 session for the root object hosted by that
+     * process's RpcServer (if one exists)
+     */
+    sp<IBinder> getRootObject();
+
+    /**
+     * Query the other side of the session for the maximum number of threads
+     * it supports (maximum number of concurrent non-nested synchronous transactions)
+     */
+    status_t getRemoteMaxThreads(size_t* maxThreads);
+
+    [[nodiscard]] status_t transact(const RpcAddress& address, uint32_t code, const Parcel& data,
+                                    Parcel* reply, uint32_t flags);
+    [[nodiscard]] status_t sendDecStrong(const RpcAddress& address);
+
+    ~RpcSession();
+
+    wp<RpcServer> server();
+
+    // internal only
+    const std::unique_ptr<RpcState>& state() { return mState; }
+
+    class PrivateAccessorForId {
+    private:
+        friend class RpcSession;
+        friend class RpcState;
+        explicit PrivateAccessorForId(const RpcSession* session) : mSession(session) {}
+
+        const std::optional<int32_t> get() { return mSession->mId; }
+
+        const RpcSession* mSession;
+    };
+    PrivateAccessorForId getPrivateAccessorForId() const { return PrivateAccessorForId(this); }
+
+private:
+    friend PrivateAccessorForId;
+    friend sp<RpcSession>;
+    friend RpcServer;
+    RpcSession();
+
+    status_t readId();
+
+    // transfer ownership of thread
+    void preJoin(std::thread thread);
+    // join on thread passed to preJoin
+    void join(base::unique_fd client);
+    void terminateLocked();
+
+    struct RpcConnection : public RefBase {
+        base::unique_fd fd;
+
+        // whether this or another thread is currently using this fd to make
+        // or receive transactions.
+        std::optional<pid_t> exclusiveTid;
+    };
+
+    bool setupSocketClient(const RpcSocketAddress& address);
+    bool setupOneSocketClient(const RpcSocketAddress& address, int32_t sessionId);
+    void addClientConnection(base::unique_fd fd);
+    void setForServer(const wp<RpcServer>& server, int32_t sessionId);
+    sp<RpcConnection> assignServerToThisThread(base::unique_fd fd);
+    bool removeServerConnection(const sp<RpcConnection>& connection);
+
+    enum class ConnectionUse {
+        CLIENT,
+        CLIENT_ASYNC,
+        CLIENT_REFCOUNT,
+    };
+
+    // RAII object for session connection
+    class ExclusiveConnection {
+    public:
+        explicit ExclusiveConnection(const sp<RpcSession>& session, ConnectionUse use);
+        ~ExclusiveConnection();
+        const base::unique_fd& fd() { return mConnection->fd; }
+
+    private:
+        static void findConnection(pid_t tid, sp<RpcConnection>* exclusive,
+                                   sp<RpcConnection>* available,
+                                   std::vector<sp<RpcConnection>>& sockets,
+                                   size_t socketsIndexHint);
+
+        sp<RpcSession> mSession; // avoid deallocation
+        sp<RpcConnection> mConnection;
+
+        // whether this is being used for a nested transaction (being on the same
+        // thread guarantees we won't write in the middle of a message, the way
+        // the wire protocol is constructed guarantees this is safe).
+        bool mReentrant = false;
+    };
+
+    // On the other side of a session, for each of mClientConnections here, there should
+    // be one of mServerConnections on the other side (and vice versa).
+    //
+    // For the simplest session, a single server with one client, you would
+    // have:
+    //  - the server has a single 'mServerConnections' and a thread listening on this
+    //  - the client has a single 'mClientConnections' and makes calls to this
+    //  - here, when the client makes a call, the server can call back into it
+    //    (nested calls), but outside of this, the client will only ever read
+    //    calls from the server when it makes a call itself.
+    //
+    // For a more complicated case, the client might itself open up a thread to
+    // serve calls to the server at all times (e.g. if it hosts a callback)
+
+    wp<RpcServer> mForServer; // maybe null, for client sessions
+
+    // TODO(b/183988761): this shouldn't be guessable
+    std::optional<int32_t> mId;
+
+    std::unique_ptr<RpcState> mState;
+
+    std::mutex mMutex; // for all below
+
+    std::condition_variable mAvailableConnectionCv; // for mWaitingThreads
+    size_t mWaitingThreads = 0;
+    // hint index into clients, ++ when sending an async transaction
+    size_t mClientConnectionsOffset = 0;
+    std::vector<sp<RpcConnection>> mClientConnections;
+    std::vector<sp<RpcConnection>> mServerConnections;
+
+    // TODO(b/185167543): use for reverse sessions (allow client to also
+    // serve calls on a session).
+    // TODO(b/185167543): allow sharing between different sessions in a
+    // process? (or combine with mServerConnections)
+    std::map<std::thread::id, std::thread> mThreads;
+    bool mTerminated = false;
+};
+
+} // namespace android
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/binder/ActivityManager.h b/libs/binder/include_activitymanager/binder/ActivityManager.h
similarity index 100%
rename from libs/binder/include/binder/ActivityManager.h
rename to libs/binder/include_activitymanager/binder/ActivityManager.h
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/ndk/Android.bp b/libs/binder/ndk/Android.bp
index eb103d3..b03e24c 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -55,7 +55,9 @@
     defaults: ["libbinder_ndk_host_user"],
     host_supported: true,
 
-    llndk_stubs: "libbinder_ndk.llndk",
+    llndk: {
+        symbol_file: "libbinder_ndk.map.txt",
+    },
 
     export_include_dirs: [
         "include_cpp",
@@ -192,13 +194,3 @@
     symbol_file: "libbinder_ndk.map.txt",
     first_version: "29",
 }
-
-llndk_library {
-    name: "libbinder_ndk.llndk",
-    symbol_file: "libbinder_ndk.map.txt",
-    export_include_dirs: [
-        "include_cpp",
-        "include_ndk",
-        "include_platform",
-    ],
-}
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 3e6c39f..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);
 
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index b9adc9a..9e2050b 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -173,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.
@@ -645,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);
 
@@ -669,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
@@ -683,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.
@@ -718,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_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 5516914..2a66941 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -35,7 +35,7 @@
  * \return EX_NONE on success.
  */
 __attribute__((warn_unused_result)) binder_exception_t AServiceManager_addService(
-        AIBinder* binder, const char* instance);
+        AIBinder* binder, const char* instance) __INTRODUCED_IN(29);
 
 /**
  * Gets a binder object with this specific instance name. Will return nullptr immediately if the
@@ -47,7 +47,8 @@
  *
  * \param instance identifier of the service used to lookup the service.
  */
-__attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance);
+__attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance)
+        __INTRODUCED_IN(29);
 
 /**
  * Gets a binder object with this specific instance name. Blocks for a couple of seconds waiting on
@@ -59,7 +60,8 @@
  *
  * \param instance identifier of the service used to lookup the service.
  */
-__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance);
+__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance)
+        __INTRODUCED_IN(29);
 
 /**
  * Registers a lazy service with the default service manager under the 'instance' name.
@@ -124,8 +126,21 @@
         __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
  *
+ * This should only be used if it is every eventually set to false. If a
+ * service needs to persist but doesn't need to dynamically shut down,
+ * prefer to control it with another mechanism.
+ *
  * \param persist 'true' if the process should not exit.
  */
 void AServiceManager_forceLazyServicesPersist(bool persist) __INTRODUCED_IN(31);
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 67c85b6..7d4b82e 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -118,14 +118,15 @@
     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
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/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/rust/Android.bp b/libs/binder/rust/Android.bp
index e12a429..49d3401 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -50,6 +50,8 @@
         "//apex_available:platform",
         "com.android.virt",
     ],
+    lints: "none",
+    clippy_lints: "none",
 }
 
 rust_bindgen {
@@ -65,15 +67,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/src/binder.rs b/libs/binder/rust/src/binder.rs
index 321b422..695a83e 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -548,6 +548,28 @@
     }
 }
 
+/// The features to enable when creating a native Binder.
+///
+/// This should always be initialised with a default value, e.g.:
+/// ```
+/// # use binder::BinderFeatures;
+/// BinderFeatures {
+///   set_requesting_sid: true,
+///   ..BinderFeatures::default(),
+/// }
+/// ```
+#[derive(Clone, Debug, Default, Eq, PartialEq)]
+pub struct BinderFeatures {
+    /// Indicates that the service intends to receive caller security contexts. This must be true
+    /// for `ThreadState::with_calling_sid` to work.
+    pub set_requesting_sid: bool,
+    // Ensure that clients include a ..BinderFeatures::default() to preserve backwards compatibility
+    // when new fields are added. #[non_exhaustive] doesn't work because it prevents struct
+    // expressions entirely.
+    #[doc(hidden)]
+    pub _non_exhaustive: (),
+}
+
 /// Declare typed interfaces for a binder object.
 ///
 /// Given an interface trait and descriptor string, create a native and remote
@@ -730,8 +752,9 @@
 
         impl $native {
             /// Create a new binder service.
-            pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T) -> $crate::Strong<dyn $interface> {
-                let binder = $crate::Binder::new_with_stability($native(Box::new(inner)), $stability);
+            pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T, features: $crate::BinderFeatures) -> $crate::Strong<dyn $interface> {
+                let mut binder = $crate::Binder::new_with_stability($native(Box::new(inner)), $stability);
+                $crate::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
                 $crate::Strong::new(Box::new(binder))
             }
         }
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 30928a5..2694cba 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -107,10 +107,9 @@
 pub mod parcel;
 
 pub use crate::binder::{
-    FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Remotable,
-    Stability, Strong, TransactionCode, TransactionFlags, Weak,
-    FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL,
-    LAST_CALL_TRANSACTION,
+    BinderFeatures, FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Remotable,
+    Stability, Strong, TransactionCode, TransactionFlags, Weak, FIRST_CALL_TRANSACTION,
+    FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
 };
 pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
 pub use native::add_service;
@@ -125,8 +124,8 @@
     pub use super::parcel::ParcelFileDescriptor;
     pub use super::{add_service, get_interface};
     pub use super::{
-        DeathRecipient, ExceptionCode, IBinder, Interface, ProcessState, SpIBinder, Status,
-        StatusCode, Strong, ThreadState, Weak, WpIBinder,
+        BinderFeatures, DeathRecipient, ExceptionCode, IBinder, Interface, ProcessState, SpIBinder,
+        Status, StatusCode, Strong, ThreadState, Weak, WpIBinder,
     };
 
     /// Binder result containing a [`Status`] on error.
diff --git a/libs/binder/rust/sys/lib.rs b/libs/binder/rust/sys/lib.rs
index 9095af2..1d1a295 100644
--- a/libs/binder/rust/sys/lib.rs
+++ b/libs/binder/rust/sys/lib.rs
@@ -16,14 +16,6 @@
 
 //! Generated Rust bindings to libbinder_ndk
 
-#![allow(
-    non_camel_case_types,
-    non_snake_case,
-    non_upper_case_globals,
-    unused,
-    improper_ctypes,
-    missing_docs
-)]
 use std::error::Error;
 use std::fmt;
 
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/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 60e3502..0332007 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -19,7 +19,7 @@
 use binder::declare_binder_interface;
 use binder::parcel::Parcel;
 use binder::{
-    Binder, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode,
+    Binder, BinderFeatures, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode,
     FIRST_CALL_TRANSACTION,
 };
 use std::convert::{TryFrom, TryInto};
@@ -55,7 +55,8 @@
         })));
         service.set_requesting_sid(true);
         if let Some(extension_name) = extension_name {
-            let extension = BnTest::new_binder(TestService { s: extension_name });
+            let extension =
+                BnTest::new_binder(TestService { s: extension_name }, BinderFeatures::default());
             service
                 .set_extension(&mut extension.as_binder())
                 .expect("Could not add extension");
@@ -212,8 +213,8 @@
     use std::time::Duration;
 
     use binder::{
-        Binder, DeathRecipient, FromIBinder, IBinder, IBinderInternal, Interface, SpIBinder,
-        StatusCode, Strong,
+        Binder, BinderFeatures, DeathRecipient, FromIBinder, IBinder, IBinderInternal, Interface,
+        SpIBinder, StatusCode, Strong,
     };
 
     use super::{BnTest, ITest, ITestSameDescriptor, TestService, RUST_SERVICE_BINARY};
@@ -495,9 +496,12 @@
     #[test]
     fn reassociate_rust_binder() {
         let service_name = "testing_service";
-        let service_ibinder = BnTest::new_binder(TestService {
-            s: service_name.to_string(),
-        })
+        let service_ibinder = BnTest::new_binder(
+            TestService {
+                s: service_name.to_string(),
+            },
+            BinderFeatures::default(),
+        )
         .as_binder();
 
         let service: Strong<dyn ITest> = service_ibinder
@@ -510,9 +514,12 @@
     #[test]
     fn weak_binder_upgrade() {
         let service_name = "testing_service";
-        let service = BnTest::new_binder(TestService {
-            s: service_name.to_string(),
-        });
+        let service = BnTest::new_binder(
+            TestService {
+                s: service_name.to_string(),
+            },
+            BinderFeatures::default(),
+        );
 
         let weak = Strong::downgrade(&service);
 
@@ -525,9 +532,12 @@
     fn weak_binder_upgrade_dead() {
         let service_name = "testing_service";
         let weak = {
-            let service = BnTest::new_binder(TestService {
-                s: service_name.to_string(),
-            });
+            let service = BnTest::new_binder(
+                TestService {
+                    s: service_name.to_string(),
+                },
+                BinderFeatures::default(),
+            );
 
             Strong::downgrade(&service)
         };
@@ -538,9 +548,12 @@
     #[test]
     fn weak_binder_clone() {
         let service_name = "testing_service";
-        let service = BnTest::new_binder(TestService {
-            s: service_name.to_string(),
-        });
+        let service = BnTest::new_binder(
+            TestService {
+                s: service_name.to_string(),
+            },
+            BinderFeatures::default(),
+        );
 
         let weak = Strong::downgrade(&service);
         let cloned = weak.clone();
@@ -556,12 +569,18 @@
     #[test]
     #[allow(clippy::eq_op)]
     fn binder_ord() {
-        let service1 = BnTest::new_binder(TestService {
-            s: "testing_service1".to_string(),
-        });
-        let service2 = BnTest::new_binder(TestService {
-            s: "testing_service2".to_string(),
-        });
+        let service1 = BnTest::new_binder(
+            TestService {
+                s: "testing_service1".to_string(),
+            },
+            BinderFeatures::default(),
+        );
+        let service2 = BnTest::new_binder(
+            TestService {
+                s: "testing_service2".to_string(),
+            },
+            BinderFeatures::default(),
+        );
 
         assert!(!(service1 < service1));
         assert!(!(service1 > service1));
diff --git a/libs/binder/rust/tests/ndk_rust_interop.rs b/libs/binder/rust/tests/ndk_rust_interop.rs
index ce75ab7..4702e45 100644
--- a/libs/binder/rust/tests/ndk_rust_interop.rs
+++ b/libs/binder/rust/tests/ndk_rust_interop.rs
@@ -16,15 +16,13 @@
 
 //! Rust Binder NDK interop tests
 
-use std::ffi::CStr;
-use std::os::raw::{c_char, c_int};
-use ::IBinderRustNdkInteropTest::binder::{self, Interface, StatusCode};
 use ::IBinderRustNdkInteropTest::aidl::IBinderRustNdkInteropTest::{
     BnBinderRustNdkInteropTest, IBinderRustNdkInteropTest,
 };
-use ::IBinderRustNdkInteropTest::aidl::IBinderRustNdkInteropTestOther::{
-    IBinderRustNdkInteropTestOther,
-};
+use ::IBinderRustNdkInteropTest::aidl::IBinderRustNdkInteropTestOther::IBinderRustNdkInteropTestOther;
+use ::IBinderRustNdkInteropTest::binder::{self, BinderFeatures, Interface, StatusCode};
+use std::ffi::CStr;
+use std::os::raw::{c_char, c_int};
 
 /// Look up the provided AIDL service and call its echo method.
 ///
@@ -37,18 +35,21 @@
 
     // The Rust class descriptor pointer will not match the NDK one, but the
     // descriptor strings match so this needs to still associate.
-    let service: binder::Strong<dyn IBinderRustNdkInteropTest> = match binder::get_interface(service_name) {
-        Err(e) => {
-            eprintln!("Could not find Ndk service {}: {:?}", service_name, e);
-            return StatusCode::NAME_NOT_FOUND as c_int;
-        }
-        Ok(service) => service,
-    };
+    let service: binder::Strong<dyn IBinderRustNdkInteropTest> =
+        match binder::get_interface(service_name) {
+            Err(e) => {
+                eprintln!("Could not find Ndk service {}: {:?}", service_name, e);
+                return StatusCode::NAME_NOT_FOUND as c_int;
+            }
+            Ok(service) => service,
+        };
 
     match service.echo("testing") {
-        Ok(s) => if s != "testing" {
-            return StatusCode::BAD_VALUE as c_int;
-        },
+        Ok(s) => {
+            if s != "testing" {
+                return StatusCode::BAD_VALUE as c_int;
+            }
+        }
         Err(e) => return e.into(),
     }
 
@@ -88,7 +89,7 @@
 #[no_mangle]
 pub unsafe extern "C" fn rust_start_service(service_name: *const c_char) -> c_int {
     let service_name = CStr::from_ptr(service_name).to_str().unwrap();
-    let service = BnBinderRustNdkInteropTest::new_binder(Service);
+    let service = BnBinderRustNdkInteropTest::new_binder(Service, BinderFeatures::default());
     match binder::add_service(&service_name, service.as_binder()) {
         Ok(_) => StatusCode::OK as c_int,
         Err(e) => e as c_int,
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index f1b068e..66ba846 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -18,11 +18,11 @@
 //! access.
 
 use binder::declare_binder_interface;
+use binder::parcel::ParcelFileDescriptor;
 use binder::{
-    Binder, ExceptionCode, Interface, Parcel, Result, SpIBinder, Status,
+    Binder, BinderFeatures, ExceptionCode, Interface, Parcel, Result, SpIBinder, Status,
     StatusCode, TransactionCode,
 };
-use binder::parcel::ParcelFileDescriptor;
 
 use std::ffi::{c_void, CStr, CString};
 use std::sync::Once;
@@ -85,7 +85,7 @@
 pub extern "C" fn rust_service() -> *mut c_void {
     unsafe {
         SERVICE_ONCE.call_once(|| {
-            SERVICE = Some(BnReadParcelTest::new_binder(()).as_binder());
+            SERVICE = Some(BnReadParcelTest::new_binder((), BinderFeatures::default()).as_binder());
         });
         SERVICE.as_ref().unwrap().as_raw().cast()
     }
@@ -108,8 +108,12 @@
 impl ReadParcelTest for () {}
 
 #[allow(clippy::float_cmp)]
-fn on_transact(_service: &dyn ReadParcelTest, code: TransactionCode,
-               parcel: &Parcel, reply: &mut Parcel) -> Result<()> {
+fn on_transact(
+    _service: &dyn ReadParcelTest,
+    code: TransactionCode,
+    parcel: &Parcel,
+    reply: &mut Parcel,
+) -> Result<()> {
     match code {
         bindings::Transaction_TEST_BOOL => {
             assert_eq!(parcel.read::<bool>()?, true);
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index afc4b1b..ec231b2 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -35,6 +35,7 @@
     name: "binderDriverInterfaceTest_IPC_32",
     defaults: ["binder_test_defaults"],
     srcs: ["binderDriverInterfaceTest.cpp"],
+    header_libs: ["libbinder_headers"],
     compile_multilib: "32",
     multilib: { lib32: { suffix: "" } },
     cflags: ["-DBINDER_IPC_32BIT=1"],
@@ -49,7 +50,7 @@
             cflags: ["-DBINDER_IPC_32BIT=1"],
         },
     },
-
+    header_libs: ["libbinder_headers"],
     srcs: ["binderDriverInterfaceTest.cpp"],
     test_suites: ["device-tests", "vts"],
 }
@@ -62,6 +63,9 @@
         "libbinder",
         "libutils",
     ],
+    static_libs: [
+        "libgmock",
+    ],
     compile_multilib: "32",
     multilib: { lib32: { suffix: "" } },
     cflags: ["-DBINDER_IPC_32BIT=1"],
@@ -100,6 +104,9 @@
         "libbinder",
         "libutils",
     ],
+    static_libs: [
+        "libgmock",
+    ],
     test_suites: ["device-tests", "vts"],
     require_root: true,
 }
@@ -151,6 +158,27 @@
     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"],
diff --git a/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/IBinderRpcBenchmark.aidl
similarity index 69%
copy from libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
copy to libs/binder/tests/IBinderRpcBenchmark.aidl
index b92a6a9..1457422 100644
--- a/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
+++ b/libs/binder/tests/IBinderRpcBenchmark.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,11 +14,7 @@
  * limitations under the License.
  */
 
-#pragma once
-
-#include <binder/Parcel.h>
-#include <fuzzer/FuzzedDataProvider.h>
-
-namespace android {
-void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider);
-} // namespace android
+interface IBinderRpcBenchmark {
+    @utf8InCpp String repeatString(@utf8InCpp String str);
+    IBinder repeatBinder(IBinder binder);
+}
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index 2bdb264..ef4198d 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -18,8 +18,8 @@
     oneway void sendString(@utf8InCpp String str);
     @utf8InCpp String doubleString(@utf8InCpp String str);
 
-    // number of known RPC binders to process, RpcState::countBinders
-    int countBinders();
+    // number of known RPC binders to process, RpcState::countBinders by session
+    int[] countBinders();
 
     // Caller sends server, callee pings caller's server and returns error code.
     int pingMe(IBinder binder);
@@ -36,7 +36,7 @@
     // should always return the same binder
     IBinder alwaysGiveMeTheSameBinder();
 
-    // Idea is that the server will not hold onto the session, the remote connection
+    // Idea is that the server will not hold onto the session, the remote session
     // object must. This is to test lifetimes of binder objects, and consequently, also
     // identity (since by assigning sessions names, we can make sure a section always
     // references the session it was originally opened with).
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index e2193fa..0c3fbcd 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <thread>
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include <binder/Binder.h>
@@ -31,16 +32,23 @@
 #include <binder/IServiceManager.h>
 #include <binder/ParcelRef.h>
 
-#include <private/binder/binder_module.h>
 #include <linux/sched.h>
 #include <sys/epoll.h>
 #include <sys/prctl.h>
 
+#include "../binder_module.h"
 #include "binderAbiHelper.h"
 
 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
 
 using namespace android;
+using testing::Not;
+
+// e.g. EXPECT_THAT(expr, StatusEq(OK)) << "additional message";
+MATCHER_P(StatusEq, expected, (negation ? "not " : "") + statusToString(expected)) {
+    *result_listener << statusToString(arg);
+    return expected == arg;
+}
 
 static ::testing::AssertionResult IsPageAligned(void *buf) {
     if (((unsigned long)buf & ((unsigned long)PAGE_SIZE - 1)) == 0)
@@ -88,6 +96,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)
@@ -204,19 +213,16 @@
     protected:
         sp<IBinder> addServerEtc(int32_t *idPtr, int code)
         {
-            int ret;
             int32_t id;
             Parcel data, reply;
             sp<IBinder> binder;
 
-            ret = m_server->transact(code, data, &reply);
-            EXPECT_EQ(NO_ERROR, ret);
+            EXPECT_THAT(m_server->transact(code, data, &reply), StatusEq(NO_ERROR));
 
             EXPECT_FALSE(binder != nullptr);
             binder = reply.readStrongBinder();
             EXPECT_TRUE(binder != nullptr);
-            ret = reply.readInt32(&id);
-            EXPECT_EQ(NO_ERROR, ret);
+            EXPECT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
             if (idPtr)
                 *idPtr = id;
             return binder;
@@ -400,29 +406,25 @@
 };
 
 TEST_F(BinderLibTest, NopTransaction) {
-    status_t ret;
     Parcel data, reply;
-    ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply),
+                StatusEq(NO_ERROR));
 }
 
 TEST_F(BinderLibTest, NopTransactionOneway) {
-    status_t ret;
     Parcel data, reply;
-    ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_ONE_WAY);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_ONE_WAY),
+                StatusEq(NO_ERROR));
 }
 
 TEST_F(BinderLibTest, NopTransactionClear) {
-    status_t ret;
     Parcel data, reply;
     // make sure it accepts the transaction flag
-    ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_CLEAR_BUF);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_CLEAR_BUF),
+                StatusEq(NO_ERROR));
 }
 
 TEST_F(BinderLibTest, Freeze) {
-    status_t ret;
     Parcel data, reply, replypid;
     std::ifstream freezer_file("/sys/fs/cgroup/freezer/cgroup.freeze");
 
@@ -441,9 +443,8 @@
         return;
     }
 
-    ret = m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid);
+    EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid), StatusEq(NO_ERROR));
     int32_t pid = replypid.readInt32();
-    EXPECT_EQ(NO_ERROR, ret);
     for (int i = 0; i < 10; i++) {
         EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, data, &reply, TF_ONE_WAY));
     }
@@ -467,42 +468,36 @@
 TEST_F(BinderLibTest, SetError) {
     int32_t testValue[] = { 0, -123, 123 };
     for (size_t i = 0; i < ARRAY_SIZE(testValue); i++) {
-        status_t ret;
         Parcel data, reply;
         data.writeInt32(testValue[i]);
-        ret = m_server->transact(BINDER_LIB_TEST_SET_ERROR_TRANSACTION, data, &reply);
-        EXPECT_EQ(testValue[i], ret);
+        EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_SET_ERROR_TRANSACTION, data, &reply),
+                    StatusEq(testValue[i]));
     }
 }
 
 TEST_F(BinderLibTest, GetId) {
-    status_t ret;
     int32_t id;
     Parcel data, reply;
-    ret = m_server->transact(BINDER_LIB_TEST_GET_ID_TRANSACTION, data, &reply);
-    EXPECT_EQ(NO_ERROR, ret);
-    ret = reply.readInt32(&id);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GET_ID_TRANSACTION, data, &reply),
+                StatusEq(NO_ERROR));
+    EXPECT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
     EXPECT_EQ(0, id);
 }
 
 TEST_F(BinderLibTest, PtrSize) {
-    status_t ret;
     int32_t ptrsize;
     Parcel data, reply;
     sp<IBinder> server = addServer();
     ASSERT_TRUE(server != nullptr);
-    ret = server->transact(BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, data, &reply);
-    EXPECT_EQ(NO_ERROR, ret);
-    ret = reply.readInt32(&ptrsize);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, data, &reply),
+                StatusEq(NO_ERROR));
+    EXPECT_THAT(reply.readInt32(&ptrsize), StatusEq(NO_ERROR));
     RecordProperty("TestPtrSize", sizeof(void *));
     RecordProperty("ServerPtrSize", sizeof(void *));
 }
 
 TEST_F(BinderLibTest, IndirectGetId2)
 {
-    status_t ret;
     int32_t id;
     int32_t count;
     Parcel data, reply;
@@ -520,22 +515,19 @@
         datai.appendTo(&data);
     }
 
-    ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply);
-    ASSERT_EQ(NO_ERROR, ret);
+    ASSERT_THAT(m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply),
+                StatusEq(NO_ERROR));
 
-    ret = reply.readInt32(&id);
-    ASSERT_EQ(NO_ERROR, ret);
+    ASSERT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
     EXPECT_EQ(0, id);
 
-    ret = reply.readInt32(&count);
-    ASSERT_EQ(NO_ERROR, ret);
+    ASSERT_THAT(reply.readInt32(&count), StatusEq(NO_ERROR));
     EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count);
 
     for (size_t i = 0; i < (size_t)count; i++) {
         BinderLibTestBundle replyi(&reply);
         EXPECT_TRUE(replyi.isValid());
-        ret = replyi.readInt32(&id);
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(replyi.readInt32(&id), StatusEq(NO_ERROR));
         EXPECT_EQ(serverId[i], id);
         EXPECT_EQ(replyi.dataSize(), replyi.dataPosition());
     }
@@ -545,7 +537,6 @@
 
 TEST_F(BinderLibTest, IndirectGetId3)
 {
-    status_t ret;
     int32_t id;
     int32_t count;
     Parcel data, reply;
@@ -570,15 +561,13 @@
         datai.appendTo(&data);
     }
 
-    ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply);
-    ASSERT_EQ(NO_ERROR, ret);
+    ASSERT_THAT(m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply),
+                StatusEq(NO_ERROR));
 
-    ret = reply.readInt32(&id);
-    ASSERT_EQ(NO_ERROR, ret);
+    ASSERT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
     EXPECT_EQ(0, id);
 
-    ret = reply.readInt32(&count);
-    ASSERT_EQ(NO_ERROR, ret);
+    ASSERT_THAT(reply.readInt32(&count), StatusEq(NO_ERROR));
     EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count);
 
     for (size_t i = 0; i < (size_t)count; i++) {
@@ -586,18 +575,15 @@
 
         BinderLibTestBundle replyi(&reply);
         EXPECT_TRUE(replyi.isValid());
-        ret = replyi.readInt32(&id);
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(replyi.readInt32(&id), StatusEq(NO_ERROR));
         EXPECT_EQ(serverId[i], id);
 
-        ret = replyi.readInt32(&counti);
-        ASSERT_EQ(NO_ERROR, ret);
+        ASSERT_THAT(replyi.readInt32(&counti), StatusEq(NO_ERROR));
         EXPECT_EQ(1, counti);
 
         BinderLibTestBundle replyi2(&replyi);
         EXPECT_TRUE(replyi2.isValid());
-        ret = replyi2.readInt32(&id);
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(replyi2.readInt32(&id), StatusEq(NO_ERROR));
         EXPECT_EQ(0, id);
         EXPECT_EQ(replyi2.dataSize(), replyi2.dataPosition());
 
@@ -609,16 +595,13 @@
 
 TEST_F(BinderLibTest, CallBack)
 {
-    status_t ret;
     Parcel data, reply;
     sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack();
     data.writeStrongBinder(callBack);
-    ret = m_server->transact(BINDER_LIB_TEST_NOP_CALL_BACK, data, &reply, TF_ONE_WAY);
-    EXPECT_EQ(NO_ERROR, ret);
-    ret = callBack->waitEvent(5);
-    EXPECT_EQ(NO_ERROR, ret);
-    ret = callBack->getResult();
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_CALL_BACK, data, &reply, TF_ONE_WAY),
+                StatusEq(NO_ERROR));
+    EXPECT_THAT(callBack->waitEvent(5), StatusEq(NO_ERROR));
+    EXPECT_THAT(callBack->getResult(), StatusEq(NO_ERROR));
 }
 
 TEST_F(BinderLibTest, AddServer)
@@ -629,7 +612,6 @@
 
 TEST_F(BinderLibTest, DeathNotificationStrongRef)
 {
-    status_t ret;
     sp<IBinder> sbinder;
 
     sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient();
@@ -637,20 +619,17 @@
     {
         sp<IBinder> binder = addServer();
         ASSERT_TRUE(binder != nullptr);
-        ret = binder->linkToDeath(testDeathRecipient);
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(binder->linkToDeath(testDeathRecipient), StatusEq(NO_ERROR));
         sbinder = binder;
     }
     {
         Parcel data, reply;
-        ret = sbinder->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY);
-        EXPECT_EQ(0, ret);
+        EXPECT_THAT(sbinder->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY),
+                    StatusEq(OK));
     }
     IPCThreadState::self()->flushCommands();
-    ret = testDeathRecipient->waitEvent(5);
-    EXPECT_EQ(NO_ERROR, ret);
-    ret = sbinder->unlinkToDeath(testDeathRecipient);
-    EXPECT_EQ(DEAD_OBJECT, ret);
+    EXPECT_THAT(testDeathRecipient->waitEvent(5), StatusEq(NO_ERROR));
+    EXPECT_THAT(sbinder->unlinkToDeath(testDeathRecipient), StatusEq(DEAD_OBJECT));
 }
 
 TEST_F(BinderLibTest, DeathNotificationMultiple)
@@ -673,8 +652,9 @@
             callBack[i] = new BinderLibTestCallBack();
             data.writeStrongBinder(target);
             data.writeStrongBinder(callBack[i]);
-            ret = linkedclient[i]->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY);
-            EXPECT_EQ(NO_ERROR, ret);
+            EXPECT_THAT(linkedclient[i]->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data,
+                                                  &reply, TF_ONE_WAY),
+                        StatusEq(NO_ERROR));
         }
         {
             Parcel data, reply;
@@ -682,8 +662,9 @@
             passiveclient[i] = addServer();
             ASSERT_TRUE(passiveclient[i] != nullptr);
             data.writeStrongBinder(target);
-            ret = passiveclient[i]->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data, &reply, TF_ONE_WAY);
-            EXPECT_EQ(NO_ERROR, ret);
+            EXPECT_THAT(passiveclient[i]->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data,
+                                                   &reply, TF_ONE_WAY),
+                        StatusEq(NO_ERROR));
         }
     }
     {
@@ -693,10 +674,8 @@
     }
 
     for (int i = 0; i < clientcount; i++) {
-        ret = callBack[i]->waitEvent(5);
-        EXPECT_EQ(NO_ERROR, ret);
-        ret = callBack[i]->getResult();
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(callBack[i]->waitEvent(5), StatusEq(NO_ERROR));
+        EXPECT_THAT(callBack[i]->getResult(), StatusEq(NO_ERROR));
     }
 }
 
@@ -711,8 +690,7 @@
 
     sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient();
 
-    ret = target->linkToDeath(testDeathRecipient);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(target->linkToDeath(testDeathRecipient), StatusEq(NO_ERROR));
 
     {
         Parcel data, reply;
@@ -749,14 +727,13 @@
         callback = new BinderLibTestCallBack();
         data.writeStrongBinder(target);
         data.writeStrongBinder(callback);
-        ret = client->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY);
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(client->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply,
+                                     TF_ONE_WAY),
+                    StatusEq(NO_ERROR));
     }
 
-    ret = callback->waitEvent(5);
-    EXPECT_EQ(NO_ERROR, ret);
-    ret = callback->getResult();
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(callback->waitEvent(5), StatusEq(NO_ERROR));
+    EXPECT_THAT(callback->getResult(), StatusEq(NO_ERROR));
 }
 
 TEST_F(BinderLibTest, PassFile) {
@@ -772,17 +749,14 @@
         Parcel data, reply;
         uint8_t writebuf[1] = { write_value };
 
-        ret = data.writeFileDescriptor(pipefd[1], true);
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(data.writeFileDescriptor(pipefd[1], true), StatusEq(NO_ERROR));
 
-        ret = data.writeInt32(sizeof(writebuf));
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(data.writeInt32(sizeof(writebuf)), StatusEq(NO_ERROR));
 
-        ret = data.write(writebuf, sizeof(writebuf));
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(data.write(writebuf, sizeof(writebuf)), StatusEq(NO_ERROR));
 
-        ret = m_server->transact(BINDER_LIB_TEST_WRITE_FILE_TRANSACTION, data, &reply);
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_WRITE_FILE_TRANSACTION, data, &reply),
+                    StatusEq(NO_ERROR));
     }
 
     ret = read(pipefd[0], buf, sizeof(buf));
@@ -863,11 +837,10 @@
 }
 
 TEST_F(BinderLibTest, CheckHandleZeroBinderHighBitsZeroCookie) {
-    status_t ret;
     Parcel data, reply;
 
-    ret = m_server->transact(BINDER_LIB_TEST_GET_SELF_TRANSACTION, data, &reply);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GET_SELF_TRANSACTION, data, &reply),
+                StatusEq(NO_ERROR));
 
     const flat_binder_object *fb = reply.readObject(false);
     ASSERT_TRUE(fb != nullptr);
@@ -887,8 +860,8 @@
     wp<IBinder> keepFreedBinder;
     {
         Parcel data, reply;
-        ret = server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply);
-        ASSERT_EQ(NO_ERROR, ret);
+        ASSERT_THAT(server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply),
+                    StatusEq(NO_ERROR));
         struct flat_binder_object *freed = (struct flat_binder_object *)(reply.data());
         freedHandle = freed->handle;
         /* Add a weak ref to the freed binder so the driver does not
@@ -949,7 +922,6 @@
 }
 
 TEST_F(BinderLibTest, CheckNoHeaderMappedInUser) {
-    status_t ret;
     Parcel data, reply;
     sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack();
     for (int i = 0; i < 2; i++) {
@@ -963,13 +935,12 @@
 
         datai.appendTo(&data);
     }
-    ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply),
+                StatusEq(NO_ERROR));
 }
 
 TEST_F(BinderLibTest, OnewayQueueing)
 {
-    status_t ret;
     Parcel data, data2;
 
     sp<IBinder> pollServer = addPollServer();
@@ -982,25 +953,21 @@
     data2.writeStrongBinder(callBack2);
     data2.writeInt32(0); // delay in us
 
-    ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data, nullptr, TF_ONE_WAY);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data, nullptr, TF_ONE_WAY),
+                StatusEq(NO_ERROR));
 
     // The delay ensures that this second transaction will end up on the async_todo list
     // (for a single-threaded server)
-    ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data2, nullptr, TF_ONE_WAY);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data2, nullptr, TF_ONE_WAY),
+                StatusEq(NO_ERROR));
 
     // The server will ensure that the two transactions are handled in the expected order;
     // If the ordering is not as expected, an error will be returned through the callbacks.
-    ret = callBack->waitEvent(2);
-    EXPECT_EQ(NO_ERROR, ret);
-    ret = callBack->getResult();
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(callBack->waitEvent(2), StatusEq(NO_ERROR));
+    EXPECT_THAT(callBack->getResult(), StatusEq(NO_ERROR));
 
-    ret = callBack2->waitEvent(2);
-    EXPECT_EQ(NO_ERROR, ret);
-    ret = callBack2->getResult();
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(callBack2->waitEvent(2), StatusEq(NO_ERROR));
+    EXPECT_THAT(callBack2->getResult(), StatusEq(NO_ERROR));
 }
 
 TEST_F(BinderLibTest, WorkSourceUnsetByDefault)
@@ -1119,8 +1086,8 @@
     ASSERT_TRUE(server != nullptr);
 
     Parcel data, reply;
-    status_t ret = server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply),
+                StatusEq(NO_ERROR));
 
     int policy = reply.readInt32();
     int priority = reply.readInt32();
@@ -1139,8 +1106,8 @@
     EXPECT_EQ(0, sched_setscheduler(getpid(), SCHED_RR, &param));
 
     Parcel data, reply;
-    status_t ret = server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply),
+                StatusEq(NO_ERROR));
 
     int policy = reply.readInt32();
     int priority = reply.readInt32();
@@ -1157,10 +1124,9 @@
     std::vector<uint64_t> const testValue = { std::numeric_limits<uint64_t>::max(), 0, 200 };
     data.writeUint64Vector(testValue);
 
-    status_t ret = server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply), StatusEq(NO_ERROR));
     std::vector<uint64_t> readValue;
-    ret = reply.readUint64Vector(&readValue);
+    EXPECT_THAT(reply.readUint64Vector(&readValue), StatusEq(OK));
     EXPECT_EQ(readValue, testValue);
 }
 
@@ -1185,11 +1151,18 @@
     memcpy(parcelData, &obj, sizeof(obj));
     data.setDataSize(sizeof(obj));
 
-    status_t ret = server->transact(BINDER_LIB_TEST_REJECT_BUF, data, &reply);
     // Either the kernel should reject this transaction (if it's correct), but
     // if it's not, the server implementation should return an error if it
     // finds an object in the received Parcel.
-    EXPECT_NE(NO_ERROR, ret);
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_REJECT_BUF, data, &reply),
+                Not(StatusEq(NO_ERROR)));
+}
+
+TEST_F(BinderLibTest, GotSid) {
+    sp<IBinder> server = addServer();
+
+    Parcel data;
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr), StatusEq(OK));
 }
 
 class BinderLibTestService : public BBinder
@@ -1494,6 +1467,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..a457e67
--- /dev/null
+++ b/libs/binder/tests/binderRpcBenchmark.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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/RpcServer.h>
+#include <binder/RpcSession.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::RpcServer;
+using android::RpcSession;
+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<RpcSession> gSession = RpcSession::make();
+
+void BM_getRootObject(benchmark::State& state) {
+    while (state.KeepRunning()) {
+        CHECK(gSession->getRootObject() != nullptr);
+    }
+}
+BENCHMARK(BM_getRootObject);
+
+void BM_pingTransaction(benchmark::State& state) {
+    sp<IBinder> binder = gSession->getRootObject();
+    CHECK(binder != nullptr);
+
+    while (state.KeepRunning()) {
+        CHECK_EQ(OK, binder->pingBinder());
+    }
+}
+BENCHMARK(BM_pingTransaction);
+
+void BM_repeatString(benchmark::State& state) {
+    sp<IBinder> binder = gSession->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 = gSession->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();
+        CHECK(server->setupUnixDomainServer(addr.c_str()));
+        server->join();
+    }).detach();
+
+    for (size_t tries = 0; tries < 5; tries++) {
+        usleep(10000);
+        if (gSession->setupUnixDomainClient(addr.c_str())) goto success;
+    }
+    LOG(FATAL) << "Could not connect.";
+success:
+
+    ::benchmark::RunSpecifiedBenchmarks();
+    return 0;
+}
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index a51c987..a96deb5 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -17,6 +17,7 @@
 #include <BnBinderRpcSession.h>
 #include <BnBinderRpcTest.h>
 #include <aidl/IBinderRpcTest.h>
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android/binder_auto_utils.h>
 #include <android/binder_libbinder.h>
@@ -24,8 +25,8 @@
 #include <binder/BpBinder.h>
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
-#include <binder/RpcConnection.h>
 #include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
 #include <gtest/gtest.h>
 
 #include <chrono>
@@ -33,14 +34,11 @@
 #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
+#include "../RpcState.h"   // for debugging
+#include "../vm_sockets.h" // for VMADDR_*
 
 namespace android {
 
@@ -51,6 +49,19 @@
     EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "");
 }
 
+TEST(BinderRpc, SetExternalServer) {
+    base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
+    int sinkFd = sink.get();
+    auto server = RpcServer::make();
+    server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+    ASSERT_FALSE(server->hasServer());
+    ASSERT_TRUE(server->setupExternalServer(std::move(sink)));
+    ASSERT_TRUE(server->hasServer());
+    base::unique_fd retrieved = server->releaseServer();
+    ASSERT_FALSE(server->hasServer());
+    ASSERT_EQ(sinkFd, retrieved.get());
+}
+
 using android::binder::Status;
 
 #define EXPECT_OK(status)                 \
@@ -77,24 +88,30 @@
 
 class MyBinderRpcTest : public BnBinderRpcTest {
 public:
-    sp<RpcConnection> connection;
+    wp<RpcServer> server;
 
     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();
     }
-    Status countBinders(int32_t* out) override {
-        if (connection == nullptr) {
+    Status countBinders(std::vector<int32_t>* out) override {
+        sp<RpcServer> spServer = server.promote();
+        if (spServer == nullptr) {
             return Status::fromExceptionCode(Status::EX_NULL_POINTER);
         }
-        *out = connection->state()->countBinders();
-        if (*out != 1) {
-            connection->state()->dump();
+        out->clear();
+        for (auto session : spServer->listSessions()) {
+            size_t count = session->state()->countBinders();
+            if (count != 1) {
+                // this is called when there is only one binder held remaining,
+                // so to aid debugging
+                session->state()->dump();
+            }
+            out->push_back(count);
         }
         return Status::ok();
     }
@@ -177,14 +194,27 @@
 };
 sp<IBinder> MyBinderRpcTest::mHeldBinder;
 
+class Pipe {
+public:
+    Pipe() { CHECK(android::base::Pipe(&mRead, &mWrite)); }
+    Pipe(Pipe&&) = default;
+    android::base::borrowed_fd readEnd() { return mRead; }
+    android::base::borrowed_fd writeEnd() { return mWrite; }
+
+private:
+    android::base::unique_fd mRead;
+    android::base::unique_fd mWrite;
+};
+
 class Process {
 public:
-    Process(const std::function<void()>& f) {
+    Process(Process&&) = default;
+    Process(const std::function<void(Pipe*)>& f) {
         if (0 == (mPid = fork())) {
             // racey: assume parent doesn't crash before this is set
             prctl(PR_SET_PDEATHSIG, SIGHUP);
 
-            f();
+            f(&mPipe);
         }
     }
     ~Process() {
@@ -192,64 +222,76 @@
             kill(mPid, SIGKILL);
         }
     }
+    Pipe* getPipe() { return &mPipe; }
 
 private:
     pid_t mPid = 0;
+    Pipe mPipe;
 };
 
 static std::string allocateSocketAddress() {
     static size_t id = 0;
-    static bool gUseTmp = access("/tmp/", F_OK) != -1;
-
-    if (gUseTmp) {
-        return "/tmp/binderRpcTest_" + std::to_string(id++);
-    } else {
-        return "/dev/binderRpcTest_" + std::to_string(id++);
-    }
+    std::string temp = getenv("TMPDIR") ?: "/tmp";
+    return temp + "/binderRpcTest_" + std::to_string(id++);
 };
 
-struct ProcessConnection {
+struct ProcessSession {
     // reference to process hosting a socket server
     Process host;
 
-    // client connection object associated with other process
-    sp<RpcConnection> connection;
+    struct SessionInfo {
+        sp<RpcSession> session;
+        sp<IBinder> root;
+    };
 
-    // pre-fetched root object
-    sp<IBinder> rootBinder;
+    // client session objects associated with other process
+    // each one represents a separate session
+    std::vector<SessionInfo> sessions;
 
-    // whether connection should be invalidated by end of run
-    bool expectInvalid = false;
+    ProcessSession(ProcessSession&&) = default;
+    ~ProcessSession() {
+        for (auto& session : sessions) {
+            session.root = nullptr;
+        }
 
-    ~ProcessConnection() {
-        rootBinder = nullptr;
-        EXPECT_NE(nullptr, connection);
-        EXPECT_NE(nullptr, connection->state());
-        EXPECT_EQ(0, connection->state()->countBinders()) << (connection->state()->dump(), "dump:");
+        for (auto& info : sessions) {
+            sp<RpcSession>& session = info.session;
 
-        wp<RpcConnection> weakConnection = connection;
-        connection = nullptr;
-        EXPECT_EQ(nullptr, weakConnection.promote()) << "Leaked connection";
+            EXPECT_NE(nullptr, session);
+            EXPECT_NE(nullptr, session->state());
+            EXPECT_EQ(0, session->state()->countBinders()) << (session->state()->dump(), "dump:");
+
+            wp<RpcSession> weakSession = session;
+            session = nullptr;
+            EXPECT_EQ(nullptr, weakSession.promote()) << "Leaked session";
+        }
     }
 };
 
-// Process connection where the process hosts IBinderRpcTest, the server used
+// Process session where the process hosts IBinderRpcTest, the server used
 // for most testing here
-struct BinderRpcTestProcessConnection {
-    ProcessConnection proc;
+struct BinderRpcTestProcessSession {
+    ProcessSession proc;
 
-    // pre-fetched root object
+    // pre-fetched root object (for first session)
     sp<IBinder> rootBinder;
 
-    // pre-casted root object
+    // pre-casted root object (for first session)
     sp<IBinderRpcTest> rootIface;
 
-    ~BinderRpcTestProcessConnection() {
-        if (!proc.expectInvalid) {
-            int32_t remoteBinders = 0;
-            EXPECT_OK(rootIface->countBinders(&remoteBinders));
-            // should only be the root binder object, iface
-            EXPECT_EQ(remoteBinders, 1);
+    // whether session should be invalidated by end of run
+    bool expectInvalid = false;
+
+    BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default;
+    ~BinderRpcTestProcessSession() {
+        if (!expectInvalid) {
+            std::vector<int32_t> remoteCounts;
+            // calling over any sessions counts across all sessions
+            EXPECT_OK(rootIface->countBinders(&remoteCounts));
+            EXPECT_EQ(remoteCounts.size(), proc.sessions.size());
+            for (auto remoteCount : remoteCounts) {
+                EXPECT_EQ(remoteCount, 1);
+            }
         }
 
         rootIface = nullptr;
@@ -259,18 +301,17 @@
 
 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 "";
@@ -280,92 +321,93 @@
 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);
+    ProcessSession createRpcTestSocketServerProcess(
+            size_t numThreads, size_t numSessions,
+            const std::function<void(const sp<RpcServer>&)>& configure) {
+        CHECK_GE(numSessions, 1) << "Must have at least one session to a server";
 
         SocketType socketType = GetParam();
 
         std::string addr = allocateSocketAddress();
         unlink(addr.c_str());
-        static unsigned int port = 3456;
-        port++;
+        static unsigned int vsockPort = 3456;
+        vsockPort++;
 
-        auto ret = ProcessConnection{
-                .host = Process([&] {
+        auto ret = ProcessSession{
+                .host = Process([&](Pipe* pipe) {
                     sp<RpcServer> server = RpcServer::make();
 
                     server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+                    server->setMaxThreads(numThreads);
 
-                    // server supporting one client on one socket
-                    sp<RpcConnection> connection = server->addClientConnection();
+                    unsigned int outPort = 0;
 
                     switch (socketType) {
                         case SocketType::UNIX:
-                            CHECK(connection->setupUnixDomainServer(addr.c_str())) << addr;
+                            CHECK(server->setupUnixDomainServer(addr.c_str())) << addr;
                             break;
-#ifdef __BIONIC__
                         case SocketType::VSOCK:
-                            CHECK(connection->setupVsockServer(port));
+                            CHECK(server->setupVsockServer(vsockPort));
                             break;
-#endif // __BIONIC__
+                        case SocketType::INET: {
+                            CHECK(server->setupInetServer(0, &outPort));
+                            CHECK_NE(0, outPort);
+                            break;
+                        }
                         default:
                             LOG_ALWAYS_FATAL("Unknown socket type");
                     }
 
-                    configure(server, connection);
+                    CHECK(android::base::WriteFully(pipe->writeEnd(), &outPort, sizeof(outPort)));
 
-                    // 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();
+                    configure(server);
+
+                    server->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__
-                    default:
-                        LOG_ALWAYS_FATAL("Unknown socket type");
-                }
-            }
-            LOG_ALWAYS_FATAL("Could not connect");
-        success:;
+        // always read socket, so that we have waited for the server to start
+        unsigned int outPort = 0;
+        CHECK(android::base::ReadFully(ret.host.getPipe()->readEnd(), &outPort, sizeof(outPort)));
+        if (socketType == SocketType::INET) {
+            CHECK_NE(0, outPort);
         }
 
-        ret.rootBinder = ret.connection->getRootObject();
+        for (size_t i = 0; i < numSessions; i++) {
+            sp<RpcSession> session = RpcSession::make();
+            switch (socketType) {
+                case SocketType::UNIX:
+                    if (session->setupUnixDomainClient(addr.c_str())) goto success;
+                    break;
+                case SocketType::VSOCK:
+                    if (session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort)) goto success;
+                    break;
+                case SocketType::INET:
+                    if (session->setupInetClient("127.0.0.1", outPort)) goto success;
+                    break;
+                default:
+                    LOG_ALWAYS_FATAL("Unknown socket type");
+            }
+            LOG_ALWAYS_FATAL("Could not connect");
+        success:
+            ret.sessions.push_back({session, session->getRootObject()});
+        }
         return ret;
     }
 
-    BinderRpcTestProcessConnection createRpcTestSocketServerProcess(size_t numThreads) {
-        BinderRpcTestProcessConnection ret{
-                .proc = createRpcTestSocketServerProcess(numThreads,
-                                                         [&](const sp<RpcServer>& server,
-                                                             const sp<RpcConnection>& connection) {
+    BinderRpcTestProcessSession createRpcTestSocketServerProcess(size_t numThreads,
+                                                                 size_t numSessions = 1) {
+        BinderRpcTestProcessSession ret{
+                .proc = createRpcTestSocketServerProcess(numThreads, numSessions,
+                                                         [&](const sp<RpcServer>& server) {
                                                              sp<MyBinderRpcTest> service =
                                                                      new MyBinderRpcTest;
                                                              server->setRootObject(service);
-                                                             service->connection =
-                                                                     connection; // for testing only
+                                                             service->server = server;
                                                          }),
         };
 
-        ret.rootBinder = ret.proc.rootBinder;
+        ret.rootBinder = ret.proc.sessions.at(0).root;
         ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
 
         return ret;
@@ -373,18 +415,12 @@
 };
 
 TEST_P(BinderRpc, RootObjectIsNull) {
-    auto proc = createRpcTestSocketServerProcess(1,
-                                                 [](const sp<RpcServer>& server,
-                                                    const sp<RpcConnection>&) {
-                                                     // this is the default, but to be explicit
-                                                     server->setRootObject(nullptr);
-                                                 });
+    auto proc = createRpcTestSocketServerProcess(1, 1, [](const sp<RpcServer>& server) {
+        // this is the default, but to be explicit
+        server->setRootObject(nullptr);
+    });
 
-    // retrieved by getRootObject when process is created above
-    EXPECT_EQ(nullptr, proc.rootBinder);
-
-    // make sure we can retrieve it again (process doesn't crash)
-    EXPECT_EQ(nullptr, proc.connection->getRootObject());
+    EXPECT_EQ(nullptr, proc.sessions.at(0).root);
 }
 
 TEST_P(BinderRpc, Ping) {
@@ -399,6 +435,14 @@
     EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
 }
 
+TEST_P(BinderRpc, MultipleSessions) {
+    auto proc = createRpcTestSocketServerProcess(1 /*threads*/, 5 /*sessions*/);
+    for (auto session : proc.proc.sessions) {
+        ASSERT_NE(nullptr, session.root);
+        EXPECT_EQ(OK, session.root->pingBinder());
+    }
+}
+
 TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) {
     auto proc = createRpcTestSocketServerProcess(1);
     Parcel data;
@@ -537,7 +581,7 @@
 // These are behavioral differences form regular binder, where certain usecases
 // aren't supported.
 
-TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketConnections) {
+TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) {
     auto proc1 = createRpcTestSocketServerProcess(1);
     auto proc2 = createRpcTestSocketServerProcess(1);
 
@@ -546,6 +590,15 @@
               proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
 }
 
+TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
+    auto proc = createRpcTestSocketServerProcess(1 /*threads*/, 2 /*sessions*/);
+
+    sp<IBinder> outBinder;
+    EXPECT_EQ(INVALID_OPERATION,
+              proc.rootIface->repeatBinder(proc.proc.sessions.at(1).root, &outBinder)
+                      .transactionError());
+}
+
 TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
     auto proc = createRpcTestSocketServerProcess(1);
 
@@ -745,7 +798,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);
             }
         }));
@@ -754,6 +807,28 @@
     for (auto& t : threads) t.join();
 }
 
+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;
@@ -808,7 +883,7 @@
         EXPECT_EQ(DEAD_OBJECT, proc.rootIface->die(doDeathCleanup).transactionError())
                 << "Do death cleanup: " << doDeathCleanup;
 
-        proc.proc.expectInvalid = true;
+        proc.expectInvalid = true;
     }
 }
 
@@ -857,14 +932,44 @@
 }
 
 INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc,
-                        ::testing::Values(SocketType::UNIX
+                        ::testing::ValuesIn({
+                                SocketType::UNIX,
+// TODO(b/185269356): working on host
 #ifdef __BIONIC__
-                                          ,
-                                          SocketType::VSOCK
-#endif // __BIONIC__
-                                          ),
+                                SocketType::VSOCK,
+#endif
+                                SocketType::INET,
+                        }),
                         PrintSocketType);
 
+class BinderRpcServerRootObject : public ::testing::TestWithParam<std::tuple<bool, bool>> {};
+
+TEST_P(BinderRpcServerRootObject, WeakRootObject) {
+    using SetFn = std::function<void(RpcServer*, sp<IBinder>)>;
+    auto setRootObject = [](bool isStrong) -> SetFn {
+        return isStrong ? SetFn(&RpcServer::setRootObject) : SetFn(&RpcServer::setRootObjectWeak);
+    };
+
+    auto server = RpcServer::make();
+    auto [isStrong1, isStrong2] = GetParam();
+    auto binder1 = sp<BBinder>::make();
+    IBinder* binderRaw1 = binder1.get();
+    setRootObject(isStrong1)(server.get(), binder1);
+    EXPECT_EQ(binderRaw1, server->getRootObject());
+    binder1.clear();
+    EXPECT_EQ((isStrong1 ? binderRaw1 : nullptr), server->getRootObject());
+
+    auto binder2 = sp<BBinder>::make();
+    IBinder* binderRaw2 = binder2.get();
+    setRootObject(isStrong2)(server.get(), binder2);
+    EXPECT_EQ(binderRaw2, server->getRootObject());
+    binder2.clear();
+    EXPECT_EQ((isStrong2 ? binderRaw2 : nullptr), server->getRootObject());
+}
+
+INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerRootObject,
+                        ::testing::Combine(::testing::Bool(), ::testing::Bool()));
+
 } // namespace android
 
 int main(int argc, char** argv) {
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 cb309bd..2ce13df 100644
--- a/libs/binder/tests/binderStabilityTest.cpp
+++ b/libs/binder/tests/binderStabilityTest.cpp
@@ -192,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/binder/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
similarity index 100%
rename from libs/binder/parcel_fuzzer/Android.bp
rename to libs/binder/tests/parcel_fuzzer/Android.bp
diff --git a/libs/binder/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/binder.cpp
rename to libs/binder/tests/parcel_fuzzer/binder.cpp
diff --git a/libs/binder/parcel_fuzzer/binder.h b/libs/binder/tests/parcel_fuzzer/binder.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/binder.h
rename to libs/binder/tests/parcel_fuzzer/binder.h
diff --git a/libs/binder/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/binder_ndk.cpp
rename to libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
diff --git a/libs/binder/parcel_fuzzer/binder_ndk.h b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
similarity index 97%
rename from libs/binder/parcel_fuzzer/binder_ndk.h
rename to libs/binder/tests/parcel_fuzzer/binder_ndk.h
index e69d9c1..cf24ab9 100644
--- a/libs/binder/parcel_fuzzer/binder_ndk.h
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
@@ -23,7 +23,7 @@
 
 // libbinder_ndk doesn't export this header which breaks down its API for NDK
 // and APEX users, but we need access to it to fuzz.
-#include "../ndk/parcel_internal.h"
+#include "../../ndk/parcel_internal.h"
 
 class NdkParcelAdapter {
 public:
diff --git a/libs/binder/parcel_fuzzer/hwbinder.cpp b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/hwbinder.cpp
rename to libs/binder/tests/parcel_fuzzer/hwbinder.cpp
diff --git a/libs/binder/parcel_fuzzer/hwbinder.h b/libs/binder/tests/parcel_fuzzer/hwbinder.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/hwbinder.h
rename to libs/binder/tests/parcel_fuzzer/hwbinder.h
diff --git a/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
rename to libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
diff --git a/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
similarity index 80%
rename from libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
rename to libs/binder/tests/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/tests/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/tests/parcel_fuzzer/main.cpp
similarity index 89%
rename from libs/binder/parcel_fuzzer/main.cpp
rename to libs/binder/tests/parcel_fuzzer/main.cpp
index 78606cc..a47b753 100644
--- a/libs/binder/parcel_fuzzer/main.cpp
+++ b/libs/binder/tests/parcel_fuzzer/main.cpp
@@ -23,6 +23,7 @@
 #include <iostream>
 
 #include <android-base/logging.h>
+#include <binder/RpcSession.h>
 #include <fuzzbinder/random_parcel.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
@@ -32,6 +33,8 @@
 #include <sys/time.h>
 
 using android::fillRandomParcel;
+using android::RpcSession;
+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 session = sp<RpcSession>::make();
+            CHECK(session->addNullDebuggingClient());
+            p.markForRpc(session);
+            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/parcel_fuzzer.h b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/parcel_fuzzer.h
rename to libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
diff --git a/libs/binder/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/random_fd.cpp
rename to libs/binder/tests/parcel_fuzzer/random_fd.cpp
diff --git a/libs/binder/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
similarity index 93%
rename from libs/binder/parcel_fuzzer/random_parcel.cpp
rename to libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index 9ca4c8a..b045a22 100644
--- a/libs/binder/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/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/parcel_fuzzer/util.cpp b/libs/binder/tests/parcel_fuzzer/util.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/util.cpp
rename to libs/binder/tests/parcel_fuzzer/util.cpp
diff --git a/libs/binder/parcel_fuzzer/util.h b/libs/binder/tests/parcel_fuzzer/util.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/util.h
rename to libs/binder/tests/parcel_fuzzer/util.h
diff --git a/libs/binder/tests/rpc_fuzzer/Android.bp b/libs/binder/tests/rpc_fuzzer/Android.bp
new file mode 100644
index 0000000..1c75306
--- /dev/null
+++ b/libs/binder/tests/rpc_fuzzer/Android.bp
@@ -0,0 +1,40 @@
+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_fuzz {
+    name: "binder_rpc_fuzzer",
+    host_supported: true,
+
+    fuzz_config: {
+        cc: ["smoreland@google.com"],
+    },
+
+    srcs: [
+        "main.cpp",
+    ],
+    static_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+
+    target: {
+        android: {
+            shared_libs: [
+                "libbinder",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libbinder",
+            ],
+        },
+    },
+}
diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp
new file mode 100644
index 0000000..3603ebe
--- /dev/null
+++ b/libs/binder/tests/rpc_fuzzer/main.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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 <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <binder/Binder.h>
+#include <binder/Parcel.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
+
+#include <sys/resource.h>
+#include <sys/un.h>
+
+namespace android {
+
+static const std::string kSock = std::string(getenv("TMPDIR") ?: "/tmp") +
+        "/binderRpcFuzzerSocket_" + std::to_string(getpid());
+
+size_t getHardMemoryLimit() {
+    struct rlimit limit;
+    CHECK(0 == getrlimit(RLIMIT_AS, &limit)) << errno;
+    return limit.rlim_max;
+}
+
+void setMemoryLimit(size_t cur, size_t max) {
+    const struct rlimit kLimit = {
+            .rlim_cur = cur,
+            .rlim_max = max,
+    };
+    CHECK(0 == setrlimit(RLIMIT_AS, &kLimit)) << errno;
+}
+
+class SomeBinder : public BBinder {
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) {
+        (void)flags;
+
+        if ((code & 1) == 0) {
+            sp<IBinder> binder;
+            (void)data.readStrongBinder(&binder);
+            if (binder != nullptr) {
+                (void)binder->pingBinder();
+            }
+        }
+        if ((code & 2) == 0) {
+            (void)data.readInt32();
+        }
+        if ((code & 4) == 0) {
+            (void)reply->writeStrongBinder(sp<BBinder>::make());
+        }
+
+        return OK;
+    }
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    if (size > 50000) return 0;
+
+    unlink(kSock.c_str());
+
+    sp<RpcServer> server = RpcServer::make();
+    server->setRootObject(sp<SomeBinder>::make());
+    server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+    CHECK(server->setupUnixDomainServer(kSock.c_str()));
+
+    static constexpr size_t kMemLimit = 1llu * 1024 * 1024 * 1024;
+    size_t hardLimit = getHardMemoryLimit();
+    setMemoryLimit(std::min(kMemLimit, hardLimit), hardLimit);
+
+    std::thread serverThread([=] { (void)server->acceptOne(); });
+
+    sockaddr_un addr{
+            .sun_family = AF_UNIX,
+    };
+    CHECK_LT(kSock.size(), sizeof(addr.sun_path));
+    memcpy(&addr.sun_path, kSock.c_str(), kSock.size());
+
+    base::unique_fd clientFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+    CHECK_NE(clientFd.get(), -1);
+    CHECK_EQ(0,
+             TEMP_FAILURE_RETRY(
+                     connect(clientFd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr))))
+            << strerror(errno);
+
+    serverThread.join();
+
+    // TODO(b/182938024): fuzz multiple sessions, instead of just one
+
+#if 0
+    // make fuzzer more productive locally by forcing it to create a new session
+    int32_t id = -1;
+    CHECK(base::WriteFully(clientFd, &id, sizeof(id)));
+#endif
+
+    CHECK(base::WriteFully(clientFd, data, size));
+
+    clientFd.reset();
+
+    // TODO(b/185167543): better way to force a server to shutdown
+    while (!server->listSessions().empty() && server->numUninitializedSessions()) {
+        usleep(1);
+    }
+
+    setMemoryLimit(hardLimit, hardLimit);
+
+    return 0;
+}
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/Android.bp b/libs/binder/tests/unit_fuzzers/Android.bp
similarity index 100%
rename from libs/binder/tests/fuzzers/Android.bp
rename to libs/binder/tests/unit_fuzzers/Android.bp
diff --git a/libs/binder/tests/fuzzers/BinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BinderFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/BinderFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/BinderFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/BinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/BinderFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/BpBinderFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp b/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/IBinderFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp b/libs/binder/tests/unit_fuzzers/MemoryDealerFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/MemoryDealerFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp b/libs/binder/tests/unit_fuzzers/PersistableBundleFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/PersistableBundleFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/PersistableBundleFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/PersistableBundleFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/StabilityFuzz.cpp b/libs/binder/tests/unit_fuzzers/StabilityFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/StabilityFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/StabilityFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/StabilityFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/StabilityFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/StabilityFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/StatusFuzz.cpp b/libs/binder/tests/unit_fuzzers/StatusFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/StatusFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/StatusFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/StatusFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/StatusFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/StatusFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/StatusFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/TextOutputFuzz.cpp b/libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/TextOutputFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/commonFuzzHelpers.h b/libs/binder/tests/unit_fuzzers/commonFuzzHelpers.h
similarity index 100%
rename from libs/binder/tests/fuzzers/commonFuzzHelpers.h
rename to libs/binder/tests/unit_fuzzers/commonFuzzHelpers.h
diff --git a/libs/binder/vm_sockets.h b/libs/binder/vm_sockets.h
new file mode 100644
index 0000000..7d9732b
--- /dev/null
+++ b/libs/binder/vm_sockets.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   Copied and modified from bionic/libc/kernel/uapi/linux/vm_sockets.h
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#pragma once
+
+#ifdef __BIONIC__
+#include <linux/vm_sockets.h>
+#else
+
+#ifndef _UAPI_VM_SOCKETS_H
+#define _UAPI_VM_SOCKETS_H
+#define SO_VM_SOCKETS_BUFFER_SIZE 0
+#define SO_VM_SOCKETS_BUFFER_MIN_SIZE 1
+#define SO_VM_SOCKETS_BUFFER_MAX_SIZE 2
+#define SO_VM_SOCKETS_PEER_HOST_VM_ID 3
+#define SO_VM_SOCKETS_TRUSTED 5
+#define SO_VM_SOCKETS_CONNECT_TIMEOUT 6
+#define SO_VM_SOCKETS_NONBLOCK_TXRX 7
+#define VMADDR_CID_ANY (-1U)
+#define VMADDR_PORT_ANY (-1U)
+#define VMADDR_CID_HYPERVISOR 0
+#define VMADDR_CID_LOCAL 1
+#define VMADDR_CID_HOST 2
+#define VM_SOCKETS_INVALID_VERSION (-1U)
+#define VM_SOCKETS_VERSION_EPOCH(_v) (((_v)&0xFF000000) >> 24)
+#define VM_SOCKETS_VERSION_MAJOR(_v) (((_v)&0x00FF0000) >> 16)
+#define VM_SOCKETS_VERSION_MINOR(_v) (((_v)&0x0000FFFF))
+struct sockaddr_vm {
+    sa_family_t svm_family;
+    // NOLINTNEXTLINE(google-runtime-int)
+    unsigned short svm_reserved1;
+    unsigned int svm_port;
+    unsigned int svm_cid;
+    // NOLINTNEXTLINE(google-runtime-int)
+    unsigned char svm_zero[sizeof(struct sockaddr) - sizeof(sa_family_t) - sizeof(unsigned short) -
+                           sizeof(unsigned int) - sizeof(unsigned int)];
+};
+#define IOCTL_VM_SOCKETS_GET_LOCAL_CID _IO(7, 0xb9)
+#ifndef AF_VSOCK
+#define AF_VSOCK 40
+#endif
+#endif
+
+#endif
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 8a82c2a..29c788b 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -47,6 +47,7 @@
 
 // Native processes to dump on debuggable builds.
 static const char* debuggable_native_processes_to_dump[] = {
+        "/system/bin/keystore2",
         "/system/bin/vold",
         NULL,
 };
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 e5afd40..a2868c6 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));
@@ -212,7 +213,8 @@
             // 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);
+                t.setDestinationFrame(mSurfaceControl,
+                                      Rect(0, 0, newSize.getWidth(), newSize.getHeight()));
             }
             applyTransaction = true;
         }
@@ -242,29 +244,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);
@@ -295,6 +317,11 @@
     std::unique_lock _lock{mMutex};
     BQA_LOGV("releaseBufferCallback graphicBufferId=%" PRIu64, graphicBufferId);
 
+    if (mSurfaceControl != nullptr) {
+        mTransformHint = mSurfaceControl->getTransformHint();
+        mBufferItemConsumer->setTransformHint(mTransformHint);
+    }
+
     auto it = mSubmitted.find(graphicBufferId);
     if (it == mSubmitted.end()) {
         BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %" PRIu64,
@@ -377,10 +404,11 @@
     // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
     incStrong((void*)transactionCallbackThunk);
 
+    Rect crop = computeCrop(bufferItem);
     mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
     mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(),
                            bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
-                           bufferItem.mScalingMode);
+                           bufferItem.mScalingMode, crop);
 
     auto releaseBufferCallback =
             std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
@@ -392,9 +420,10 @@
     t->setAcquireFence(mSurfaceControl,
                        bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
     t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
+    mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
 
-    setMatrix(t, mLastBufferInfo);
-    t->setCrop(mSurfaceControl, computeCrop(bufferItem));
+    t->setDestinationFrame(mSurfaceControl, Rect(0, 0, mSize.getWidth(), mSize.getHeight()));
+    t->setBufferCrop(mSurfaceControl, crop);
     t->setTransform(mSurfaceControl, bufferItem.mTransform);
     t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
     if (!bufferItem.mIsAutoTimestamp) {
@@ -498,6 +527,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;
     }
@@ -519,17 +549,6 @@
     return mSize != bufferSize;
 }
 
-void BLASTBufferQueue::setMatrix(SurfaceComposerClient::Transaction* t,
-                                 const BufferInfo& bufferInfo) {
-    uint32_t bufWidth = bufferInfo.width;
-    uint32_t bufHeight = bufferInfo.height;
-
-    float dsdx = mSize.width / static_cast<float>(bufWidth);
-    float dsdy = mSize.height / static_cast<float>(bufHeight);
-
-    t->setMatrix(mSurfaceControl, dsdx, 0, 0, dsdy);
-}
-
 void BLASTBufferQueue::setTransactionCompleteCallback(
         uint64_t frameNumber, std::function<void(int64_t)>&& transactionCompleteCallback) {
     std::lock_guard _lock{mMutex};
@@ -582,6 +601,14 @@
     status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override {
         return mBbq->setFrameTimelineInfo(frameTimelineInfo);
     }
+ protected:
+    uint32_t getTransformHint() const override {
+        if (mStickyTransform == 0 && !transformToDisplayInverse()) {
+            return mBbq->getLastTransformHint();
+        } else {
+            return 0;
+        }
+    }
 };
 
 // TODO: Can we coalesce this with frame updates? Need to confirm
@@ -640,12 +667,12 @@
     void run() {
         std::unique_lock<std::mutex> lock(mMutex);
         while (!mDone) {
-            mCv.wait(lock);
             while (!mRunnables.empty()) {
                 std::function<void()> runnable = mRunnables.front();
                 mRunnables.pop_front();
                 runnable();
             }
+            mCv.wait(lock);
         }
     }
 
@@ -751,4 +778,12 @@
     return convertedFormat;
 }
 
+uint32_t BLASTBufferQueue::getLastTransformHint() const {
+    if (mSurfaceControl != nullptr) {
+        return mSurfaceControl->getTransformHint();
+    } else {
+        return 0;
+    }
+}
+
 } // namespace android
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 454aa9e..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());
@@ -1246,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);
             }
 
@@ -2021,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/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp
index e707684..0827bbe 100644
--- a/libs/gui/LayerDebugInfo.cpp
+++ b/libs/gui/LayerDebugInfo.cpp
@@ -119,9 +119,11 @@
     info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion");
     if (info.mStretchEffect.hasEffect()) {
         const auto& se = info.mStretchEffect;
-        StringAppendF(&result, "  StretchEffect area=[%f, %f, %f, %f] vec=(%f, %f) maxAmount=%f\n",
-                      se.area.left, se.area.top, se.area.right, se.area.bottom, se.vectorX,
-                      se.vectorY, se.maxAmount);
+        StringAppendF(&result,
+                      "  StretchEffect width = %f, height = %f vec=(%f, %f) "
+                      "maxAmount=(%f, %f)\n",
+                      se.width, se.height,
+                      se.vectorX, se.vectorY, se.maxAmountX, se.maxAmountY);
     }
 
     StringAppendF(&result, "      layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ",
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 0ca4977..e65c721 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -45,7 +45,6 @@
         reserved(0),
         cornerRadius(0.0f),
         backgroundBlurRadius(0),
-        barrierFrameNumber(0),
         transform(0),
         transformToDisplayInverse(false),
         crop(Rect::INVALID_RECT),
@@ -65,6 +64,8 @@
         fixedTransformHint(ui::Transform::ROT_INVALID),
         frameNumber(0),
         autoRefresh(false),
+        bufferCrop(Rect::INVALID_RECT),
+        destinationFrame(Rect::INVALID_RECT),
         releaseBufferListener(nullptr) {
     matrix.dsdx = matrix.dtdy = 1.0f;
     matrix.dsdy = matrix.dtdx = 0.0f;
@@ -87,9 +88,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);
@@ -101,7 +100,6 @@
     SAFE_PARCEL(output.write, transparentRegion);
     SAFE_PARCEL(output.writeUint32, transform);
     SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
-    SAFE_PARCEL(output.write, crop);
     SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
 
     if (buffer) {
@@ -143,7 +141,7 @@
 
     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);
@@ -170,6 +168,8 @@
     }
 
     SAFE_PARCEL(output.write, stretchEffect);
+    SAFE_PARCEL(output.write, bufferCrop);
+    SAFE_PARCEL(output.write, destinationFrame);
 
     return NO_ERROR;
 }
@@ -193,9 +193,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);
@@ -214,7 +212,6 @@
     SAFE_PARCEL(input.read, transparentRegion);
     SAFE_PARCEL(input.readUint32, &transform);
     SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
-    SAFE_PARCEL(input.read, crop);
     SAFE_PARCEL(input.read, orientedDisplaySpaceRect);
 
     bool tmpBool = false;
@@ -263,7 +260,7 @@
         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);
@@ -301,6 +298,8 @@
     }
 
     SAFE_PARCEL(input.read, stretchEffect);
+    SAFE_PARCEL(input.read, bufferCrop);
+    SAFE_PARCEL(input.read, destinationFrame);
 
     return NO_ERROR;
 }
@@ -425,15 +424,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;
@@ -553,6 +543,14 @@
         what |= eStretchChanged;
         stretchEffect = other.stretchEffect;
     }
+    if (other.what & eBufferCropChanged) {
+        what |= eBufferCropChanged;
+        bufferCrop = other.bufferCrop;
+    }
+    if (other.what & eDestinationFrameChanged) {
+        what |= eDestinationFrameChanged;
+        destinationFrame = other.destinationFrame;
+    }
     if ((other.what & what) != other.what) {
         ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
               "other.what=0x%" PRIu64 " what=0x%" PRIu64,
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 2fc9d47..83aaf36 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1288,7 +1288,7 @@
                         mUserHeight ? mUserHeight : mDefaultHeight);
                 return NO_ERROR;
             case NATIVE_WINDOW_TRANSFORM_HINT:
-                *value = static_cast<int>(mTransformHint);
+                *value = static_cast<int>(getTransformHint());
                 return NO_ERROR;
             case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: {
                 status_t err = NO_ERROR;
@@ -1822,7 +1822,7 @@
     return getExtraBufferCount(extraBuffers);
 }
 
-bool Surface::transformToDisplayInverse() {
+bool Surface::transformToDisplayInverse() const {
     return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
             NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
 }
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index c888312..aa93808 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;
@@ -1417,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,
@@ -1427,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);
@@ -1632,8 +1648,7 @@
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setStretchEffect(
-        const sp<SurfaceControl>& sc, float left, float top, float right, float bottom, float vecX,
-        float vecY, float maxAmount) {
+    const sp<SurfaceControl>& sc, const StretchEffect& stretchEffect) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
@@ -1641,10 +1656,37 @@
     }
 
     s->what |= layer_state_t::eStretchChanged;
-    s->stretchEffect = StretchEffect{.area = {left, top, right, bottom},
-                                     .vectorX = vecX,
-                                     .vectorY = vecY,
-                                     .maxAmount = maxAmount};
+    s->stretchEffect = stretchEffect;
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferCrop(
+        const sp<SurfaceControl>& sc, const Rect& bufferCrop) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    s->what |= layer_state_t::eBufferCropChanged;
+    s->bufferCrop = bufferCrop;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDestinationFrame(
+        const sp<SurfaceControl>& sc, const Rect& destinationFrame) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    s->what |= layer_state_t::eDestinationFrameChanged;
+    s->destinationFrame = destinationFrame;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -1824,7 +1866,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;
@@ -1977,6 +2020,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..d7c07b9 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(mBbqChild, 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 a48f95a..c4ca399 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -103,6 +103,8 @@
 
     void setSidebandStream(const sp<NativeHandle>& stream);
 
+    uint32_t getLastTransformHint() const;
+
     virtual ~BLASTBufferQueue();
 
 private:
@@ -151,14 +153,20 @@
         // 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) {
+                    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);
+            }
         }
     };
 
@@ -203,6 +211,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 ccd6d4e..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;
@@ -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/JankInfo.h b/libs/gui/include/gui/JankInfo.h
index 85ae9cb..ce9716f 100644
--- a/libs/gui/include/gui/JankInfo.h
+++ b/libs/gui/include/gui/JankInfo.h
@@ -42,6 +42,10 @@
     BufferStuffing = 0x40,
     // Jank due to unknown reasons.
     Unknown = 0x80,
+    // SF is said to be stuffed if the previous frame ran longer than expected resulting in the case
+    // where the previous frame was presented in the current frame's expected vsync. This pushes the
+    // current frame to the next vsync. The behavior is similar to BufferStuffing.
+    SurfaceFlingerStuffing = 0x100,
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 706a09c..16430b3 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -82,12 +82,10 @@
         eTransparentRegionChanged = 0x00000020,
         eFlagsChanged = 0x00000040,
         eLayerStackChanged = 0x00000080,
-        /* was eCropChanged_legacy, now available 0x00000100, */
-        eDeferTransaction_legacy = 0x00000200,
         eReleaseBufferListenerChanged = 0x00000400,
         eShadowRadiusChanged = 0x00000800,
-        eReparentChildren = 0x00001000,
-        /* was eDetachChildren, now available 0x00002000, */
+        eLayerCreated = 0x00001000,
+        eBufferCropChanged = 0x00002000,
         eRelativeLayerChanged = 0x00004000,
         eReparent = 0x00008000,
         eColorChanged = 0x00010000,
@@ -106,7 +104,7 @@
         eHasListenerCallbacksChanged = 0x20000000,
         eInputInfoChanged = 0x40000000,
         eCornerRadiusChanged = 0x80000000,
-        /* was eFrameChanged, now available 0x1'00000000, */
+        eDestinationFrameChanged = 0x1'00000000,
         eCachedBufferChanged = 0x2'00000000,
         eBackgroundColorChanged = 0x4'00000000,
         eMetadataChanged = 0x8'00000000,
@@ -153,9 +151,7 @@
     matrix22_t matrix;
     float cornerRadius;
     uint32_t backgroundBlurRadius;
-    sp<SurfaceControl> barrierSurfaceControl_legacy;
     sp<SurfaceControl> reparentSurfaceControl;
-    uint64_t barrierFrameNumber;
 
     sp<SurfaceControl> relativeLayerSurfaceControl;
 
@@ -231,6 +227,9 @@
     // Stretch effect to be applied to this layer
     StretchEffect stretchEffect;
 
+    Rect bufferCrop;
+    Rect destinationFrame;
+
     // Listens to when the buffer is safe to be released. This is used for blast
     // layers only. The callback includes a release fence as well as the graphic
     // buffer id to identify the buffer.
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index d22bdaa..89e1909 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -278,7 +278,6 @@
     int dispatchGetLastQueuedBuffer(va_list args);
     int dispatchSetFrameTimelineInfo(va_list args);
     int dispatchGetExtraBufferCount(va_list args);
-    bool transformToDisplayInverse();
 
 protected:
     virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
@@ -490,6 +489,8 @@
     // mTransformHint is the transform probably applied to buffers of this
     // window. this is only a hint, actual transform may differ.
     uint32_t mTransformHint;
+    virtual uint32_t getTransformHint() const { return mTransformHint; }
+    bool transformToDisplayInverse() const;
 
     // mProducerControlledByApp whether this buffer producer is controlled
     // by the application
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 731af58..0940e9d 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);
@@ -496,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.
@@ -544,9 +538,24 @@
         // transactions from blocking each other.
         Transaction& setApplyToken(const sp<IBinder>& token);
 
-        Transaction& setStretchEffect(const sp<SurfaceControl>& sc, float left, float top,
-                                      float right, float bottom, float vecX, float vecY,
-                                      float maxAmount);
+        /**
+         * Provides the stretch effect configured on a container that the
+         * surface is rendered within.
+         * @param sc target surface the stretch should be applied to
+         * @param stretchEffect the corresponding stretch effect to be applied
+         *    to the surface. This can be directly on the surface itself or
+         *    configured from a parent of the surface in which case the
+         *    StretchEffect provided has parameters mapping the position of
+         *    the surface within the container that has the stretch configured
+         *    on it
+         * @return The transaction being constructed
+         */
+        Transaction& setStretchEffect(const sp<SurfaceControl>& sc,
+                                      const StretchEffect& stretchEffect);
+
+        Transaction& setBufferCrop(const sp<SurfaceControl>& sc, const Rect& bufferCrop);
+        Transaction& setDestinationFrame(const sp<SurfaceControl>& sc,
+                                         const Rect& destinationFrame);
 
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
@@ -579,6 +588,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);
 
@@ -631,14 +642,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>
@@ -656,7 +666,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);
@@ -672,10 +683,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 9b1f0db..5a5da97 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -522,16 +522,146 @@
     adapter.waitForCallbacks();
     // capture screen and verify that it is red
     ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
-
-    Rect bounds;
-    bounds.left = finalCropSideLength / 2;
-    bounds.top = 0;
-    bounds.right = bounds.left + finalCropSideLength;
-    bounds.bottom = finalCropSideLength;
-
-    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b, bounds));
+    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, bounds, /*border*/ 0, /*outsideRegion*/ true));
+            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 {
@@ -836,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;
 };
 
@@ -911,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;
@@ -1010,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 751b95a..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;
     }
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 5600eb3..649a140 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -340,7 +340,8 @@
                              int32_t flags, int32_t edgeFlags, int32_t metaState,
                              int32_t buttonState, MotionClassification classification,
                              const ui::Transform& transform, float xPrecision, float yPrecision,
-                             float rawXCursorPosition, float rawYCursorPosition, nsecs_t downTime,
+                             float rawXCursorPosition, float rawYCursorPosition,
+                             int32_t displayWidth, int32_t displayHeight, nsecs_t downTime,
                              nsecs_t eventTime, size_t pointerCount,
                              const PointerProperties* pointerProperties,
                              const PointerCoords* pointerCoords) {
@@ -357,6 +358,8 @@
     mYPrecision = yPrecision;
     mRawXCursorPosition = rawXCursorPosition;
     mRawYCursorPosition = rawYCursorPosition;
+    mDisplayWidth = displayWidth;
+    mDisplayHeight = displayHeight;
     mDownTime = downTime;
     mPointerProperties.clear();
     mPointerProperties.appendArray(pointerProperties, pointerCount);
@@ -380,6 +383,8 @@
     mYPrecision = other->mYPrecision;
     mRawXCursorPosition = other->mRawXCursorPosition;
     mRawYCursorPosition = other->mRawYCursorPosition;
+    mDisplayWidth = other->mDisplayWidth;
+    mDisplayHeight = other->mDisplayHeight;
     mDownTime = other->mDownTime;
     mPointerProperties = other->mPointerProperties;
 
@@ -426,7 +431,7 @@
 }
 
 float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const {
-    return getRawPointerCoords(pointerIndex)->getAxisValue(axis);
+    return getHistoricalRawAxisValue(axis, pointerIndex, getHistorySize());
 }
 
 float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const {
@@ -440,7 +445,32 @@
 
 float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
         size_t historicalIndex) const {
-    return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+    if (axis != AMOTION_EVENT_AXIS_X && axis != AMOTION_EVENT_AXIS_Y) {
+        return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+    }
+    // 0x7 encapsulates all 3 rotations (see ui::Transform::RotationFlags)
+    static const int ALL_ROTATIONS_MASK = 0x7;
+    uint32_t orientation = (mTransform.getOrientation() & ALL_ROTATIONS_MASK);
+    if (orientation == ui::Transform::ROT_0) {
+        return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
+    }
+
+    // For compatibility, convert raw coordinates into "oriented screen space". Once app developers
+    // are educated about getRaw, we can consider removing this.
+    vec2 xy = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getXYValue();
+    const float unrotatedX = xy.x;
+    if (orientation == ui::Transform::ROT_90) {
+        xy.x = mDisplayHeight - xy.y;
+        xy.y = unrotatedX;
+    } else if (orientation == ui::Transform::ROT_180) {
+        xy.x = mDisplayWidth - xy.x;
+        xy.y = mDisplayHeight - xy.y;
+    } else if (orientation == ui::Transform::ROT_270) {
+        xy.x = xy.y;
+        xy.y = mDisplayWidth - unrotatedX;
+    }
+    static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
+    return xy[axis];
 }
 
 float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
@@ -449,19 +479,10 @@
         return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
     }
 
-    float rawX = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getX();
-    float rawY = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getY();
-    vec2 vals = mTransform.transform(rawX, rawY);
-
-    switch (axis) {
-    case AMOTION_EVENT_AXIS_X:
-        return vals.x;
-    case AMOTION_EVENT_AXIS_Y:
-        return vals.y;
-    }
-
-    // This should never happen
-    return 0;
+    vec2 vals = mTransform.transform(
+            getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getXYValue());
+    static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
+    return vals[axis];
 }
 
 ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
@@ -548,6 +569,24 @@
     }
 }
 
+void MotionEvent::applyTransform(const std::array<float, 9>& matrix) {
+    // Determine how the origin is transformed by the matrix so that we
+    // can transform orientation vectors.
+    vec2 origin = transformPoint(matrix, 0, 0);
+
+    // Apply the transformation to all samples.
+    size_t numSamples = mSamplePointerCoords.size();
+    for (size_t i = 0; i < numSamples; i++) {
+        PointerCoords& c = mSamplePointerCoords.editItemAt(i);
+        float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+        c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
+                       transformAngle(matrix, orientation, origin.x, origin.y));
+        vec2 xy = transformPoint(matrix, c.getX(), c.getY());
+        c.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+        c.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+    }
+}
+
 #ifdef __linux__
 static status_t readFromParcel(ui::Transform& transform, const Parcel& parcel) {
     float dsdx, dtdx, tx, dtdy, dsdy, ty;
@@ -606,6 +645,8 @@
     mYPrecision = parcel->readFloat();
     mRawXCursorPosition = parcel->readFloat();
     mRawYCursorPosition = parcel->readFloat();
+    mDisplayWidth = parcel->readInt32();
+    mDisplayHeight = parcel->readInt32();
     mDownTime = parcel->readInt64();
 
     mPointerProperties.clear();
@@ -665,6 +706,8 @@
     parcel->writeFloat(mYPrecision);
     parcel->writeFloat(mRawXCursorPosition);
     parcel->writeFloat(mRawYCursorPosition);
+    parcel->writeInt32(mDisplayWidth);
+    parcel->writeInt32(mDisplayHeight);
     parcel->writeInt64(mDownTime);
 
     for (size_t i = 0; i < pointerCount; i++) {
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 31027b6..61d72ad 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -87,8 +87,10 @@
     // Search system repository.
     std::string path;
 
-    // Treblized input device config files will be located /odm/usr or /vendor/usr.
-    const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")};
+    // Treblized input device config files will be located /product/usr, /system_ext/usr,
+    // /odm/usr or /vendor/usr.
+    const char* rootsForPartition[]{"/product", "/system_ext", "/odm", "/vendor",
+                                    getenv("ANDROID_ROOT")};
     for (size_t i = 0; i < size(rootsForPartition); i++) {
         if (rootsForPartition[i] == nullptr) {
             continue;
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index f7962e0..dd1c462 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -96,28 +96,42 @@
 // --- InputMessage ---
 
 bool InputMessage::isValid(size_t actualSize) const {
-    if (size() == actualSize) {
-        switch (header.type) {
-            case Type::KEY:
-                return true;
-            case Type::MOTION:
-                return body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS;
-            case Type::FINISHED:
-                return true;
-            case Type::FOCUS:
-                return true;
-            case Type::CAPTURE:
-                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;
+    if (size() != actualSize) {
+        ALOGE("Received message of incorrect size %zu (expected %zu)", actualSize, size());
+        return false;
+    }
+
+    switch (header.type) {
+        case Type::KEY:
+            return true;
+        case Type::MOTION: {
+            const bool valid =
+                    body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS;
+            if (!valid) {
+                ALOGE("Received invalid MOTION: pointerCount = %" PRIu32, body.motion.pointerCount);
+            }
+            return valid;
+        }
+        case Type::FINISHED:
+        case Type::FOCUS:
+        case Type::CAPTURE:
+        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];
+            const bool valid = presentTime > gpuCompletedTime;
+            if (!valid) {
+                ALOGE("Received invalid TIMELINE: gpuCompletedTime = %" PRId64
+                      " presentTime = %" PRId64,
+                      gpuCompletedTime, presentTime);
+            }
+            return valid;
         }
     }
+    ALOGE("Invalid message type: %" PRIu32, header.type);
     return false;
 }
 
@@ -228,6 +242,10 @@
             msg->body.motion.xCursorPosition = body.motion.xCursorPosition;
             // float yCursorPosition
             msg->body.motion.yCursorPosition = body.motion.yCursorPosition;
+            // int32_t displayW
+            msg->body.motion.displayWidth = body.motion.displayWidth;
+            // int32_t displayH
+            msg->body.motion.displayHeight = body.motion.displayHeight;
             // uint32_t pointerCount
             msg->body.motion.pointerCount = body.motion.pointerCount;
             //struct Pointer pointers[MAX_POINTERS]
@@ -309,7 +327,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;
@@ -399,9 +418,7 @@
     }
 
     if (!msg->isValid(nRead)) {
-#if DEBUG_CHANNEL_MESSAGES
-        ALOGD("channel '%s' ~ received invalid message", mName.c_str());
-#endif
+        ALOGE("channel '%s' ~ received invalid message of size %zd", mName.c_str(), nRead);
         return BAD_VALUE;
     }
 
@@ -516,9 +533,9 @@
         std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
         int32_t edgeFlags, int32_t metaState, int32_t buttonState,
         MotionClassification classification, const ui::Transform& transform, float xPrecision,
-        float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime,
-        nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties,
-        const PointerCoords* pointerCoords) {
+        float yPrecision, float xCursorPosition, float yCursorPosition, int32_t displayWidth,
+        int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
+        const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) {
     if (ATRACE_ENABLED()) {
         std::string message = StringPrintf(
                 "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")",
@@ -576,6 +593,8 @@
     msg.body.motion.yPrecision = yPrecision;
     msg.body.motion.xCursorPosition = xCursorPosition;
     msg.body.motion.yCursorPosition = yCursorPosition;
+    msg.body.motion.displayWidth = displayWidth;
+    msg.body.motion.displayHeight = displayHeight;
     msg.body.motion.downTime = downTime;
     msg.body.motion.eventTime = eventTime;
     msg.body.motion.pointerCount = pointerCount;
@@ -1342,6 +1361,7 @@
                       msg->body.motion.buttonState, msg->body.motion.classification, transform,
                       msg->body.motion.xPrecision, msg->body.motion.yPrecision,
                       msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
+                      msg->body.motion.displayWidth, msg->body.motion.displayHeight,
                       msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount,
                       pointerProperties, pointerCoords);
 }
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index 8546bbb..9947720 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -57,11 +57,12 @@
             info.frameLeft == frameLeft && info.frameTop == frameTop &&
             info.frameRight == frameRight && info.frameBottom == frameBottom &&
             info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
-            info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) &&
-            info.visible == visible && info.trustedOverlay == trustedOverlay &&
-            info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode &&
-            info.hasWallpaper == hasWallpaper && info.paused == paused &&
-            info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
+            info.transform == transform && info.displayWidth == displayWidth &&
+            info.displayHeight == displayHeight &&
+            info.touchableRegion.hasSameRects(touchableRegion) && info.visible == visible &&
+            info.trustedOverlay == trustedOverlay && info.focusable == focusable &&
+            info.touchOcclusionMode == touchOcclusionMode && info.hasWallpaper == hasWallpaper &&
+            info.paused == paused && info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
             info.packageName == packageName && info.inputFeatures == inputFeatures &&
             info.displayId == displayId && info.portalToDisplayId == portalToDisplayId &&
             info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop &&
@@ -99,6 +100,8 @@
         parcel->writeFloat(transform.dtdy()) ?:
         parcel->writeFloat(transform.dsdy()) ?:
         parcel->writeFloat(transform.ty()) ?:
+        parcel->writeInt32(displayWidth) ?:
+        parcel->writeInt32(displayHeight) ?:
         parcel->writeBool(visible) ?:
         parcel->writeBool(focusable) ?:
         parcel->writeBool(hasWallpaper) ?:
@@ -153,6 +156,8 @@
         parcel->readFloat(&dtdy) ?:
         parcel->readFloat(&dsdy) ?:
         parcel->readFloat(&ty) ?:
+        parcel->readInt32(&displayWidth) ?:
+        parcel->readInt32(&displayHeight) ?:
         parcel->readBool(&visible) ?:
         parcel->readBool(&focusable) ?:
         parcel->readBool(&hasWallpaper) ?:
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index fe8a567..767878b 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -49,8 +49,8 @@
 cc_library_static {
     name: "StructLayout_test",
     srcs: ["StructLayout_test.cpp"],
+    compile_multilib: "both",
     cflags: [
-        "-O0",
         "-Wall",
         "-Werror",
         "-Wextra",
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 601d8da..32b72ba 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -271,6 +271,7 @@
                       AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
                       MotionClassification::NONE, mTransform, 2.0f, 2.1f,
                       AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                      AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
                       ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
                       pointerCoords);
 
@@ -592,6 +593,7 @@
                      AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
                      MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
                      0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/,
+                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
                      0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
                      pointerCoords);
     float originalRawX = 0 + 3;
@@ -634,6 +636,97 @@
     ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
 }
 
+MotionEvent createTouchDownEvent(int x, int y, ui::Transform transform) {
+    std::vector<PointerProperties> pointerProperties;
+    pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER});
+    std::vector<PointerCoords> pointerCoords;
+    pointerCoords.emplace_back().clear();
+    pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, x);
+    pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+    nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    MotionEvent event;
+    event.initialize(InputEvent::nextId(), /* deviceId */ 1, AINPUT_SOURCE_TOUCHSCREEN,
+                     /* displayId */ 0, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
+                     /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
+                     /* buttonState */ 0, MotionClassification::NONE, transform,
+                     /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, /* displayWidth */ 400,
+                     /* displayHeight */ 800, eventTime, eventTime, pointerCoords.size(),
+                     pointerProperties.data(), pointerCoords.data());
+    return event;
+}
+
+TEST_F(MotionEventTest, ApplyTransform) {
+    // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+    ui::Transform identity;
+    ui::Transform xform(ui::Transform::ROT_90, 800, 400);
+    xform.set(xform.tx() + 20, xform.ty() + 40);
+    MotionEvent event = createTouchDownEvent(60, 100, xform);
+    ASSERT_EQ(700, event.getRawX(0));
+    ASSERT_EQ(60, event.getRawY(0));
+    ASSERT_NE(event.getRawX(0), event.getX(0));
+    ASSERT_NE(event.getRawY(0), event.getY(0));
+
+    MotionEvent changedEvent = createTouchDownEvent(60, 100, identity);
+    const std::array<float, 9> rowMajor{xform[0][0], xform[1][0], xform[2][0],
+                                        xform[0][1], xform[1][1], xform[2][1],
+                                        xform[0][2], xform[1][2], xform[2][2]};
+    changedEvent.applyTransform(rowMajor);
+
+    // transformContent effectively rotates the raw coordinates, so those should now include
+    // both rotation AND offset
+    ASSERT_EQ(720, changedEvent.getRawX(0));
+    ASSERT_EQ(100, changedEvent.getRawY(0));
+
+    // The transformed output should be the same then
+    ASSERT_NEAR(event.getX(0), changedEvent.getX(0), 0.001);
+    ASSERT_NEAR(event.getY(0), changedEvent.getY(0), 0.001);
+}
+
+TEST_F(MotionEventTest, RawCompatTransform) {
+    {
+        // Make sure raw is raw regardless of transform translation.
+        ui::Transform xform;
+        xform.set(20, 40);
+        MotionEvent event = createTouchDownEvent(60, 100, xform);
+        ASSERT_EQ(60, event.getRawX(0));
+        ASSERT_EQ(100, event.getRawY(0));
+        ASSERT_NE(event.getRawX(0), event.getX(0));
+        ASSERT_NE(event.getRawY(0), event.getY(0));
+    }
+
+    // Next check that getRaw contains rotation (for compatibility) but otherwise is still
+    // "Screen-space". The following tests check all 3 rotations.
+    {
+        // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+        ui::Transform xform(ui::Transform::ROT_90, 800, 400);
+        xform.set(xform.tx() + 20, xform.ty() + 40);
+        MotionEvent event = createTouchDownEvent(60, 100, xform);
+        ASSERT_EQ(700, event.getRawX(0));
+        ASSERT_EQ(60, event.getRawY(0));
+        ASSERT_NE(event.getRawX(0), event.getX(0));
+        ASSERT_NE(event.getRawY(0), event.getY(0));
+    }
+
+    {
+        // Same as above, but check rotate-180.
+        ui::Transform xform(ui::Transform::ROT_180, 400, 800);
+        xform.set(xform.tx() + 20, xform.ty() + 40);
+        MotionEvent event = createTouchDownEvent(60, 100, xform);
+        ASSERT_EQ(340, event.getRawX(0));
+        ASSERT_EQ(700, event.getRawY(0));
+    }
+
+    {
+        // Same as above, but check rotate-270.
+        ui::Transform xform(ui::Transform::ROT_270, 800, 400);
+        xform.set(xform.tx() + 20, xform.ty() + 40);
+        MotionEvent event = createTouchDownEvent(60, 100, xform);
+        ASSERT_EQ(100, event.getRawX(0));
+        ASSERT_EQ(340, event.getRawY(0));
+    }
+}
+
 TEST_F(MotionEventTest, Initialize_SetsClassification) {
     std::array<MotionClassification, 3> classifications = {
             MotionClassification::NONE,
@@ -657,7 +750,8 @@
                          DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
                          AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification,
                          identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                         AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/,
+                         AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+                         AMOTION_EVENT_INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/,
                          pointerCount, pointerProperties, pointerCoords);
         ASSERT_EQ(classification, event.getClassification());
     }
@@ -678,8 +772,10 @@
     event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID,
                      INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE,
                      AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0,
-                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
-                     0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
+                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/,
+                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+                     0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
+                     pointerCoords);
     event.offsetLocation(20, 60);
     ASSERT_EQ(280, event.getRawXCursorPosition());
     ASSERT_EQ(540, event.getRawYCursorPosition());
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 088e00b..a2cfaa1 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -162,6 +162,8 @@
     constexpr float yPrecision = 0.5;
     constexpr float xCursorPosition = 1.3;
     constexpr float yCursorPosition = 50.6;
+    constexpr int32_t displayWidth = 1000;
+    constexpr int32_t displayHeight = 2000;
     constexpr nsecs_t downTime = 3;
     constexpr size_t pointerCount = 3;
     constexpr nsecs_t eventTime = 4;
@@ -190,8 +192,9 @@
     status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action,
                                             actionButton, flags, edgeFlags, metaState, buttonState,
                                             classification, transform, xPrecision, yPrecision,
-                                            xCursorPosition, yCursorPosition, downTime, eventTime,
-                                            pointerCount, pointerProperties, pointerCoords);
+                                            xCursorPosition, yCursorPosition, displayWidth,
+                                            displayHeight, downTime, eventTime, pointerCount,
+                                            pointerProperties, pointerCoords);
     ASSERT_EQ(OK, status)
             << "publisher publishMotionEvent should return OK";
 
@@ -228,6 +231,8 @@
     EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
     EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
     EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
+    EXPECT_EQ(displayWidth, motionEvent->getDisplaySize().x);
+    EXPECT_EQ(displayHeight, motionEvent->getDisplaySize().y);
     EXPECT_EQ(downTime, motionEvent->getDownTime());
     EXPECT_EQ(eventTime, motionEvent->getEventTime());
     EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
@@ -455,7 +460,7 @@
     status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
                                             0, 0, 0, MotionClassification::NONE, identityTransform,
                                             0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                            AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0,
                                             pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
@@ -471,7 +476,7 @@
     status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
                                             0, 0, 0, MotionClassification::NONE, identityTransform,
                                             0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                            AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0,
                                             pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
@@ -492,7 +497,7 @@
     status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
                                             0, 0, 0, MotionClassification::NONE, identityTransform,
                                             0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                            AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0,
                                             pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index c18a17f..493f2f4 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -55,6 +55,8 @@
     i.globalScaleFactor = 0.3;
     i.alpha = 0.7;
     i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
+    i.displayWidth = 1000;
+    i.displayHeight = 2000;
     i.visible = false;
     i.focusable = false;
     i.hasWallpaper = false;
@@ -91,6 +93,8 @@
     ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
     ASSERT_EQ(i.alpha, i2.alpha);
     ASSERT_EQ(i.transform, i2.transform);
+    ASSERT_EQ(i.displayWidth, i2.displayWidth);
+    ASSERT_EQ(i.displayHeight, i2.displayHeight);
     ASSERT_EQ(i.visible, i2.visible);
     ASSERT_EQ(i.focusable, i2.focusable);
     ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 585779e..5861d55 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -74,9 +74,11 @@
   CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124);
   CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128);
   CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132);
-  CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 136);
-  CHECK_OFFSET(InputMessage::Body::Motion, empty3, 140);
-  CHECK_OFFSET(InputMessage::Body::Motion, pointers, 144);
+  CHECK_OFFSET(InputMessage::Body::Motion, displayWidth, 136);
+  CHECK_OFFSET(InputMessage::Body::Motion, displayHeight, 140);
+  CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 144);
+  CHECK_OFFSET(InputMessage::Body::Motion, empty3, 148);
+  CHECK_OFFSET(InputMessage::Body::Motion, pointers, 152);
 
   CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0);
   CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index d049d05..aefc2ec 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -183,7 +183,8 @@
                          AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
                          MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
                          0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                         AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/,
+                         AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+                         AMOTION_EVENT_INVALID_DISPLAY_SIZE, 0 /*downTime*/,
                          entry.eventTime.count(), pointerCount, properties, coords);
 
         events.emplace_back(event);
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
index 36f87b8..f79098c 100644
--- a/libs/input/tests/VerifiedInputEvent_test.cpp
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -46,8 +46,10 @@
                      INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
                      AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
                      MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/,
-                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 100 /*downTime*/,
-                     200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
+                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/,
+                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+                     100 /*downTime*/, 200 /*eventTime*/, pointerCount, pointerProperties,
+                     pointerCoords);
     return event;
 }
 
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/Android.bp b/libs/nativewindow/Android.bp
index 8675439..9286009 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -60,7 +60,13 @@
 
 cc_library {
     name: "libnativewindow",
-    llndk_stubs: "libnativewindow.llndk",
+    llndk: {
+        symbol_file: "libnativewindow.map.txt",
+        unversioned: true,
+        override_export_include_dirs: [
+            "include"
+        ],
+    },
     export_include_dirs: [
         "include",
         "include-private",
@@ -115,11 +121,4 @@
     },
 }
 
-llndk_library {
-    name: "libnativewindow.llndk",
-    symbol_file: "libnativewindow.map.txt",
-    unversioned: true,
-    export_include_dirs: ["include"],
-}
-
 subdirs = ["tests"]
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 50e9d53..f0e1c4d 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.
@@ -297,8 +300,13 @@
  * this ANativeWindow is consumed by something other than the system compositor,
  * e.g. a media codec, this call has no effect.
  *
+ * You can register for changes in the refresh rate using
+ * \a AChoreographer_registerRefreshRateCallback.
+ *
  * Available since API level 31.
  *
+ * \param window pointer to an ANativeWindow object.
+ *
  * \param frameRate The intended frame rate of this window, in frames per
  * second. 0 is a special value that indicates the app will accept the system's
  * choice for the display frame rate, which is the default behavior if this
@@ -309,10 +317,13 @@
  * \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.
+ * This parameter is ignored when frameRate is 0.
  *
- * \param changeFrameRateStrategy Whether display refresh rate transitions should be seamless.
+ * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this
+ * window 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.
+ * This parameter is ignored when frameRate is 0.
  *
  * \return 0 for success, -EINVAL if the window, frame rate, or compatibility
  * value are invalid.
diff --git a/libs/permission/Android.bp b/libs/permission/Android.bp
new file mode 100644
index 0000000..3243a6b
--- /dev/null
+++ b/libs/permission/Android.bp
@@ -0,0 +1,49 @@
+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"],
+}
+
+aidl_interface {
+    name: "framework-permission-aidl",
+    unstable: true,
+    local_include_dir: "aidl",
+    backend: {
+        ndk: {
+            enabled: false
+        }
+    },
+    srcs: [
+        "aidl/android/content/AttributionSourceState.aidl",
+        "aidl/android/permission/IPermissionChecker.aidl",
+    ],
+}
+
+cc_library_shared {
+    name: "libpermission",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    srcs: [
+        "AppOpsManager.cpp",
+        "IAppOpsCallback.cpp",
+        "IAppOpsService.cpp",
+        "android/permission/PermissionChecker.cpp",
+    ],
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "libutils",
+        "libbinder",
+        "libcutils",
+        "liblog",
+    ],
+    static_libs: [
+        "framework-permission-aidl-cpp",
+    ],
+    export_static_lib_headers: ["framework-permission-aidl-cpp"],
+}
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 98%
rename from libs/binder/IAppOpsService.cpp
rename to libs/permission/IAppOpsService.cpp
index 44aa55e..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,8 @@
         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();
     }
@@ -85,6 +87,8 @@
         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/permission/aidl/android/content/AttributionSourceState.aidl b/libs/permission/aidl/android/content/AttributionSourceState.aidl
new file mode 100644
index 0000000..b6e54bf
--- /dev/null
+++ b/libs/permission/aidl/android/content/AttributionSourceState.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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 android.content;
+
+/**
+ * Payload for the {@link AttributionSource} class needed to interoperate
+ * with different languages.
+ *
+ * {@hide}
+ */
+parcelable AttributionSourceState {
+    /** The UID that is accessing the permission protected data. */
+    int uid;
+    /** The package that is accessing the permission protected data. */
+    @nullable @utf8InCpp String packageName;
+    /** The attribution tag of the app accessing the permission protected data. */
+    @nullable @utf8InCpp String attributionTag;
+    /** Unique token for that source. */
+    @nullable IBinder token;
+    /** Permissions that should be considered revoked regardless if granted. */
+    @nullable @utf8InCpp String[] renouncedPermissions;
+    /** The next app to receive the permission protected data. */
+    // TODO: We use an array as a workaround - the C++ backend doesn't
+    // support referring to the parcelable as it expects ctor/dtor
+    @nullable AttributionSourceState[] next;
+}
diff --git a/libs/permission/aidl/android/permission/IPermissionChecker.aidl b/libs/permission/aidl/android/permission/IPermissionChecker.aidl
new file mode 100644
index 0000000..1f0e32d
--- /dev/null
+++ b/libs/permission/aidl/android/permission/IPermissionChecker.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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 android.permission;
+
+import android.content.AttributionSourceState;
+
+/**
+ * Interface to communicate directly with the permission checker service.
+ */
+interface IPermissionChecker {
+    const int PERMISSION_GRANTED = 0;
+    const int PERMISSION_SOFT_DENIED = 1;
+    const int PERMISSION_HARD_DENIED = 2;
+
+    int checkPermission(String permission, in AttributionSourceState attributionSource,
+            @nullable String message, boolean forDataDelivery, boolean startDataDelivery,
+            boolean fromDatasource);
+
+    void finishDataDelivery(String op, in AttributionSourceState attributionSource);
+
+    int checkOp(int op, in AttributionSourceState attributionSource,
+            String message, boolean forDataDelivery, boolean startDataDelivery);
+}
diff --git a/libs/permission/android/permission/PermissionChecker.cpp b/libs/permission/android/permission/PermissionChecker.cpp
new file mode 100644
index 0000000..a8083ee
--- /dev/null
+++ b/libs/permission/android/permission/PermissionChecker.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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 <mutex>
+#include <include/android/permission/PermissionChecker.h>
+#include <binder/Binder.h>
+#include <binder/IServiceManager.h>
+
+#include <utils/SystemClock.h>
+
+#include <sys/types.h>
+#include <private/android_filesystem_config.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "PermissionChecker"
+
+namespace android {
+
+using android::content::AttributionSourceState;
+
+PermissionChecker::PermissionChecker()
+{
+}
+
+sp<IPermissionChecker> PermissionChecker::getService()
+{
+    static String16 permission_checker("permission_checker");
+
+    std::lock_guard<Mutex> scoped_lock(mLock);
+    int64_t startTime = 0;
+    sp<IPermissionChecker> service = mService;
+    while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) {
+        sp<IBinder> binder = defaultServiceManager()->checkService(permission_checker);
+        if (binder == nullptr) {
+            // Wait for the permission checker service to come back...
+            if (startTime == 0) {
+                startTime = uptimeMillis();
+                ALOGW("Waiting for permission checker service");
+            } else if ((uptimeMillis() - startTime) > 10000) {
+                ALOGE("Waiting too long for permission checker service, giving up");
+                service = nullptr;
+                break;
+            }
+            sleep(1);
+        } else {
+            mService = interface_cast<IPermissionChecker>(binder);
+        }
+    }
+    return mService;
+}
+
+PermissionChecker::PermissionResult
+    PermissionChecker::checkPermissionForDataDeliveryFromDatasource(
+        const String16& permission, AttributionSourceState& attributionSource,
+        const String16& message)
+{
+    return static_cast<PermissionResult>(checkPermission(permission, attributionSource, message,
+            /*forDataDelivery*/ true, /*startDataDelivery*/ false,/*fromDatasource*/ true));
+}
+
+PermissionChecker::PermissionResult
+    PermissionChecker::checkPermissionForStartDataDeliveryFromDatasource(
+        const String16& permission, AttributionSourceState& attributionSource,
+        const String16& message)
+{
+    return static_cast<PermissionResult>(checkPermission(permission, attributionSource, message,
+            /*forDataDelivery*/ true, /*startDataDelivery*/ true, /*fromDatasource*/ true));
+}
+
+void PermissionChecker::finishDataDelivery(const String16& op,
+        AttributionSourceState& attributionSource)
+{
+    sp<IPermissionChecker> service = getService();
+    if (service != nullptr) {
+        binder::Status status = service->finishDataDelivery(op, attributionSource);
+        if (!status.isOk()) {
+            ALOGE("finishDataDelivery failed: %s", status.exceptionMessage().c_str());
+        }
+    }
+}
+
+int32_t PermissionChecker::checkPermission(const String16& permission,
+        AttributionSourceState& attributionSource, const String16& message,
+        bool forDataDelivery, bool startDataDelivery, bool fromDatasource)
+{
+    sp<IPermissionChecker> service = getService();
+    if (service != nullptr) {
+        int32_t result;
+        binder::Status status = service->checkPermission(permission, attributionSource, message,
+                forDataDelivery, startDataDelivery, fromDatasource, &result);
+        if (status.isOk()) {
+            return result;
+        }
+        ALOGE("checkPermission failed: %s", status.exceptionMessage().c_str());
+    }
+    return PERMISSION_DENIED;
+}
+
+} // namespace android
diff --git a/libs/permission/include/android/permission/PermissionChecker.h b/libs/permission/include/android/permission/PermissionChecker.h
new file mode 100644
index 0000000..20ab51f
--- /dev/null
+++ b/libs/permission/include/android/permission/PermissionChecker.h
@@ -0,0 +1,136 @@
+/*
+ * 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 <android/content/AttributionSourceState.h>
+#include <android/permission/IPermissionChecker.h>
+
+#include <utils/threads.h>
+
+#include <optional>
+
+#ifdef __ANDROID_VNDK__
+#error "This header is not visible to vendors"
+#endif
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+using android::content::AttributionSourceState;
+using android::permission::IPermissionChecker;
+
+class PermissionChecker
+{
+public:
+
+    enum PermissionResult {
+
+        /**
+         * The permission is granted.
+         */
+        PERMISSION_GRANTED = IPermissionChecker::PERMISSION_GRANTED,
+
+        /**
+         * The permission is denied. Applicable only to runtime and app op permissions.
+         *
+         * Returned when:
+         *   - the runtime permission is granted, but the corresponding app op is denied
+         *       for runtime permissions.
+         *   - the app ops is ignored for app op permissions.
+         *
+         */
+        PERMISSION_SOFT_DENIED = IPermissionChecker::PERMISSION_SOFT_DENIED,
+
+        /**
+         * The permission is denied.
+         *
+         * Returned when:
+         *  - the permission is denied for non app op permissions.
+         *  - the app op is denied or app op is AppOpsManager#MODE_DEFAULT and permission is denied.
+         */
+        PERMISSION_HARD_DENIED = IPermissionChecker::PERMISSION_HARD_DENIED
+    };
+
+    PermissionChecker();
+
+    /**
+     * Checks whether a given data access chain described by the given attribution source
+     * has a given permission and whether the app op that corresponds to this permission
+     * is allowed. Call this method if you are the datasource which would not blame you for
+     * access to the data since you are the data. Note that the attribution source chain
+     *
+     * NOTE: The attribution source should be for yourself with its next attribution
+     * source being the app that would receive the data from you.
+     *
+     * NOTE: Use this method only for permission checks at the point where you will deliver
+     * the permission protected data to clients.
+     *
+     * @param permission The permission to check.
+     * @param attributionSource The attribution chain to check.
+     * @param message A message describing the reason the permission was checked.
+     * @return The permission check result which is either PERMISSION_GRANTED,
+     *     or PERMISSION_SOFT_DENIED or PERMISSION_HARD_DENIED.
+     */
+    PermissionChecker::PermissionResult checkPermissionForDataDeliveryFromDatasource(
+            const String16& permission, AttributionSourceState& attributionSource,
+            const String16& message);
+
+   /**
+     * Checks whether a given data access chain described by the given attribution source
+     * has a given permission and whether the app op that corresponds to this permission
+     * is allowed. The app ops are also marked as started. This is useful for long running
+     * permissions like camera and microphone.
+     *
+     * NOTE: The attribution source should be for yourself with its next attribution
+     * source being the app that would receive the data from you.
+     *
+     * NOTE: Use this method only for permission checks at the point where you will deliver
+     * the permission protected data to clients.
+     *
+     * @param permission The permission to check.
+     * @param attributionSource The attribution chain to check.
+     * @param message A message describing the reason the permission was checked.
+     * @return The permission check result which is either PERMISSION_GRANTED,
+     *     or PERMISSION_SOFT_DENIED or PERMISSION_HARD_DENIED.
+     */
+    PermissionResult checkPermissionForStartDataDeliveryFromDatasource(
+            const String16& permission, AttributionSourceState& attributionSource,
+            const String16& message);
+
+    /**
+     * Finishes an ongoing op for data access chain described by the given
+     * attribution source.
+     *
+     * @param op The op to finish.
+     * @param attributionSource The attribution chain for which to finish data delivery.
+     */
+    void finishDataDelivery(const String16& op, AttributionSourceState& attributionSource);
+
+private:
+    Mutex mLock;
+    sp<IPermissionChecker> mService;
+    sp<IPermissionChecker> getService();
+
+    int32_t checkPermission(const String16& permission, AttributionSourceState& attributionSource,
+            const String16& message, bool forDataDelivery, bool startDataDelivery,
+            bool fromDatasource);
+};
+
+
+} // namespace android
+
+// ---------------------------------------------------------------------------
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 be6667d..e3d705f 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/permission/include/binder/AppOpsManager.h
@@ -144,7 +144,10 @@
         OP_MANAGE_MEDIA = 110,
         OP_BLUETOOTH_CONNECT = 111,
         OP_UWB_RANGING = 112,
-        _NUM_OP = 113
+        OP_ACTIVITY_RECOGNITION_SOURCE = 113,
+        OP_BLUETOOTH_ADVERTISE = 114,
+        OP_RECORD_INCOMING_PHONE_AUDIO = 115,
+        _NUM_OP = 116
     };
 
     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..570c7bc 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,8 +92,10 @@
         "skia/debug/CaptureTimer.cpp",
         "skia/debug/CommonPool.cpp",
         "skia/debug/SkiaCapture.cpp",
+        "skia/debug/SkiaMemoryReporter.cpp",
         "skia/filters/BlurFilter.cpp",
         "skia/filters/LinearEffect.cpp",
+        "skia/filters/StretchShaderFactory.cpp"
     ],
 }
 
@@ -115,7 +118,7 @@
     include_dirs: [
         "external/skia/src/gpu",
     ],
-    whole_static_libs: ["libskia"],
+    whole_static_libs: ["libskia_renderengine"],
     lto: {
         thin: true,
     },
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..3c58238 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -249,7 +249,7 @@
     // initialize EGL for the default display
     EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
     if (!eglInitialize(display, nullptr, nullptr)) {
-        LOG_ALWAYS_FATAL("failed to initialize EGL");
+        LOG_ALWAYS_FATAL("failed to initialize EGL. EGL error=0x%x", eglGetError());
     }
 
     const auto eglVersion = eglQueryString(display, EGL_VERSION);
@@ -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,14 +1227,16 @@
             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;
             }
         }
 
-        mState.maxMasteringLuminance = layer->source.buffer.maxMasteringLuminance;
-        mState.maxContentLuminance = layer->source.buffer.maxContentLuminance;
+        // Ensure luminance is at least 100 nits to avoid div-by-zero
+        const float maxLuminance = std::max(100.f, layer->source.buffer.maxLuminanceNits);
+        mState.maxMasteringLuminance = maxLuminance;
+        mState.maxContentLuminance = maxLuminance;
         mState.projectionMatrix = projectionMatrix * layer->geometry.positionTransform;
 
         const FloatRect bounds = layer->geometry.boundaries;
@@ -1250,7 +1256,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 +1280,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 cd7a86b..e7ed9c0 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -60,16 +60,14 @@
     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; }
@@ -105,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/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index a637796..53fa622 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -60,6 +60,9 @@
     // capture of a device in landscape while the buffer is in portrait
     // orientation.
     uint32_t orientation = ui::Transform::ROT_0;
+
+    // SDR white point, -1f if unknown
+    float sdrWhitePointNits = -1.f;
 };
 
 static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
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..63ed1ba 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;
@@ -64,8 +64,8 @@
 
     // HDR color-space setting for Y410.
     bool isY410BT2020 = false;
-    float maxMasteringLuminance = 0.0;
-    float maxContentLuminance = 0.0;
+
+    float maxLuminanceNits = 0.0;
 };
 
 // Metadata describing the layer geometry.
@@ -156,6 +156,10 @@
 
     std::vector<BlurRegion> blurRegions;
 
+    // Transform matrix used to convert the blurRegions geometry into the same
+    // coordinate space as LayerSettings.geometry
+    mat4 blurRegionTransform = mat4();
+
     StretchEffect stretchEffect;
 
     // Name associated with the layer for debugging purposes.
@@ -171,8 +175,7 @@
             lhs.textureTransform == rhs.textureTransform &&
             lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
             lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 &&
-            lhs.maxMasteringLuminance == rhs.maxMasteringLuminance &&
-            lhs.maxContentLuminance == rhs.maxContentLuminance;
+            lhs.maxLuminanceNits == rhs.maxLuminanceNits;
 }
 
 static inline bool operator==(const Geometry& lhs, const Geometry& rhs) {
@@ -191,18 +194,6 @@
             lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent;
 }
 
-static inline bool operator==(const BlurRegion& lhs, const BlurRegion& rhs) {
-    return lhs.alpha == rhs.alpha && lhs.cornerRadiusTL == rhs.cornerRadiusTL &&
-            lhs.cornerRadiusTR == rhs.cornerRadiusTR && lhs.cornerRadiusBL == rhs.cornerRadiusBL &&
-            lhs.cornerRadiusBR == rhs.cornerRadiusBR && lhs.blurRadius == rhs.blurRadius &&
-            lhs.left == rhs.left && lhs.top == rhs.top && lhs.right == rhs.right &&
-            lhs.bottom == rhs.bottom;
-}
-
-static inline bool operator!=(const BlurRegion& lhs, const BlurRegion& rhs) {
-    return !(lhs == rhs);
-}
-
 static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
     if (lhs.blurRegions.size() != rhs.blurRegions.size()) {
         return false;
@@ -218,7 +209,9 @@
             lhs.sourceDataspace == rhs.sourceDataspace &&
             lhs.colorTransform == rhs.colorTransform &&
             lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
-            lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
+            lhs.backgroundBlurRadius == rhs.backgroundBlurRadius &&
+            lhs.blurRegionTransform == rhs.blurRegionTransform &&
+            lhs.stretchEffect == rhs.stretchEffect;
 }
 
 // Defining PrintTo helps with Google Tests.
@@ -233,8 +226,7 @@
     *os << "\n    .usePremultipliedAlpha = " << settings.usePremultipliedAlpha;
     *os << "\n    .isOpaque = " << settings.isOpaque;
     *os << "\n    .isY410BT2020 = " << settings.isY410BT2020;
-    *os << "\n    .maxMasteringLuminance = " << settings.maxMasteringLuminance;
-    *os << "\n    .maxContentLuminance = " << settings.maxContentLuminance;
+    *os << "\n    .maxLuminanceNits = " << settings.maxLuminanceNits;
     *os << "\n}";
 }
 
@@ -268,8 +260,23 @@
     *os << "\n}";
 }
 
+static inline void PrintTo(const StretchEffect& effect, ::std::ostream* os) {
+    *os << "StretchEffect {";
+    *os << "\n     .width = " << effect.width;
+    *os << "\n     .height = " << effect.height;
+    *os << "\n     .vectorX = " << effect.vectorX;
+    *os << "\n     .vectorY = " << effect.vectorY;
+    *os << "\n     .maxAmountX = " << effect.maxAmountX;
+    *os << "\n     .maxAmountY = " << effect.maxAmountY;
+    *os << "\n     .mappedLeft = " << effect.mappedChildBounds.left;
+    *os << "\n     .mappedTop = " << effect.mappedChildBounds.top;
+    *os << "\n     .mappedRight = " << effect.mappedChildBounds.right;
+    *os << "\n     .mappedBottom = " << effect.mappedChildBounds.bottom;
+    *os << "\n}";
+}
+
 static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) {
-    *os << "LayerSettings {";
+    *os << "LayerSettings for '" << settings.name.c_str() << "' {";
     *os << "\n    .geometry = ";
     PrintTo(settings.geometry, os);
     *os << "\n    .source = ";
@@ -286,6 +293,8 @@
     }
     *os << "\n    .shadow = ";
     PrintTo(settings.shadow, os);
+    *os << "\n    .stretchEffect = ";
+    PrintTo(settings.stretchEffect, os);
     *os << "\n}";
 }
 
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index a69d1f0..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,8 +134,11 @@
 
     // ----- 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
@@ -188,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
@@ -197,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.
@@ -209,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;
 };
@@ -293,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 228553d..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,12 +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..8ae69de 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, createProtectedImage, isOutputBuffer, 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,10 +121,12 @@
     }
 
     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;
 }
 
 } // namespace skia
 } // namespace renderengine
-} // namespace android
\ No newline at end of file
+} // namespace android
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..0eee564 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,29 @@
 
 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);
+const auto kScaleYOnly = mat4(1.f,   0.f, 0.f, 0.f,
+                              0.f,  0.7f, 0.f, 0.f,
+                              0.f,   0.f, 1.f, 0.f,
+                              0.f,   0.f, 0.f, 1.f);
+// clang-format on
+// When setting layer.sourceDataspace, whether it matches the destination or not determines whether
+// a color correction effect is added to the shader.
+constexpr auto kDestDataSpace = ui::Dataspace::SRGB;
+constexpr auto kOtherDataSpace = ui::Dataspace::DISPLAY_P3;
+} // 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 +68,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,37 +107,32 @@
                     },
             .source = PixelSource{.buffer =
                                           Buffer{
-                                                  .buffer = srcBuffer,
-                                                  .maxMasteringLuminance = 1000.f,
-                                                  .maxContentLuminance = 1000.f,
+                                                  .buffer = srcTexture,
+                                                  .maxLuminanceNits = 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}) {
-            // 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 (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
+        layer.sourceDataspace = dataspace;
+        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;
                 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,
+                        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
                                                  base::unique_fd(), nullptr);
                     }
                 }
@@ -123,7 +142,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,46 +178,128 @@
                             .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);
+    }
 }
 
-void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
-    const nsecs_t timeBefore = systemTime();
-    // The dimensions should not matter, so long as we draw inside them.
-    const Rect displayRect(0, 0, 1080, 2340);
-    DisplaySettings display{
-            .physicalDisplay = displayRect,
-            .clip = displayRect,
-            .maxLuminance = 500,
-            .outputDataspace = ui::Dataspace::DISPLAY_P3,
+static void drawTextureScaleLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+                                   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{
+            .geometry =
+                    Geometry{
+                            .boundaries = rect,
+                            .roundedCornersCrop = rect,
+                            .positionTransform = kScaleAndTranslate,
+                            .roundedCornersRadius = 300,
+                    },
+            .source = PixelSource{.buffer =
+                                          Buffer{
+                                                  .buffer = srcTexture,
+                                                  .maxLuminanceNits = 1000.f,
+                                                  .textureTransform = kScaleYOnly,
+                                          }},
+            .sourceDataspace = kOtherDataSpace,
     };
 
-    const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+    auto layers = std::vector<const LayerSettings*>{&layer};
+    for (float alpha : {0.5f, 1.f}) {
+        layer.alpha = alpha,
+        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+                                 base::unique_fd(), nullptr);
+    }
+}
 
-    sp<GraphicBuffer> dstBuffer =
-            new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
-                              usage, "primeShaderCache_dst");
-    // 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
-    // to populate it.
-    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 nsecs_t timeAfter = systemTime();
-    const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
-    ALOGD("shader cache generated in %f ms\n", compileTimeMs);
+//
+// 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);
+    }
+
+    // The loop is beneficial for debugging and should otherwise be optimized out by the compiler.
+    // Adding additional bounds to the loop is useful for verifying that the size of the dst buffer
+    // does not impact the shader compilation counts by triggering different behaviors in RE/Skia.
+    for (SkSize bounds : {SkSize::Make(128, 128), /*SkSize::Make(1080, 2340)*/}) {
+        const nsecs_t timeBefore = systemTime();
+        // The dimensions should not matter, so long as we draw inside them.
+        const Rect displayRect(0, 0, bounds.fWidth, bounds.fHeight);
+        DisplaySettings display{
+                .physicalDisplay = displayRect,
+                .clip = displayRect,
+                .maxLuminance = 500,
+                .outputDataspace = kDestDataSpace,
+        };
+
+        const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+
+        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
+        // to populate it.
+        sp<GraphicBuffer> srcBuffer =
+                new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
+                                  1, usage, "drawImageLayer_src");
+
+        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);
+
+        // Draw layers for b/185569240.
+        drawTextureScaleLayers(renderengine, display, dstTexture, externalTexture);
+
+        const nsecs_t timeAfter = systemTime();
+        const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
+        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 94a0dca..6cc3d37 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,16 @@
 #include "filters/LinearEffect.h"
 #include "log/log_main.h"
 #include "skia/debug/SkiaCapture.h"
+#include "skia/debug/SkiaMemoryReporter.h"
+#include "skia/filters/StretchShaderFactory.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 +294,10 @@
                         numShaders, cached);
 }
 
+int SkiaGLRenderEngine::reportShadersCompiled() {
+    return mSkSLCacheMonitor.shadersCachedSinceLastCall();
+}
+
 SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
                                        EGLContext ctxt, EGLSurface placeholder,
                                        EGLContext protectedContext, EGLSurface protectedPlaceholder)
@@ -317,8 +330,6 @@
 }
 
 SkiaGLRenderEngine::~SkiaGLRenderEngine() {
-    cleanFramebufferCache();
-
     std::lock_guard<std::mutex> lock(mRenderingMutex);
     if (mBlurFilter) {
         delete mBlurFilter;
@@ -355,6 +366,10 @@
     return mProtectedEGLContext != EGL_NO_CONTEXT;
 }
 
+GrDirectContext* SkiaGLRenderEngine::getActiveGrContext() const {
+    return mInProtectedContext ? mProtectedGrContext.get() : mGrContext.get();
+}
+
 bool SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) {
     if (useProtectedContext == mInProtectedContext) {
         return true;
@@ -362,6 +377,12 @@
     if (useProtectedContext && !supportsProtectedContent()) {
         return false;
     }
+
+    // release any scratch resources before switching into a new mode
+    if (getActiveGrContext()) {
+        getActiveGrContext()->purgeUnlockedResources(true);
+    }
+
     const EGLSurface surface =
             useProtectedContext ? mProtectedPlaceholderSurface : mPlaceholderSurface;
     const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
@@ -369,6 +390,11 @@
 
     if (success) {
         mInProtectedContext = useProtectedContext;
+        // given that we are sharing the same thread between two GrContexts we need to
+        // make sure that the thread state is reset when switching between the two.
+        if (getActiveGrContext()) {
+            getActiveGrContext()->resetContext();
+        }
     }
     return success;
 }
@@ -472,56 +498,80 @@
             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) {
         return;
     }
-    ATRACE_CALL();
-
-    // We need to switch the currently bound context if the buffer is protected but the current
-    // context is not. The current state must then be restored after the buffer is cached.
-    const bool protectedContextState = mInProtectedContext;
-    if (!useProtectedContext(protectedContextState ||
-                             (buffer->getUsage() & GRALLOC_USAGE_PROTECTED))) {
-        ALOGE("Attempting to cache a buffer into a different context than what is currently bound");
+    // we currently don't attempt to map a buffer if the buffer contains protected content
+    // because GPU resources for protected buffers is much more limited.
+    const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
+    if (isProtectedBuffer) {
         return;
     }
+    ATRACE_CALL();
 
-    auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+    // If we were to support caching protected buffers then we will need to switch the currently
+    // bound context if we are not already using the protected context (and subsequently switch
+    // back after the buffer is cached).
+    auto grContext = getActiveGrContext();
     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,
+                                                               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,
-                                                              const LayerSettings* layer,
-                                                              const DisplaySettings& display,
-                                                              bool undoPremultipliedAlpha,
-                                                              bool requiresLinearEffect) {
-    if (layer->stretchEffect.hasEffect()) {
-        // TODO: Implement
+sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(
+        sk_sp<SkShader> shader,
+        const LayerSettings* layer, const DisplaySettings& display, bool undoPremultipliedAlpha,
+        bool requiresLinearEffect) {
+    const auto stretchEffect = layer->stretchEffect;
+    // The given surface will be stretched by HWUI via matrix transformation
+    // which gets similar results for most surfaces
+    // Determine later on if we need to leverage the stertch shader within
+    // surface flinger
+    if (stretchEffect.hasEffect()) {
+        const auto targetBuffer = layer->source.buffer.buffer;
+        const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
+        if (graphicBuffer && shader) {
+            shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
+        }
     }
+
     if (requiresLinearEffect) {
         const ui::Dataspace inputDataspace =
                 mUseColorManagement ? layer->sourceDataspace : ui::Dataspace::UNKNOWN;
@@ -540,10 +590,14 @@
         } else {
             runtimeEffect = effectIter->second;
         }
+        float maxLuminance = layer->source.buffer.maxLuminanceNits;
+        // If the buffer doesn't have a max luminance, treat it as SDR & use the display's SDR
+        // white point
+        if (maxLuminance <= 0.f) {
+            maxLuminance = display.sdrWhitePointNits;
+        }
         return createLinearEffectShader(shader, effect, runtimeEffect, layer->colorTransform,
-                                        display.maxLuminance,
-                                        layer->source.buffer.maxMasteringLuminance,
-                                        layer->source.buffer.maxContentLuminance);
+                                        display.maxLuminance, maxLuminance);
     }
     return shader;
 }
@@ -607,10 +661,21 @@
     int mSaveCount;
 };
 
+static SkRRect getBlurRRect(const BlurRegion& region) {
+    const auto rect = SkRect::MakeLTRB(region.left, region.top, region.right, region.bottom);
+    const SkVector radii[4] = {SkVector::Make(region.cornerRadiusTL, region.cornerRadiusTL),
+                               SkVector::Make(region.cornerRadiusTR, region.cornerRadiusTR),
+                               SkVector::Make(region.cornerRadiusBR, region.cornerRadiusBR),
+                               SkVector::Make(region.cornerRadiusBL, region.cornerRadiusBL)};
+    SkRRect roundedRect;
+    roundedRect.setRectRadii(rect, radii);
+    return roundedRect;
+}
+
 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");
 
@@ -633,38 +698,25 @@
         return BAD_VALUE;
     }
 
-    validateOutputBufferUsage(buffer);
+    validateOutputBufferUsage(buffer->getBuffer());
 
-    auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+    auto grContext = getActiveGrContext();
     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,
+                                                               buffer->getBuffer()
+                                                                       ->toAHardwareBuffer(),
+                                                               true);
     }
 
     const ui::Dataspace dstDataspace =
             mUseColorManagement ? display.outputDataspace : ui::Dataspace::UNKNOWN;
-    sk_sp<SkSurface> dstSurface =
-            surfaceTextureRef->getTexture()->getOrCreateSurface(dstDataspace, grContext.get());
+    sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext);
 
     SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
     if (dstCanvas == nullptr) {
@@ -735,6 +787,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);
@@ -783,7 +846,7 @@
         // Layers have a local transform that should be applied to them
         canvas->concat(getSkM44(layer->geometry.positionTransform).asM33());
 
-        const auto bounds = getSkRect(layer->geometry.boundaries);
+        const auto [bounds, roundRectClip] = getBoundsAndClip(layer);
         if (mBlurFilter && layerHasBlur(layer)) {
             std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
 
@@ -793,29 +856,42 @@
                 blurInput = activeSurface->makeImageSnapshot();
             }
             // rect to be blurred in the coordinate space of blurInput
-            const auto blurRect = canvas->getTotalMatrix().mapRect(bounds);
+            const auto blurRect = canvas->getTotalMatrix().mapRect(bounds.rect());
 
-            if (layer->backgroundBlurRadius > 0) {
-                ATRACE_NAME("BackgroundBlur");
-                auto blurredImage =
-                        mBlurFilter->generate(grContext.get(), layer->backgroundBlurRadius,
-                                              blurInput, blurRect);
-
-                cachedBlurs[layer->backgroundBlurRadius] = blurredImage;
-
-                mBlurFilter->drawBlurRegion(canvas, getBlurRegion(layer), blurRect, blurredImage,
-                                            blurInput);
+            // if the clip needs to be applied then apply it now and make sure
+            // it is restored before we attempt to draw any shadows.
+            SkAutoCanvasRestore acr(canvas, true);
+            if (!roundRectClip.isEmpty()) {
+                canvas->clipRRect(roundRectClip, true);
             }
-            for (auto region : layer->blurRegions) {
-                if (cachedBlurs[region.blurRadius] == nullptr) {
-                    ATRACE_NAME("BlurRegion");
-                    cachedBlurs[region.blurRadius] =
-                            mBlurFilter->generate(grContext.get(), region.blurRadius, blurInput,
+
+            // TODO(b/182216890): Filter out empty layers earlier
+            if (blurRect.width() > 0 && blurRect.height() > 0) {
+                if (layer->backgroundBlurRadius > 0) {
+                    ATRACE_NAME("BackgroundBlur");
+                    auto blurredImage =
+                            mBlurFilter->generate(grContext, layer->backgroundBlurRadius, blurInput,
                                                   blurRect);
+
+                    cachedBlurs[layer->backgroundBlurRadius] = blurredImage;
+
+                    mBlurFilter->drawBlurRegion(canvas, bounds, layer->backgroundBlurRadius, 1.0f,
+                                                blurRect, blurredImage, blurInput);
                 }
 
-                mBlurFilter->drawBlurRegion(canvas, region, blurRect,
-                                            cachedBlurs[region.blurRadius], blurInput);
+                canvas->concat(getSkM44(layer->blurRegionTransform).asM33());
+                for (auto region : layer->blurRegions) {
+                    if (cachedBlurs[region.blurRadius] == nullptr) {
+                        ATRACE_NAME("BlurRegion");
+                        cachedBlurs[region.blurRadius] =
+                                mBlurFilter->generate(grContext, region.blurRadius, blurInput,
+                                                      blurRect);
+                    }
+
+                    mBlurFilter->drawBlurRegion(canvas, getBlurRRect(region), region.blurRadius,
+                                                region.alpha, blurRect,
+                                                cachedBlurs[region.blurRadius], blurInput);
+                }
             }
         }
 
@@ -824,18 +900,26 @@
         // TODO(b/175915334): consider relaxing this restriction to enable more flexible
         // composition - using a well-defined invalid color is long-term less error-prone.
         if (layer->shadow.length > 0) {
-            const auto rect = layer->geometry.roundedCornersRadius > 0
-                    ? getSkRect(layer->geometry.roundedCornersCrop)
-                    : bounds;
             // This would require a new parameter/flag to SkShadowUtils::DrawShadow
             LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow");
-            drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
+
+            // Technically, if bounds is a rect and roundRectClip is not empty,
+            // it means that the bounds and roundedCornersCrop were different
+            // enough that we should intersect them to find the proper shadow.
+            // In practice, this often happens when the two rectangles appear to
+            // not match due to rounding errors. Draw the rounded version, which
+            // looks more like the intent.
+            const auto& rrect =
+                    bounds.isRect() && !roundRectClip.isEmpty() ? roundRectClip : bounds;
+            drawShadow(canvas, rrect, layer->shadow);
             continue;
         }
 
         const bool requiresLinearEffect = layer->colorTransform != mat4() ||
                 (mUseColorManagement &&
-                 needsToneMapping(layer->sourceDataspace, display.outputDataspace));
+                 needsToneMapping(layer->sourceDataspace, display.outputDataspace)) ||
+                (display.sdrWhitePointNits > 0.f &&
+                 display.sdrWhitePointNits != display.maxLuminance);
 
         // quick abort from drawing the remaining portion of the layer
         if (layer->alpha == 0 && !requiresLinearEffect && !layer->disableBlending &&
@@ -853,26 +937,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,
+                                                      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);
 
             auto texMatrix = getSkM44(item.textureTransform).asM33();
             // textureTansform was intended to be passed directly into a shader, so when
@@ -888,7 +975,7 @@
             // The shader does not respect the translation, so we add it to the texture
             // transform for the SkImage. This will make sure that the correct layer contents
             // are drawn in the correct part of the screen.
-            matrix.postTranslate(layer->geometry.boundaries.left, layer->geometry.boundaries.top);
+            matrix.postTranslate(bounds.rect().fLeft, bounds.rect().fTop);
 
             sk_sp<SkShader> shader;
 
@@ -950,11 +1037,19 @@
 
         paint.setColorFilter(displayColorTransform);
 
-        if (layer->geometry.roundedCornersRadius > 0) {
+        if (!roundRectClip.isEmpty()) {
+            canvas->clipRRect(roundRectClip, true);
+        }
+
+        if (!bounds.isRect()) {
             paint.setAntiAlias(true);
-            canvas->drawRRect(getRoundedRect(layer), paint);
+            canvas->drawRRect(bounds, paint);
         } else {
-            canvas->drawRect(bounds, paint);
+            canvas->drawRect(bounds.rect(), paint);
+        }
+        if (kFlushAfterEveryLayer) {
+            ATRACE_NAME("flush surface");
+            activeSurface->flush();
         }
     }
     surfaceAutoSaveRestore.restore();
@@ -998,25 +1093,89 @@
     return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
 }
 
-inline SkRRect SkiaGLRenderEngine::getRoundedRect(const LayerSettings* layer) {
-    const auto rect = getSkRect(layer->geometry.roundedCornersCrop);
+inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(
+        const LayerSettings* layer) {
+    const auto bounds = getSkRect(layer->geometry.boundaries);
+    const auto crop = getSkRect(layer->geometry.roundedCornersCrop);
     const auto cornerRadius = layer->geometry.roundedCornersRadius;
-    return SkRRect::MakeRectXY(rect, cornerRadius, cornerRadius);
-}
 
-inline BlurRegion SkiaGLRenderEngine::getBlurRegion(const LayerSettings* layer) {
-    const auto rect = getSkRect(layer->geometry.boundaries);
-    const auto cornersRadius = layer->geometry.roundedCornersRadius;
-    return BlurRegion{.blurRadius = static_cast<uint32_t>(layer->backgroundBlurRadius),
-                      .cornerRadiusTL = cornersRadius,
-                      .cornerRadiusTR = cornersRadius,
-                      .cornerRadiusBL = cornersRadius,
-                      .cornerRadiusBR = cornersRadius,
-                      .alpha = 1,
-                      .left = static_cast<int>(rect.fLeft),
-                      .top = static_cast<int>(rect.fTop),
-                      .right = static_cast<int>(rect.fRight),
-                      .bottom = static_cast<int>(rect.fBottom)};
+    SkRRect clip;
+    if (cornerRadius > 0) {
+        // it the crop and the bounds are equivalent or there is no crop then we don't need a clip
+        if (bounds == crop || crop.isEmpty()) {
+            return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip};
+        }
+
+        // This makes an effort to speed up common, simple bounds + clip combinations by
+        // converting them to a single RRect draw. It is possible there are other cases
+        // that can be converted.
+        if (crop.contains(bounds)) {
+            bool intersectionIsRoundRect = true;
+            // check each cropped corner to ensure that it exactly matches the crop or is full
+            SkVector radii[4];
+
+            const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
+
+            const bool leftEqual = bounds.fLeft == crop.fLeft;
+            const bool topEqual = bounds.fTop == crop.fTop;
+            const bool rightEqual = bounds.fRight == crop.fRight;
+            const bool bottomEqual = bounds.fBottom == crop.fBottom;
+
+            // compute the UpperLeft corner radius
+            if (leftEqual && topEqual) {
+                radii[0].set(cornerRadius, cornerRadius);
+            } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
+                       (topEqual && bounds.fLeft >= insetCrop.fLeft) ||
+                       insetCrop.contains(bounds.fLeft, bounds.fTop)) {
+                radii[0].set(0, 0);
+            } else {
+                intersectionIsRoundRect = false;
+            }
+            // compute the UpperRight corner radius
+            if (rightEqual && topEqual) {
+                radii[1].set(cornerRadius, cornerRadius);
+            } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
+                       (topEqual && bounds.fRight <= insetCrop.fRight) ||
+                       insetCrop.contains(bounds.fRight, bounds.fTop)) {
+                radii[1].set(0, 0);
+            } else {
+                intersectionIsRoundRect = false;
+            }
+            // compute the BottomRight corner radius
+            if (rightEqual && bottomEqual) {
+                radii[2].set(cornerRadius, cornerRadius);
+            } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
+                       (bottomEqual && bounds.fRight <= insetCrop.fRight) ||
+                       insetCrop.contains(bounds.fRight, bounds.fBottom)) {
+                radii[2].set(0, 0);
+            } else {
+                intersectionIsRoundRect = false;
+            }
+            // compute the BottomLeft corner radius
+            if (leftEqual && bottomEqual) {
+                radii[3].set(cornerRadius, cornerRadius);
+            } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
+                       (bottomEqual && bounds.fLeft >= insetCrop.fLeft) ||
+                       insetCrop.contains(bounds.fLeft, bounds.fBottom)) {
+                radii[3].set(0, 0);
+            } else {
+                intersectionIsRoundRect = false;
+            }
+
+            if (intersectionIsRoundRect) {
+                SkRRect intersectionBounds;
+                intersectionBounds.setRectRadii(bounds, radii);
+                return {intersectionBounds, clip};
+            }
+        }
+
+        // we didn't it any of our fast paths so set the clip to the cropRect
+        clip.setRectXY(crop, cornerRadius, cornerRadius);
+    }
+
+    // if we hit this point then we either don't have rounded corners or we are going to rely
+    // on the clip to round the corners for us
+    return {SkRRect::MakeRect(bounds), clip};
 }
 
 inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer) {
@@ -1046,17 +1205,14 @@
     return mGrContext->maxRenderTargetSize();
 }
 
-void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRect& casterRect, float cornerRadius,
+void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
                                     const ShadowSettings& settings) {
     ATRACE_CALL();
     const float casterZ = settings.length / 2.0f;
-    const auto shadowShape = cornerRadius > 0
-            ? SkPath::RRect(SkRRect::MakeRectXY(casterRect, cornerRadius, cornerRadius))
-            : SkPath::Rect(casterRect);
     const auto flags =
             settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag;
 
-    SkShadowUtils::DrawShadow(canvas, shadowShape, SkPoint3::Make(0, 0, casterZ),
+    SkShadowUtils::DrawShadow(canvas, SkPath::RRect(casterRRect), SkPoint3::Make(0, 0, casterZ),
                               getSkPoint3(settings.lightPos), settings.lightRadius,
                               getSkColor(settings.ambientColor), getSkColor(settings.spotColor),
                               flags);
@@ -1173,15 +1329,6 @@
     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);
@@ -1197,13 +1344,11 @@
     const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER;
 
     // start by resizing the current context
-    auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
-    grContext->setResourceCacheLimit(maxResourceBytes);
+    getActiveGrContext()->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);
+        getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
         // reset back to the initial context that was active when this method was called
         useProtectedContext(!mInProtectedContext);
     }
@@ -1224,9 +1369,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.
@@ -1234,7 +1414,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 25557f1..1f528b7 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>
 
@@ -38,6 +39,7 @@
 #include "debug/SkiaCapture.h"
 #include "filters/BlurFilter.h"
 #include "filters/LinearEffect.h"
+#include "filters/StretchShaderFactory.h"
 
 namespace android {
 namespace renderengine {
@@ -52,13 +54,12 @@
     ~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;
@@ -66,11 +67,14 @@
     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);
@@ -84,21 +88,22 @@
                                                          int hwcFormat, Protection protection);
     inline SkRect getSkRect(const FloatRect& layer);
     inline SkRect getSkRect(const Rect& layer);
-    inline SkRRect getRoundedRect(const LayerSettings* layer);
-    inline BlurRegion getBlurRegion(const LayerSettings* layer);
+    inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const LayerSettings* layer);
     inline bool layerHasBlur(const LayerSettings* layer);
     inline SkColor getSkColor(const vec4& color);
     inline SkM44 getSkM44(const mat4& matrix);
     inline SkPoint3 getSkPoint3(const vec3& vector);
+    inline GrDirectContext* getActiveGrContext() const;
 
     base::unique_fd flush();
     bool waitFence(base::unique_fd fenceFd);
     void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
-    void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius,
+    void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
                     const ShadowSettings& shadowSettings);
     // If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned.
     // Otherwise it returns the input shader.
-    sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, const LayerSettings* layer,
+    sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader,
+                                              const LayerSettings* layer,
                                               const DisplaySettings& display,
                                               bool undoPremultipliedAlpha,
                                               bool requiresLinearEffect);
@@ -113,12 +118,21 @@
     const PixelFormat mDefaultPixelFormat;
     const bool mUseColorManagement;
 
-    // Cache of GL textures that we'll store per GraphicBuffer ID
-    std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
+    // Identifier used or various mappings of layers to various
+    // textures or shaders
+    using GraphicBufferId = uint64_t;
+
+    // Number of external holders of ExternalTexture references, per GraphicBuffer ID.
+    std::unordered_map<GraphicBufferId, int32_t> mGraphicBufferExternalRefs
             GUARDED_BY(mRenderingMutex);
-    std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>>
+    // Cache of GL textures that we'll store per GraphicBuffer ID, sliced by GPU context.
+    std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
+            GUARDED_BY(mRenderingMutex);
+    std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>>
             mProtectedTextureCache GUARDED_BY(mRenderingMutex);
     std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects;
+
+    StretchShaderFactory mStretchShaderFactory;
     // Mutex guarding rendering operations, so that:
     // 1. GL operations aren't interleaved, and
     // 2. Internal state related to rendering that is potentially modified by
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/debug/record.sh b/libs/renderengine/skia/debug/record.sh
index bc406d9..25c8cef 100755
--- a/libs/renderengine/skia/debug/record.sh
+++ b/libs/renderengine/skia/debug/record.sh
@@ -31,7 +31,7 @@
 # give the device time to both record, and starting writing the file.
 # Total time needed to write the file depends on how much data was recorded.
 # the loop at the end waits for this.
-sleep $(($1 / 1000 + 2));
+sleep $(($1 / 1000 + 4));
 
 # There is no guarantee that at least one frame passed through renderengine during that time
 # but as far as I know it always at least writes a 0-byte file with a new name, unless it crashes
@@ -54,7 +54,7 @@
 
 mskp_size=$(adb_filesize "/data/user/$name")
 if [[ $mskp_size = "0" ]]; then
-  echo "Empty file, probably no RenderEngine activity during recording period."
+  echo "File opened, but remains empty after recording period + wait. Either there was no RenderEngine activity during recording period, or recording process is still working. Check /data/user/$name manually later."
   exit 1
 fi
 
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index ec710d9..4ad6e94 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;
 
@@ -53,23 +53,23 @@
         }
     )");
 
-    auto [blurEffect, error] = SkRuntimeEffect::Make(blurString);
+    auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
     if (!blurEffect) {
         LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
     }
     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) {
-            return half4(mix(sample(originalInput), sample(blurredInput), mixFactor));
+            return half4(mix(sample(originalInput, xy), sample(blurredInput, xy), mixFactor));
         }
     )");
 
-    auto [mixEffect, mixError] = SkRuntimeEffect::Make(mixString);
+    auto [mixEffect, mixError] = SkRuntimeEffect::MakeForShader(mixString);
     if (!mixEffect) {
         LOG_ALWAYS_FATAL("RuntimeShader error: %s", mixError.c_str());
     }
@@ -86,8 +86,8 @@
     float radiusByPasses = tmpRadius / (float)numberOfPasses;
 
     // create blur surface with the bit depth and colorspace of the original surface
-    SkImageInfo scaledInfo = input->imageInfo().makeWH(blurRect.width() * kInputScale,
-                                                       blurRect.height() * kInputScale);
+    SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
+                                                       std::ceil(blurRect.height() * kInputScale));
 
     const float stepX = radiusByPasses;
     const float stepY = radiusByPasses;
@@ -105,7 +105,7 @@
             input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
     blurBuilder.uniform("in_blurOffset") = SkV2{stepX * kInputScale, stepY * kInputScale};
     blurBuilder.uniform("in_maxSizeXY") =
-            SkV2{blurRect.width() * kInputScale - 1, blurRect.height() * kInputScale - 1};
+            SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale};
 
     sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
 
@@ -116,7 +116,7 @@
                 tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
         blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};
         blurBuilder.uniform("in_maxSizeXY") =
-                SkV2{blurRect.width() * kInputScale - 1, blurRect.height() * kInputScale - 1};
+                SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale};
         tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
     }
 
@@ -139,23 +139,21 @@
     return matrix;
 }
 
-void BlurFilter::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion,
+void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion,
+                                const uint32_t blurRadius, const float blurAlpha,
                                 const SkRect& blurRect, sk_sp<SkImage> blurredImage,
                                 sk_sp<SkImage> input) {
     ATRACE_CALL();
 
     SkPaint paint;
-    paint.setAlphaf(effectRegion.alpha);
-    if (effectRegion.alpha == 1.0f) {
-        paint.setBlendMode(SkBlendMode::kSrc);
-    }
+    paint.setAlphaf(blurAlpha);
 
     const auto blurMatrix = getShaderTransform(canvas, blurRect, kInverseInputScale);
     SkSamplingOptions linearSampling(SkFilterMode::kLinear, SkMipmapMode::kNone);
     const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
                                                      linearSampling, &blurMatrix);
 
-    if (effectRegion.blurRadius < kMaxCrossFadeRadius) {
+    if (blurRadius < kMaxCrossFadeRadius) {
         // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
         // case you might expect the matrix to simply be the canvas matrix.
         SkMatrix inputMatrix;
@@ -168,30 +166,21 @@
         blurBuilder.child("originalInput") =
                 input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling,
                                   inputMatrix);
-        blurBuilder.uniform("mixFactor") = effectRegion.blurRadius / kMaxCrossFadeRadius;
+        blurBuilder.uniform("mixFactor") = blurRadius / kMaxCrossFadeRadius;
 
         paint.setShader(blurBuilder.makeShader(nullptr, true));
     } else {
         paint.setShader(blurShader);
     }
 
-    // TODO we should AA at least the drawRoundRect which would mean no SRC blending
-    // TODO this round rect calculation doesn't match the one used to draw in RenderEngine
-    auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right,
-                                 effectRegion.bottom);
-
-    if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 ||
-        effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) {
-        const SkVector radii[4] =
-                {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL),
-                 SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR),
-                 SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL),
-                 SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)};
-        SkRRect roundedRect;
-        roundedRect.setRectRadii(rect, radii);
-        canvas->drawRRect(roundedRect, paint);
+    if (effectRegion.isRect()) {
+        if (blurAlpha == 1.0f) {
+            paint.setBlendMode(SkBlendMode::kSrc);
+        }
+        canvas->drawRect(effectRegion.rect(), paint);
     } else {
-        canvas->drawRect(rect, paint);
+        paint.setAntiAlias(true);
+        canvas->drawRRect(effectRegion, paint);
     }
 }
 
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 731ba11..a8e6dd7 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -20,7 +20,6 @@
 #include <SkImage.h>
 #include <SkRuntimeEffect.h>
 #include <SkSurface.h>
-#include <ui/BlurRegion.h>
 
 using namespace std;
 
@@ -52,8 +51,19 @@
     sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
                             const sk_sp<SkImage> blurInput, const SkRect& blurRect) const;
 
-    void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& blurRect,
-                        sk_sp<SkImage> blurredImage, sk_sp<SkImage> input);
+    /**
+     * Draw the blurred content (from the generate method) into the canvas.
+     * @param canvas is the destination/output for the blur
+     * @param effectRegion the RoundRect in canvas coordinates that determines the blur coverage
+     * @param blurRadius radius of the blur used to determine the intensity of the crossfade effect
+     * @param blurAlpha alpha value applied to the effectRegion when the blur is drawn
+     * @param blurRect bounds of the blurredImage translated into canvas coordinates
+     * @param blurredImage down-sampled blurred content that was produced by the generate() method
+     * @param input original unblurred input that is used to crossfade with the blurredImage
+     */
+    void drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, const uint32_t blurRadius,
+                        const float blurAlpha, const SkRect& blurRect, sk_sp<SkImage> blurredImage,
+                        sk_sp<SkImage> input);
 
 private:
     sk_sp<SkRuntimeEffect> mBlurEffect;
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index 84af016..9b044e1 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -127,7 +127,7 @@
         default:
             shader.append(R"(
                     float3 ScaleLuminance(float3 xyz) {
-                        return xyz * in_displayMaxLuminance;
+                        return xyz * in_inputMaxLuminance;
                     }
                 )");
             break;
@@ -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));
     )");
@@ -438,7 +438,7 @@
     generateOETF(linearEffect.outputDataspace, shaderString);
     generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
 
-    auto [shader, error] = SkRuntimeEffect::Make(shaderString);
+    auto [shader, error] = SkRuntimeEffect::MakeForShader(shaderString);
     if (!shader) {
         LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
     }
@@ -448,7 +448,7 @@
 sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEffect& linearEffect,
                                          sk_sp<SkRuntimeEffect> runtimeEffect,
                                          const mat4& colorTransform, float maxDisplayLuminance,
-                                         float maxMasteringLuminance, float maxContentLuminance) {
+                                         float maxLuminance) {
     ATRACE_CALL();
     SkRuntimeShaderBuilder effectBuilder(runtimeEffect);
 
@@ -467,8 +467,10 @@
     }
 
     effectBuilder.uniform("in_displayMaxLuminance") = maxDisplayLuminance;
+    // If the input luminance is unknown, use display luminance (aka, no-op any luminance changes)
+    // This will be the case for eg screenshots in addition to uncalibrated displays
     effectBuilder.uniform("in_inputMaxLuminance") =
-            std::min(maxMasteringLuminance, maxContentLuminance);
+            maxLuminance > 0 ? maxLuminance : maxDisplayLuminance;
     return effectBuilder.makeShader(nullptr, false);
 }
 
diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h
index 20b8338..14a3b61 100644
--- a/libs/renderengine/skia/filters/LinearEffect.h
+++ b/libs/renderengine/skia/filters/LinearEffect.h
@@ -90,15 +90,13 @@
 // matrix transforming from linear XYZ to linear RGB immediately before OETF.
 // We also provide additional HDR metadata upon creating the shader:
 // * The max display luminance is the max luminance of the physical display in nits
-// * The max mastering luminance is provided as the max luminance from the SMPTE 2086
-// standard.
-// * The max content luminance is provided as the max light level from the CTA 861.3
-// standard.
+// * The max luminance is provided as the max luminance for the buffer, either from the SMPTE 2086
+// or as the max light level from the CTA 861.3 standard.
 sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> inputShader,
                                          const LinearEffect& linearEffect,
                                          sk_sp<SkRuntimeEffect> runtimeEffect,
                                          const mat4& colorTransform, float maxDisplayLuminance,
-                                         float maxMasteringLuminance, float maxContentLuminance);
+                                         float maxLuminance);
 } // namespace skia
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.cpp b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
new file mode 100644
index 0000000..4ac5c40
--- /dev/null
+++ b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
@@ -0,0 +1,246 @@
+/*
+ * 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 "StretchShaderFactory.h"
+#include <SkImageFilter.h>
+#include <SkRefCnt.h>
+#include <SkRuntimeEffect.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include "log/log.h"
+#include <memory>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+static const SkString stretchShader = SkString(R"(
+    uniform shader uContentTexture;
+
+    // multiplier to apply to scale effect
+    uniform float uMaxStretchIntensity;
+
+    // Maximum percentage to stretch beyond bounds  of target
+    uniform float uStretchAffectedDistX;
+    uniform float uStretchAffectedDistY;
+
+    // Distance stretched as a function of the normalized overscroll times
+    // scale intensity
+    uniform float uDistanceStretchedX;
+    uniform float uDistanceStretchedY;
+    uniform float uInverseDistanceStretchedX;
+    uniform float uInverseDistanceStretchedY;
+    uniform float uDistDiffX;
+
+    // Difference between the peak stretch amount and overscroll amount normalized
+    uniform float uDistDiffY;
+
+    // Horizontal offset represented as a ratio of pixels divided by the target width
+    uniform float uScrollX;
+    // Vertical offset represented as a ratio of pixels divided by the target height
+    uniform float uScrollY;
+
+    // Normalized overscroll amount in the horizontal direction
+    uniform float uOverscrollX;
+
+    // Normalized overscroll amount in the vertical direction
+    uniform float uOverscrollY;
+    uniform float viewportWidth; // target height in pixels
+    uniform float viewportHeight; // target width in pixels
+
+    // uInterpolationStrength is the intensity of the interpolation.
+    // if uInterpolationStrength is 0, then the stretch is constant for all the
+    // uStretchAffectedDist. if uInterpolationStrength is 1, then stretch intensity
+    // is interpolated based on the pixel position in the uStretchAffectedDist area;
+    // The closer we are from the scroll anchor point, the more it stretches,
+    // and the other way around.
+    uniform float uInterpolationStrength;
+
+    float easeIn(float t, float d) {
+        return t * d;
+    }
+
+    float computeOverscrollStart(
+        float inPos,
+        float overscroll,
+        float uStretchAffectedDist,
+        float uInverseStretchAffectedDist,
+        float distanceStretched,
+        float interpolationStrength
+    ) {
+        float offsetPos = uStretchAffectedDist - inPos;
+        float posBasedVariation = mix(
+                1. ,easeIn(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
+        float stretchIntensity = overscroll * posBasedVariation;
+        return distanceStretched - (offsetPos / (1. + stretchIntensity));
+    }
+
+    float computeOverscrollEnd(
+        float inPos,
+        float overscroll,
+        float reverseStretchDist,
+        float uStretchAffectedDist,
+        float uInverseStretchAffectedDist,
+        float distanceStretched,
+        float interpolationStrength
+    ) {
+        float offsetPos = inPos - reverseStretchDist;
+        float posBasedVariation = mix(
+                1. ,easeIn(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
+        float stretchIntensity = (-overscroll) * posBasedVariation;
+        return 1 - (distanceStretched - (offsetPos / (1. + stretchIntensity)));
+    }
+
+    // Prefer usage of return values over out parameters as it enables
+    // SKSL to properly inline method calls and works around potential GPU
+    // driver issues on Wembly. See b/182566543 for details
+    float computeOverscroll(
+        float inPos,
+        float overscroll,
+        float uStretchAffectedDist,
+        float uInverseStretchAffectedDist,
+        float distanceStretched,
+        float distanceDiff,
+        float interpolationStrength
+    ) {
+      float outPos = inPos;
+      // overscroll is provided via uniform so there is no concern
+      // for potential incoherent branches
+      if (overscroll > 0) {
+            if (inPos <= uStretchAffectedDist) {
+                outPos = computeOverscrollStart(
+                  inPos,
+                  overscroll,
+                  uStretchAffectedDist,
+                  uInverseStretchAffectedDist,
+                  distanceStretched,
+                  interpolationStrength
+                );
+            } else if (inPos >= distanceStretched) {
+                outPos = distanceDiff + inPos;
+            }
+        }
+        if (overscroll < 0) {
+            float stretchAffectedDist = 1. - uStretchAffectedDist;
+            if (inPos >= stretchAffectedDist) {
+                outPos = computeOverscrollEnd(
+                  inPos,
+                  overscroll,
+                  stretchAffectedDist,
+                  uStretchAffectedDist,
+                  uInverseStretchAffectedDist,
+                  distanceStretched,
+                  interpolationStrength
+                );
+            } else if (inPos < stretchAffectedDist) {
+                outPos = -distanceDiff + inPos;
+            }
+        }
+        return outPos;
+    }
+
+    vec4 main(vec2 coord) {
+        // Normalize SKSL pixel coordinate into a unit vector
+        float inU = coord.x / viewportWidth;
+        float inV = coord.y / viewportHeight;
+        float outU;
+        float outV;
+        float stretchIntensity;
+        // Add the normalized scroll position within scrolling list
+        inU += uScrollX;
+        inV += uScrollY;
+        outU = inU;
+        outV = inV;
+        outU = computeOverscroll(
+            inU,
+            uOverscrollX,
+            uStretchAffectedDistX,
+            uInverseDistanceStretchedX,
+            uDistanceStretchedX,
+            uDistDiffX,
+            uInterpolationStrength
+        );
+        outV = computeOverscroll(
+            inV,
+            uOverscrollY,
+            uStretchAffectedDistY,
+            uInverseDistanceStretchedY,
+            uDistanceStretchedY,
+            uDistDiffY,
+            uInterpolationStrength
+        );
+        coord.x = (outU - uScrollX) * viewportWidth;
+        coord.y = (outV - uScrollY) * viewportHeight;
+        return sample(uContentTexture, coord);
+    })");
+
+const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
+
+sk_sp<SkShader> StretchShaderFactory::createSkShader(const sk_sp<SkShader>& inputShader,
+                                                     const StretchEffect& stretchEffect) {
+    if (!stretchEffect.hasEffect()) {
+        return nullptr;
+    }
+
+    float viewportWidth = stretchEffect.width;
+    float viewportHeight = stretchEffect.height;
+    float normOverScrollDistX = stretchEffect.vectorX;
+    float normOverScrollDistY = stretchEffect.vectorY;
+    float distanceStretchedX =
+        StretchEffect::CONTENT_DISTANCE_STRETCHED / (1 + abs(normOverScrollDistX));
+    float distanceStretchedY =
+        StretchEffect::CONTENT_DISTANCE_STRETCHED / (1 + abs(normOverScrollDistY));
+    float inverseDistanceStretchedX =
+        1.f / StretchEffect::CONTENT_DISTANCE_STRETCHED;
+    float inverseDistanceStretchedY =
+        1.f / StretchEffect::CONTENT_DISTANCE_STRETCHED;
+    float diffX =
+        distanceStretchedX - StretchEffect::CONTENT_DISTANCE_STRETCHED;
+    float diffY =
+        distanceStretchedY - StretchEffect::CONTENT_DISTANCE_STRETCHED;
+    auto& srcBounds = stretchEffect.mappedChildBounds;
+    float normalizedScrollX = srcBounds.left / viewportWidth;
+    float normalizedScrollY = srcBounds.top / viewportHeight;
+
+    if (mBuilder == nullptr) {
+        const static SkRuntimeEffect::Result instance =
+            SkRuntimeEffect::MakeForShader(stretchShader);
+        mBuilder = std::make_unique<SkRuntimeShaderBuilder>(instance.effect);
+    }
+
+    mBuilder->child("uContentTexture") = inputShader;
+    mBuilder->uniform("uInterpolationStrength").set(&INTERPOLATION_STRENGTH_VALUE, 1);
+    mBuilder->uniform("uStretchAffectedDistX").set(&StretchEffect::CONTENT_DISTANCE_STRETCHED, 1);
+    mBuilder->uniform("uStretchAffectedDistY").set(&StretchEffect::CONTENT_DISTANCE_STRETCHED, 1);
+    mBuilder->uniform("uDistanceStretchedX").set(&distanceStretchedX, 1);
+    mBuilder->uniform("uDistanceStretchedY").set(&distanceStretchedY, 1);
+    mBuilder->uniform("uInverseDistanceStretchedX").set(&inverseDistanceStretchedX, 1);
+    mBuilder->uniform("uInverseDistanceStretchedY").set(&inverseDistanceStretchedY, 1);
+    mBuilder->uniform("uDistDiffX").set(&diffX, 1);
+    mBuilder->uniform("uDistDiffY").set(&diffY, 1);
+    mBuilder->uniform("uOverscrollX").set(&normOverScrollDistX, 1);
+    mBuilder->uniform("uOverscrollY").set(&normOverScrollDistY, 1);
+    mBuilder->uniform("uScrollX").set(&normalizedScrollX, 1);
+    mBuilder->uniform("uScrollY").set(&normalizedScrollY, 1);
+    mBuilder->uniform("viewportWidth").set(&viewportWidth, 1);
+    mBuilder->uniform("viewportHeight").set(&viewportHeight, 1);
+
+    return mBuilder->makeShader(nullptr, false);
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.h b/libs/renderengine/skia/filters/StretchShaderFactory.h
new file mode 100644
index 0000000..9c3ab7c
--- /dev/null
+++ b/libs/renderengine/skia/filters/StretchShaderFactory.h
@@ -0,0 +1,36 @@
+/*
+ * 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 <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkShader.h>
+#include <ui/StretchEffect.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+class StretchShaderFactory {
+public:
+    sk_sp<SkShader> createSkShader(const sk_sp<SkShader>& inputShader,
+                                   const StretchEffect& stretchEffect);
+
+private:
+    std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
+};
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 7846156..72e32ed 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) {
@@ -246,6 +263,11 @@
         }
     }
 
+    void expectBufferColor(const Point& point, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+                           uint8_t tolerance = 0) {
+        expectBufferColor(Rect(point.x, point.y, point.x + 1, point.y + 1), r, g, b, a, tolerance);
+    }
+
     void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
                            uint8_t tolerance = 0) {
         auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) {
@@ -262,13 +284,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 +311,7 @@
                 break;
             }
         }
-        mBuffer->unlock();
+        mBuffer->getBuffer()->unlock();
     }
 
     void expectAlpha(const Rect& rect, uint8_t a) {
@@ -387,7 +409,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 +418,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()));
         }
     }
 
@@ -471,6 +492,9 @@
     void fillBufferAndBlurBackground();
 
     template <typename SourceVariant>
+    void fillSmallLayerAndBlurBackground();
+
+    template <typename SourceVariant>
     void overlayCorners();
 
     void fillRedBufferTextureTransform();
@@ -503,17 +527,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 +548,7 @@
     } else {
         mRE = renderEngineFactory->createRenderEngine();
     }
+    mBuffer = allocateDefaultBuffer();
 }
 
 struct ColorSourceVariant {
@@ -566,18 +585,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 +605,7 @@
             }
         }
 
-        buf->unlock();
+        buf->getBuffer()->unlock();
 
         layer.source.buffer.buffer = buf;
         layer.source.buffer.textureName = texName;
@@ -955,11 +974,44 @@
     if (mRE->supportsBackgroundBlur()) {
         // blurred color (downsampling should result in the center color being close to 128)
         expectBufferColor(Rect(center - 1, center - 5, center + 1, center + 5), 128, 128, 0, 255,
-                          10 /* tolerance */);
+                          50 /* tolerance */);
     }
 }
 
 template <typename SourceVariant>
+void RenderEngineTest::fillSmallLayerAndBlurBackground() {
+    auto blurRadius = 50;
+    renderengine::DisplaySettings settings;
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+
+    std::vector<const renderengine::LayerSettings*> layers;
+
+    renderengine::LayerSettings backgroundLayer;
+    backgroundLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+    SourceVariant::fillColor(backgroundLayer, 1.0f, 0.0f, 0.0f, this);
+    backgroundLayer.alpha = 1.0f;
+    layers.push_back(&backgroundLayer);
+
+    renderengine::LayerSettings blurLayer;
+    blurLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    blurLayer.geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f);
+    blurLayer.backgroundBlurRadius = blurRadius;
+    SourceVariant::fillColor(blurLayer, 0.0f, 0.0f, 1.0f, this);
+    blurLayer.alpha = 0;
+    layers.push_back(&blurLayer);
+
+    invokeDraw(settings, layers);
+
+    // Give a generous tolerance - the blur rectangle is very small and this test is
+    // mainly concerned with ensuring that there's no device failure.
+    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), 255, 0, 0, 255,
+                      40 /* tolerance */);
+}
+
+template <typename SourceVariant>
 void RenderEngineTest::overlayCorners() {
     renderengine::DisplaySettings settings;
     settings.physicalDisplay = fullscreenRect();
@@ -1012,14 +1064,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 +1085,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 +1113,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 +1152,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 +1285,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 +1346,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 +1373,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);
 }
 
@@ -1399,6 +1448,11 @@
     fillBufferAndBlurBackground<ColorSourceVariant>();
 }
 
+TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_colorSource) {
+    initializeRenderEngine();
+    fillSmallLayerAndBlurBackground<ColorSourceVariant>();
+}
+
 TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) {
     initializeRenderEngine();
     overlayCorners<ColorSourceVariant>();
@@ -1474,6 +1528,11 @@
     fillBufferAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
+TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_opaqueBufferSource) {
+    initializeRenderEngine();
+    fillSmallLayerAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
 TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) {
     initializeRenderEngine();
     overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
@@ -1549,6 +1608,11 @@
     fillBufferAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
+TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_bufferSource) {
+    initializeRenderEngine();
+    fillSmallLayerAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
 TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) {
     initializeRenderEngine();
     overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
@@ -1574,98 +1638,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 +1830,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));
@@ -1920,6 +1892,51 @@
                       0, 255, 0, 255);
 }
 
+TEST_P(RenderEngineTest, testRoundedCornersParentCrop) {
+    initializeRenderEngine();
+
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+    std::vector<const renderengine::LayerSettings*> layers;
+
+    renderengine::LayerSettings redLayer;
+    redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+    redLayer.geometry.roundedCornersRadius = 5.0f;
+    redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
+    // Red background.
+    redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+    redLayer.alpha = 1.0f;
+
+    layers.push_back(&redLayer);
+
+    // Green layer with 1/2 size with parent crop rect.
+    renderengine::LayerSettings greenLayer = redLayer;
+    greenLayer.geometry.boundaries =
+            FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2);
+    greenLayer.source.solidColor = half3(0.0f, 1.0f, 0.0f);
+
+    layers.push_back(&greenLayer);
+
+    invokeDraw(settings, layers);
+
+    // Due to roundedCornersRadius, the corners are untouched.
+    expectBufferColor(Point(0, 0), 0, 0, 0, 0);
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0);
+    expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+
+    // top middle should be green and the bottom middle red
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 0), 0, 255, 0, 255);
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, 255);
+
+    // the bottom edge of the green layer should not be rounded
+    expectBufferColor(Point(0, (DEFAULT_DISPLAY_HEIGHT / 2) - 1), 0, 255, 0, 255);
+}
+
 TEST_P(RenderEngineTest, testClear) {
     initializeRenderEngine();
 
@@ -1966,16 +1983,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 783e37f..9009ce4 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,28 @@
             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();
+    ATRACE_CALL();
+    // 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) {
@@ -119,6 +132,7 @@
 }
 
 void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
+    ATRACE_CALL();
     std::promise<void> resultPromise;
     std::future<void> resultFuture = resultPromise.get_future();
     {
@@ -134,6 +148,7 @@
 }
 
 void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
+    ATRACE_CALL();
     std::promise<void> resultPromise;
     std::future<void> resultFuture = resultPromise.get_future();
     {
@@ -148,90 +163,56 @@
     resultFuture.wait();
 }
 
-void RenderEngineThreaded::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
+                                                    bool isRenderable) {
+    ATRACE_CALL();
     // 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) {
+    ATRACE_CALL();
     // 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,10 +248,11 @@
 
 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) {
+    ATRACE_CALL();
     std::promise<status_t> resultPromise;
     std::future<status_t> resultFuture = resultPromise.get_future();
     {
@@ -288,18 +270,17 @@
 }
 
 void RenderEngineThreaded::cleanFramebufferCache() {
-    std::promise<void> resultPromise;
-    std::future<void> resultFuture = resultPromise.get_future();
+    ATRACE_CALL();
+    // 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,33 +299,21 @@
 }
 
 bool RenderEngineThreaded::supportsBackgroundBlur() {
-    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::supportsBackgroundBlur");
-            bool returnValue = instance.supportsBackgroundBlur();
-            resultPromise.set_value(returnValue);
-        });
-    }
-    mCondition.notify_one();
-    return resultFuture.get();
+    waitUntilInitialized();
+    return mRenderEngine->supportsBackgroundBlur();
 }
 
 void RenderEngineThreaded::onPrimaryDisplaySizeChanged(ui::Size size) {
-    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, size](renderengine::RenderEngine& instance) {
+        mFunctionCalls.push([size](renderengine::RenderEngine& instance) {
             ATRACE_NAME("REThreaded::onPrimaryDisplaySizeChanged");
             instance.onPrimaryDisplaySizeChanged(size);
-            resultPromise.set_value();
         });
     }
     mCondition.notify_one();
-    resultFuture.wait();
 }
 
 } // namespace threaded
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 117257a..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,16 +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
@@ -83,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/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index 240738d..0a49008 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -231,6 +231,10 @@
             mFlags |= SENSOR_FLAG_WAKE_UP;
         }
         break;
+    case SENSOR_TYPE_DEVICE_ORIENTATION:
+        mStringType = SENSOR_STRING_TYPE_DEVICE_ORIENTATION;
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        break;
     case SENSOR_TYPE_DYNAMIC_SENSOR_META:
         mStringType = SENSOR_STRING_TYPE_DYNAMIC_SENSOR_META;
         mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE; // special trigger
diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp
index 4714469..b3537ce 100644
--- a/libs/sensorprivacy/SensorPrivacyManager.cpp
+++ b/libs/sensorprivacy/SensorPrivacyManager.cpp
@@ -55,6 +55,22 @@
     return service;
 }
 
+bool SensorPrivacyManager::supportsSensorToggle(int sensor) {
+    if (mSupportedCache.find(sensor) == mSupportedCache.end()) {
+        sp<hardware::ISensorPrivacyManager> service = getService();
+        if (service != nullptr) {
+            bool result;
+            service->supportsSensorToggle(sensor, &result);
+            mSupportedCache[sensor] = result;
+            return result;
+        }
+        // if the SensorPrivacyManager is not available then assume sensor privacy feature isn't
+        // supported
+        return false;
+    }
+    return mSupportedCache[sensor];
+}
+
 void SensorPrivacyManager::addSensorPrivacyListener(
         const sp<hardware::ISensorPrivacyListener>& listener)
 {
diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
index 629b8c2..c8ceeb8 100644
--- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
+++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
@@ -20,6 +20,8 @@
 
 /** @hide */
 interface ISensorPrivacyManager {
+    boolean supportsSensorToggle(int sensor);
+
     void addSensorPrivacyListener(in ISensorPrivacyListener listener);
 
     void addIndividualSensorPrivacyListener(int userId, int sensor, in ISensorPrivacyListener listener);
diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
index 12778e1..fb4cbe9 100644
--- a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
+++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
@@ -22,6 +22,8 @@
 
 #include <utils/threads.h>
 
+#include <unordered_map>
+
 // ---------------------------------------------------------------------------
 namespace android {
 
@@ -35,6 +37,7 @@
 
     SensorPrivacyManager();
 
+    bool supportsSensorToggle(int sensor);
     void addSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener);
     status_t addIndividualSensorPrivacyListener(int userId, int sensor,
             const sp<hardware::ISensorPrivacyListener>& listener);
@@ -50,6 +53,8 @@
     Mutex mLock;
     sp<hardware::ISensorPrivacyManager> mService;
     sp<hardware::ISensorPrivacyManager> getService();
+
+    std::unordered_map<int, bool> mSupportedCache;
 };
 
 
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 636fbde..9dc9beb 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -36,6 +36,7 @@
 using android::hardware::graphics::mapper::V4_0::BufferDescriptor;
 using android::hardware::graphics::mapper::V4_0::Error;
 using android::hardware::graphics::mapper::V4_0::IMapper;
+using AidlDataspace = ::aidl::android::hardware::graphics::common::Dataspace;
 using BufferDump = android::hardware::graphics::mapper::V4_0::IMapper::BufferDump;
 using MetadataDump = android::hardware::graphics::mapper::V4_0::IMapper::MetadataDump;
 using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
@@ -597,7 +598,7 @@
     if (!outDataspace) {
         return BAD_VALUE;
     }
-    aidl::android::hardware::graphics::common::Dataspace dataspace;
+    AidlDataspace dataspace;
     status_t error = get(bufferHandle, gralloc4::MetadataType_Dataspace, gralloc4::decodeDataspace,
                          &dataspace);
     if (error) {
@@ -841,6 +842,7 @@
     uint32_t pixelFormatFourCC;
     uint64_t pixelFormatModifier;
     uint64_t usage;
+    AidlDataspace dataspace;
     uint64_t allocationSize;
     uint64_t protectedContent;
     ExtendableType compression;
@@ -892,6 +894,11 @@
     if (error != NO_ERROR) {
         return error;
     }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::DATASPACE,
+                               gralloc4::decodeDataspace, &dataspace);
+    if (error != NO_ERROR) {
+        return error;
+    }
     error = metadataDumpHelper(bufferDump, StandardMetadataType::ALLOCATION_SIZE,
                                gralloc4::decodeAllocationSize, &allocationSize);
     if (error != NO_ERROR) {
@@ -932,6 +939,7 @@
              << "KiB, w/h:" << width << "x" << height << ", usage: 0x" << std::hex << usage
              << std::dec << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested)
              << ", fourcc/mod:" << pixelFormatFourCC << "/" << pixelFormatModifier
+             << ", dataspace: 0x" << std::hex << static_cast<uint32_t>(dataspace)
              << ", compressed: ";
 
     if (less) {
diff --git a/libs/ui/include/ui/BlurRegion.h b/libs/ui/include/ui/BlurRegion.h
index 69a586e..a9ca369 100644
--- a/libs/ui/include/ui/BlurRegion.h
+++ b/libs/ui/include/ui/BlurRegion.h
@@ -20,6 +20,8 @@
 #include <iosfwd>
 #include <iostream>
 
+#include <math/HashCombine.h>
+
 namespace android {
 
 struct BlurRegion {
@@ -33,6 +35,16 @@
     int top;
     int right;
     int bottom;
+
+    inline bool operator==(const BlurRegion& other) const {
+        return blurRadius == other.blurRadius && cornerRadiusTL == other.cornerRadiusTL &&
+                cornerRadiusTR == other.cornerRadiusTR && cornerRadiusBL == other.cornerRadiusBL &&
+                cornerRadiusBR == other.cornerRadiusBR && alpha == other.alpha &&
+                left == other.left && top == other.top && right == other.right &&
+                bottom == other.bottom;
+    }
+
+    inline bool operator!=(const BlurRegion& other) const { return !(*this == other); }
 };
 
 static inline void PrintTo(const BlurRegion& blurRegion, ::std::ostream* os) {
@@ -50,4 +62,15 @@
     *os << "\n}";
 }
 
-} // namespace android
\ No newline at end of file
+} // namespace android
+
+namespace std {
+template <>
+struct hash<android::BlurRegion> {
+    size_t operator()(const android::BlurRegion& region) const {
+        return android::hashCombine(region.blurRadius, region.cornerRadiusTL, region.cornerRadiusTR,
+                                    region.cornerRadiusBL, region.cornerRadiusBR, region.alpha,
+                                    region.left, region.top, region.right, region.bottom);
+    }
+};
+} // namespace std
\ No newline at end of file
diff --git a/libs/ui/include/ui/StretchEffect.h b/libs/ui/include/ui/StretchEffect.h
index 0803df3..cf08acb 100644
--- a/libs/ui/include/ui/StretchEffect.h
+++ b/libs/ui/include/ui/StretchEffect.h
@@ -25,31 +25,49 @@
 namespace android {
 
 struct StretchEffect : public LightFlattenablePod<StretchEffect> {
-    FloatRect area = {0, 0, 0, 0};
-    float vectorX = 0;
-    float vectorY = 0;
-    float maxAmount = 0;
+  constexpr static const float CONTENT_DISTANCE_STRETCHED = 1.f;
 
-    bool operator==(const StretchEffect& other) const {
-        return area == other.area && vectorX == other.vectorX && vectorY == other.vectorY &&
-                maxAmount == other.maxAmount;
+  float width = 0;
+  float height = 0;
+  float vectorX = 0;
+  float vectorY = 0;
+  float maxAmountX = 0;
+  float maxAmountY = 0;
+  FloatRect mappedChildBounds = {0, 0, 0, 0};
+
+  bool operator==(const StretchEffect& other) const {
+    return width == other.width && height == other.height &&
+        vectorX == other.vectorX &&
+        vectorY == other.vectorY &&
+        maxAmountX == other.maxAmountX &&
+        maxAmountY == other.maxAmountY &&
+        mappedChildBounds == other.mappedChildBounds;
+  }
+
+  static bool isZero(float value) {
+    constexpr float NON_ZERO_EPSILON = 0.001f;
+    return fabsf(value) <= NON_ZERO_EPSILON;
+  }
+
+  bool isNoOp() const { return isZero(vectorX) && isZero(vectorY); }
+
+  bool hasEffect() const { return !isNoOp(); }
+
+  void sanitize() {
+    // If the area is empty, or the max amount is zero, then reset back to defaults
+    if (width == 0.f || height == 0.f || isZero(maxAmountX) ||
+        isZero(maxAmountY)) {
+      *this = StretchEffect{};
     }
+  }
 
-    static bool isZero(float value) {
-        constexpr float NON_ZERO_EPSILON = 0.001f;
-        return fabsf(value) <= NON_ZERO_EPSILON;
-    }
+  float getStretchWidthMultiplier() const {
+      return CONTENT_DISTANCE_STRETCHED / (1.f + abs(vectorX));
+  }
 
-    bool isNoOp() const { return isZero(vectorX) && isZero(vectorY); }
-
-    bool hasEffect() const { return !isNoOp(); }
-
-    void sanitize() {
-        // If the area is empty, or the max amount is zero, then reset back to defaults
-        if (area.isEmpty() || isZero(maxAmount)) {
-            *this = StretchEffect{};
-        }
-    }
+  float getStretchHeightMultiplier() const {
+      return CONTENT_DISTANCE_STRETCHED / (1.f + abs(vectorY));
+  }
 };
 
 static_assert(std::is_trivially_copyable<StretchEffect>::value,
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 3878cb1..b15694b 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -69,11 +69,9 @@
     host_supported: true,
     vendor_available: true,
     export_include_dirs: ["include"],
-}
-
-llndk_headers {
-    name: "gl_llndk_headers",
-    export_include_dirs: ["include"],
+    llndk: {
+        llndk_headers: true,
+    },
 }
 
 subdirs = [
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 7861d62..c9fce8a 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -137,7 +137,12 @@
 cc_library_shared {
     name: "libEGL",
     defaults: ["egl_libs_defaults"],
-    llndk_stubs: "libEGL.llndk",
+    llndk: {
+        symbol_file: "libEGL.map.txt",
+        export_llndk_headers: ["gl_headers"],
+        // Don't export EGL/include from the LLNDK variant.
+        override_export_include_dirs: [],
+    },
     srcs: [
         "EGL/egl_tls.cpp",
         "EGL/egl_cache.cpp",
@@ -203,7 +208,12 @@
 cc_library_shared {
     name: "libGLESv1_CM",
     defaults: ["gles_libs_defaults"],
-    llndk_stubs: "libGLESv1_CM.llndk",
+    llndk: {
+        symbol_file: "libGLESv1_CM.map.txt",
+        export_llndk_headers: ["gl_headers"],
+        // Don't export EGL/include from the LLNDK variant.
+        override_export_include_dirs: [],
+    },
     srcs: ["GLES_CM/gl.cpp"],
     cflags: ["-DLOG_TAG=\"libGLESv1\""],
     version_script: "libGLESv1_CM.map.txt",
@@ -215,7 +225,12 @@
 cc_library_shared {
     name: "libGLESv2",
     defaults: ["gles_libs_defaults"],
-    llndk_stubs: "libGLESv2.llndk",
+    llndk: {
+        symbol_file: "libGLESv2.map.txt",
+        export_llndk_headers: ["gl_headers"],
+        // Don't export EGL/include from the LLNDK variant.
+        override_export_include_dirs: [],
+    },
     srcs: ["GLES2/gl2.cpp"],
     cflags: ["-DLOG_TAG=\"libGLESv2\""],
 
@@ -230,31 +245,12 @@
 cc_library_shared {
     name: "libGLESv3",
     defaults: ["gles_libs_defaults"],
-    llndk_stubs: "libGLESv3.llndk",
+    llndk: {
+        symbol_file: "libGLESv3.map.txt",
+        export_llndk_headers: ["gl_headers"],
+        // Don't export EGL/include from the LLNDK variant.
+        override_export_include_dirs: [],
+    },
     srcs: ["GLES2/gl2.cpp"],
     cflags: ["-DLOG_TAG=\"libGLESv3\""],
 }
-
-llndk_library {
-    name: "libEGL.llndk",
-    symbol_file: "libEGL.map.txt",
-    export_llndk_headers: ["gl_llndk_headers"],
-}
-
-llndk_library {
-    name: "libGLESv1_CM.llndk",
-    symbol_file: "libGLESv1_CM.map.txt",
-    export_llndk_headers: ["gl_llndk_headers"],
-}
-
-llndk_library {
-    name: "libGLESv2.llndk",
-    symbol_file: "libGLESv2.map.txt",
-    export_llndk_headers: ["gl_llndk_headers"],
-}
-
-llndk_library {
-    name: "libGLESv3.llndk",
-    symbol_file: "libGLESv3.map.txt",
-    export_llndk_headers: ["gl_llndk_headers"],
-}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 7bd0c6b..1b5f1ab 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -232,7 +232,8 @@
                      /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
                      identityTransform, /* xPrecision */ 0,
                      /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, currentTime, currentTime,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     return event;
 }
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 5270b8a..881024f 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -296,12 +296,13 @@
 volatile int32_t DispatchEntry::sNextSeqAtomic;
 
 DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
-                             ui::Transform transform, float globalScaleFactor)
+                             ui::Transform transform, float globalScaleFactor, int2 displaySize)
       : seq(nextSeq()),
         eventEntry(std::move(eventEntry)),
         targetFlags(targetFlags),
         transform(transform),
         globalScaleFactor(globalScaleFactor),
+        displaySize(displaySize),
         deliveryTime(0),
         resolvedAction(0),
         resolvedFlags(0) {}
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index f3ef64b..b5d3571 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -215,6 +215,7 @@
     int32_t targetFlags;
     ui::Transform transform;
     float globalScaleFactor;
+    int2 displaySize;
     // Both deliveryTime and timeoutTime are only populated when the entry is sent to the app,
     // and will be undefined before that.
     nsecs_t deliveryTime; // time when the event was actually delivered
@@ -227,7 +228,7 @@
     int32_t resolvedFlags;
 
     DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
-                  ui::Transform transform, float globalScaleFactor);
+                  ui::Transform transform, float globalScaleFactor, int2 displaySize);
 
     inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; }
 
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 073455a..485a53a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -283,27 +283,6 @@
     return it != map.end() ? it->second : V{};
 }
 
-/**
- * Find the entry in std::unordered_map by value, and remove it.
- * If more than one entry has the same value, then all matching
- * key-value pairs will be removed.
- *
- * Return true if at least one value has been removed.
- */
-template <typename K, typename V>
-static bool removeByValue(std::unordered_map<K, V>& map, const V& value) {
-    bool removed = false;
-    for (auto it = map.begin(); it != map.end();) {
-        if (it->second == value) {
-            it = map.erase(it);
-            removed = true;
-        } else {
-            it++;
-        }
-    }
-    return removed;
-}
-
 static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) {
     if (first == second) {
         return true;
@@ -339,14 +318,16 @@
             // values do not represent on-screen coordinates, so they should not have any window
             // transformations applied to them.
             return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, identityTransform,
-                                                   1.0f /*globalScaleFactor*/);
+                                                   1.0f /*globalScaleFactor*/,
+                                                   inputTarget.displaySize);
         }
     }
 
     if (inputTarget.useDefaultPointerTransform()) {
         const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
         return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
-                                               inputTarget.globalScaleFactor);
+                                               inputTarget.globalScaleFactor,
+                                               inputTarget.displaySize);
     }
 
     ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
@@ -397,7 +378,8 @@
 
     std::unique_ptr<DispatchEntry> dispatchEntry =
             std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
-                                            firstPointerTransform, inputTarget.globalScaleFactor);
+                                            firstPointerTransform, inputTarget.globalScaleFactor,
+                                            inputTarget.displaySize);
     return dispatchEntry;
 }
 
@@ -504,8 +486,8 @@
         drainInboundQueueLocked();
     }
 
-    while (!mConnectionsByFd.empty()) {
-        sp<Connection> connection = mConnectionsByFd.begin()->second;
+    while (!mConnectionsByToken.empty()) {
+        sp<Connection> connection = mConnectionsByToken.begin()->second;
         removeInputChannel(connection->inputChannel->getConnectionToken());
     }
 }
@@ -2326,6 +2308,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 +2356,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();
     }
 }
@@ -2399,6 +2384,8 @@
         inputTarget.inputChannel = inputChannel;
         inputTarget.flags = targetFlags;
         inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
+        inputTarget.displaySize =
+                int2(windowHandle->getInfo()->displayWidth, windowHandle->getInfo()->displayHeight);
         inputTargets.push_back(inputTarget);
         it = inputTargets.end() - 1;
     }
@@ -2899,6 +2886,12 @@
                 ATRACE_NAME(message.c_str());
             }
 
+            if ((motionEntry.flags & AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE) &&
+                (motionEntry.policyFlags & POLICY_FLAG_TRUSTED)) {
+                // Skip reporting pointer down outside focus to the policy.
+                break;
+            }
+
             dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction,
                                             inputTarget.inputChannel->getConnectionToken());
 
@@ -2964,7 +2957,7 @@
         return; // Not a key or a motion
     }
 
-    std::unordered_set<sp<IBinder>, IBinderHash> newConnectionTokens;
+    std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> newConnectionTokens;
     std::vector<sp<Connection>> newConnections;
     for (const InputTarget& target : targets) {
         if ((target.flags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) ==
@@ -3104,6 +3097,8 @@
                                                      motionEntry.xPrecision, motionEntry.yPrecision,
                                                      motionEntry.xCursorPosition,
                                                      motionEntry.yCursorPosition,
+                                                     dispatchEntry->displaySize.x,
+                                                     dispatchEntry->displaySize.y,
                                                      motionEntry.downTime, motionEntry.eventTime,
                                                      motionEntry.pointerCount,
                                                      motionEntry.pointerProperties, usingCoords);
@@ -3154,8 +3149,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 +3164,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;
@@ -3285,85 +3282,78 @@
     delete dispatchEntry;
 }
 
-int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
-    InputDispatcher* d = static_cast<InputDispatcher*>(data);
+int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionToken) {
+    std::scoped_lock _l(mLock);
+    sp<Connection> connection = getConnectionLocked(connectionToken);
+    if (connection == nullptr) {
+        ALOGW("Received looper callback for unknown input channel token %p.  events=0x%x",
+              connectionToken.get(), events);
+        return 0; // remove the callback
+    }
 
-    { // acquire lock
-        std::scoped_lock _l(d->mLock);
-
-        if (d->mConnectionsByFd.find(fd) == d->mConnectionsByFd.end()) {
-            ALOGE("Received spurious receive callback for unknown input channel.  "
-                  "fd=%d, events=0x%x",
-                  fd, events);
-            return 0; // remove the callback
+    bool notify;
+    if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
+        if (!(events & ALOOPER_EVENT_INPUT)) {
+            ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
+                  "events=0x%x",
+                  connection->getInputChannelName().c_str(), events);
+            return 1;
         }
 
-        bool notify;
-        sp<Connection> connection = d->mConnectionsByFd[fd];
-        if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
-            if (!(events & ALOOPER_EVENT_INPUT)) {
-                ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
-                      "events=0x%x",
-                      connection->getInputChannelName().c_str(), events);
+        nsecs_t currentTime = now();
+        bool gotOne = false;
+        status_t status = OK;
+        for (;;) {
+            Result<InputPublisher::ConsumerResponse> result =
+                    connection->inputPublisher.receiveConsumerResponse();
+            if (!result.ok()) {
+                status = result.error().code();
+                break;
+            }
+
+            if (std::holds_alternative<InputPublisher::Finished>(*result)) {
+                const InputPublisher::Finished& finish =
+                        std::get<InputPublisher::Finished>(*result);
+                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) {
+            runCommandsLockedInterruptible();
+            if (status == WOULD_BLOCK) {
                 return 1;
             }
-
-            nsecs_t currentTime = now();
-            bool gotOne = false;
-            status_t status = OK;
-            for (;;) {
-                Result<InputPublisher::ConsumerResponse> result =
-                        connection->inputPublisher.receiveConsumerResponse();
-                if (!result.ok()) {
-                    status = result.error().code();
-                    break;
-                }
-
-                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) {
-                d->runCommandsLockedInterruptible();
-                if (status == WOULD_BLOCK) {
-                    return 1;
-                }
-            }
-
-            notify = status != DEAD_OBJECT || !connection->monitor;
-            if (notify) {
-                ALOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",
-                      connection->getInputChannelName().c_str(), status);
-            }
-        } else {
-            // Monitor channels are never explicitly unregistered.
-            // We do it automatically when the remote endpoint is closed so don't warn about them.
-            const bool stillHaveWindowHandle =
-                    d->getWindowHandleLocked(connection->inputChannel->getConnectionToken()) !=
-                    nullptr;
-            notify = !connection->monitor && stillHaveWindowHandle;
-            if (notify) {
-                ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred.  "
-                      "events=0x%x",
-                      connection->getInputChannelName().c_str(), events);
-            }
         }
 
-        // Remove the channel.
-        d->removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify);
-        return 0; // remove the callback
-    }             // release lock
+        notify = status != DEAD_OBJECT || !connection->monitor;
+        if (notify) {
+            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.
+        // We do it automatically when the remote endpoint is closed so don't warn about them.
+        const bool stillHaveWindowHandle =
+                getWindowHandleLocked(connection->inputChannel->getConnectionToken()) != nullptr;
+        notify = !connection->monitor && stillHaveWindowHandle;
+        if (notify) {
+            ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred.  events=0x%x",
+                  connection->getInputChannelName().c_str(), events);
+        }
+    }
+
+    // Remove the channel.
+    removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify);
+    return 0; // remove the callback
 }
 
 void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
         const CancelationOptions& options) {
-    for (const auto& [fd, connection] : mConnectionsByFd) {
+    for (const auto& [token, connection] : mConnectionsByToken) {
         synthesizeCancelationEventsForConnectionLocked(connection, options);
     }
 }
@@ -3813,7 +3803,8 @@
                              args->action, args->actionButton, args->flags, args->edgeFlags,
                              args->metaState, args->buttonState, args->classification, transform,
                              args->xPrecision, args->yPrecision, args->xCursorPosition,
-                             args->yCursorPosition, args->downTime, args->eventTime,
+                             args->yCursorPosition, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+                             AMOTION_EVENT_INVALID_DISPLAY_SIZE, args->downTime, args->eventTime,
                              args->pointerCount, args->pointerProperties, args->pointerCoords);
 
             policyFlags |= POLICY_FLAG_FILTERED;
@@ -4278,12 +4269,8 @@
     return nullptr;
 }
 
-sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
-    sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(displayId);
-    return getWindowHandleLocked(focusedToken, displayId);
-}
-
-bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const {
+sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
+        const sp<InputWindowHandle>& windowHandle) const {
     for (auto& it : mWindowHandlesByDisplay) {
         const std::vector<sp<InputWindowHandle>>& windowHandles = it.second;
         for (const sp<InputWindowHandle>& handle : windowHandles) {
@@ -4295,11 +4282,16 @@
                           windowHandle->getName().c_str(), it.first,
                           windowHandle->getInfo()->displayId);
                 }
-                return true;
+                return handle;
             }
         }
     }
-    return false;
+    return nullptr;
+}
+
+sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
+    sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(displayId);
+    return getWindowHandleLocked(focusedToken, displayId);
 }
 
 bool InputDispatcher::hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const {
@@ -4327,11 +4319,11 @@
 
 std::shared_ptr<InputChannel> InputDispatcher::getInputChannelLocked(
         const sp<IBinder>& token) const {
-    size_t count = mInputChannelsByToken.count(token);
-    if (count == 0) {
+    auto connectionIt = mConnectionsByToken.find(token);
+    if (connectionIt == mConnectionsByToken.end()) {
         return nullptr;
     }
-    return mInputChannelsByToken.at(token);
+    return connectionIt->second->inputChannel;
 }
 
 void InputDispatcher::updateWindowHandlesForDisplayLocked(
@@ -4455,7 +4447,7 @@
         TouchState& state = stateIt->second;
         for (size_t i = 0; i < state.windows.size();) {
             TouchedWindow& touchedWindow = state.windows[i];
-            if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
+            if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) {
                 if (DEBUG_FOCUS) {
                     ALOGD("Touched window was removed: %s in display %" PRId32,
                           touchedWindow.windowHandle->getName().c_str(), displayId);
@@ -4487,7 +4479,7 @@
     // Otherwise, they might stick around until the window handle is destroyed
     // which might not happen until the next GC.
     for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) {
-        if (!hasWindowHandleLocked(oldWindowHandle)) {
+        if (getWindowHandleLocked(oldWindowHandle) == nullptr) {
             if (DEBUG_FOCUS) {
                 ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
             }
@@ -4755,6 +4747,40 @@
     return true;
 }
 
+// Binder call
+bool InputDispatcher::transferTouch(const sp<IBinder>& destChannelToken) {
+    sp<IBinder> fromToken;
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+
+        sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(destChannelToken);
+        if (toWindowHandle == nullptr) {
+            ALOGW("Could not find window associated with token=%p", destChannelToken.get());
+            return false;
+        }
+
+        const int32_t displayId = toWindowHandle->getInfo()->displayId;
+
+        auto touchStateIt = mTouchStatesByDisplay.find(displayId);
+        if (touchStateIt == mTouchStatesByDisplay.end()) {
+            ALOGD("Could not transfer touch because the display %" PRId32 " is not being touched",
+                  displayId);
+            return false;
+        }
+
+        TouchState& state = touchStateIt->second;
+        if (state.windows.size() != 1) {
+            ALOGW("Cannot transfer touch state because there are %zu windows being touched",
+                  state.windows.size());
+            return false;
+        }
+        const TouchedWindow& touchedWindow = state.windows[0];
+        fromToken = touchedWindow.windowHandle->getToken();
+    } // release lock
+
+    return transferTouchFocus(fromToken, destChannelToken);
+}
+
 void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {
     if (DEBUG_FOCUS) {
         ALOGD("Resetting and dropping all events (%s).", reason);
@@ -4981,13 +5007,13 @@
         dump += INDENT "ReplacedKeys: <empty>\n";
     }
 
-    if (!mConnectionsByFd.empty()) {
+    if (!mConnectionsByToken.empty()) {
         dump += INDENT "Connections:\n";
-        for (const auto& pair : mConnectionsByFd) {
-            const sp<Connection>& connection = pair.second;
+        for (const auto& [token, connection] : mConnectionsByToken) {
             dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', "
                                          "status=%s, monitor=%s, responsive=%s\n",
-                                 pair.first, connection->getInputChannelName().c_str(),
+                                 connection->inputChannel->getFd().get(),
+                                 connection->getInputChannelName().c_str(),
                                  connection->getWindowName().c_str(), connection->getStatusLabel(),
                                  toString(connection->monitor), toString(connection->responsive));
 
@@ -5035,14 +5061,23 @@
     }
 }
 
+class LooperEventCallback : public LooperCallback {
+public:
+    LooperEventCallback(std::function<int(int events)> callback) : mCallback(callback) {}
+    int handleEvent(int /*fd*/, int events, void* /*data*/) override { return mCallback(events); }
+
+private:
+    std::function<int(int events)> mCallback;
+};
+
 Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
 #if DEBUG_CHANNEL_CREATION
     ALOGD("channel '%s' ~ createInputChannel", name.c_str());
 #endif
 
-    std::shared_ptr<InputChannel> serverChannel;
+    std::unique_ptr<InputChannel> serverChannel;
     std::unique_ptr<InputChannel> clientChannel;
-    status_t result = openInputChannelPair(name, serverChannel, clientChannel);
+    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
 
     if (result) {
         return base::Error(result) << "Failed to open input channel pair with name " << name;
@@ -5050,13 +5085,20 @@
 
     { // acquire lock
         std::scoped_lock _l(mLock);
-        sp<Connection> connection = new Connection(serverChannel, false /*monitor*/, mIdGenerator);
-
+        const sp<IBinder>& token = serverChannel->getConnectionToken();
         int fd = serverChannel->getFd();
-        mConnectionsByFd[fd] = connection;
-        mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
+        sp<Connection> connection =
+                new Connection(std::move(serverChannel), false /*monitor*/, mIdGenerator);
 
-        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
+        if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) {
+            ALOGE("Created a new connection, but the token %p is already known", token.get());
+        }
+        mConnectionsByToken.emplace(token, connection);
+
+        std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
+                                                            this, std::placeholders::_1, token);
+
+        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
     } // release lock
 
     // Wake the looper because some connections have changed.
@@ -5084,16 +5126,21 @@
         }
 
         sp<Connection> connection = new Connection(serverChannel, true /*monitor*/, mIdGenerator);
-
+        const sp<IBinder>& token = serverChannel->getConnectionToken();
         const int fd = serverChannel->getFd();
-        mConnectionsByFd[fd] = connection;
-        mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
+
+        if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) {
+            ALOGE("Created a new connection, but the token %p is already known", token.get());
+        }
+        mConnectionsByToken.emplace(token, connection);
+        std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
+                                                            this, std::placeholders::_1, token);
 
         auto& monitorsByDisplay =
                 isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay;
         monitorsByDisplay[displayId].emplace_back(serverChannel, pid);
 
-        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
+        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
     }
 
     // Wake the looper because some connections have changed.
@@ -5126,7 +5173,6 @@
     }
 
     removeConnectionLocked(connection);
-    mInputChannelsByToken.erase(connectionToken);
 
     if (connection->monitor) {
         removeMonitorChannelLocked(connectionToken);
@@ -5154,6 +5200,8 @@
         const size_t numMonitors = monitors.size();
         for (size_t i = 0; i < numMonitors; i++) {
             if (monitors[i].inputChannel->getConnectionToken() == connectionToken) {
+                ALOGI("Erasing monitor %s on display %" PRId32 ", pid=%" PRId32,
+                      monitors[i].inputChannel->getName().c_str(), it->first, monitors[i].pid);
                 monitors.erase(monitors.begin() + i);
                 break;
             }
@@ -5282,9 +5330,8 @@
         return nullptr;
     }
 
-    for (const auto& pair : mConnectionsByFd) {
-        const sp<Connection>& connection = pair.second;
-        if (connection->inputChannel->getConnectionToken() == inputConnectionToken) {
+    for (const auto& [token, connection] : mConnectionsByToken) {
+        if (token == inputConnectionToken) {
             return connection;
         }
     }
@@ -5302,7 +5349,7 @@
 
 void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
     mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
-    removeByValue(mConnectionsByFd, connection);
+    mConnectionsByToken.erase(connection->inputChannel->getConnectionToken());
 }
 
 void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 5708fac..6edc5f1 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -81,60 +81,60 @@
  */
 class InputDispatcher : public android::InputDispatcherInterface {
 protected:
-    virtual ~InputDispatcher();
+    ~InputDispatcher() override;
 
 public:
     explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
 
-    virtual void dump(std::string& dump) override;
-    virtual void monitor() override;
-    virtual bool waitForIdle() override;
-    virtual status_t start() override;
-    virtual status_t stop() override;
+    void dump(std::string& dump) override;
+    void monitor() override;
+    bool waitForIdle() override;
+    status_t start() override;
+    status_t stop() override;
 
-    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
-    virtual void notifyKey(const NotifyKeyArgs* args) override;
-    virtual void notifyMotion(const NotifyMotionArgs* args) override;
-    virtual void notifySwitch(const NotifySwitchArgs* args) override;
-    virtual void notifySensor(const NotifySensorArgs* args) override;
-    virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
-    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
-    virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
+    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
+    void notifyKey(const NotifyKeyArgs* args) override;
+    void notifyMotion(const NotifyMotionArgs* args) override;
+    void notifySwitch(const NotifySwitchArgs* args) override;
+    void notifySensor(const NotifySensorArgs* args) override;
+    void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
+    void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+    void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
 
-    virtual android::os::InputEventInjectionResult injectInputEvent(
+    android::os::InputEventInjectionResult injectInputEvent(
             const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
             android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
             uint32_t policyFlags) override;
 
-    virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
+    std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
 
-    virtual void setInputWindows(
-            const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
-                    handlesPerDisplay) override;
-    virtual void setFocusedApplication(
+    void setInputWindows(const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
+                                 handlesPerDisplay) override;
+    void setFocusedApplication(
             int32_t displayId,
             const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override;
-    virtual void setFocusedDisplay(int32_t displayId) override;
-    virtual void setInputDispatchMode(bool enabled, bool frozen) override;
-    virtual void setInputFilterEnabled(bool enabled) override;
-    virtual void setInTouchMode(bool inTouchMode) override;
-    virtual void setMaximumObscuringOpacityForTouch(float opacity) override;
-    virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
+    void setFocusedDisplay(int32_t displayId) override;
+    void setInputDispatchMode(bool enabled, bool frozen) override;
+    void setInputFilterEnabled(bool enabled) override;
+    void setInTouchMode(bool inTouchMode) override;
+    void setMaximumObscuringOpacityForTouch(float opacity) override;
+    void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
 
-    virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
-                                    bool isDragDrop = false) override;
+    bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+                            bool isDragDrop = false) override;
+    bool transferTouch(const sp<IBinder>& destChannelToken) override;
 
-    virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
+    base::Result<std::unique_ptr<InputChannel>> createInputChannel(
             const std::string& name) override;
-    virtual void setFocusedWindow(const FocusRequest&) override;
-    virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
-                                                                           bool isGestureMonitor,
-                                                                           const std::string& name,
-                                                                           int32_t pid) override;
-    virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
-    virtual status_t pilferPointers(const sp<IBinder>& token) override;
-    virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;
-    virtual bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override;
+    void setFocusedWindow(const FocusRequest&) override;
+    base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
+                                                                   bool isGestureMonitor,
+                                                                   const std::string& name,
+                                                                   int32_t pid) override;
+    status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
+    status_t pilferPointers(const sp<IBinder>& token) override;
+    void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;
+    bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override;
 
     std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
 
@@ -211,9 +211,6 @@
                                                     bool addPortalWindows = false,
                                                     bool ignoreDragWindow = false) REQUIRES(mLock);
 
-    // All registered connections mapped by channel file descriptor.
-    std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
-
     sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
             REQUIRES(mLock);
 
@@ -221,13 +218,14 @@
 
     void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock);
 
-    struct IBinderHash {
-        std::size_t operator()(const sp<IBinder>& b) const {
-            return std::hash<IBinder*>{}(b.get());
-        }
+    template <typename T>
+    struct StrongPointerHash {
+        std::size_t operator()(const sp<T>& b) const { return std::hash<T*>{}(b.get()); }
     };
-    std::unordered_map<sp<IBinder>, std::shared_ptr<InputChannel>, IBinderHash>
-            mInputChannelsByToken GUARDED_BY(mLock);
+
+    // All registered connections mapped by input channel token.
+    std::unordered_map<sp<IBinder>, sp<Connection>, StrongPointerHash<IBinder>> mConnectionsByToken
+            GUARDED_BY(mLock);
 
     // Finds the display ID of the gesture monitor identified by the provided token.
     std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
@@ -327,10 +325,11 @@
     // to loop through all displays.
     sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
                                                 int displayId) const REQUIRES(mLock);
+    sp<InputWindowHandle> getWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const
+            REQUIRES(mLock);
     std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const
             REQUIRES(mLock);
     sp<InputWindowHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock);
-    bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
     bool hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const REQUIRES(mLock);
 
     /*
@@ -371,7 +370,8 @@
     std::string mLastAnrState GUARDED_BY(mLock);
 
     // The connection tokens of the channels that the user last interacted, for debugging
-    std::unordered_set<sp<IBinder>, IBinderHash> mInteractionConnectionTokens GUARDED_BY(mLock);
+    std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> mInteractionConnectionTokens
+            GUARDED_BY(mLock);
     void updateInteractionTokensLocked(const EventEntry& entry,
                                        const std::vector<InputTarget>& targets) REQUIRES(mLock);
 
@@ -543,7 +543,7 @@
                                         bool notify) REQUIRES(mLock);
     void drainDispatchQueue(std::deque<DispatchEntry*>& queue);
     void releaseDispatchEntry(DispatchEntry* dispatchEntry);
-    static int handleReceiveCallback(int fd, int events, void* data);
+    int handleReceiveCallback(int events, sp<IBinder> connectionToken);
     // The action sent should only be of type AMOTION_EVENT_*
     void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
                                          const sp<IBinder>& newToken) REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index debf805..1c4980b 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -100,6 +100,9 @@
     // (ignored for KeyEvents)
     float globalScaleFactor = 1.0f;
 
+    // Display-size in its natural rotation. Used for compatibility transform of raw coordinates.
+    int2 displaySize = {AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE};
+
     // The subset of pointer ids to include in motion events dispatched to this input target
     // if FLAG_SPLIT is set.
     BitSet32 pointerIds;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index b601dfc..7f85e53 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -151,6 +151,14 @@
      */
     virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
                                     bool isDragDrop) = 0;
+
+    /**
+     * Transfer touch focus to the provided channel, no matter where the current touch is.
+     *
+     * Return true on success, false if there was no on-going touch.
+     */
+    virtual bool transferTouch(const sp<IBinder>& destChannelToken) = 0;
+
     /**
      * Sets focus on the specified window.
      */
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 3bf212a..19abfd9 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -198,6 +198,10 @@
     // Used to determine which DisplayViewport should be tied to which InputDevice.
     std::unordered_map<std::string, uint8_t> portAssociations;
 
+    // The associations between input device names and display unique ids.
+    // Used to determine which DisplayViewport should be tied to which InputDevice.
+    std::unordered_map<std::string, std::string> uniqueIdAssociations;
+
     // The suggested display ID to show the cursor.
     int32_t defaultPointerDisplayId;
 
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 7468894..7db32e3 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -36,7 +36,7 @@
     srcs: [
         "EventHub.cpp",
         "InputDevice.cpp",
-        "controller/InputController.cpp",
+        "controller/PeripheralController.cpp",
         "mapper/accumulator/CursorButtonAccumulator.cpp",
         "mapper/accumulator/CursorScrollAccumulator.cpp",
         "mapper/accumulator/SingleTouchMotionAccumulator.cpp",
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 045d24c..e3e6c12 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -363,7 +363,7 @@
         virtualKeyMap(nullptr),
         ffEffectPlaying(false),
         ffEffectId(-1),
-        miscDevice(nullptr),
+        associatedDevice(nullptr),
         controllerNumber(0),
         enabled(true),
         isVirtual(fd < 0) {}
@@ -554,7 +554,7 @@
 }
 
 // Check the sysfs path for any input device batteries, returns true if battery found.
-bool EventHub::MiscDevice::configureBatteryLocked() {
+bool EventHub::AssociatedDevice::configureBatteryLocked() {
     nextBatteryId = 0;
     // Check if device has any battery.
     const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::POWER_SUPPLY);
@@ -580,7 +580,7 @@
 }
 
 // Check the sysfs path for any input device lights, returns true if lights found.
-bool EventHub::MiscDevice::configureLightsLocked() {
+bool EventHub::AssociatedDevice::configureLightsLocked() {
     nextLightId = 0;
     // Check if device has any lights.
     const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::LEDS);
@@ -988,14 +988,10 @@
         int32_t deviceId) const {
     static const std::unordered_map<int32_t, RawBatteryInfo> EMPTY_BATTERY_INFO = {};
     Device* device = getDeviceLocked(deviceId);
-    if (device == nullptr) {
+    if (device == nullptr || !device->associatedDevice) {
         return EMPTY_BATTERY_INFO;
     }
-    auto it = mMiscDevices.find(device->identifier.descriptor);
-    if (it == mMiscDevices.end()) {
-        return EMPTY_BATTERY_INFO;
-    }
-    return it->second->batteryInfos;
+    return device->associatedDevice->batteryInfos;
 }
 
 const std::vector<int32_t> EventHub::getRawBatteryIds(int32_t deviceId) {
@@ -1028,14 +1024,10 @@
         int32_t deviceId) const {
     static const std::unordered_map<int32_t, RawLightInfo> EMPTY_LIGHT_INFO = {};
     Device* device = getDeviceLocked(deviceId);
-    if (device == nullptr) {
+    if (device == nullptr || !device->associatedDevice) {
         return EMPTY_LIGHT_INFO;
     }
-    auto it = mMiscDevices.find(device->identifier.descriptor);
-    if (it == mMiscDevices.end()) {
-        return EMPTY_LIGHT_INFO;
-    }
-    return it->second->lightInfos;
+    return device->associatedDevice->lightInfos;
 }
 
 const std::vector<int32_t> EventHub::getRawLightIds(int32_t deviceId) {
@@ -1946,18 +1938,20 @@
     // Check the sysfs root path
     std::optional<std::filesystem::path> sysfsRootPath = getSysfsRootPath(devicePath.c_str());
     if (sysfsRootPath.has_value()) {
-        std::shared_ptr<MiscDevice> miscDevice;
-        auto it = mMiscDevices.find(device->identifier.descriptor);
-        if (it == mMiscDevices.end()) {
-            miscDevice = std::make_shared<MiscDevice>(sysfsRootPath.value());
-        } else {
-            miscDevice = it->second;
+        std::shared_ptr<AssociatedDevice> associatedDevice;
+        for (const auto& [id, dev] : mDevices) {
+            if (device->identifier.descriptor == dev->identifier.descriptor &&
+                !dev->associatedDevice) {
+                associatedDevice = dev->associatedDevice;
+            }
         }
-        hasBattery = miscDevice->configureBatteryLocked();
-        hasLights = miscDevice->configureLightsLocked();
+        if (!associatedDevice) {
+            associatedDevice = std::make_shared<AssociatedDevice>(sysfsRootPath.value());
+        }
+        hasBattery = associatedDevice->configureBatteryLocked();
+        hasLights = associatedDevice->configureLightsLocked();
 
-        device->miscDevice = miscDevice;
-        mMiscDevices.insert_or_assign(device->identifier.descriptor, std::move(miscDevice));
+        device->associatedDevice = associatedDevice;
     }
 
     // Figure out the kinds of events the device reports.
@@ -2329,12 +2323,6 @@
     mClosingDevices.push_back(std::move(mDevices[device.id]));
 
     mDevices.erase(device.id);
-    // If all devices with the descriptor have been removed then the miscellaneous device should
-    // be removed too.
-    std::string descriptor = device.identifier.descriptor;
-    if (getDeviceByDescriptorLocked(descriptor) == nullptr) {
-        mMiscDevices.erase(descriptor);
-    }
 }
 
 status_t EventHub::readNotifyLocked() {
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index f935c36..ad503fd 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -23,11 +23,11 @@
 
 #include "CursorInputMapper.h"
 #include "ExternalStylusInputMapper.h"
-#include "InputController.h"
 #include "InputReaderContext.h"
 #include "JoystickInputMapper.h"
 #include "KeyboardInputMapper.h"
 #include "MultiTouchInputMapper.h"
+#include "PeripheralController.h"
 #include "RotaryEncoderInputMapper.h"
 #include "SensorInputMapper.h"
 #include "SingleTouchInputMapper.h"
@@ -103,6 +103,12 @@
     } else {
         dump += "<none>\n";
     }
+    dump += StringPrintf(INDENT2 "AssociatedDisplayUniqueId: ");
+    if (mAssociatedDisplayUniqueId) {
+        dump += StringPrintf("%s\n", mAssociatedDisplayUniqueId->c_str());
+    } else {
+        dump += "<none>\n";
+    }
     dump += StringPrintf(INDENT2 "HasMic:     %s\n", toString(mHasMic));
     dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
     dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
@@ -165,9 +171,9 @@
     }
 
     // Battery-like devices or light-containing devices.
-    // InputController will be created with associated EventHub device.
+    // PeripheralController will be created with associated EventHub device.
     if (classes.test(InputDeviceClass::BATTERY) || classes.test(InputDeviceClass::LIGHT)) {
-        mController = std::make_unique<InputController>(*contextPtr);
+        mController = std::make_unique<PeripheralController>(*contextPtr);
     }
 
     // Keyboard-like devices.
@@ -293,8 +299,9 @@
         }
 
         if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
-            // In most situations, no port will be specified.
+            // In most situations, no port or name will be specified.
             mAssociatedDisplayPort = std::nullopt;
+            mAssociatedDisplayUniqueId = std::nullopt;
             mAssociatedViewport = std::nullopt;
             // Find the display port that corresponds to the current input port.
             const std::string& inputPort = mIdentifier.location;
@@ -305,6 +312,13 @@
                     mAssociatedDisplayPort = std::make_optional(displayPort->second);
                 }
             }
+            const std::string& inputDeviceName = mIdentifier.name;
+            const std::unordered_map<std::string, std::string>& names =
+                    config->uniqueIdAssociations;
+            const auto& displayUniqueId = names.find(inputDeviceName);
+            if (displayUniqueId != names.end()) {
+                mAssociatedDisplayUniqueId = displayUniqueId->second;
+            }
 
             // If the device was explicitly disabled by the user, it would be present in the
             // "disabledDevices" list. If it is associated with a specific display, and it was not
@@ -319,6 +333,15 @@
                           getName().c_str(), *mAssociatedDisplayPort);
                     enabled = false;
                 }
+            } else if (mAssociatedDisplayUniqueId != std::nullopt) {
+                mAssociatedViewport =
+                        config->getDisplayViewportByUniqueId(*mAssociatedDisplayUniqueId);
+                if (!mAssociatedViewport) {
+                    ALOGW("Input device %s should be associated with display %s but the "
+                          "corresponding viewport cannot be found",
+                          inputDeviceName.c_str(), mAssociatedDisplayUniqueId->c_str());
+                    enabled = false;
+                }
             }
 
             if (changes) {
@@ -504,8 +527,6 @@
     for_each_mapper([when, readTime](InputMapper& mapper) { mapper.cancelTouch(when, readTime); });
 }
 
-// TODO b/180733860 support multiple battery in API and remove this.
-constexpr int32_t DEFAULT_BATTERY_ID = 1;
 std::optional<int32_t> InputDevice::getBatteryCapacity() {
     return mController ? mController->getBatteryCapacity(DEFAULT_BATTERY_ID) : std::nullopt;
 }
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 2d0fdf7..e91f84e 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -413,8 +413,7 @@
     }
 }
 
-void InputReader::dispatchExternalStylusState(const StylusState& state) {
-    std::scoped_lock _l(mLock);
+void InputReader::dispatchExternalStylusStateLocked(const StylusState& state) {
     for (auto& devicePair : mDevices) {
         std::shared_ptr<InputDevice>& device = devicePair.second;
         device->updateExternalStylusState(state);
@@ -945,7 +944,7 @@
 }
 
 void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) {
-    mReader->dispatchExternalStylusState(state);
+    mReader->dispatchExternalStylusStateLocked(state);
 }
 
 InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() {
diff --git a/services/inputflinger/reader/controller/InputController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
similarity index 90%
rename from services/inputflinger/reader/controller/InputController.cpp
rename to services/inputflinger/reader/controller/PeripheralController.cpp
index 45266fb..1a40d06 100644
--- a/services/inputflinger/reader/controller/InputController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -19,7 +19,7 @@
 
 #include "../Macros.h"
 
-#include "InputController.h"
+#include "PeripheralController.h"
 #include "input/NamedEnum.h"
 
 // Log detailed debug messages about input device lights.
@@ -52,15 +52,15 @@
  * lights, getting and setting the lights brightness and color, by interacting with EventHub
  * devices.
  */
-InputController::InputController(InputDeviceContext& deviceContext)
+PeripheralController::PeripheralController(InputDeviceContext& deviceContext)
       : mDeviceContext(deviceContext) {
     configureBattries();
     configureLights();
 }
 
-InputController::~InputController() {}
+PeripheralController::~PeripheralController() {}
 
-std::optional<std::int32_t> InputController::Light::getRawLightBrightness(int32_t rawLightId) {
+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;
@@ -85,7 +85,7 @@
     return brightness;
 }
 
-void InputController::Light::setRawLightBrightness(int32_t rawLightId, int32_t brightness) {
+void PeripheralController::Light::setRawLightBrightness(int32_t rawLightId, int32_t brightness) {
     std::optional<RawLightInfo> rawInfo = context.getRawLightInfo(rawLightId);
     if (!rawInfo.has_value()) {
         return;
@@ -104,14 +104,14 @@
     context.setLightBrightness(rawLightId, brightness);
 }
 
-bool InputController::SingleLight::setLightColor(int32_t color) {
+bool PeripheralController::SingleLight::setLightColor(int32_t color) {
     int32_t brightness = getAlpha(color);
     setRawLightBrightness(rawId, brightness);
 
     return true;
 }
 
-bool InputController::RgbLight::setLightColor(int32_t color) {
+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);
@@ -137,7 +137,7 @@
     return true;
 }
 
-bool InputController::MultiColorLight::setLightColor(int32_t color) {
+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));
@@ -148,7 +148,7 @@
     return true;
 }
 
-std::optional<int32_t> InputController::SingleLight::getLightColor() {
+std::optional<int32_t> PeripheralController::SingleLight::getLightColor() {
     std::optional<int32_t> brightness = getRawLightBrightness(rawId);
     if (!brightness.has_value()) {
         return std::nullopt;
@@ -157,7 +157,7 @@
     return toArgb(brightness.value(), 0 /* red */, 0 /* green */, 0 /* blue */);
 }
 
-std::optional<int32_t> InputController::RgbLight::getLightColor() {
+std::optional<int32_t> PeripheralController::RgbLight::getLightColor() {
     // If the Alpha component is zero, then return color 0.
     if (brightness == 0) {
         return 0;
@@ -192,7 +192,7 @@
     return toArgb(brightness, red, green, blue);
 }
 
-std::optional<int32_t> InputController::MultiColorLight::getLightColor() {
+std::optional<int32_t> PeripheralController::MultiColorLight::getLightColor() {
     auto ret = context.getLightIntensities(rawId);
     if (!ret.has_value()) {
         return std::nullopt;
@@ -210,7 +210,7 @@
     return std::nullopt;
 }
 
-bool InputController::PlayerIdLight::setLightPlayerId(int32_t playerId) {
+bool PeripheralController::PlayerIdLight::setLightPlayerId(int32_t playerId) {
     if (rawLightIds.find(playerId) == rawLightIds.end()) {
         return false;
     }
@@ -224,7 +224,7 @@
     return true;
 }
 
-std::optional<int32_t> InputController::PlayerIdLight::getLightPlayerId() {
+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) {
@@ -234,11 +234,11 @@
     return std::nullopt;
 }
 
-void InputController::SingleLight::dump(std::string& dump) {
+void PeripheralController::SingleLight::dump(std::string& dump) {
     dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
 }
 
-void InputController::PlayerIdLight::dump(std::string& dump) {
+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) {
@@ -247,7 +247,7 @@
     dump += "\n";
 }
 
-void InputController::RgbLight::dump(std::string& dump) {
+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));
@@ -257,11 +257,11 @@
     dump += "\n";
 }
 
-void InputController::MultiColorLight::dump(std::string& dump) {
+void PeripheralController::MultiColorLight::dump(std::string& dump) {
     dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
 }
 
-void InputController::populateDeviceInfo(InputDeviceInfo* deviceInfo) {
+void PeripheralController::populateDeviceInfo(InputDeviceInfo* deviceInfo) {
     // TODO: b/180733860 Remove this after enabling multi-battery
     if (!mBatteries.empty()) {
         deviceInfo->setHasBattery(true);
@@ -279,7 +279,7 @@
     }
 }
 
-void InputController::dump(std::string& dump) {
+void PeripheralController::dump(std::string& dump) {
     dump += INDENT2 "Input Controller:\n";
     if (!mLights.empty()) {
         dump += INDENT3 "Lights:\n";
@@ -340,7 +340,7 @@
     }
 }
 
-void InputController::configureBattries() {
+void PeripheralController::configureBattries() {
     // Check raw batteries
     const std::vector<int32_t> rawBatteryIds = getDeviceContext().getRawBatteryIds();
 
@@ -355,7 +355,7 @@
     }
 }
 
-void InputController::configureLights() {
+void PeripheralController::configureLights() {
     bool hasRedLed = false;
     bool hasGreenLed = false;
     bool hasBlueLed = false;
@@ -472,15 +472,15 @@
     }
 }
 
-std::optional<int32_t> InputController::getBatteryCapacity(int batteryId) {
+std::optional<int32_t> PeripheralController::getBatteryCapacity(int batteryId) {
     return getDeviceContext().getBatteryCapacity(batteryId);
 }
 
-std::optional<int32_t> InputController::getBatteryStatus(int batteryId) {
+std::optional<int32_t> PeripheralController::getBatteryStatus(int batteryId) {
     return getDeviceContext().getBatteryStatus(batteryId);
 }
 
-bool InputController::setLightColor(int32_t lightId, int32_t color) {
+bool PeripheralController::setLightColor(int32_t lightId, int32_t color) {
     auto it = mLights.find(lightId);
     if (it == mLights.end()) {
         return false;
@@ -493,7 +493,7 @@
     return light->setLightColor(color);
 }
 
-std::optional<int32_t> InputController::getLightColor(int32_t lightId) {
+std::optional<int32_t> PeripheralController::getLightColor(int32_t lightId) {
     auto it = mLights.find(lightId);
     if (it == mLights.end()) {
         return std::nullopt;
@@ -507,7 +507,7 @@
     return color;
 }
 
-bool InputController::setLightPlayerId(int32_t lightId, int32_t playerId) {
+bool PeripheralController::setLightPlayerId(int32_t lightId, int32_t playerId) {
     auto it = mLights.find(lightId);
     if (it == mLights.end()) {
         return false;
@@ -516,7 +516,7 @@
     return light->setLightPlayerId(playerId);
 }
 
-std::optional<int32_t> InputController::getLightPlayerId(int32_t lightId) {
+std::optional<int32_t> PeripheralController::getLightPlayerId(int32_t lightId) {
     auto it = mLights.find(lightId);
     if (it == mLights.end()) {
         return std::nullopt;
diff --git a/services/inputflinger/reader/controller/InputController.h b/services/inputflinger/reader/controller/PeripheralController.h
similarity index 96%
rename from services/inputflinger/reader/controller/InputController.h
rename to services/inputflinger/reader/controller/PeripheralController.h
index d4222e2..ff3607f 100644
--- a/services/inputflinger/reader/controller/InputController.h
+++ b/services/inputflinger/reader/controller/PeripheralController.h
@@ -17,19 +17,19 @@
 #ifndef _UI_INPUTREADER_LIGHT_CONTROLLER_H
 #define _UI_INPUTREADER_LIGHT_CONTROLLER_H
 
-#include "InputControllerInterface.h"
+#include "PeripheralControllerInterface.h"
 
 namespace android {
 
-class InputController : public InputControllerInterface {
+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 InputController(InputDeviceContext& deviceContext);
-    ~InputController() override;
+    explicit PeripheralController(InputDeviceContext& deviceContext);
+    ~PeripheralController() override;
 
     void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     void dump(std::string& dump) override;
diff --git a/services/inputflinger/reader/controller/InputControllerInterface.h b/services/inputflinger/reader/controller/PeripheralControllerInterface.h
similarity index 87%
rename from services/inputflinger/reader/controller/InputControllerInterface.h
rename to services/inputflinger/reader/controller/PeripheralControllerInterface.h
index 504eded..7688a43 100644
--- a/services/inputflinger/reader/controller/InputControllerInterface.h
+++ b/services/inputflinger/reader/controller/PeripheralControllerInterface.h
@@ -24,14 +24,14 @@
 
 namespace android {
 
-/* An input controller manages the miscellaneous devices associated with the input device,
+/* A peripheral controller manages the input device peripherals associated with the input device,
  * like the sysfs based battery and light class devices.
  *
  */
-class InputControllerInterface {
+class PeripheralControllerInterface {
 public:
-    InputControllerInterface() {}
-    virtual ~InputControllerInterface() {}
+    PeripheralControllerInterface() {}
+    virtual ~PeripheralControllerInterface() {}
 
     // Interface methods for Battery
     virtual std::optional<int32_t> getBatteryCapacity(int32_t batteryId) = 0;
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index c970c8b..410a706 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -528,7 +528,7 @@
     ~EventHub() override;
 
 private:
-    struct MiscDevice {
+    struct AssociatedDevice {
         // The device descriptor from evdev device the misc device associated with.
         std::string descriptor;
         // The sysfs root path of the misc device.
@@ -538,7 +538,7 @@
         int32_t nextLightId;
         std::unordered_map<int32_t, RawBatteryInfo> batteryInfos;
         std::unordered_map<int32_t, RawLightInfo> lightInfos;
-        explicit MiscDevice(std::filesystem::path sysfsRootPath)
+        explicit AssociatedDevice(std::filesystem::path sysfsRootPath)
               : sysfsRootPath(sysfsRootPath), nextBatteryId(0), nextLightId(0) {}
         bool configureBatteryLocked();
         bool configureLightsLocked();
@@ -573,7 +573,9 @@
         bool ffEffectPlaying;
         int16_t ffEffectId; // initially -1
 
-        std::shared_ptr<MiscDevice> miscDevice;
+        // 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;
 
@@ -690,11 +692,6 @@
     std::vector<std::unique_ptr<Device>> mOpeningDevices;
     std::vector<std::unique_ptr<Device>> mClosingDevices;
 
-    // Map from std::string descriptor, to a shared_ptr of a miscellaneous device associated with
-    // the input device. The descriptor is the same from the EventHub device which it associates
-    // with.
-    std::unordered_map<std::string, std::shared_ptr<MiscDevice>> mMiscDevices;
-
     bool mNeedToSendFinishedDeviceScan;
     bool mNeedToReopenDevices;
     bool mNeedToScanDevices;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 5d56f5a..291f105 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -32,9 +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 InputController;
-class InputControllerInterface;
+class PeripheralController;
+class PeripheralControllerInterface;
 class InputDeviceContext;
 class InputMapper;
 
@@ -162,11 +164,12 @@
     // 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<InputControllerInterface> mController;
+    std::unique_ptr<PeripheralControllerInterface> mController;
 
     uint32_t mSources;
     bool mIsExternal;
     std::optional<uint8_t> mAssociatedDisplayPort;
+    std::optional<std::string> mAssociatedDisplayUniqueId;
     std::optional<DisplayViewport> mAssociatedViewport;
     bool mHasMic;
     bool mDropUntilNextSync;
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 1405671..bc79ccf 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -203,7 +203,7 @@
 
     void notifyExternalStylusPresenceChangedLocked() REQUIRES(mLock);
     void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) REQUIRES(mLock);
-    void dispatchExternalStylusState(const StylusState& state);
+    void dispatchExternalStylusStateLocked(const StylusState& state) REQUIRES(mLock);
 
     // The PointerController that is shared among all the input devices that need it.
     std::weak_ptr<PointerControllerInterface> mPointerController;
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 2c5a576..104d087 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -132,9 +132,7 @@
 
 std::optional<DisplayViewport> KeyboardInputMapper::findViewport(
         nsecs_t when, const InputReaderConfiguration* config) {
-    const std::optional<uint8_t> displayPort = getDeviceContext().getAssociatedDisplayPort();
-    if (displayPort) {
-        // Find the viewport that contains the same port
+    if (getDeviceContext().getAssociatedViewport()) {
         return getDeviceContext().getAssociatedViewport();
     }
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 13712ee..fb65484 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -2456,6 +2456,12 @@
     int32_t metaState = getContext()->getGlobalMetaState();
     int32_t buttonState = mCurrentCookedState.buttonState;
 
+    uint32_t flags = 0;
+
+    if (!PointerGesture::canGestureAffectWindowFocus(mPointerGesture.currentGestureMode)) {
+        flags |= AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE;
+    }
+
     // Update last coordinates of pointers that have moved so that we observe the new
     // pointer positions at the same time as other pointers that have just gone up.
     bool down = mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP ||
@@ -2485,8 +2491,8 @@
     BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits);
     if (!dispatchedGestureIdBits.isEmpty()) {
         if (cancelPreviousGesture) {
-            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
-                           metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0,
+                           flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                            mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
                            mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
                            mPointerGesture.downTime);
@@ -2504,7 +2510,7 @@
                 uint32_t id = upGestureIdBits.clearFirstMarkedBit();
 
                 dispatchMotion(when, readTime, policyFlags, mSource,
-                               AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState,
+                               AMOTION_EVENT_ACTION_POINTER_UP, 0, flags, metaState, buttonState,
                                AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties,
                                mPointerGesture.lastGestureCoords,
                                mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0,
@@ -2517,7 +2523,7 @@
 
     // Send motion events for all pointers that moved.
     if (moveNeeded) {
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
+        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, flags,
                        metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                        mPointerGesture.currentGestureProperties,
                        mPointerGesture.currentGestureCoords,
@@ -2538,7 +2544,7 @@
             }
 
             dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN,
-                           0, 0, metaState, buttonState, 0,
+                           0, flags, metaState, buttonState, 0,
                            mPointerGesture.currentGestureProperties,
                            mPointerGesture.currentGestureCoords,
                            mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0,
@@ -2548,8 +2554,8 @@
 
     // Send motion events for hover.
     if (mPointerGesture.currentGestureMode == PointerGesture::Mode::HOVER) {
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
-                       metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
+                       flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                        mPointerGesture.currentGestureProperties,
                        mPointerGesture.currentGestureCoords,
                        mPointerGesture.currentGestureIdToIndex,
@@ -2573,7 +2579,7 @@
 
         const int32_t displayId = mPointerController->getDisplayId();
         NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+                              displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags,
                               metaState, buttonState, MotionClassification::NONE,
                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
                               0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {});
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 5146299..920f842 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -590,6 +590,27 @@
             QUIET,
         };
 
+        // When a gesture is sent to an unfocused window, return true if it can bring that window
+        // into focus, false otherwise.
+        static bool canGestureAffectWindowFocus(Mode mode) {
+            switch (mode) {
+                case Mode::TAP:
+                case Mode::TAP_DRAG:
+                case Mode::BUTTON_CLICK_OR_DRAG:
+                    // Taps can affect window focus.
+                    return true;
+                case Mode::FREEFORM:
+                case Mode::HOVER:
+                case Mode::NEUTRAL:
+                case Mode::PRESS:
+                case Mode::QUIET:
+                case Mode::SWIPE:
+                    // Most gestures can be performed on an unfocused window, so they should not
+                    // not affect window focus.
+                    return false;
+            }
+        }
+
         // Time the first finger went down.
         nsecs_t firstTouchTime;
 
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index b62fce4..855453e 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);
@@ -403,6 +406,7 @@
 
     void notifyDropWindow(const sp<IBinder>& token, float x, float y) override {
         std::scoped_lock lock(mLock);
+        mNotifyDropWindowWasCalled = true;
         mDropTargetWindowToken = token;
     }
 
@@ -522,7 +526,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -535,6 +540,7 @@
                              (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                      0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
                      ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
                      pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
@@ -547,6 +553,7 @@
                              (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                      0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
                      ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
                      pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
@@ -560,6 +567,7 @@
                              (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                      0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
                      ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
                      pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
@@ -572,6 +580,7 @@
                              (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                      0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
                      ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
                      pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
@@ -583,7 +592,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 0, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -593,7 +603,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -605,7 +616,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -616,7 +628,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -629,7 +642,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 2, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -1211,6 +1225,11 @@
         return *this;
     }
 
+    MotionEventBuilder& addFlag(uint32_t flags) {
+        mFlags |= flags;
+        return *this;
+    }
+
     MotionEvent build() {
         std::vector<PointerProperties> pointerProperties;
         std::vector<PointerCoords> pointerCoords;
@@ -1230,11 +1249,11 @@
         MotionEvent event;
         ui::Transform identityTransform;
         event.initialize(InputEvent::nextId(), DEVICE_ID, mSource, mDisplayId, INVALID_HMAC,
-                         mAction, mActionButton, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
+                         mAction, mActionButton, mFlags, /* edgeFlags */ 0, AMETA_NONE,
                          mButtonState, MotionClassification::NONE, identityTransform,
                          /* xPrecision */ 0, /* yPrecision */ 0, mRawXCursorPosition,
-                         mRawYCursorPosition, mEventTime, mEventTime, mPointers.size(),
-                         pointerProperties.data(), pointerCoords.data());
+                         mRawYCursorPosition, mDisplayWidth, mDisplayHeight, mEventTime, mEventTime,
+                         mPointers.size(), pointerProperties.data(), pointerCoords.data());
 
         return event;
     }
@@ -1246,8 +1265,11 @@
     int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
     int32_t mActionButton{0};
     int32_t mButtonState{0};
+    int32_t mFlags{0};
     float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
     float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+    int32_t mDisplayWidth{AMOTION_EVENT_INVALID_DISPLAY_SIZE};
+    int32_t mDisplayHeight{AMOTION_EVENT_INVALID_DISPLAY_SIZE};
 
     std::vector<PointerBuilder> mPointers;
 };
@@ -1692,7 +1714,13 @@
                          0 /*expectedFlags*/);
 }
 
-TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) {
+using TransferFunction =
+        std::function<bool(sp<InputDispatcher> dispatcher, sp<IBinder>, sp<IBinder>)>;
+
+class TransferTouchFixture : public InputDispatcherTest,
+                             public ::testing::WithParamInterface<TransferFunction> {};
+
+TEST_P(TransferTouchFixture, TransferTouch_OnePointer) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
     // Create a couple of windows
@@ -1713,8 +1741,10 @@
     firstWindow->consumeMotionDown();
     secondWindow->assertNoEvents();
 
-    // Transfer touch focus to the second window
-    mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+    // Transfer touch to the second window
+    TransferFunction f = GetParam();
+    const bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken());
+    ASSERT_TRUE(success);
     // The first window gets cancel and the second gets down
     firstWindow->consumeMotionCancel();
     secondWindow->consumeMotionDown();
@@ -1729,7 +1759,7 @@
     secondWindow->consumeMotionUp();
 }
 
-TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) {
+TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
     PointF touchPoint = {10, 10};
@@ -1764,7 +1794,9 @@
     secondWindow->assertNoEvents();
 
     // Transfer touch focus to the second window
-    mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+    TransferFunction f = GetParam();
+    bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken());
+    ASSERT_TRUE(success);
     // The first window gets cancel and the second gets down and pointer down
     firstWindow->consumeMotionCancel();
     secondWindow->consumeMotionDown();
@@ -1791,6 +1823,21 @@
     secondWindow->consumeMotionUp();
 }
 
+// For the cases of single pointer touch and two pointers non-split touch, the api's
+// 'transferTouch' and 'transferTouchFocus' are equivalent in behaviour. They only differ
+// for the case where there are multiple pointers split across several windows.
+INSTANTIATE_TEST_SUITE_P(TransferFunctionTests, TransferTouchFixture,
+                         ::testing::Values(
+                                 [&](sp<InputDispatcher> dispatcher, sp<IBinder> /*ignored*/,
+                                     sp<IBinder> destChannelToken) {
+                                     return dispatcher->transferTouch(destChannelToken);
+                                 },
+                                 [&](sp<InputDispatcher> dispatcher, sp<IBinder> from,
+                                     sp<IBinder> to) {
+                                     return dispatcher->transferTouchFocus(from, to,
+                                                                           false /*isDragAndDrop*/);
+                                 }));
+
 TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
@@ -1861,6 +1908,82 @@
     secondWindow->consumeMotionUp();
 }
 
+// Same as TransferTouchFocus_TwoPointersSplitTouch, but using 'transferTouch' api.
+// Unlike 'transferTouchFocus', calling 'transferTouch' when there are two windows receiving
+// touch is not supported, so the touch should continue on those windows and the transferred-to
+// window should get nothing.
+TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+    // Create a non touch modal window that supports split touch
+    sp<FakeWindowHandle> firstWindow =
+            new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
+    firstWindow->setFrame(Rect(0, 0, 600, 400));
+    firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+                          InputWindowInfo::Flag::SPLIT_TOUCH);
+
+    // Create a non touch modal window that supports split touch
+    sp<FakeWindowHandle> secondWindow =
+            new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
+    secondWindow->setFrame(Rect(0, 400, 600, 800));
+    secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+                           InputWindowInfo::Flag::SPLIT_TOUCH);
+
+    // Add the windows to the dispatcher
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+
+    PointF pointInFirst = {300, 200};
+    PointF pointInSecond = {300, 600};
+
+    // Send down to the first window
+    NotifyMotionArgs firstDownMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, {pointInFirst});
+    mDispatcher->notifyMotion(&firstDownMotionArgs);
+    // Only the first window should get the down event
+    firstWindow->consumeMotionDown();
+    secondWindow->assertNoEvents();
+
+    // Send down to the second window
+    NotifyMotionArgs secondDownMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
+                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {pointInFirst, pointInSecond});
+    mDispatcher->notifyMotion(&secondDownMotionArgs);
+    // The first window gets a move and the second a down
+    firstWindow->consumeMotionMove();
+    secondWindow->consumeMotionDown();
+
+    // Transfer touch focus to the second window
+    const bool transferred = mDispatcher->transferTouch(secondWindow->getToken());
+    // The 'transferTouch' call should not succeed, because there are 2 touched windows
+    ASSERT_FALSE(transferred);
+    firstWindow->assertNoEvents();
+    secondWindow->assertNoEvents();
+
+    // The rest of the dispatch should proceed as normal
+    // Send pointer up to the second window
+    NotifyMotionArgs pointerUpMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
+                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {pointInFirst, pointInSecond});
+    mDispatcher->notifyMotion(&pointerUpMotionArgs);
+    // The first window gets MOVE and the second gets pointer up
+    firstWindow->consumeMotionMove();
+    secondWindow->consumeMotionUp();
+
+    // Send up event to the first window
+    NotifyMotionArgs upMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&upMotionArgs);
+    // The first window gets nothing and the second gets up
+    firstWindow->consumeMotionUp();
+    secondWindow->assertNoEvents();
+}
+
 TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
@@ -2025,6 +2148,19 @@
                                      expectedDisplayId, expectedFlags);
     }
 
+    MotionEvent* consumeMotion() {
+        InputEvent* event = mInputReceiver->consume();
+        if (!event) {
+            ADD_FAILURE() << "No event was produced";
+            return nullptr;
+        }
+        if (event->getType() != AINPUT_EVENT_TYPE_MOTION) {
+            ADD_FAILURE() << "Received event of type " << event->getType() << " instead of motion";
+            return nullptr;
+        }
+        return static_cast<MotionEvent*>(event);
+    }
+
     void assertNoEvents() { mInputReceiver->assertNoEvents(); }
 
 private:
@@ -2111,6 +2247,27 @@
     mFakePolicy->assertNotifyMonitorResponsiveWasCalled();
 }
 
+// Tests for gesture monitors
+TEST_F(InputDispatcherTest, GestureMonitor_NoWindowTransform) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    window->setWindowOffset(20, 40);
+    window->setWindowTransform(0, 1, -1, 0);
+
+    FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+                                                      true /*isGestureMonitor*/);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    MotionEvent* event = monitor.consumeMotion();
+    // Even though window has transform, gesture monitor must not.
+    ASSERT_EQ(ui::Transform(), event->getTransform());
+}
+
 TEST_F(InputDispatcherTest, TestMoveEvent) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
@@ -3055,6 +3212,25 @@
     mFakePolicy->assertOnPointerDownWasNotCalled();
 }
 
+// Have two windows, one with focus. Injecting a trusted DOWN MotionEvent with the flag
+// NO_FOCUS_CHANGE on the unfocused window should not call the onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, NoFocusChangeFlag) {
+    const MotionEvent event =
+            MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+                    .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                    .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(20).y(20))
+                    .addFlag(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, event))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mUnfocusedWindow->consumeAnyMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertOnPointerDownWasNotCalled();
+    // Ensure that the unfocused window did not receive any FOCUS events.
+    mUnfocusedWindow->assertNoEvents();
+}
+
 // These tests ensures we can send touch events to a single client when there are multiple input
 // windows that point to the same client token.
 class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest {
@@ -4951,4 +5127,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 3d99a6b..5eaca71 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -15,7 +15,6 @@
  */
 
 #include <CursorInputMapper.h>
-#include <InputController.h>
 #include <InputDevice.h>
 #include <InputMapper.h>
 #include <InputReader.h>
@@ -23,6 +22,7 @@
 #include <InputReaderFactory.h>
 #include <KeyboardInputMapper.h>
 #include <MultiTouchInputMapper.h>
+#include <PeripheralController.h>
 #include <SensorInputMapper.h>
 #include <SingleTouchInputMapper.h>
 #include <SwitchInputMapper.h>
@@ -269,6 +269,11 @@
         mConfig.portAssociations.insert({inputPort, displayPort});
     }
 
+    void addInputUniqueIdAssociation(const std::string& inputUniqueId,
+                                     const std::string& displayUniqueId) {
+        mConfig.uniqueIdAssociations.insert({inputUniqueId, displayUniqueId});
+    }
+
     void addDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.insert(deviceId); }
 
     void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); }
@@ -2015,13 +2020,13 @@
     ASSERT_EQ(mReader->getVibratorIds(deviceId).size(), 2U);
 }
 
-// --- FakeInputController ---
+// --- FakePeripheralController ---
 
-class FakeInputController : public InputControllerInterface {
+class FakePeripheralController : public PeripheralControllerInterface {
 public:
-    FakeInputController(InputDeviceContext& deviceContext) : mDeviceContext(deviceContext) {}
+    FakePeripheralController(InputDeviceContext& deviceContext) : mDeviceContext(deviceContext) {}
 
-    ~FakeInputController() override {}
+    ~FakePeripheralController() override {}
 
     void populateDeviceInfo(InputDeviceInfo* deviceInfo) override {}
 
@@ -2064,7 +2069,8 @@
     constexpr int32_t eventHubId = 1;
     const char* DEVICE_LOCATION = "BLUETOOTH";
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
-    FakeInputController& controller = device->addController<FakeInputController>(eventHubId);
+    FakePeripheralController& controller =
+            device->addController<FakePeripheralController>(eventHubId);
     mReader->pushNextDevice(device);
 
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
@@ -2079,7 +2085,8 @@
     constexpr int32_t eventHubId = 1;
     const char* DEVICE_LOCATION = "BLUETOOTH";
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
-    FakeInputController& controller = device->addController<FakeInputController>(eventHubId);
+    FakePeripheralController& controller =
+            device->addController<FakePeripheralController>(eventHubId);
     mReader->pushNextDevice(device);
 
     ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
@@ -2094,7 +2101,8 @@
     constexpr int32_t eventHubId = 1;
     const char* DEVICE_LOCATION = "BLUETOOTH";
     std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
-    FakeInputController& controller = device->addController<FakeInputController>(eventHubId);
+    FakePeripheralController& controller =
+            device->addController<FakePeripheralController>(eventHubId);
     mReader->pushNextDevice(device);
     RawLightInfo info = {.id = 1,
                          .name = "Mono",
@@ -2618,6 +2626,41 @@
     ASSERT_FALSE(mDevice->isEnabled());
 }
 
+TEST_F(InputDeviceTest, Configure_AssignsDisplayUniqueId) {
+    // Device should be enabled by default.
+    mFakePolicy->clearViewports();
+    mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+    ASSERT_TRUE(mDevice->isEnabled());
+
+    // Device should be disabled because it is associated with a specific display, but the
+    // corresponding display is not found.
+    const std::string DISPLAY_UNIQUE_ID = "displayUniqueId";
+    mFakePolicy->addInputUniqueIdAssociation(DEVICE_NAME, DISPLAY_UNIQUE_ID);
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    ASSERT_FALSE(mDevice->isEnabled());
+
+    // Device should be enabled when a display is found.
+    mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                                    DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
+                                    NO_PORT, ViewportType::INTERNAL);
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    ASSERT_TRUE(mDevice->isEnabled());
+
+    // Device should be disabled after set disable.
+    mFakePolicy->addDisabledDevice(mDevice->getId());
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_ENABLED_STATE);
+    ASSERT_FALSE(mDevice->isEnabled());
+
+    // Device should still be disabled even found the associated display.
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    ASSERT_FALSE(mDevice->isEnabled());
+}
+
 // --- InputMapperTest ---
 
 class InputMapperTest : public testing::Test {
@@ -7850,8 +7893,6 @@
     ASSERT_EQ(SECONDARY_DISPLAY_ID, args.displayId);
 }
 
-
-
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
@@ -8598,9 +8639,9 @@
     ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
 }
 
-// --- InputControllerTest ---
+// --- PeripheralControllerTest ---
 
-class InputControllerTest : public testing::Test {
+class PeripheralControllerTest : public testing::Test {
 protected:
     static const char* DEVICE_NAME;
     static const char* DEVICE_LOCATION;
@@ -8663,41 +8704,43 @@
     }
 };
 
-const char* InputControllerTest::DEVICE_NAME = "device";
-const char* InputControllerTest::DEVICE_LOCATION = "BLUETOOTH";
-const int32_t InputControllerTest::DEVICE_ID = END_RESERVED_ID + 1000;
-const int32_t InputControllerTest::DEVICE_GENERATION = 2;
-const int32_t InputControllerTest::DEVICE_CONTROLLER_NUMBER = 0;
-const Flags<InputDeviceClass> InputControllerTest::DEVICE_CLASSES =
+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 InputControllerTest::EVENTHUB_ID = 1;
+const int32_t PeripheralControllerTest::EVENTHUB_ID = 1;
 
 // --- BatteryControllerTest ---
-class BatteryControllerTest : public InputControllerTest {
+class BatteryControllerTest : public PeripheralControllerTest {
 protected:
     void SetUp() override {
-        InputControllerTest::SetUp(DEVICE_CLASSES | InputDeviceClass::BATTERY);
+        PeripheralControllerTest::SetUp(DEVICE_CLASSES | InputDeviceClass::BATTERY);
     }
 };
 
 TEST_F(BatteryControllerTest, GetBatteryCapacity) {
-    InputController& controller = addControllerAndConfigure<InputController>();
+    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) {
-    InputController& controller = addControllerAndConfigure<InputController>();
+    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 InputControllerTest {
+class LightControllerTest : public PeripheralControllerTest {
 protected:
-    void SetUp() override { InputControllerTest::SetUp(DEVICE_CLASSES | InputDeviceClass::LIGHT); }
+    void SetUp() override {
+        PeripheralControllerTest::SetUp(DEVICE_CLASSES | InputDeviceClass::LIGHT);
+    }
 };
 
 TEST_F(LightControllerTest, SingleLight) {
@@ -8708,7 +8751,7 @@
                                .path = ""};
     mFakeEventHub->addRawLightInfo(infoSingle.id, std::move(infoSingle));
 
-    InputController& controller = addControllerAndConfigure<InputController>();
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
     InputDeviceInfo info;
     controller.populateDeviceInfo(&info);
     const auto& ids = info.getLightIds();
@@ -8739,7 +8782,7 @@
     mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen));
     mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue));
 
-    InputController& controller = addControllerAndConfigure<InputController>();
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
     InputDeviceInfo info;
     controller.populateDeviceInfo(&info);
     const auto& ids = info.getLightIds();
@@ -8761,7 +8804,7 @@
 
     mFakeEventHub->addRawLightInfo(infoColor.id, std::move(infoColor));
 
-    InputController& controller = addControllerAndConfigure<InputController>();
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
     InputDeviceInfo info;
     controller.populateDeviceInfo(&info);
     const auto& ids = info.getLightIds();
@@ -8798,7 +8841,7 @@
     mFakeEventHub->addRawLightInfo(info3.id, std::move(info3));
     mFakeEventHub->addRawLightInfo(info4.id, std::move(info4));
 
-    InputController& controller = addControllerAndConfigure<InputController>();
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
     InputDeviceInfo info;
     controller.populateDeviceInfo(&info);
     const auto& ids = info.getLightIds();
diff --git a/services/memtrackproxy/MemtrackProxy.cpp b/services/memtrackproxy/MemtrackProxy.cpp
index 8da6e89..4676167 100644
--- a/services/memtrackproxy/MemtrackProxy.cpp
+++ b/services/memtrackproxy/MemtrackProxy.cpp
@@ -122,11 +122,9 @@
 
     _aidl_return->clear();
 
-    if (memtrack_aidl_instance_ ||
-        (memtrack_aidl_instance_ = MemtrackProxy::MemtrackAidlInstance())) {
+    if (memtrack_aidl_instance_) {
         return memtrack_aidl_instance_->getMemory(pid, type, _aidl_return);
-    } else if (memtrack_hidl_instance_ ||
-               (memtrack_hidl_instance_ = MemtrackProxy::MemtrackHidlInstance())) {
+    } else if (memtrack_hidl_instance_) {
         ndk::ScopedAStatus aidl_status;
 
         Return<void> ret = memtrack_hidl_instance_->getMemory(
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 c769e97..4151b45 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -49,10 +49,12 @@
         "libhardware_legacy",
         "libutils",
         "liblog",
+        "libactivitymanager_aidl",
         "libbatterystats_aidl",
         "libbinder",
         "libsensor",
         "libsensorprivacy",
+        "libpermission",
         "libprotoutil",
         "libcrypto",
         "libbase",
@@ -69,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/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index d8e8b52..5d6f8c7 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -132,8 +132,6 @@
 }
 
 void SensorDevice::initializeSensorList() {
-    float minPowerMa = 0.001; // 1 microAmp
-
     checkReturn(mSensors->getSensorsList(
             [&](const auto &list) {
                 const size_t count = list.size();
@@ -151,13 +149,18 @@
                         // Don't crash in this case since CTS will verify that devices don't go to
                         // production with a resolution of 0.
                         if (sensor.resolution != 0) {
-                            double promotedResolution = sensor.resolution;
-                            double promotedMaxRange = sensor.maxRange;
-                            if (fmod(promotedMaxRange, promotedResolution) != 0) {
-                                ALOGW("%s's max range %f is not a multiple of the resolution %f",
-                                        sensor.name, sensor.maxRange, sensor.resolution);
-                                SensorDeviceUtils::quantizeValue(
-                                        &sensor.maxRange, promotedResolution);
+                            float quantizedRange = sensor.maxRange;
+                            SensorDeviceUtils::quantizeValue(
+                                    &quantizedRange, sensor.resolution, /*factor=*/ 1);
+                            // Only rewrite maxRange if the requantization produced a "significant"
+                            // change, which is fairly arbitrarily defined as resolution / 8.
+                            // Smaller deltas are permitted, as they may simply be due to floating
+                            // point representation error, etc.
+                            if (fabsf(sensor.maxRange - quantizedRange) > sensor.resolution / 8) {
+                                ALOGW("%s's max range %.12f is not a multiple of the resolution "
+                                      "%.12f - updated to %.12f", sensor.name, sensor.maxRange,
+                                      sensor.resolution, quantizedRange);
+                                sensor.maxRange = quantizedRange;
                             }
                         } else {
                             // Don't crash here or the device will go into a crashloop.
@@ -166,10 +169,11 @@
                     }
 
                     // Sanity check and clamp power if it is 0 (or close)
-                    if (sensor.power < minPowerMa) {
-                        ALOGI("Reported power %f not deemed sane, clamping to %f",
-                              sensor.power, minPowerMa);
-                        sensor.power = minPowerMa;
+                    constexpr float MIN_POWER_MA = 0.001; // 1 microAmp
+                    if (sensor.power < MIN_POWER_MA) {
+                        ALOGI("%s's reported power %f invalid, clamped to %f",
+                              sensor.name, sensor.power, MIN_POWER_MA);
+                        sensor.power = MIN_POWER_MA;
                     }
                     mSensorList.push_back(sensor);
 
diff --git a/services/sensorservice/SensorDeviceUtils.h b/services/sensorservice/SensorDeviceUtils.h
index 1309971..255f7e1 100644
--- a/services/sensorservice/SensorDeviceUtils.h
+++ b/services/sensorservice/SensorDeviceUtils.h
@@ -32,16 +32,15 @@
 namespace android {
 namespace SensorDeviceUtils {
 
-// Quantizes a single value using a sensor's resolution.
-inline void quantizeValue(float *value, double resolution) {
+// Quantizes a single value to (a fractional factor of) a sensor's resolution. Typically we
+// increase the value of the sensor's nominal resolution to ensure that sensor accuracy
+// improvements, like runtime calibration, are not masked during requantization.
+inline void quantizeValue(float *value, double resolution, double factor = 0.125) {
     if (resolution == 0) {
         return;
     }
 
-    // Increase the value of the sensor's nominal resolution to ensure that
-    // sensor accuracy improvements, like runtime calibration, are not masked
-    // during requantization.
-    double incRes = 0.125 * resolution;
+    double incRes = factor * resolution;
     *value = round(static_cast<double>(*value) / incRes) * incRes;
 }
 
diff --git a/services/stats/android.frameworks.stats@1.0-service.xml b/services/stats/android.frameworks.stats@1.0-service.xml
index 5fd361c..c564b7b 100644
--- a/services/stats/android.frameworks.stats@1.0-service.xml
+++ b/services/stats/android.frameworks.stats@1.0-service.xml
@@ -1,5 +1,5 @@
 <manifest version="1.0" type="framework">
-    <hal>
+    <hal max-level="5">
         <name>android.frameworks.stats</name>
         <transport>hwbinder</transport>
         <version>1.0</version>
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index b976eb5..f20bfe1 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -25,7 +25,7 @@
     name: "libsurfaceflinger_defaults",
     defaults: [
         "surfaceflinger_defaults",
-        "skia_deps",
+        "skia_renderengine_deps",
     ],
     cflags: [
         "-DLOG_TAG=\"SurfaceFlinger\"",
@@ -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 be9bce0..cacad52 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -58,8 +58,7 @@
 
 namespace android {
 
-static constexpr float defaultMaxMasteringLuminance = 1000.0;
-static constexpr float defaultMaxContentLuminance = 1000.0;
+static constexpr float defaultMaxLuminance = 1000.0;
 
 BufferLayer::BufferLayer(const LayerCreationArgs& args)
       : Layer(args),
@@ -186,9 +185,9 @@
         }
     }
     const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
-            (isSecure() && !targetSettings.isSecure);
+            ((isSecure() || isProtected()) && !targetSettings.isSecure);
     const bool bufferCanBeUsedAsHwTexture =
-            mBufferInfo.mBuffer->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
+            mBufferInfo.mBuffer->getBuffer()->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
     compositionengine::LayerFE::LayerSettings& layer = *result;
     if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
         ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
@@ -206,14 +205,26 @@
     layer.source.buffer.isY410BT2020 = isHdrY410();
     bool hasSmpte2086 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::SMPTE2086;
     bool hasCta861_3 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::CTA861_3;
-    layer.source.buffer.maxMasteringLuminance = hasSmpte2086
-            ? mBufferInfo.mHdrMetadata.smpte2086.maxLuminance
-            : defaultMaxMasteringLuminance;
-    layer.source.buffer.maxContentLuminance = hasCta861_3
-            ? mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel
-            : defaultMaxContentLuminance;
+    float maxLuminance = 0.f;
+    if (hasSmpte2086 && hasCta861_3) {
+        maxLuminance = std::min(mBufferInfo.mHdrMetadata.smpte2086.maxLuminance,
+                                mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel);
+    } else if (hasSmpte2086) {
+        maxLuminance = mBufferInfo.mHdrMetadata.smpte2086.maxLuminance;
+    } else if (hasCta861_3) {
+        maxLuminance = mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel;
+    } else {
+        switch (layer.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+            case HAL_DATASPACE_TRANSFER_ST2084:
+            case HAL_DATASPACE_TRANSFER_HLG:
+                // Behavior-match previous releases for HDR content
+                maxLuminance = defaultMaxLuminance;
+                break;
+        }
+    }
+    layer.source.buffer.maxLuminanceNits = maxLuminance;
     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();
@@ -314,7 +325,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;
@@ -442,7 +453,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;
 }
 
@@ -507,11 +518,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;
@@ -544,10 +550,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;
         }
     }
@@ -556,53 +562,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();
 }
@@ -612,35 +574,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
@@ -727,8 +662,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) {
@@ -759,8 +694,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) {
@@ -802,7 +737,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();
@@ -847,12 +782,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 09e3cd9..54daa10 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,25 +253,6 @@
              (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;
-}
-
 Rect BufferStateLayer::getCrop(const Layer::State& s) const {
     return s.crop;
 }
@@ -309,12 +284,83 @@
     return true;
 }
 
+bool BufferStateLayer::setBufferCrop(const Rect& bufferCrop) {
+    if (mCurrentState.bufferCrop == bufferCrop) return false;
+
+    mCurrentState.sequence++;
+    mCurrentState.bufferCrop = bufferCrop;
+
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool BufferStateLayer::setDestinationFrame(const Rect& destinationFrame) {
+    if (mCurrentState.destinationFrame == destinationFrame) return false;
+
+    mCurrentState.sequence++;
+    mCurrentState.destinationFrame = destinationFrame;
+
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+// Translate destination frame into scale and position. If a destination frame is not set, use the
+// provided scale and position
+void BufferStateLayer::updateGeometry() {
+    if (mCurrentState.destinationFrame.isEmpty()) {
+        // If destination frame is not set, use the requested transform set via
+        // BufferStateLayer::setPosition and BufferStateLayer::setMatrix.
+        mCurrentState.transform = mRequestedTransform;
+        return;
+    }
+
+    Rect destRect = mCurrentState.destinationFrame;
+    int32_t destW = destRect.width();
+    int32_t destH = destRect.height();
+    if (destRect.left < 0) {
+        destRect.left = 0;
+        destRect.right = destW;
+    }
+    if (destRect.top < 0) {
+        destRect.top = 0;
+        destRect.bottom = destH;
+    }
+
+    if (!mCurrentState.buffer) {
+        ui::Transform t;
+        t.set(destRect.left, destRect.top);
+        mCurrentState.transform = t;
+        return;
+    }
+
+    uint32_t bufferWidth = mCurrentState.buffer->getBuffer()->getWidth();
+    uint32_t bufferHeight = mCurrentState.buffer->getBuffer()->getHeight();
+    // Undo any transformations on the buffer.
+    if (mCurrentState.bufferTransform & ui::Transform::ROT_90) {
+        std::swap(bufferWidth, bufferHeight);
+    }
+    uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+    if (mCurrentState.transformToDisplayInverse) {
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufferWidth, bufferHeight);
+        }
+    }
+
+    float sx = destW / static_cast<float>(bufferWidth);
+    float sy = destH / static_cast<float>(bufferHeight);
+    ui::Transform t;
+    t.set(sx, 0, 0, sy);
+    t.set(destRect.left, destRect.top);
+    mCurrentState.transform = t;
+    return;
+}
+
 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) {
+    if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy &&
+        mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) {
         return false;
     }
 
@@ -327,7 +373,7 @@
         return false;
     }
 
-    mCurrentState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
+    mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
 
     mCurrentState.sequence++;
     mCurrentState.modified = true;
@@ -337,11 +383,11 @@
 }
 
 bool BufferStateLayer::setPosition(float x, float y) {
-    if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) {
+    if (mRequestedTransform.tx() == x && mRequestedTransform.ty() == y) {
         return false;
     }
 
-    mCurrentState.transform.set(x, y);
+    mRequestedTransform.set(x, y);
 
     mCurrentState.sequence++;
     mCurrentState.modified = true;
@@ -363,8 +409,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) {
@@ -372,12 +419,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) {
@@ -415,8 +464,8 @@
 
     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);
@@ -424,8 +473,8 @@
                                                FrameTracer::FrameEvent::QUEUE);
     }
 
-    mCurrentState.width = mCurrentState.buffer->width;
-    mCurrentState.height = mCurrentState.buffer->height;
+    mCurrentState.width = mCurrentState.buffer->getBuffer()->getWidth();
+    mCurrentState.height = mCurrentState.buffer->getBuffer()->getHeight();
 
     return true;
 }
@@ -534,35 +583,35 @@
 
 Rect BufferStateLayer::getBufferSize(const State& s) const {
     // for buffer state layers we use the display frame size as the buffer size.
-    if (getActiveWidth(s) < UINT32_MAX && getActiveHeight(s) < UINT32_MAX) {
-        return Rect(getActiveWidth(s), getActiveHeight(s));
-    }
 
     if (mBufferInfo.mBuffer == nullptr) {
         return Rect::INVALID_RECT;
     }
 
-    // if the display frame is not defined, use the parent bounds as the buffer size.
-    const auto& p = mDrawingParent.promote();
-    if (p != nullptr) {
-        Rect parentBounds = Rect(p->getBounds(Region()));
-        if (!parentBounds.isEmpty()) {
-            return parentBounds;
+    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) {
+        std::swap(bufWidth, bufHeight);
+    }
+
+    if (getTransformToDisplayInverse()) {
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufWidth, bufHeight);
         }
     }
 
-    return Rect::INVALID_RECT;
+    return Rect(0, 0, bufWidth, bufHeight);
 }
 
 FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) const {
-    const State& s(getDrawingState());
-    // for buffer state layers we use the display frame size as the buffer size.
-    if (getActiveWidth(s) < UINT32_MAX && getActiveHeight(s) < UINT32_MAX) {
-        return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s));
+    if (mBufferInfo.mBuffer == nullptr) {
+        return parentBounds;
     }
 
-    // if the display frame is not defined, use the parent bounds as the buffer size.
-    return parentBounds;
+    return getBufferSize(getDrawingState()).toFloatRect();
 }
 
 // -----------------------------------------------------------------------
@@ -633,7 +682,10 @@
 }
 
 bool BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
-    if (mSidebandStreamChanged.exchange(false)) {
+    // We need to update the sideband stream if the layer has both a buffer and a sideband stream.
+    const bool updateSidebandStream = hasFrameUpdate() && mSidebandStream.get();
+
+    if (mSidebandStreamChanged.exchange(false) || updateSidebandStream) {
         const State& s(getDrawingState());
         // mSidebandStreamChanged was true
         mSidebandStream = s.sidebandStream;
@@ -674,7 +726,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);
@@ -696,6 +748,11 @@
                                           latchTime);
     }
 
+    std::deque<sp<CallbackHandle>> remainingHandles;
+    mFlinger->getTransactionCallbackInvoker()
+            .finalizeOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles);
+    mDrawingState.callbackHandles = remainingHandles;
+
     mCurrentStateModified = false;
 
     return NO_ERROR;
@@ -708,7 +765,7 @@
         return BAD_VALUE;
     }
 
-    if (s.buffer != mBufferInfo.mBuffer) {
+    if (!mBufferInfo.mBuffer || s.buffer->getBuffer() != mBufferInfo.mBuffer->getBuffer()) {
         decrementPendingBufferCount();
     }
 
@@ -812,7 +869,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;
@@ -825,27 +882,16 @@
    return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
 }
 
-Rect BufferStateLayer::computeCrop(const State& s) {
-    if (s.crop.isEmpty() && s.buffer) {
-        return s.buffer->getBounds();
+Rect BufferStateLayer::computeBufferCrop(const State& s) {
+    if (s.buffer && !s.bufferCrop.isEmpty()) {
+        Rect bufferCrop;
+        s.buffer->getBuffer()->getBounds().intersect(s.bufferCrop, &bufferCrop);
+        return bufferCrop;
     } else if (s.buffer) {
-        Rect crop = s.crop;
-        crop.left = std::max(crop.left, 0);
-        crop.top = std::max(crop.top, 0);
-        uint32_t bufferWidth = s.buffer->getWidth();
-        uint32_t bufferHeight = s.buffer->getHeight();
-        if (bufferHeight <= std::numeric_limits<int32_t>::max() &&
-            bufferWidth <= std::numeric_limits<int32_t>::max()) {
-            crop.right = std::min(crop.right, static_cast<int32_t>(bufferWidth));
-            crop.bottom = std::min(crop.bottom, static_cast<int32_t>(bufferHeight));
-        }
-        if (!crop.isValid()) {
-            // Crop rect is out of bounds, return whole buffer
-            return s.buffer->getBounds();
-        }
-        return crop;
+        return s.buffer->getBuffer()->getBounds();
+    } else {
+        return s.bufferCrop;
     }
-    return s.crop;
 }
 
 sp<Layer> BufferStateLayer::createClone() {
@@ -863,8 +909,8 @@
         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) {
@@ -891,18 +937,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 af2819e..2e48452 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -50,14 +50,7 @@
     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; }
-    ui::Transform getActiveTransform(const Layer::State& s) const override { return s.transform; }
     Region getActiveTransparentRegion(const Layer::State& s) const override {
         return s.transparentRegionHint;
     }
@@ -66,9 +59,9 @@
     bool setTransform(uint32_t transform) override;
     bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
     bool setCrop(const Rect& crop) 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;
@@ -87,15 +80,15 @@
     // 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 setTransparentRegionHint(const Region& transparent) override;
-    void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/,
-                                      uint64_t /*frameNumber*/) override {}
-    void deferTransactionUntil_legacy(const sp<Layer>& /*barrierLayer*/,
-                                      uint64_t /*frameNumber*/) override {}
 
     Rect getBufferSize(const State& s) const override;
     FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
     void setAutoRefresh(bool autoRefresh) override;
 
+    bool setBufferCrop(const Rect& bufferCrop) override;
+    bool setDestinationFrame(const Rect& destinationFrame) override;
+    void updateGeometry() override;
+
     // -----------------------------------------------------------------------
 
     // -----------------------------------------------------------------------
@@ -108,7 +101,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; }
 
@@ -118,6 +111,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;
@@ -146,7 +141,7 @@
     sp<Layer> createClone() override;
 
     // Crop that applies to the buffer
-    Rect computeCrop(const State& s);
+    Rect computeBufferCrop(const State& s);
 
     bool willPresentCurrentTransaction() const;
 
@@ -163,7 +158,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
@@ -186,6 +180,10 @@
     //     - If the integer decreases in setBuffer or doTransaction, a buffer was dropped
     std::atomic<int32_t> mPendingBufferTransactions{0};
 
+    // Contains requested position and matrix updates. This will be applied if the client does
+    // not specify a destination frame.
+    ui::Transform mRequestedTransform;
+
     // TODO(marissaw): support sticky transform for LEGACY camera mode
 
     class HwcSlotGenerator : public ClientCache::ErasedRecipient {
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index 44b33ef..f310738 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -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;
@@ -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/Clock.h b/services/surfaceflinger/Clock.h
new file mode 100644
index 0000000..3f23c6d
--- /dev/null
+++ b/services/surfaceflinger/Clock.h
@@ -0,0 +1,42 @@
+/*
+ * 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 <chrono>
+
+namespace android {
+
+// Abstract interface for timekeeping which can be injected for unit tests.
+class Clock {
+public:
+    Clock() = default;
+    virtual ~Clock() = default;
+
+    // Returns the current time
+    virtual std::chrono::steady_clock::time_point now() const = 0;
+};
+
+class SteadyClock : public Clock {
+public:
+    virtual ~SteadyClock() = default;
+
+    std::chrono::steady_clock::time_point now() const override {
+        return std::chrono::steady_clock::now();
+    }
+};
+
+} // namespace android
\ No newline at end of file
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/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 1fd07b0..791e7db 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -138,6 +138,9 @@
 
     // Gets the sequence number: a serial number that uniquely identifies a Layer
     virtual int32_t getSequence() const = 0;
+
+    // Whether the layer should be rendered with rounded corners.
+    virtual bool hasRoundedCorners() const = 0;
 };
 
 // TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 4976213..257974f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -163,6 +163,9 @@
     // Enables (or disables) composition on this output
     virtual void setCompositionEnabled(bool) = 0;
 
+    // Enables (or disables) layer caching on this output
+    virtual void setLayerCachingEnabled(bool) = 0;
+
     // Sets the projection state to use
     virtual void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                                const Rect& orientedDisplaySpaceRect) = 0;
@@ -181,6 +184,9 @@
     // Sets the output color mode
     virtual void setColorProfile(const ColorProfile&) = 0;
 
+    // Sets current calibrated display brightness information
+    virtual void setDisplayBrightness(float sdrWhitePointNits, float displayBrightnessNits) = 0;
+
     // Outputs a string with a state dump
     virtual void dump(std::string&) const = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index 3a84327..ead941d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <cstdint>
 #include <optional>
 #include <string>
 
@@ -90,8 +91,13 @@
     // Writes the geometry state to the HWC, or does nothing if this layer does
     // not use the HWC. If includeGeometry is false, the geometry state can be
     // skipped. If skipLayer is true, then the alpha of the layer is forced to
-    // 0 so that HWC will ignore it.
-    virtual void writeStateToHWC(bool includeGeometry, bool skipLayer) = 0;
+    // 0 so that HWC will ignore it. z specifies the order to draw the layer in
+    // (starting with 0 for the back layer, and increasing for each following
+    // layer). zIsOverridden specifies whether the layer has been reordered.
+    // isPeekingThrough specifies whether this layer will be shown through a
+    // hole punch in a layer above it.
+    virtual void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z,
+                                 bool zIsOverridden, bool isPeekingThrough) = 0;
 
     // Updates the cursor position with the HWC
     virtual void writeCursorPositionToHWC() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
index 7ca91d8..58bb41a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
@@ -96,6 +96,11 @@
         // physical translation and finally rotate to the physical orientation.
         return rotationTransform * destTranslation * scale * sourceTranslation;
     }
+
+    bool operator==(const ProjectionSpace& other) const {
+        return bounds == other.bounds && content == other.content &&
+                orientation == other.orientation;
+    }
 };
 
 } // namespace compositionengine
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/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index aa049a8..fd22aa3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -57,11 +57,15 @@
     void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
                       sp<GraphicBuffer>* outBuffer);
 
+    // Special caching slot for the layer caching feature.
+    static const constexpr size_t FLATTENER_CACHING_SLOT = BufferQueue::NUM_BUFFER_SLOTS;
+
 private:
     // an array where the index corresponds to a slot and the value corresponds to a (counter,
     // buffer) pair. "counter" is a unique value that indicates the last time this slot was updated
     // or used and allows us to keep track of the least-recently used buffer.
-    wp<GraphicBuffer> mBuffers[BufferQueue::NUM_BUFFER_SLOTS];
+    static const constexpr size_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
+    wp<GraphicBuffer> mBuffers[kMaxLayerBufferCount];
 };
 
 } // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index eeb20fc..f10ff25 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -20,6 +20,7 @@
 #include <compositionengine/Output.h>
 #include <compositionengine/impl/ClientCompositionRequestCache.h>
 #include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/impl/planner/Planner.h>
 #include <renderengine/DisplaySettings.h>
 #include <renderengine/LayerSettings.h>
 #include <memory>
@@ -28,21 +29,18 @@
 
 namespace android::compositionengine::impl {
 
-namespace planner {
-class Planner;
-} // namespace planner
-
 // The implementation class contains the common implementation, but does not
 // actually contain the final output state.
 class Output : public virtual compositionengine::Output {
 public:
-    Output();
+    Output() = default;
     ~Output() override;
 
     // compositionengine::Output overrides
     bool isValid() const override;
     std::optional<DisplayId> getDisplayId() const override;
     void setCompositionEnabled(bool) override;
+    void setLayerCachingEnabled(bool) override;
     void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                        const Rect& orientedDisplaySpaceRect) override;
     void setDisplaySize(const ui::Size&) override;
@@ -51,6 +49,7 @@
 
     void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
     void setColorProfile(const ColorProfile&) override;
+    void setDisplayBrightness(float sdrWhitePointNits, float displayBrightnessNits) override;
 
     void dump(std::string&) const override;
     void dumpPlannerInfo(const Vector<String16>& args, std::string&) const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 8f767d3..d41c2dd 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -115,6 +115,15 @@
     // 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;
+
+    // Current display brightness
+    float displayBrightnessNits{-1.f};
+
+    // SDR white point
+    float sdrWhitePointNits{-1.f};
+
     // 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 ae88e78..2ffd472 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <cstdint>
 #include <memory>
 #include <string>
 
@@ -42,7 +43,8 @@
 
     void updateCompositionState(bool includeGeometry, bool forceClientComposition,
                                 ui::Transform::RotationFlags) override;
-    void writeStateToHWC(bool includeGeometry, bool skipLayer) override;
+    void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, bool zIsOverridden,
+                         bool isPeekingThrough) override;
     void writeCursorPositionToHWC() const override;
 
     HWC2::Layer* getHwcLayer() const override;
@@ -66,7 +68,8 @@
 
 private:
     Rect calculateInitialCrop() const;
-    void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
+    void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition,
+                                                uint32_t z);
     void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&,
                                                   bool skipLayer);
     void writeOutputDependentPerFrameStateToHWC(HWC2::Layer*);
@@ -74,9 +77,11 @@
     void writeSolidColorStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
     void writeSidebandStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
     void writeBufferStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
-    void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
+    void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition,
+                                   bool isPeekingThrough, bool skipLayer);
     void detectDisallowedCompositionTypeChange(Hwc2::IComposerClient::Composition from,
                                                Hwc2::IComposerClient::Composition to) const;
+    bool isClientCompositionForced(bool isPeekingThrough) const;
 };
 
 // This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 4c065ec..3f670a1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -18,7 +18,7 @@
 
 #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>
@@ -46,6 +46,10 @@
 
 class HWComposer;
 
+namespace compositionengine {
+class OutputLayer;
+} // namespace compositionengine
+
 namespace compositionengine::impl {
 
 // Note that fields that affect HW composer state may need to be mirrored into
@@ -84,18 +88,21 @@
     // The dataspace for this layer
     ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
 
-    // The Z order index of this layer on this output
-    uint32_t z{0};
-
     // 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;
+
+        // The OutputLayer pointed to by this field will be rearranged to draw
+        // behind the OutputLayer represented by this CompositionState and will
+        // be visible through it. Unowned - the OutputLayer's lifetime will
+        // outlast this.)
+        compositionengine::OutputLayer* peekThroughLayer = nullptr;
     } overrideInfo;
 
     /*
@@ -115,6 +122,12 @@
         // 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;
+
+        // True when this layer was skipped as part of SF-side layer caching.
+        bool layerSkipped = 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 afa02cd..dc6eab4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -60,16 +60,16 @@
     void addLayer(const LayerState*, std::chrono::steady_clock::time_point lastUpdate);
 
     std::chrono::steady_clock::time_point getLastUpdate() const { return mLastUpdate; }
-    NonBufferHash getFingerprint() const { return mFingerprint; }
     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; }
+    const std::vector<Layer>& getConstituentLayers() const { return mLayers; }
 
     NonBufferHash getNonBufferHash() const;
 
@@ -87,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;
 
@@ -105,41 +105,40 @@
 
     void dump(std::string& result) const;
 
+    // Whether this represents a single layer with a buffer and rounded corners.
+    // If it is, we may be able to draw it by placing it behind another
+    // CachedSet and punching a hole.
+    bool requiresHolePunch() const;
+
+    // True if any constituent layer is configured to blur any layers behind.
+    bool hasBlurBehind() const;
+
+    // Add a layer that will be drawn behind this one. ::render() will render a
+    // hole in this CachedSet's buffer, allowing the supplied layer to peek
+    // through. Must be called before ::render().
+    // Will do nothing if this CachedSet is not opaque where the hole punch
+    // layer is displayed.
+    // If isFirstLayer is true, this CachedSet can be considered opaque because
+    // nothing (besides the hole punch layer) will be drawn behind it.
+    void addHolePunchLayerIfFeasible(const CachedSet&, bool isFirstLayer);
+
+    // Retrieve the layer that will be drawn behind this one.
+    compositionengine::OutputLayer* getHolePunchLayer() 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;
+
+    // Unowned.
+    const LayerState* mHolePunchLayer = nullptr;
     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;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 313a180..213c55e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -20,6 +20,7 @@
 #include <compositionengine/impl/planner/CachedSet.h>
 #include <compositionengine/impl/planner/LayerState.h>
 
+#include <numeric>
 #include <vector>
 
 namespace android {
@@ -36,7 +37,7 @@
 
 class Flattener {
 public:
-    Flattener(Predictor& predictor) : mPredictor(predictor) {}
+    Flattener(bool enableHolePunch = false) : mEnableHolePunch(enableHolePunch) {}
 
     void setDisplaySize(ui::Size size) { mDisplaySize = size; }
 
@@ -48,20 +49,88 @@
                           const OutputCompositionState& outputState);
 
     void dump(std::string& result) const;
+    void dumpLayers(std::string& result) const;
 
 private:
     size_t calculateDisplayCost(const std::vector<const LayerState*>& layers) const;
 
     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);
 
+    // A Run is a sequence of CachedSets, which is a candidate for flattening into a single
+    // CachedSet. Because it is wasteful to flatten 1 CachedSet, a Run must contain more than 1
+    // CachedSet
+    class Run {
+    public:
+        // A builder for a Run, to aid in construction
+        class Builder {
+        private:
+            std::vector<CachedSet>::const_iterator mStart;
+            std::vector<size_t> mLengths;
+            const CachedSet* mHolePunchCandidate = nullptr;
+
+        public:
+            // Initializes a Builder a CachedSet to start from.
+            // This start iterator must be an iterator for mLayers
+            void init(const std::vector<CachedSet>::const_iterator& start) {
+                mStart = start;
+                mLengths.push_back(start->getLayerCount());
+            }
+
+            // Appends a new CachedSet to the end of the run
+            // The provided length must be the size of the next sequential CachedSet in layers
+            void append(size_t length) { mLengths.push_back(length); }
+
+            // Sets the hole punch candidate for the Run.
+            void setHolePunchCandidate(const CachedSet* holePunchCandidate) {
+                mHolePunchCandidate = holePunchCandidate;
+            }
+
+            // Builds a Run instance, if a valid Run may be built.
+            std::optional<Run> validateAndBuild() {
+                if (mLengths.size() <= 1) {
+                    return std::nullopt;
+                }
+
+                return Run(mStart,
+                           std::reduce(mLengths.cbegin(), mLengths.cend(), 0u,
+                                       [](size_t left, size_t right) { return left + right; }),
+                           mHolePunchCandidate);
+            }
+
+            void reset() { *this = {}; }
+        };
+
+        // Gets the starting CachedSet of this run.
+        // This is an iterator into mLayers
+        const std::vector<CachedSet>::const_iterator& getStart() const { return mStart; }
+        // Gets the total number of layers encompassing this Run.
+        size_t getLayerLength() const { return mLength; }
+        // Gets the hole punch candidate for this Run.
+        const CachedSet* getHolePunchCandidate() const { return mHolePunchCandidate; }
+
+    private:
+        Run(std::vector<CachedSet>::const_iterator start, size_t length,
+            const CachedSet* holePunchCandidate)
+              : mStart(start), mLength(length), mHolePunchCandidate(holePunchCandidate) {}
+        const std::vector<CachedSet>::const_iterator mStart;
+        const size_t mLength;
+        const CachedSet* const mHolePunchCandidate;
+
+        friend class Builder;
+    };
+
+    std::vector<Run> findCandidateRuns(std::chrono::steady_clock::time_point now) const;
+
+    std::optional<Run> findBestRun(std::vector<Run>& runs) const;
+
     void buildCachedSets(std::chrono::steady_clock::time_point now);
 
-    Predictor& mPredictor;
+    const bool mEnableHolePunch;
 
     ui::Size mDisplaySize;
 
@@ -69,7 +138,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 050e264..fef0dfb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -26,6 +26,7 @@
 #include <string>
 
 #include "DisplayHardware/Hal.h"
+#include "math/HashCombine.h"
 
 namespace std {
 template <typename T>
@@ -48,25 +49,26 @@
 
 // clang-format off
 enum class LayerStateField : uint32_t {
-    None            = 0u,
-    Id              = 1u << 0,
-    Name            = 1u << 1,
-    DisplayFrame    = 1u << 2,
-    SourceCrop      = 1u << 3,
-    ZOrder          = 1u << 4,
-    BufferTransform = 1u << 5,
-    BlendMode       = 1u << 6,
-    Alpha           = 1u << 7,
-    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,
+    None                  = 0u,
+    Id                    = 1u << 0,
+    Name                  = 1u << 1,
+    DisplayFrame          = 1u << 2,
+    SourceCrop            = 1u << 3,
+    BufferTransform       = 1u << 4,
+    BlendMode             = 1u << 5,
+    Alpha                 = 1u << 6,
+    LayerMetadata         = 1u << 7,
+    VisibleRegion         = 1u << 8,
+    Dataspace             = 1u << 9,
+    PixelFormat           = 1u << 10,
+    ColorTransform        = 1u << 11,
+    SurfaceDamage         = 1u << 12,
+    CompositionType       = 1u << 13,
+    SidebandStream        = 1u << 14,
+    Buffer                = 1u << 15,
+    SolidColor            = 1u << 16,
+    BackgroundBlurRadius  = 1u << 17,
+    BlurRegions           = 1u << 18,
 };
 // clang-format on
 
@@ -226,6 +228,9 @@
     const std::string& getName() const { return mName.get(); }
     Rect getDisplayFrame() const { return mDisplayFrame.get(); }
     const Region& getVisibleRegion() const { return mVisibleRegion.get(); }
+    bool hasBlurBehind() const {
+        return mBackgroundBlurRadius.get() > 0 || !mBlurRegions.get().empty();
+    }
     hardware::graphics::composer::hal::Composition getCompositionType() const {
         return mCompositionType.get();
     }
@@ -271,9 +276,6 @@
                                                        rect.top, rect.right, rect.bottom)};
                         }};
 
-    OutputLayerState<uint32_t, LayerStateField::ZOrder> mZOrder{
-            [](auto layer) { return layer->getState().z; }};
-
     using BufferTransformState = OutputLayerState<hardware::graphics::composer::hal::Transform,
                                                   LayerStateField::BufferTransform>;
     BufferTransformState mBufferTransform{[](auto layer) {
@@ -360,14 +362,6 @@
 
     OutputLayerState<mat4, LayerStateField::ColorTransform> mColorTransform;
 
-    using SurfaceDamageState = OutputLayerState<Region, LayerStateField::SurfaceDamage>;
-    SurfaceDamageState
-            mSurfaceDamage{[](auto layer) {
-                               return layer->getLayerFE().getCompositionState()->surfaceDamage;
-                           },
-                           SurfaceDamageState::getRegionToStrings(),
-                           SurfaceDamageState::getRegionEquals()};
-
     using CompositionTypeState = OutputLayerState<hardware::graphics::composer::hal::Composition,
                                                   LayerStateField::CompositionType>;
     CompositionTypeState
@@ -410,6 +404,44 @@
                             return std::vector<std::string>{stream.str()};
                         }};
 
+    OutputLayerState<int32_t, LayerStateField::BackgroundBlurRadius> mBackgroundBlurRadius{
+            [](auto layer) {
+                return layer->getLayerFE().getCompositionState()->backgroundBlurRadius;
+            }};
+
+    using BlurRegionsState =
+            OutputLayerState<std::vector<BlurRegion>, LayerStateField::BlurRegions>;
+    BlurRegionsState mBlurRegions{[](auto layer) {
+                                      return layer->getLayerFE().getCompositionState()->blurRegions;
+                                  },
+                                  [](const std::vector<BlurRegion>& regions) {
+                                      std::vector<std::string> result;
+                                      for (const auto region : regions) {
+                                          std::string str;
+                                          base::StringAppendF(&str,
+                                                              "{radius=%du, cornerRadii=[%f, %f, "
+                                                              "%f, %f], alpha=%f, rect=[%d, "
+                                                              "%d, %d, %d]",
+                                                              region.blurRadius,
+                                                              region.cornerRadiusTL,
+                                                              region.cornerRadiusTR,
+                                                              region.cornerRadiusBL,
+                                                              region.cornerRadiusBR, region.alpha,
+                                                              region.left, region.top, region.right,
+                                                              region.bottom);
+                                          result.push_back(str);
+                                      }
+                                      return result;
+                                  },
+                                  BlurRegionsState::getDefaultEquals(),
+                                  [](const std::vector<BlurRegion>& regions) {
+                                      size_t hash = 0;
+                                      for (const auto& region : regions) {
+                                          android::hashCombineSingle(hash, region);
+                                      }
+                                      return hash;
+                                  }};
+
     static const constexpr size_t kNumNonUniqueFields = 16;
 
     std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() {
@@ -425,10 +457,10 @@
 
     std::array<const StateInterface*, kNumNonUniqueFields> getNonUniqueFields() const {
         return {
-                &mDisplayFrame,    &mSourceCrop,     &mZOrder,         &mBufferTransform,
-                &mBlendMode,       &mAlpha,          &mLayerMetadata,  &mVisibleRegion,
-                &mOutputDataspace, &mPixelFormat,    &mColorTransform, &mSurfaceDamage,
-                &mCompositionType, &mSidebandStream, &mBuffer,         &mSolidColor,
+                &mDisplayFrame, &mSourceCrop,     &mBufferTransform,      &mBlendMode,
+                &mAlpha,        &mLayerMetadata,  &mVisibleRegion,        &mOutputDataspace,
+                &mPixelFormat,  &mColorTransform, &mCompositionType,      &mSidebandStream,
+                &mBuffer,       &mSolidColor,     &mBackgroundBlurRadius, &mBlurRegions,
         };
     }
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index e6d2b63..4365b93 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -41,7 +41,7 @@
 // as a more efficient representation of parts of the layer stack.
 class Planner {
 public:
-    Planner() : mFlattener(mPredictor) {}
+    Planner();
 
     void setDisplaySize(ui::Size);
 
@@ -76,6 +76,8 @@
 
     std::optional<Predictor::PredictedPlan> mPredictedPlan;
     NonBufferHash mFlattenedHash = 0;
+
+    bool mPredictorEnabled = false;
 };
 
 } // namespace compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index dde8999..d215bda 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -43,6 +43,7 @@
 
     MOCK_CONST_METHOD0(getDebugName, const char*());
     MOCK_CONST_METHOD0(getSequence, int32_t());
+    MOCK_CONST_METHOD0(hasRoundedCorners, bool());
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 5aa53e5..4b4d375 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -36,6 +36,7 @@
     MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
 
     MOCK_METHOD1(setCompositionEnabled, void(bool));
+    MOCK_METHOD1(setLayerCachingEnabled, void(bool));
     MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&));
     MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
     MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
@@ -43,6 +44,7 @@
 
     MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
     MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
+    MOCK_METHOD2(setDisplayBrightness, void(float, float));
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
     MOCK_CONST_METHOD2(dumpPlannerInfo, void(const Vector<String16>&, std::string&));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 2454ff7..358ed5a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -22,6 +22,7 @@
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <gmock/gmock.h>
+#include <cstdint>
 
 namespace android::compositionengine::mock {
 
@@ -39,7 +40,7 @@
     MOCK_METHOD0(editState, impl::OutputLayerCompositionState&());
 
     MOCK_METHOD3(updateCompositionState, void(bool, bool, ui::Transform::RotationFlags));
-    MOCK_METHOD2(writeStateToHWC, void(bool, bool));
+    MOCK_METHOD5(writeStateToHWC, void(bool, bool, uint32_t, bool, bool));
     MOCK_CONST_METHOD0(writeCursorPositionToHWC, void());
 
     MOCK_CONST_METHOD0(getHwcLayer, HWC2::Layer*());
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/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
index 2d9f01b..7e020ee 100644
--- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -36,7 +36,8 @@
             lhs.sourceDataspace == rhs.sourceDataspace &&
             lhs.colorTransform == rhs.colorTransform &&
             lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
-            lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
+            lhs.backgroundBlurRadius == rhs.backgroundBlurRadius &&
+            lhs.stretchEffect == rhs.stretchEffect;
 }
 
 inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
@@ -45,8 +46,7 @@
             lhs.textureTransform == rhs.textureTransform &&
             lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
             lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 &&
-            lhs.maxMasteringLuminance == rhs.maxMasteringLuminance &&
-            lhs.maxContentLuminance == rhs.maxContentLuminance;
+            lhs.maxLuminanceNits == rhs.maxLuminanceNits;
 }
 
 inline bool equalIgnoringBuffer(const renderengine::LayerSettings& lhs,
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index a605fe1..8c8331c 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -267,7 +267,7 @@
     auto& hwc = getCompositionEngine().getHwComposer();
     if (status_t result =
                 hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(),
-                                                &changes);
+                                                getState().earliestPresentTime, &changes);
         result != NO_ERROR) {
         ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
               strerror(-result));
@@ -368,7 +368,7 @@
     }
 
     auto& hwc = getCompositionEngine().getHwComposer();
-    hwc.presentAndGetReleaseFences(*halDisplayIdOpt);
+    hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime);
 
     fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt);
 
diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
index 0cc2c6e..5565396 100644
--- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -101,9 +101,9 @@
 }
 
 void dumpVal(std::string& out, const char* name, const StretchEffect& effect) {
-    StringAppendF(&out, "%s={ area=[%f, %f, %f, %f], vec=(%f, %f), max=%f } ", name,
-                  effect.area.left, effect.area.top, effect.area.right, effect.area.bottom,
-                  effect.vectorX, effect.vectorY, effect.maxAmount);
+    StringAppendF(&out, "%s={ width =%f, height = %f, vec=(%f, %f), max=(%f, %f) } ", name,
+                  effect.width, effect.height,
+                  effect.vectorX, effect.vectorY, effect.maxAmountX, effect.maxAmountY);
 }
 
 } // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index cedc333..f95382d 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -36,7 +36,7 @@
                                   sp<GraphicBuffer>* outBuffer) {
     // default is 0
     if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0 ||
-        slot >= BufferQueue::NUM_BUFFER_SLOTS) {
+        slot >= static_cast<int32_t>(kMaxLayerBufferCount)) {
         *outSlot = 0;
     } else {
         *outSlot = static_cast<uint32_t>(slot);
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 430945a..ff7d430 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -78,6 +78,20 @@
         dumpVal(out, "stretchEffect", stretchEffect);
     }
 
+    if (!blurRegions.empty()) {
+        out.append("\n      blurRegions {");
+        for (const auto& region : blurRegions) {
+            out.append("\n           ");
+            base::StringAppendF(&out,
+                                "{radius=%du, cornerRadii=[%f, %f, %f, %f], alpha=%f, rect=[%d, "
+                                "%d, %d, %d]",
+                                region.blurRadius, region.cornerRadiusTL, region.cornerRadiusTR,
+                                region.cornerRadiusBL, region.cornerRadiusBR, region.alpha,
+                                region.left, region.top, region.right, region.bottom);
+        }
+        out.append("\n      }\n      ");
+    }
+
     if (!metadata.empty()) {
         out.append("\n      metadata {");
         for (const auto& [key, entry] : metadata) {
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index aed3be9..bf36355 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
@@ -54,18 +55,6 @@
 
 namespace impl {
 
-Output::Output() {
-    const bool enableLayerCaching = [] {
-        const bool enable =
-                android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false);
-        return base::GetBoolProperty(std::string("debug.sf.enable_layer_caching"), enable);
-    }();
-
-    if (enableLayerCaching) {
-        mPlanner = std::make_unique<planner::Planner>();
-    }
-}
-
 namespace {
 
 template <typename T>
@@ -134,6 +123,21 @@
     dirtyEntireOutput();
 }
 
+void Output::setLayerCachingEnabled(bool enabled) {
+    if (enabled == (mPlanner != nullptr)) {
+        return;
+    }
+
+    if (enabled) {
+        mPlanner = std::make_unique<planner::Planner>();
+        if (mRenderSurface) {
+            mPlanner->setDisplaySize(mRenderSurface->getSize());
+        }
+    } else {
+        mPlanner.reset();
+    }
+}
+
 void Output::setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                            const Rect& orientedDisplaySpaceRect) {
     auto& outputState = editState();
@@ -255,6 +259,18 @@
     dirtyEntireOutput();
 }
 
+void Output::setDisplayBrightness(float sdrWhitePointNits, float displayBrightnessNits) {
+    auto& outputState = editState();
+    if (outputState.sdrWhitePointNits == sdrWhitePointNits &&
+        outputState.displayBrightnessNits == displayBrightnessNits) {
+        // Nothing changed
+        return;
+    }
+    outputState.sdrWhitePointNits = sdrWhitePointNits;
+    outputState.displayBrightnessNits = displayBrightnessNits;
+    dirtyEntireOutput();
+}
+
 void Output::dump(std::string& out) const {
     using android::base::StringAppendF;
 
@@ -454,12 +470,6 @@
     setReleasedLayers(refreshArgs);
 
     finalizePendingOutputLayers();
-
-    // Generate a simple Z-order values to each visible output layer
-    uint32_t zOrder = 0;
-    for (auto* outputLayer : getOutputLayersOrderedByZ()) {
-        outputLayer->editState().z = zOrder++;
-    }
 }
 
 void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE,
@@ -710,23 +720,47 @@
         return;
     }
 
+    editState().earliestPresentTime = refreshArgs.earliestPresentTime;
+
+    compositionengine::OutputLayer* peekThroughLayer = nullptr;
     sp<GraphicBuffer> previousOverride = nullptr;
+    bool includeGeometry = refreshArgs.updatingGeometryThisFrame;
+    uint32_t z = 0;
+    bool overrideZ = false;
     for (auto* layer : getOutputLayersOrderedByZ()) {
+        if (layer == peekThroughLayer) {
+            // No longer needed, although it should not show up again, so
+            // resetting it is not truly needed either.
+            peekThroughLayer = nullptr;
+
+            // peekThroughLayer was already drawn ahead of its z order.
+            continue;
+        }
         bool skipLayer = false;
-        if (layer->getState().overrideInfo.buffer != nullptr) {
-            if (previousOverride != nullptr &&
-                layer->getState().overrideInfo.buffer == previousOverride) {
+        const auto& overrideInfo = layer->getState().overrideInfo;
+        if (overrideInfo.buffer != nullptr) {
+            if (previousOverride && overrideInfo.buffer->getBuffer() == previousOverride) {
                 ALOGV("Skipping redundant buffer");
                 skipLayer = true;
+            } else {
+                // First layer with the override buffer.
+                if (overrideInfo.peekThroughLayer) {
+                    peekThroughLayer = overrideInfo.peekThroughLayer;
+
+                    // Draw peekThroughLayer first.
+                    overrideZ = true;
+                    includeGeometry = true;
+                    constexpr bool isPeekingThrough = true;
+                    peekThroughLayer->writeStateToHWC(includeGeometry, false, z++, overrideZ,
+                                                      isPeekingThrough);
+                }
+
+                previousOverride = overrideInfo.buffer->getBuffer();
             }
-            previousOverride = layer->getState().overrideInfo.buffer;
         }
 
-        // 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;
-        layer->writeStateToHWC(includeGeometry, skipLayer);
+        constexpr bool isPeekingThrough = false;
+        layer->writeStateToHWC(includeGeometry, skipLayer, z++, overrideZ, isPeekingThrough);
     }
 }
 
@@ -981,14 +1015,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());
@@ -1012,8 +1047,13 @@
     clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
             ? outputState.dataspace
             : ui::Dataspace::UNKNOWN;
-    clientCompositionDisplay.maxLuminance =
-            mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
+
+    // If we have a valid current display brightness use that, otherwise fall back to the
+    // display's max desired
+    clientCompositionDisplay.maxLuminance = outputState.displayBrightnessNits > 0.f
+            ? outputState.displayBrightnessNits
+            : mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
+    clientCompositionDisplay.sdrWhitePointNits = outputState.sdrWhitePointNits;
 
     // Compute the global color transform matrix.
     if (!outputState.usesDeviceComposition && !getSkipColorTransform()) {
@@ -1033,13 +1073,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 +1113,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 +1145,7 @@
     Region stubRegion;
 
     bool disableBlurs = false;
+    sp<GraphicBuffer> previousOverrideBuffer = nullptr;
 
     for (auto* layer : getOutputLayersOrderedByZ()) {
         const auto& layerState = layer->getState();
@@ -1153,8 +1195,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()) {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 3f36a8f..cd14327 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -14,13 +14,16 @@
  * limitations under the License.
  */
 
+#include <DisplayHardware/Hal.h>
 #include <android-base/stringprintf.h>
 #include <compositionengine/DisplayColorProfile.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/Output.h>
+#include <compositionengine/impl/HwcBufferCache.h>
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <cstdint>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
@@ -312,7 +315,8 @@
     }
 }
 
-void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer) {
+void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z,
+                                  bool zIsOverridden, bool isPeekingThrough) {
     const auto& state = getState();
     // Skip doing this if there is no HWC interface
     if (!state.hwc) {
@@ -333,8 +337,13 @@
 
     auto requestedCompositionType = outputIndependentState->compositionType;
 
-    if (includeGeometry) {
-        writeOutputDependentGeometryStateToHWC(hwcLayer.get(), requestedCompositionType);
+    // 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 || isPeekingThrough || zIsOverridden;
+    const bool prevOverridden = state.hwc->stateOverridden;
+    if (isOverridden || prevOverridden || skipLayer || includeGeometry) {
+        writeOutputDependentGeometryStateToHWC(hwcLayer.get(), requestedCompositionType, z);
         writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), *outputIndependentState,
                                                  skipLayer);
     }
@@ -342,14 +351,19 @@
     writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
     writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState);
 
-    writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType);
+    writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough,
+                              skipLayer);
 
     // Always set the layer color after setting the composition type.
     writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
+
+    editState().hwc->stateOverridden = isOverridden;
+    editState().hwc->layerSkipped = skipLayer;
 }
 
-void OutputLayer::writeOutputDependentGeometryStateToHWC(
-        HWC2::Layer* hwcLayer, hal::Composition requestedCompositionType) {
+void OutputLayer::writeOutputDependentGeometryStateToHWC(HWC2::Layer* hwcLayer,
+                                                         hal::Composition requestedCompositionType,
+                                                         uint32_t z) {
     const auto& outputDependentState = getState();
 
     Rect displayFrame = outputDependentState.displayFrame;
@@ -357,7 +371,12 @@
 
     if (outputDependentState.overrideInfo.buffer != nullptr) {
         displayFrame = outputDependentState.overrideInfo.displayFrame;
-        sourceCrop = displayFrame.toFloatRect();
+        sourceCrop =
+                FloatRect(0.f, 0.f,
+                          static_cast<float>(outputDependentState.overrideInfo.buffer->getBuffer()
+                                                     ->getWidth()),
+                          static_cast<float>(outputDependentState.overrideInfo.buffer->getBuffer()
+                                                     ->getHeight()));
     }
 
     ALOGV("Writing display frame [%d, %d, %d, %d]", displayFrame.left, displayFrame.top,
@@ -376,9 +395,9 @@
               sourceCrop.bottom, to_string(error).c_str(), static_cast<int32_t>(error));
     }
 
-    if (auto error = hwcLayer->setZOrder(outputDependentState.z); error != hal::Error::NONE) {
-        ALOGE("[%s] Failed to set Z %u: %s (%d)", getLayerFE().getDebugName(),
-              outputDependentState.z, to_string(error).c_str(), static_cast<int32_t>(error));
+    if (auto error = hwcLayer->setZOrder(z); error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set Z %u: %s (%d)", getLayerFE().getDebugName(), z,
+              to_string(error).c_str(), static_cast<int32_t>(error));
     }
 
     // Solid-color layers and overridden buffers should always use an identity transform.
@@ -397,7 +416,10 @@
 void OutputLayer::writeOutputIndependentGeometryStateToHWC(
         HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState,
         bool skipLayer) {
-    const auto blendMode = getState().overrideInfo.buffer
+    // If there is a peekThroughLayer, then this layer has a hole in it. We need to use
+    // PREMULTIPLIED so it will peek through.
+    const auto& overrideInfo = getState().overrideInfo;
+    const auto blendMode = overrideInfo.buffer || overrideInfo.peekThroughLayer
             ? hardware::graphics::composer::hal::BlendMode::PREMULTIPLIED
             : outputIndependentState.blendMode;
     if (auto error = hwcLayer->setBlendMode(blendMode); error != hal::Error::NONE) {
@@ -463,7 +485,8 @@
 
     const Region& surfaceDamage = getState().overrideInfo.buffer
             ? getState().overrideInfo.damageRegion
-            : outputIndependentState.surfaceDamage;
+            : (getState().hwc->stateOverridden ? Region::INVALID_REGION
+                                               : outputIndependentState.surfaceDamage);
 
     if (auto error = hwcLayer->setSurfaceDamage(surfaceDamage); error != hal::Error::NONE) {
         ALOGE("[%s] Failed to set surface damage: %s (%d)", getLayerFE().getDebugName(),
@@ -530,9 +553,11 @@
 
     sp<GraphicBuffer> buffer = outputIndependentState.buffer;
     sp<Fence> acquireFence = outputIndependentState.acquireFence;
+    int slot = outputIndependentState.bufferSlot;
     if (getState().overrideInfo.buffer != nullptr) {
-        buffer = getState().overrideInfo.buffer;
+        buffer = getState().overrideInfo.buffer->getBuffer();
         acquireFence = getState().overrideInfo.acquireFence;
+        slot = HwcBufferCache::FLATTENER_CACHING_SLOT;
     }
 
     ALOGV("Writing buffer %p", buffer.get());
@@ -541,8 +566,7 @@
     sp<GraphicBuffer> hwcBuffer;
     // We need access to the output-dependent state for the buffer cache there,
     // though otherwise the buffer is not output-dependent.
-    editState().hwc->hwcBufferCache.getHwcBuffer(outputIndependentState.bufferSlot, buffer,
-                                                 &hwcSlot, &hwcBuffer);
+    editState().hwc->hwcBufferCache.getHwcBuffer(slot, buffer, &hwcSlot, &hwcBuffer);
 
     if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
         error != hal::Error::NONE) {
@@ -552,16 +576,20 @@
 }
 
 void OutputLayer::writeCompositionTypeToHWC(HWC2::Layer* hwcLayer,
-                                            hal::Composition requestedCompositionType) {
+                                            hal::Composition requestedCompositionType,
+                                            bool isPeekingThrough, bool skipLayer) {
     auto& outputDependentState = editState();
 
-    // If we are forcing client composition, we need to tell the HWC
-    if (outputDependentState.forceClientComposition) {
+    if (isClientCompositionForced(isPeekingThrough)) {
+        // If we are forcing client composition, we need to tell the HWC
         requestedCompositionType = hal::Composition::CLIENT;
     }
 
     // Set the requested composition type with the HWC whenever it changes
-    if (outputDependentState.hwc->hwcCompositionType != requestedCompositionType) {
+    // We also resend the composition type when this layer was previously skipped, to ensure that
+    // the composition type is up-to-date.
+    if (outputDependentState.hwc->hwcCompositionType != requestedCompositionType ||
+        (outputDependentState.hwc->layerSkipped && !skipLayer)) {
         outputDependentState.hwc->hwcCompositionType = requestedCompositionType;
 
         if (auto error = hwcLayer->setCompositionType(requestedCompositionType);
@@ -641,12 +669,22 @@
     }
 }
 
+bool OutputLayer::isClientCompositionForced(bool isPeekingThrough) const {
+    return getState().forceClientComposition ||
+            (!isPeekingThrough && getLayerFE().hasRoundedCorners());
+}
+
 void OutputLayer::applyDeviceCompositionTypeChange(hal::Composition compositionType) {
     auto& state = editState();
     LOG_FATAL_IF(!state.hwc);
     auto& hwcState = *state.hwc;
 
-    detectDisallowedCompositionTypeChange(hwcState.hwcCompositionType, compositionType);
+    // Only detected disallowed changes if this was not a skip layer, because the
+    // validated composition type may be arbitrary (usually DEVICE, to reflect that there were
+    // fewer GPU layers)
+    if (!hwcState.layerSkipped) {
+        detectDisallowedCompositionTypeChange(hwcState.hwcCompositionType, compositionType);
+    }
 
     hwcState.hwcCompositionType = compositionType;
 }
@@ -693,7 +731,7 @@
     settings.geometry = renderengine::Geometry{
             .boundaries = boundaries.toFloatRect(),
     };
-    settings.bufferId = getState().overrideInfo.buffer->getId();
+    settings.bufferId = getState().overrideInfo.buffer->getBuffer()->getId();
     settings.source = renderengine::PixelSource{
             .buffer = renderengine::Buffer{
                     .buffer = getState().overrideInfo.buffer,
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index efd23dc..b4c314c 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -67,7 +67,6 @@
     dumpVal(out, "sourceCrop", sourceCrop);
     dumpVal(out, "bufferTransform", toString(bufferTransform), bufferTransform);
     dumpVal(out, "dataspace", toString(dataspace), dataspace);
-    dumpVal(out, "z-index", z);
     dumpVal(out, "override buffer", overrideInfo.buffer.get());
     dumpVal(out, "override acquire fence", overrideInfo.acquireFence.get());
     dumpVal(out, "override display frame", overrideInfo.displayFrame);
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 dcb7555..63085cc 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -130,7 +130,7 @@
 }
 
 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 {
@@ -158,8 +158,12 @@
     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()),
+            .physicalDisplay = Rect(-mBounds.left + outputState.framebufferSpace.content.left,
+                                    -mBounds.top + outputState.framebufferSpace.content.top,
+                                    -mBounds.left + outputState.framebufferSpace.content.right,
+                                    -mBounds.top + outputState.framebufferSpace.content.bottom),
             .clip = viewport,
             .outputDataspace = outputDataspace,
             .orientation = orientation,
@@ -193,6 +197,23 @@
     std::transform(layerSettings.cbegin(), layerSettings.cend(),
                    std::back_inserter(layerSettingsPointers),
                    [](const renderengine::LayerSettings& settings) { return &settings; });
+    renderengine::LayerSettings holePunchSettings;
+    if (mHolePunchLayer) {
+        auto clientCompositionList =
+                mHolePunchLayer->getOutputLayer()->getLayerFE().prepareClientCompositionList(
+                        targetSettings);
+        // Assume that the final layer contains the buffer that we want to
+        // replace with a hole punch.
+        holePunchSettings = clientCompositionList.back();
+        LOG_ALWAYS_FATAL_IF(!holePunchSettings.source.buffer.buffer, "Expected to have a buffer!");
+        // This mimics Layer::prepareClearClientComposition
+        holePunchSettings.source.buffer.buffer = nullptr;
+        holePunchSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f);
+        holePunchSettings.disableBlending = true;
+        holePunchSettings.alpha = 0.0f;
+        holePunchSettings.name = std::string("hole punch layer");
+        layerSettingsPointers.push_back(&holePunchSettings);
+    }
 
     if (sDebugHighlighLayers) {
         highlight = {
@@ -217,24 +238,83 @@
     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);
+        mOutputSpace = outputState.framebufferSpace;
+        mTexture = std::move(texture);
         mOutputSpace.orientation = outputState.framebufferSpace.orientation;
         mOutputDataspace = outputDataspace;
         mOrientation = orientation;
+    } else {
+        mTexture = nullptr;
     }
 }
 
+bool CachedSet::requiresHolePunch() const {
+    // In order for the hole punch to be beneficial, the layer must be updating
+    // regularly, meaning  it should not have been merged with other layers.
+    if (getLayerCount() != 1) {
+        return false;
+    }
+
+    // There is no benefit to a hole punch unless the layer has a buffer.
+    if (!mLayers[0].getBuffer()) {
+        return false;
+    }
+
+    const auto& layerFE = mLayers[0].getState()->getOutputLayer()->getLayerFE();
+    if (layerFE.getCompositionState()->forceClientComposition) {
+        return false;
+    }
+
+    return layerFE.hasRoundedCorners();
+}
+
+bool CachedSet::hasBlurBehind() const {
+    return std::any_of(mLayers.cbegin(), mLayers.cend(),
+                       [](const Layer& layer) { return layer.getState()->hasBlurBehind(); });
+}
+
+namespace {
+bool contains(const Rect& outer, const Rect& inner) {
+    return outer.left <= inner.left && outer.right >= inner.right && outer.top <= inner.top &&
+            outer.bottom >= inner.bottom;
+}
+}; // namespace
+
+void CachedSet::addHolePunchLayerIfFeasible(const CachedSet& holePunchLayer, bool isFirstLayer) {
+    // Verify that this CachedSet is opaque where the hole punch layer
+    // will draw.
+    const Rect& holePunchBounds = holePunchLayer.getBounds();
+    for (const auto& layer : mLayers) {
+        // The first layer is considered opaque because nothing is behind it.
+        // Note that isOpaque is always false for a layer with rounded
+        // corners, even if the interior is opaque. In theory, such a layer
+        // could be used for a hole punch, but this is unlikely to happen in
+        // practice.
+        const auto* outputLayer = layer.getState()->getOutputLayer();
+        if (contains(outputLayer->getState().displayFrame, holePunchBounds) &&
+            (isFirstLayer || outputLayer->getLayerFE().getCompositionState()->isOpaque)) {
+            mHolePunchLayer = holePunchLayer.getFirstLayer().getState();
+            return;
+        }
+    }
+}
+
+compositionengine::OutputLayer* CachedSet::getHolePunchLayer() const {
+    return mHolePunchLayer ? mHolePunchLayer->getOutputLayer() : nullptr;
+}
+
 void CachedSet::dump(std::string& result) const {
     const auto now = std::chrono::steady_clock::now();
 
@@ -242,6 +322,11 @@
             std::chrono::duration_cast<std::chrono::milliseconds>(now - mLastUpdate);
     base::StringAppendF(&result, "  + Fingerprint %016zx, last update %sago, age %zd\n",
                         mFingerprint, durationString(lastUpdate).c_str(), mAge);
+    {
+        const auto b = mTexture ? mTexture->getBuffer().get() : nullptr;
+        base::StringAppendF(&result, "    Override buffer: %p\n", b);
+    }
+    base::StringAppendF(&result, "    HolePunchLayer: %p\n", mHolePunchLayer);
 
     if (mLayers.size() == 1) {
         base::StringAppendF(&result, "    Layer [%s]\n", mLayers[0].getName().c_str());
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index ffca5ba..294ec4b 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -20,19 +20,53 @@
 
 #include <compositionengine/impl/planner/Flattener.h>
 #include <compositionengine/impl/planner/LayerState.h>
-#include <compositionengine/impl/planner/Predictor.h>
 
 using time_point = std::chrono::steady_clock::time_point;
 using namespace std::chrono_literals;
 
 namespace android::compositionengine::impl::planner {
 
+namespace {
+
+// True if the underlying layer stack is the same modulo state that would be expected to be
+// different like specific buffers, false otherwise.
+bool isSameStack(const std::vector<const LayerState*>& incomingLayers,
+                 const std::vector<CachedSet>& cachedSets) {
+    std::vector<const LayerState*> existingLayers;
+    for (auto& cachedSet : cachedSets) {
+        for (auto& layer : cachedSet.getConstituentLayers()) {
+            existingLayers.push_back(layer.getState());
+        }
+    }
+
+    if (incomingLayers.size() != existingLayers.size()) {
+        return false;
+    }
+
+    for (size_t i = 0; i < incomingLayers.size(); i++) {
+        // Checking the IDs here is very strict, but we do this as otherwise we may mistakenly try
+        // to access destroyed OutputLayers later on.
+        if (incomingLayers[i]->getId() != existingLayers[i]->getId() ||
+            incomingLayers[i]->getDifferingFields(*(existingLayers[i])) != LayerStateField::None) {
+            return false;
+        }
+    }
+    return true;
+}
+
+} // namespace
+
 NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers,
                                        NonBufferHash hash, time_point now) {
     const size_t unflattenedDisplayCost = calculateDisplayCost(layers);
     mUnflattenedDisplayCost += unflattenedDisplayCost;
 
-    if (mCurrentGeometry != hash) {
+    // We invalidate the layer cache if:
+    // 1. We're not tracking any layers, or
+    // 2. The last seen hashed geometry changed between frames, or
+    // 3. A stricter equality check demonstrates that the layer stack really did change, since the
+    // hashed geometry does not guarantee uniqueness.
+    if (mCurrentGeometry != hash || (!mLayers.empty() && !isSameStack(layers, mLayers))) {
         resetActivities(hash, now);
         mFlattenedDisplayCost += unflattenedDisplayCost;
         return hash;
@@ -40,13 +74,17 @@
 
     ++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;
 }
@@ -60,6 +98,14 @@
     mNewCachedSet->render(renderEngine, outputState);
 }
 
+void Flattener::dumpLayers(std::string& result) const {
+    result.append("  Current layers:");
+    for (const CachedSet& layer : mLayers) {
+        result.append("\n");
+        layer.dump(result);
+    }
+}
+
 void Flattener::dump(std::string& result) const {
     const auto now = std::chrono::steady_clock::now();
 
@@ -79,9 +125,11 @@
         return left.first < right.first;
     };
 
-    const size_t maxLayerCount = std::max_element(mInitialLayerCounts.cbegin(),
-                                                  mInitialLayerCounts.cend(), compareLayerCounts)
-                                         ->first;
+    const size_t maxLayerCount = mInitialLayerCounts.empty()
+            ? 0u
+            : std::max_element(mInitialLayerCounts.cbegin(), mInitialLayerCounts.cend(),
+                               compareLayerCounts)
+                      ->first;
 
     result.append("\n    Initial counts:\n");
     for (size_t count = 1; count < maxLayerCount; ++count) {
@@ -104,11 +152,7 @@
     base::StringAppendF(&result, "\n  Current hash %016zx, last update %sago\n\n", mCurrentGeometry,
                         durationString(lastUpdate).c_str());
 
-    result.append("  Current layers:");
-    for (const CachedSet& layer : mLayers) {
-        result.append("\n");
-        layer.dump(result);
-    }
+    dumpLayers(result);
 }
 
 size_t Flattener::calculateDisplayCost(const std::vector<const LayerState*>& layers) const {
@@ -157,14 +201,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;
 
@@ -193,7 +240,8 @@
     auto currentLayerIter = mLayers.begin();
     auto incomingLayerIter = layers.begin();
     while (incomingLayerIter != layers.end()) {
-        if (mNewCachedSet && mNewCachedSet->getFingerprint() == (*incomingLayerIter)->getHash()) {
+        if (mNewCachedSet &&
+            mNewCachedSet->getFirstLayer().getState()->getId() == (*incomingLayerIter)->getId()) {
             if (mNewCachedSet->hasBufferUpdate()) {
                 ALOGV("[%s] Dropping new cached set", __func__);
                 ++mInvalidatedCachedSetAges[0];
@@ -202,6 +250,7 @@
                 ALOGV("[%s] Found ready buffer", __func__);
                 size_t skipCount = mNewCachedSet->getLayerCount();
                 while (skipCount != 0) {
+                    auto* peekThroughLayer = mNewCachedSet->getHolePunchLayer();
                     const size_t layerCount = currentLayerIter->getLayerCount();
                     for (size_t i = 0; i < layerCount; ++i) {
                         OutputLayer::CompositionState& state =
@@ -214,6 +263,7 @@
                                 .displaySpace = mNewCachedSet->getOutputSpace(),
                                 .damageRegion = Region::INVALID_REGION,
                                 .visibleRegion = mNewCachedSet->getVisibleRegion(),
+                                .peekThroughLayer = peekThroughLayer,
                         };
                         ++incomingLayerIter;
                     }
@@ -237,6 +287,7 @@
 
             // Skip the incoming layers corresponding to this valid current layer
             const size_t layerCount = currentLayerIter->getLayerCount();
+            auto* peekThroughLayer = currentLayerIter->getHolePunchLayer();
             for (size_t i = 0; i < layerCount; ++i) {
                 OutputLayer::CompositionState& state =
                         (*incomingLayerIter)->getOutputLayer()->editState();
@@ -248,6 +299,7 @@
                         .displaySpace = currentLayerIter->getOutputSpace(),
                         .damageRegion = Region(),
                         .visibleRegion = currentLayerIter->getVisibleRegion(),
+                        .peekThroughLayer = peekThroughLayer,
                 };
                 ++incomingLayerIter;
             }
@@ -272,71 +324,101 @@
     }
 
     mLayers = std::move(merged);
-    updateLayersHash();
     return true;
 }
 
-void Flattener::buildCachedSets(time_point now) {
-    struct Run {
-        Run(std::vector<CachedSet>::const_iterator start, size_t length)
-              : start(start), length(length) {}
-
-        std::vector<CachedSet>::const_iterator start;
-        size_t length;
-    };
-
-    if (mLayers.empty()) {
-        ALOGV("[%s] No layers found, returning", __func__);
-        return;
-    }
-
+std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const {
     std::vector<Run> runs;
     bool isPartOfRun = false;
+    Run::Builder builder;
+    bool firstLayer = true;
+    bool runHasFirstLayer = false;
+
     for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
-        if (now - currentSet->getLastUpdate() > kActiveLayerTimeout) {
-            // Layer is inactive
+        const bool layerIsInactive = now - currentSet->getLastUpdate() > kActiveLayerTimeout;
+        const bool layerHasBlur = currentSet->hasBlurBehind();
+        if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur)) {
             if (isPartOfRun) {
-                runs.back().length += currentSet->getLayerCount();
+                builder.append(currentSet->getLayerCount());
             } else {
                 // Runs can't start with a non-buffer layer
                 if (currentSet->getFirstLayer().getBuffer() == nullptr) {
                     ALOGV("[%s] Skipping initial non-buffer layer", __func__);
                 } else {
-                    runs.emplace_back(currentSet, currentSet->getLayerCount());
+                    builder.init(currentSet);
+                    if (firstLayer) {
+                        runHasFirstLayer = true;
+                    }
                     isPartOfRun = true;
                 }
             }
-        } else {
-            // Runs must be at least 2 sets long or there's nothing to combine
-            if (isPartOfRun && runs.back().start->getLayerCount() == runs.back().length) {
-                runs.pop_back();
+        } else if (isPartOfRun) {
+            builder.setHolePunchCandidate(&(*currentSet));
+            if (auto run = builder.validateAndBuild(); run) {
+                runs.push_back(*run);
             }
 
+            runHasFirstLayer = false;
+            builder.reset();
             isPartOfRun = false;
         }
+
+        firstLayer = false;
     }
 
-    // Check for at least 2 sets one more time in case the set includes the last layer
-    if (isPartOfRun && runs.back().start->getLayerCount() == runs.back().length) {
-        runs.pop_back();
+    // If we're in the middle of a run at the end, we still need to validate and build it.
+    if (isPartOfRun) {
+        if (auto run = builder.validateAndBuild(); run) {
+            runs.push_back(*run);
+        }
     }
 
     ALOGV("[%s] Found %zu candidate runs", __func__, runs.size());
 
+    return runs;
+}
+
+std::optional<Flattener::Run> Flattener::findBestRun(std::vector<Flattener::Run>& runs) const {
     if (runs.empty()) {
+        return std::nullopt;
+    }
+
+    // TODO (b/181192467): Choose the best run, instead of just the first.
+    return runs[0];
+}
+
+void Flattener::buildCachedSets(time_point now) {
+    if (mLayers.empty()) {
+        ALOGV("[%s] No layers found, returning", __func__);
         return;
     }
 
-    mNewCachedSet.emplace(*runs[0].start);
+    std::vector<Run> runs = findCandidateRuns(now);
+
+    std::optional<Run> bestRun = findBestRun(runs);
+
+    if (!bestRun) {
+        return;
+    }
+
+    mNewCachedSet.emplace(*bestRun->getStart());
     mNewCachedSet->setLastUpdate(now);
-    auto currentSet = runs[0].start;
-    while (mNewCachedSet->getLayerCount() < runs[0].length) {
+    auto currentSet = bestRun->getStart();
+    while (mNewCachedSet->getLayerCount() < bestRun->getLayerLength()) {
         ++currentSet;
         mNewCachedSet->append(*currentSet);
     }
 
+    if (mEnableHolePunch && bestRun->getHolePunchCandidate() &&
+        bestRun->getHolePunchCandidate()->requiresHolePunch()) {
+        // Add the pip layer to mNewCachedSet, but in a special way - it should
+        // replace the buffer with a clear round rect.
+        mNewCachedSet->addHolePunchLayerIfFeasible(*bestRun->getHolePunchCandidate(),
+                                                   bestRun->getStart() == mLayers.cbegin());
+    }
+
     // TODO(b/181192467): Actually compute new LayerState vector and corresponding hash for each run
-    mPredictor.getPredictedPlan({}, 0);
+    // and feedback into the predictor
 
     ++mCachedSetCreationCount;
     mCachedSetCreationCost += mNewCachedSet->getCreationCost();
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index 7c32e94..8423a12 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -155,13 +155,11 @@
 
 bool operator==(const LayerState& lhs, const LayerState& rhs) {
     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.mLayerMetadata == rhs.mLayerMetadata &&
-            lhs.mVisibleRegion == rhs.mVisibleRegion &&
+            lhs.mSourceCrop == rhs.mSourceCrop && lhs.mBufferTransform == rhs.mBufferTransform &&
+            lhs.mBlendMode == rhs.mBlendMode && 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.mSurfaceDamage == rhs.mSurfaceDamage &&
             lhs.mCompositionType == rhs.mCompositionType &&
             lhs.mSidebandStream == rhs.mSidebandStream && lhs.mBuffer == rhs.mBuffer &&
             (lhs.mCompositionType.get() != hal::Composition::SOLID_COLOR ||
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index ad75557..c26eb1e 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -19,12 +19,22 @@
 #undef LOG_TAG
 #define LOG_TAG "Planner"
 
+#include <android-base/properties.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/impl/planner/Planner.h>
 
 namespace android::compositionengine::impl::planner {
 
+Planner::Planner()
+      : mFlattener(base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), false)) {
+    // Implicitly, layer caching must also be enabled.
+    // E.g., setprop debug.sf.enable_layer_caching 1, or
+    // adb shell service call SurfaceFlinger 1040 i32 1 [i64 <display ID>]
+    mPredictorEnabled =
+            base::GetBoolProperty(std::string("debug.sf.enable_planner_prediction"), false);
+}
+
 void Planner::setDisplaySize(ui::Size size) {
     mFlattener.setDisplaySize(size);
 }
@@ -94,24 +104,35 @@
     const bool layersWereFlattened = hash != mFlattenedHash;
     ALOGV("[%s] Initial hash %zx flattened hash %zx", __func__, hash, mFlattenedHash);
 
-    mPredictedPlan =
-            mPredictor.getPredictedPlan(layersWereFlattened ? std::vector<const LayerState*>()
-                                                            : mCurrentLayers,
-                                        mFlattenedHash);
-    if (mPredictedPlan) {
-        ALOGV("[%s] Predicting plan %s", __func__, to_string(mPredictedPlan->plan).c_str());
-    } else {
-        ALOGV("[%s] No prediction found\n", __func__);
+    if (mPredictorEnabled) {
+        mPredictedPlan =
+                mPredictor.getPredictedPlan(layersWereFlattened ? std::vector<const LayerState*>()
+                                                                : mCurrentLayers,
+                                            mFlattenedHash);
+        if (mPredictedPlan) {
+            ALOGV("[%s] Predicting plan %s", __func__, to_string(mPredictedPlan->plan).c_str());
+        } else {
+            ALOGV("[%s] No prediction found\n", __func__);
+        }
     }
 }
 
 void Planner::reportFinalPlan(
         compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
+    if (!mPredictorEnabled) {
+        return;
+    }
+
     Plan finalPlan;
     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;
@@ -175,7 +196,9 @@
                 return;
             }
 
-            mPredictor.compareLayerStacks(leftHash, rightHash, result);
+            if (mPredictorEnabled) {
+                mPredictor.compareLayerStacks(leftHash, rightHash, result);
+            }
         } else if (command == "--describe" || command == "-d") {
             if (args.size() < 3) {
                 base::StringAppendF(&result,
@@ -199,7 +222,9 @@
                 return;
             }
 
-            mPredictor.describeLayerStack(hash, result);
+            if (mPredictorEnabled) {
+                mPredictor.describeLayerStack(hash, result);
+            }
         } else if (command == "--help" || command == "-h") {
             dumpUsage(result);
         } else if (command == "--similar" || command == "-s") {
@@ -222,7 +247,11 @@
                 return;
             }
 
-            mPredictor.listSimilarStacks(*plan, result);
+            if (mPredictorEnabled) {
+                mPredictor.listSimilarStacks(*plan, result);
+            }
+        } else if (command == "--layers" || command == "-l") {
+            mFlattener.dumpLayers(result);
         } else {
             base::StringAppendF(&result, "Unknown command '%s'\n\n", command.string());
             dumpUsage(result);
@@ -235,7 +264,9 @@
     mFlattener.dump(result);
     result.append("\n");
 
-    mPredictor.dump(result);
+    if (mPredictorEnabled) {
+        mPredictor.dump(result);
+    }
 }
 
 void Planner::dumpUsage(std::string& result) const {
@@ -258,6 +289,9 @@
 
     result.append("[--similar|-s] <plan>\n");
     result.append("  Prints the example layer names for similar stacks matching <plan>\n");
+
+    result.append("[--layers|-l]\n");
+    result.append("  Prints the current layers\n");
 }
 
 } // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 8a83639..e63d09a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -627,7 +627,7 @@
 TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) {
     EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(mHwComposer,
-                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _))
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _))
             .WillOnce(Return(INVALID_OPERATION));
 
     mDisplay->chooseCompositionStrategy();
@@ -649,7 +649,8 @@
             .InSequence(s)
             .WillOnce(Return(false));
 
-    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _))
+    EXPECT_CALL(mHwComposer,
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
 
@@ -679,8 +680,9 @@
             .InSequence(s)
             .WillOnce(Return(false));
 
-    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _))
-            .WillOnce(DoAll(SetArgPointee<2>(changes), Return(NO_ERROR)));
+    EXPECT_CALL(mHwComposer,
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _))
+            .WillOnce(DoAll(SetArgPointee<3>(changes), Return(NO_ERROR)));
     EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
     EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
     EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(changes.layerRequests)).Times(1);
@@ -867,7 +869,8 @@
     sp<Fence> layer1Fence = new Fence();
     sp<Fence> layer2Fence = new Fence();
 
-    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
+    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID), _))
+            .Times(1);
     EXPECT_CALL(mHwComposer, getPresentFence(HalDisplayId(DEFAULT_DISPLAY_ID)))
             .WillOnce(Return(presentFence));
     EXPECT_CALL(mHwComposer,
@@ -1039,7 +1042,7 @@
 
     mDisplay->editState().isEnabled = true;
 
-    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_));
+    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_, _));
     EXPECT_CALL(*mDisplaySurface, onFrameCommitted());
 
     mDisplay->postFramebuffer();
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index bac894a..74d4b92 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -50,13 +50,14 @@
     MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
     MOCK_METHOD1(createLayer, HWC2::Layer*(HalDisplayId));
     MOCK_METHOD2(destroyLayer, void(HalDisplayId, HWC2::Layer*));
-    MOCK_METHOD3(getDeviceCompositionChanges,
-                 status_t(HalDisplayId, bool,
+    MOCK_METHOD4(getDeviceCompositionChanges,
+                 status_t(HalDisplayId, bool, std::chrono::steady_clock::time_point,
                           std::optional<android::HWComposer::DeviceRequestedChanges>*));
     MOCK_METHOD5(setClientTarget,
                  status_t(HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
                           ui::Dataspace));
-    MOCK_METHOD1(presentAndGetReleaseFences, status_t(HalDisplayId));
+    MOCK_METHOD2(presentAndGetReleaseFences,
+                 status_t(HalDisplayId, std::chrono::steady_clock::time_point));
     MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode));
     MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t));
     MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 8a4d161..5bd1216 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <compositionengine/impl/HwcBufferCache.h>
 #include <compositionengine/impl/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/mock/CompositionEngine.h>
@@ -23,6 +24,8 @@
 #include <gtest/gtest.h>
 #include <log/log.h>
 
+#include <renderengine/mock/RenderEngine.h>
+#include <ui/PixelFormat.h>
 #include "MockHWC2.h"
 #include "MockHWComposer.h"
 #include "RegionMatcher.h"
@@ -688,7 +691,6 @@
 struct OutputLayerWriteStateToHWCTest : public OutputLayerTest {
     static constexpr hal::Error kError = hal::Error::UNSUPPORTED;
     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 =
@@ -701,12 +703,14 @@
     static constexpr ui::Dataspace kOverrideDataspace = static_cast<ui::Dataspace>(72);
     static constexpr int kSupportedPerFrameMetadata = 101;
     static constexpr int kExpectedHwcSlot = 0;
+    static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::FLATTENER_CACHING_SLOT;
     static constexpr bool kLayerGenericMetadata1Mandatory = true;
     static constexpr bool kLayerGenericMetadata2Mandatory = true;
 
     static const half4 kColor;
     static const Rect kDisplayFrame;
     static const Rect kOverrideDisplayFrame;
+    static const FloatRect kOverrideSourceCrop;
     static const Region kOutputSpaceVisibleRegion;
     static const Region kOverrideVisibleRegion;
     static const mat4 kColorTransform;
@@ -729,7 +733,6 @@
 
         outputLayerState.displayFrame = kDisplayFrame;
         outputLayerState.sourceCrop = kSourceCrop;
-        outputLayerState.z = kZOrder;
         outputLayerState.bufferTransform = static_cast<Hwc2::Transform>(kBufferTransform);
         outputLayerState.outputSpaceVisibleRegion = kOutputSpaceVisibleRegion;
         outputLayerState.dataspace = kDataspace;
@@ -764,7 +767,11 @@
     void includeOverrideInfo() {
         auto& overrideInfo = mOutputLayer.editState().overrideInfo;
 
-        overrideInfo.buffer = kOverrideBuffer;
+        overrideInfo.buffer = std::make_shared<
+                renderengine::ExternalTexture>(kOverrideBuffer, mRenderEngine,
+                                               renderengine::ExternalTexture::Usage::READABLE |
+                                                       renderengine::ExternalTexture::Usage::
+                                                               WRITEABLE);
         overrideInfo.acquireFence = kOverrideFence;
         overrideInfo.displayFrame = kOverrideDisplayFrame;
         overrideInfo.dataspace = kOverrideDataspace;
@@ -779,7 +786,7 @@
                                    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, setZOrder(_)).WillOnce(Return(kError));
         EXPECT_CALL(*mHwcLayer, setTransform(bufferTransform)).WillOnce(Return(kError));
 
         EXPECT_CALL(*mHwcLayer, setBlendMode(blendMode)).WillOnce(Return(kError));
@@ -819,10 +826,11 @@
         EXPECT_CALL(*mHwcLayer, setSidebandStream(kSidebandStreamHandle));
     }
 
-    void expectSetHdrMetadataAndBufferCalls(sp<GraphicBuffer> buffer = kBuffer,
+    void expectSetHdrMetadataAndBufferCalls(uint32_t hwcSlot = kExpectedHwcSlot,
+                                            sp<GraphicBuffer> buffer = kBuffer,
                                             sp<Fence> fence = kFence) {
         EXPECT_CALL(*mHwcLayer, setPerFrameMetadata(kSupportedPerFrameMetadata, kHdrMetadata));
-        EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, buffer, fence));
+        EXPECT_CALL(*mHwcLayer, setBuffer(hwcSlot, buffer, fence));
     }
 
     void expectGenericLayerMetadataCalls() {
@@ -839,12 +847,14 @@
 
     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 FloatRect OutputLayerWriteStateToHWCTest::kOverrideSourceCrop{0.f, 0.f, 4.f, 5.f};
 const Region OutputLayerWriteStateToHWCTest::kOutputSpaceVisibleRegion{
         Rect{1005, 1006, 1007, 1008}};
 const Region OutputLayerWriteStateToHWCTest::kOverrideVisibleRegion{Rect{1006, 1007, 1008, 1009}};
@@ -858,7 +868,10 @@
 native_handle_t* OutputLayerWriteStateToHWCTest::kSidebandStreamHandle =
         reinterpret_cast<native_handle_t*>(1031);
 const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer;
-const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kOverrideBuffer = new GraphicBuffer();
+const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kOverrideBuffer =
+        new GraphicBuffer(4, 5, PIXEL_FORMAT_RGBA_8888,
+                          AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
+                                  AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
 const sp<Fence> OutputLayerWriteStateToHWCTest::kFence;
 const sp<Fence> OutputLayerWriteStateToHWCTest::kOverrideFence = new Fence();
 const std::string OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Key =
@@ -872,19 +885,22 @@
 TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoFECompositionState) {
     EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
 
-    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) {
     mOutputLayer.editState().hwc.reset();
 
-    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCLayer) {
     mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc(nullptr);
 
-    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, canSetAllState) {
@@ -892,8 +908,10 @@
     expectPerFrameCommonCalls();
 
     expectNoSetCompositionTypeCall();
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
 
-    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
 TEST_F(OutputLayerTest, displayInstallOrientationBufferTransformSetTo90) {
@@ -915,6 +933,7 @@
     mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
 
     expectPerFrameCommonCalls();
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
 
     // Setting the composition type should happen before setting the color. We
     // check this in this test only by setting up an testing::InSeqeuence
@@ -923,7 +942,8 @@
     expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SOLID_COLOR);
     expectSetColorCall();
 
-    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) {
@@ -933,7 +953,10 @@
     expectSetSidebandHandleCall();
     expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SIDEBAND);
 
-    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) {
@@ -943,7 +966,10 @@
     expectSetHdrMetadataAndBufferCalls();
     expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CURSOR);
 
-    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) {
@@ -953,7 +979,10 @@
     expectSetHdrMetadataAndBufferCalls();
     expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
 
-    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) {
@@ -966,7 +995,10 @@
     expectSetColorCall();
     expectNoSetCompositionTypeCall();
 
-    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) {
@@ -976,7 +1008,8 @@
     expectSetColorCall();
     expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
 
-    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) {
@@ -988,7 +1021,8 @@
     expectSetColorCall();
     expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
 
-    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, allStateIncludesMetadataIfPresent) {
@@ -1001,7 +1035,10 @@
     expectGenericLayerMetadataCalls();
     expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
 
-    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPresent) {
@@ -1012,21 +1049,137 @@
     expectSetHdrMetadataAndBufferCalls();
     expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
 
-    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) {
     mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
     includeOverrideInfo();
 
-    expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideDisplayFrame.toFloatRect(),
-                              kOverrideBufferTransform, kOverrideBlendMode, kOverrideAlpha);
+    expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
+                              kOverrideBlendMode, kOverrideAlpha);
     expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
                               kOverrideSurfaceDamage);
-    expectSetHdrMetadataAndBufferCalls(kOverrideBuffer, kOverrideFence);
+    expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, previousOverriddenLayerSendsSurfaceDamage) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mOutputLayer.editState().hwc->stateOverridden = true;
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
+                              Region::INVALID_REGION);
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedDeviceCompositionInfo) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mOutputLayer.editState().hwc->stateOverridden = true;
+    mOutputLayer.editState().hwc->layerSkipped = true;
+    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
+                              Region::INVALID_REGION);
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedClientCompositionInfo) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mOutputLayer.editState().forceClientComposition = true;
+    mOutputLayer.editState().hwc->stateOverridden = true;
+    mOutputLayer.editState().hwc->layerSkipped = true;
+    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT;
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
+                              Region::INVALID_REGION);
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, peekThroughChangesBlendMode) {
+    auto peekThroughLayerFE = sp<compositionengine::mock::LayerFE>::make();
+    OutputLayer peekThroughLayer{mOutput, peekThroughLayerFE};
+
+    mOutputLayer.mState.overrideInfo.peekThroughLayer = &peekThroughLayer;
+
+    expectGeometryCommonCalls(kDisplayFrame, kSourceCrop, kBufferTransform,
+                              Hwc2::IComposerClient::BlendMode::PREMULTIPLIED);
+    expectPerFrameCommonCalls();
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, isPeekingThroughSetsOverride) {
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ true);
+    EXPECT_TRUE(mOutputLayer.getState().hwc->stateOverridden);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, zIsOverriddenSetsOverride) {
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ true, /*isPeekingThrough*/
+                                 false);
+    EXPECT_TRUE(mOutputLayer.getState().hwc->stateOverridden);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, roundedCornersForceClientComposition) {
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(true));
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/
+                                 false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, roundedCornersPeekingThroughAllowsDeviceComposition) {
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
     expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
 
-    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/
+                                 true);
+    EXPECT_EQ(Hwc2::IComposerClient::Composition::DEVICE,
+              mOutputLayer.getState().hwc->hwcCompositionType);
 }
 
 /*
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 5f0b0ee..11736d1 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,13 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
+#include <cmath>
+#include <cstdint>
+
 #include "CallOrderStateMachineHelper.h"
 #include "MockHWC2.h"
 #include "RegionMatcher.h"
+#include "renderengine/ExternalTexture.h"
 
 namespace android::compositionengine {
 namespace {
@@ -236,6 +238,28 @@
 }
 
 /*
+ * Output::setLayerCachingEnabled()
+ */
+
+TEST_F(OutputTest, setLayerCachingEnabled_enablesCaching) {
+    const auto kSize = ui::Size(1, 1);
+    EXPECT_CALL(*mRenderSurface, getSize()).WillRepeatedly(ReturnRef(kSize));
+    mOutput->setLayerCachingEnabled(false);
+    mOutput->setLayerCachingEnabled(true);
+
+    EXPECT_TRUE(mOutput->plannerEnabled());
+}
+
+TEST_F(OutputTest, setLayerCachingEnabled_disablesCaching) {
+    const auto kSize = ui::Size(1, 1);
+    EXPECT_CALL(*mRenderSurface, getSize()).WillRepeatedly(ReturnRef(kSize));
+    mOutput->setLayerCachingEnabled(true);
+    mOutput->setLayerCachingEnabled(false);
+
+    EXPECT_FALSE(mOutput->plannerEnabled());
+}
+
+/*
  * Output::setProjection()
  */
 
@@ -782,15 +806,19 @@
     InjectedLayer layer2;
     InjectedLayer layer3;
 
+    uint32_t z = 0;
     EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
     EXPECT_CALL(*layer1.outputLayer,
-                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
     EXPECT_CALL(*layer2.outputLayer,
-                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
     EXPECT_CALL(*layer3.outputLayer,
-                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
 
     injectOutputLayer(layer1);
     injectOutputLayer(layer2);
@@ -812,15 +840,19 @@
     InjectedLayer layer2;
     InjectedLayer layer3;
 
+    uint32_t z = 0;
     EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
     EXPECT_CALL(*layer1.outputLayer,
-                writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false));
+                writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
     EXPECT_CALL(*layer2.outputLayer,
-                writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false));
+                writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
     EXPECT_CALL(*layer3.outputLayer,
-                writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false));
+                writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
 
     injectOutputLayer(layer1);
     injectOutputLayer(layer2);
@@ -841,15 +873,19 @@
     InjectedLayer layer2;
     InjectedLayer layer3;
 
+    uint32_t z = 0;
     EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
     EXPECT_CALL(*layer1.outputLayer,
-                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
     EXPECT_CALL(*layer2.outputLayer,
-                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
     EXPECT_CALL(*layer3.outputLayer,
-                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
 
     injectOutputLayer(layer1);
     injectOutputLayer(layer2);
@@ -865,6 +901,64 @@
     mOutput->writeCompositionState(args);
 }
 
+TEST_F(OutputUpdateAndWriteCompositionStateTest, peekThroughLayerChangesOrder) {
+    renderengine::mock::RenderEngine renderEngine;
+    InjectedLayer layer0;
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    InjectedLayer layer3;
+
+    InSequence seq;
+    EXPECT_CALL(*layer0.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+
+    uint32_t z = 0;
+    EXPECT_CALL(*layer0.outputLayer,
+                writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+
+    // After calling planComposition (which clears overrideInfo), this test sets
+    // layer3 to be the peekThroughLayer for layer1 and layer2. As a result, it
+    // comes first, setting isPeekingThrough to true and zIsOverridden to true
+    // for it and the following layers.
+    EXPECT_CALL(*layer3.outputLayer,
+                writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ true, /*isPeekingThrough*/
+                                true));
+    EXPECT_CALL(*layer1.outputLayer,
+                writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ true, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer2.outputLayer,
+                writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, z++,
+                                /*zIsOverridden*/ true, /*isPeekingThrough*/ false));
+
+    injectOutputLayer(layer0);
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+    injectOutputLayer(layer3);
+
+    mOutput->editState().isEnabled = true;
+
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = true;
+    args.devOptForceClientComposition = false;
+    mOutput->updateCompositionState(args);
+    mOutput->planComposition();
+
+    std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared<
+            renderengine::ExternalTexture>(new GraphicBuffer(), renderEngine,
+                                           renderengine::ExternalTexture::Usage::READABLE |
+                                                   renderengine::ExternalTexture::Usage::WRITEABLE);
+    layer1.outputLayerState.overrideInfo.buffer = buffer;
+    layer2.outputLayerState.overrideInfo.buffer = buffer;
+    layer1.outputLayerState.overrideInfo.peekThroughLayer = layer3.outputLayer;
+    layer2.outputLayerState.overrideInfo.peekThroughLayer = layer3.outputLayer;
+
+    mOutput->writeCompositionState(args);
+}
+
 /*
  * Output::prepareFrame()
  */
@@ -900,9 +994,7 @@
     mOutput.editState().usesDeviceComposition = true;
 
     EXPECT_CALL(mOutput, chooseCompositionStrategy()).Times(1);
-    if (mOutput.plannerEnabled()) {
-        EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
-    }
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
     EXPECT_CALL(*mRenderSurface, prepareFrame(false, true));
 
     mOutput.prepareFrame();
@@ -1143,11 +1235,6 @@
     EXPECT_CALL(mOutput, finalizePendingOutputLayers());
 
     mOutput.collectVisibleLayers(mRefreshArgs, mCoverageState);
-
-    // Ensure all output layers have been assigned a simple/flattened z-order.
-    EXPECT_EQ(0u, mLayer1.outputLayerState.z);
-    EXPECT_EQ(1u, mLayer2.outputLayerState.z);
-    EXPECT_EQ(2u, mLayer3.outputLayerState.z);
 }
 
 /*
@@ -2960,7 +3047,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 +3263,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));
@@ -3494,7 +3587,8 @@
 
         EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
         EXPECT_CALL(mLayer.outputLayer,
-                    writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+                    writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+                                    /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
         EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
                 .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
         EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
@@ -4073,16 +4167,20 @@
     InjectedLayer layer2;
     InjectedLayer layer3;
 
+    uint32_t z = 0;
     // Layer requesting blur, or below, should request client composition.
     EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
     EXPECT_CALL(*layer1.outputLayer,
-                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
     EXPECT_CALL(*layer2.outputLayer,
-                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
     EXPECT_CALL(*layer3.outputLayer,
-                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
 
     layer2.layerFEState.backgroundBlurRadius = 10;
 
@@ -4105,16 +4203,20 @@
     InjectedLayer layer2;
     InjectedLayer layer3;
 
+    uint32_t z = 0;
     // Layer requesting blur, or below, should request client composition.
     EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
     EXPECT_CALL(*layer1.outputLayer,
-                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
     EXPECT_CALL(*layer2.outputLayer,
-                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
     EXPECT_CALL(*layer3.outputLayer,
-                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
+                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
 
     BlurRegion region;
     layer2.layerFEState.blurRegions.push_back(region);
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 f01fe27..488f64d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -19,8 +19,12 @@
 #include <compositionengine/impl/planner/LayerState.h>
 #include <compositionengine/mock/LayerFE.h>
 #include <compositionengine/mock/OutputLayer.h>
+#include <gmock/gmock-actions.h>
 #include <gtest/gtest.h>
+#include <renderengine/ExternalTexture.h>
 #include <renderengine/mock/RenderEngine.h>
+#include <ui/GraphicTypes.h>
+#include <memory>
 
 namespace android::compositionengine {
 using namespace std::chrono_literals;
@@ -105,7 +109,6 @@
 }
 
 void expectEqual(const CachedSet& cachedSet, const CachedSet::Layer& layer) {
-    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()));
@@ -154,7 +157,6 @@
     CachedSet cachedSet(layer1);
     cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
 
-    EXPECT_EQ(layer1.getHash(), cachedSet.getFingerprint());
     EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
     EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getBounds());
     Region expectedRegion;
@@ -243,7 +245,6 @@
     cachedSet1.addLayer(layer3.getState(), kStartTime + 10ms);
     cachedSet1.append(cachedSet2);
 
-    EXPECT_EQ(layer1.getHash(), cachedSet1.getFingerprint());
     EXPECT_EQ(kStartTime, cachedSet1.getLastUpdate());
     EXPECT_EQ(Rect(0, 0, 3, 3), cachedSet1.getBounds());
     Region expectedRegion;
@@ -287,10 +288,11 @@
 }
 
 TEST_F(CachedSetTest, render) {
-    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
-    sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE;
-    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
-    sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE;
+    // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
+    CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE;
+    CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE;
 
     CachedSet cachedSet(layer1);
     cachedSet.append(CachedSet(layer2));
@@ -305,9 +307,9 @@
 
     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 {
-        EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.physicalDisplay);
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&, base::unique_fd*) -> size_t {
+        EXPECT_EQ(Rect(-1, -1, 9, 4), displaySettings.physicalDisplay);
         EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
         EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
                   displaySettings.orientation);
@@ -321,20 +323,274 @@
     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, 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);
+    EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
 
     // 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));
 }
 
+TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) {
+    // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
+    CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE;
+    CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE;
+
+    CachedSet cachedSet(layer1);
+    cachedSet.append(CachedSet(layer2));
+
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+    clientCompList1.push_back({});
+    clientCompList1[0].alpha = 0.5f;
+
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+    clientCompList2.push_back({});
+    clientCompList2[0].alpha = 0.75f;
+
+    mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(2, 3, 10, 5));
+
+    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+                                const std::vector<const renderengine::LayerSettings*>& layers,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&, base::unique_fd*) -> size_t {
+        EXPECT_EQ(Rect(1, 2, 9, 4), displaySettings.physicalDisplay);
+        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);
+
+        return NO_ERROR;
+    };
+
+    EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
+    EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    cachedSet.render(mRenderEngine, mOutputState);
+    expectReadyBuffer(cachedSet);
+
+    EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
+
+    // Now check that appending a new cached set properly cleans up RenderEngine resources.
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+    cachedSet.append(CachedSet(layer3));
+}
+
+TEST_F(CachedSetTest, holePunch_requiresBuffer) {
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE;
+
+    CachedSet cachedSet(layer1);
+    EXPECT_CALL(*layerFE1, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+    EXPECT_FALSE(cachedSet.requiresHolePunch());
+}
+
+TEST_F(CachedSetTest, holePunch_requiresRoundedCorners) {
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make();
+
+    CachedSet cachedSet(layer1);
+
+    EXPECT_FALSE(cachedSet.requiresHolePunch());
+}
+
+TEST_F(CachedSetTest, holePunch_requiresSingleLayer) {
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make();
+    sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE;
+    EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+
+    CachedSet cachedSet(layer1);
+    cachedSet.append(layer2);
+
+    EXPECT_FALSE(cachedSet.requiresHolePunch());
+}
+
+TEST_F(CachedSetTest, requiresHolePunch) {
+    CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+    mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make();
+    sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE;
+
+    CachedSet cachedSet(layer);
+    EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+    EXPECT_TRUE(cachedSet.requiresHolePunch());
+}
+
+TEST_F(CachedSetTest, holePunch_requiresDeviceComposition) {
+    CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE;
+    auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState;
+    layerFECompositionState.buffer = sp<GraphicBuffer>::make();
+    layerFECompositionState.forceClientComposition = true;
+
+    CachedSet cachedSet(layer);
+    EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+    EXPECT_FALSE(cachedSet.requiresHolePunch());
+}
+
+TEST_F(CachedSetTest, addHolePunch_requiresOverlap) {
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+    CachedSet cachedSet(layer1);
+    cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+    cachedSet.addHolePunchLayerIfFeasible(layer3, true);
+
+    ASSERT_EQ(nullptr, cachedSet.getHolePunchLayer());
+}
+
+TEST_F(CachedSetTest, addHolePunch_requiresOpaque) {
+    mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+    mTestLayers[0]->layerFECompositionState.isOpaque = false;
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+    CachedSet cachedSet(layer1);
+    cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+    cachedSet.addHolePunchLayerIfFeasible(layer3, false);
+
+    ASSERT_EQ(nullptr, cachedSet.getHolePunchLayer());
+}
+
+TEST_F(CachedSetTest, addHolePunch_opaque) {
+    mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+    mTestLayers[0]->layerFECompositionState.isOpaque = true;
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+    CachedSet cachedSet(layer1);
+    cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+    cachedSet.addHolePunchLayerIfFeasible(layer3, false);
+
+    ASSERT_EQ(&mTestLayers[2]->outputLayer, cachedSet.getHolePunchLayer());
+}
+
+TEST_F(CachedSetTest, addHolePunch_firstLayer) {
+    mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+    mTestLayers[0]->layerFECompositionState.isOpaque = false;
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+    CachedSet cachedSet(layer1);
+    cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+    cachedSet.addHolePunchLayerIfFeasible(layer3, true);
+
+    ASSERT_EQ(&mTestLayers[2]->outputLayer, cachedSet.getHolePunchLayer());
+}
+
+TEST_F(CachedSetTest, addHolePunch) {
+    mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE;
+
+    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE;
+
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE3 = mTestLayers[2]->layerFE;
+
+    CachedSet cachedSet(layer1);
+    cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+    cachedSet.addHolePunchLayerIfFeasible(layer3, true);
+
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+    clientCompList1.push_back({});
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+    clientCompList2.push_back({});
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3;
+    clientCompList3.push_back({});
+
+    clientCompList3[0].source.buffer.buffer = std::make_shared<
+            renderengine::ExternalTexture>(sp<GraphicBuffer>::make(), mRenderEngine,
+                                           renderengine::ExternalTexture::READABLE);
+
+    EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
+    EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
+    EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3));
+
+    const auto drawLayers = [&](const renderengine::DisplaySettings&,
+                                const std::vector<const renderengine::LayerSettings*>& layers,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&, base::unique_fd*) -> size_t {
+        // If the highlight layer is enabled, it will increase the size by 1.
+        // We're interested in the third layer either way.
+        EXPECT_GE(layers.size(), 3u);
+        const auto* holePunchSettings = layers[2];
+        EXPECT_EQ(nullptr, holePunchSettings->source.buffer.buffer);
+        EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings->source.solidColor);
+        EXPECT_TRUE(holePunchSettings->disableBlending);
+        EXPECT_EQ(0.0f, holePunchSettings->alpha);
+
+        return NO_ERROR;
+    };
+
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    cachedSet.render(mRenderEngine, mOutputState);
+}
+
+TEST_F(CachedSetTest, decompose_removesHolePunch) {
+    mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+    CachedSet cachedSet(layer1);
+    cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+    cachedSet.addHolePunchLayerIfFeasible(layer3, true);
+
+    ASSERT_EQ(&mTestLayers[2]->outputLayer, cachedSet.getHolePunchLayer());
+
+    std::vector<CachedSet> decomposed = cachedSet.decompose();
+    EXPECT_EQ(2u, decomposed.size());
+    for (const auto& set : decomposed) {
+        EXPECT_EQ(nullptr, set.getHolePunchLayer());
+    }
+}
+
+TEST_F(CachedSetTest, hasBlurBehind) {
+    mTestLayers[1]->layerFECompositionState.backgroundBlurRadius = 1;
+    mTestLayers[1]->layerState->update(&mTestLayers[1]->outputLayer);
+    mTestLayers[2]->layerFECompositionState.blurRegions.push_back(
+            BlurRegion{1, 0, 0, 0, 0, 0, 0, 0, 0, 0});
+    mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer);
+
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+    CachedSet cachedSet1(layer1);
+    CachedSet cachedSet2(layer2);
+    CachedSet cachedSet3(layer3);
+
+    // Cached set 4 will consist of layers 1 and 2, which will contain a blur behind
+    CachedSet cachedSet4(layer1);
+    cachedSet4.addLayer(layer2.getState(), kStartTime);
+
+    EXPECT_FALSE(cachedSet1.hasBlurBehind());
+    EXPECT_TRUE(cachedSet2.hasBlurBehind());
+    EXPECT_TRUE(cachedSet3.hasBlurBehind());
+    EXPECT_TRUE(cachedSet4.hasBlurBehind());
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index c528087..42096f3 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -18,10 +18,11 @@
 #include <compositionengine/impl/planner/CachedSet.h>
 #include <compositionengine/impl/planner/Flattener.h>
 #include <compositionengine/impl/planner/LayerState.h>
-#include <compositionengine/impl/planner/Predictor.h>
 #include <compositionengine/mock/LayerFE.h>
 #include <compositionengine/mock/OutputLayer.h>
 #include <gtest/gtest.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/LayerSettings.h>
 #include <renderengine/mock/RenderEngine.h>
 
 namespace android::compositionengine {
@@ -29,7 +30,6 @@
 using impl::planner::Flattener;
 using impl::planner::LayerState;
 using impl::planner::NonBufferHash;
-using impl::planner::Predictor;
 
 using testing::_;
 using testing::ByMove;
@@ -45,7 +45,7 @@
 
 class FlattenerTest : public testing::Test {
 public:
-    FlattenerTest() : mFlattener(std::make_unique<Flattener>(mPredictor)) {}
+    FlattenerTest() : mFlattener(std::make_unique<Flattener>(true)) {}
     void SetUp() override;
 
 protected:
@@ -53,10 +53,6 @@
     void initializeFlattener(const std::vector<const LayerState*>& layers);
     void expectAllLayersFlattened(const std::vector<const LayerState*>& layers);
 
-    // TODO(b/181192467): Once Flattener starts to do something useful with Predictor,
-    // mPredictor should be mocked and checked for expectations.
-    Predictor mPredictor;
-
     // mRenderEngine may be held as a pointer to mFlattener, so mFlattener must be destroyed first.
     renderengine::mock::RenderEngine mRenderEngine;
     std::unique_ptr<Flattener> mFlattener;
@@ -275,11 +271,7 @@
     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);
+    EXPECT_EQ(overrideDisplaySpace, mOutputState.framebufferSpace);
 }
 
 TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsDamageRegions) {
@@ -532,5 +524,267 @@
     EXPECT_EQ(overrideBuffer4, overrideBuffer5);
 }
 
+// Tests for a PIP
+TEST_F(FlattenerTest, flattenLayers_pipRequiresRoundedCorners) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState3 = mTestLayers[2]->layerState;
+    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+            layerState3.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // 3 has a buffer update, so it will not be merged, but it has no round
+    // corners, so it is not a PIP.
+    mTime += 200ms;
+    layerState3->resetFramesSinceBufferUpdate();
+
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+    // This will render a CachedSet.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+    // We've rendered a CachedSet, but we haven't merged it in.
+    EXPECT_EQ(nullptr, overrideBuffer1);
+    EXPECT_EQ(nullptr, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+
+    // This time we merge the CachedSet in, so we have a new hash, and we should
+    // only have two sets.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+    EXPECT_NE(nullptr, overrideBuffer1);
+    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_pip) {
+    mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState3 = mTestLayers[2]->layerState;
+    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+    EXPECT_CALL(*mTestLayers[2]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+    std::vector<LayerFE::LayerSettings> clientCompositionList = {
+            LayerFE::LayerSettings{},
+    };
+    clientCompositionList[0].source.buffer.buffer = std::make_shared<
+            renderengine::ExternalTexture>(mTestLayers[2]->layerFECompositionState.buffer,
+                                           mRenderEngine,
+                                           renderengine::ExternalTexture::Usage::READABLE);
+    EXPECT_CALL(*mTestLayers[2]->layerFE, prepareClientCompositionList(_))
+            .WillOnce(Return(clientCompositionList));
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+            layerState3.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // 3 has a buffer update, so it will not be merged, and it has round
+    // corners, so it is a PIP.
+    mTime += 200ms;
+    layerState3->resetFramesSinceBufferUpdate();
+
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+    // This will render a CachedSet.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+    // We've rendered a CachedSet, but we haven't merged it in.
+    EXPECT_EQ(nullptr, overrideBuffer1);
+    EXPECT_EQ(nullptr, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+
+    // This time we merge the CachedSet in, so we have a new hash, and we should
+    // only have two sets.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+    EXPECT_NE(nullptr, overrideBuffer1);
+    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+
+    const auto* peekThroughLayer1 =
+            layerState1->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+    const auto* peekThroughLayer2 =
+            layerState2->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+    EXPECT_EQ(&mTestLayers[2]->outputLayer, peekThroughLayer1);
+    EXPECT_EQ(peekThroughLayer1, peekThroughLayer2);
+}
+
+TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+    mTestLayers[1]->layerFECompositionState.backgroundBlurRadius = 1;
+    layerState2->update(&mTestLayers[1]->outputLayer);
+
+    auto& layerState3 = mTestLayers[2]->layerState;
+    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+            layerState3.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // Mark the first two layers inactive, which contain the blur behind
+    mTime += 200ms;
+    layerState3->resetFramesSinceBufferUpdate();
+
+    // layers would be flattened but the buffer would not be overridden
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+    for (const auto layer : layers) {
+        EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
+    }
+
+    // the new flattened layer is replaced
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+    EXPECT_NE(nullptr, overrideBuffer1);
+    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_doesNotFlattenBlurBehindRun) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+    mTestLayers[1]->layerFECompositionState.backgroundBlurRadius = 1;
+    layerState2->update(&mTestLayers[1]->outputLayer);
+
+    auto& layerState3 = mTestLayers[2]->layerState;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+            layerState3.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // Mark the last two layers inactive, which contains the blur layer, but does not contain the
+    // first layer
+    mTime += 200ms;
+    layerState1->resetFramesSinceBufferUpdate();
+
+    // layers would be flattened but the buffer would not be overridden
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillRepeatedly(Return(NO_ERROR));
+
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+    for (const auto layer : layers) {
+        EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
+    }
+
+    // nothing is flattened because the last two frames cannot be cached due to containing a blur
+    // layer
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+    for (const auto layer : layers) {
+        EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
+    }
+}
+
+TEST_F(FlattenerTest, flattenLayers_flattenSkipsLayerWithBlurBehind) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+
+    auto& layerStateWithBlurBehind = mTestLayers[1]->layerState;
+    mTestLayers[1]->layerFECompositionState.backgroundBlurRadius = 1;
+    layerStateWithBlurBehind->update(&mTestLayers[1]->outputLayer);
+
+    auto& layerState3 = mTestLayers[2]->layerState;
+    auto& layerState4 = mTestLayers[3]->layerState;
+    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+    const auto& blurOverrideBuffer =
+            layerStateWithBlurBehind->getOutputLayer()->getState().overrideInfo.buffer;
+    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+    const auto& overrideBuffer4 = layerState4->getOutputLayer()->getState().overrideInfo.buffer;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerStateWithBlurBehind.get(),
+            layerState3.get(),
+            layerState4.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // Mark the last three layers inactive, which contains the blur layer, but does not contain the
+    // first layer
+    mTime += 200ms;
+    layerState1->resetFramesSinceBufferUpdate();
+
+    // layers would be flattened but the buffer would not be overridden
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+    for (const auto layer : layers) {
+        EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
+    }
+
+    // the new flattened layer is replaced
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+    EXPECT_EQ(nullptr, overrideBuffer1);
+    EXPECT_EQ(nullptr, blurOverrideBuffer);
+    EXPECT_NE(nullptr, overrideBuffer3);
+    EXPECT_EQ(overrideBuffer3, overrideBuffer4);
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index 47b1ae8..a09ce14 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -41,8 +41,6 @@
 const Rect sRectTwo = Rect(40, 30, 20, 10);
 const FloatRect sFloatRectOne = FloatRect(100.f, 200.f, 300.f, 400.f);
 const FloatRect sFloatRectTwo = FloatRect(400.f, 300.f, 200.f, 100.f);
-const constexpr int32_t sZOne = 100;
-const constexpr int32_t sZTwo = 101;
 const constexpr float sAlphaOne = 0.25f;
 const constexpr float sAlphaTwo = 0.5f;
 const Region sRegionOne = Region(sRectOne);
@@ -60,6 +58,10 @@
 const GenericLayerMetadataEntry sMetadataValueTwo = GenericLayerMetadataEntry{
         .value = std::vector<uint8_t>({1, 3}),
 };
+const constexpr int32_t sBgBlurRadiusOne = 3;
+const constexpr int32_t sBgBlurRadiusTwo = 4;
+const BlurRegion sBlurRegionOne = BlurRegion{1, 2.f, 3.f, 4.f, 5.f, 6.f, 7, 8, 9, 10};
+const BlurRegion sBlurRegionTwo = BlurRegion{2, 3.f, 4.f, 5.f, 6.f, 7.f, 8, 9, 10, 11};
 
 struct LayerStateTest : public testing::Test {
     LayerStateTest() {
@@ -408,45 +410,6 @@
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
 }
 
-TEST_F(LayerStateTest, updateZOrder) {
-    OutputLayerCompositionState outputLayerCompositionState;
-    outputLayerCompositionState.z = sZOne;
-    LayerFECompositionState layerFECompositionState;
-    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
-                       layerFECompositionState);
-    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
-
-    mock::OutputLayer newOutputLayer;
-    mock::LayerFE newLayerFE;
-    OutputLayerCompositionState outputLayerCompositionStateTwo;
-    outputLayerCompositionStateTwo.z = sZTwo;
-    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
-                       layerFECompositionState);
-    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder), updates);
-}
-
-TEST_F(LayerStateTest, compareZOrder) {
-    OutputLayerCompositionState outputLayerCompositionState;
-    outputLayerCompositionState.z = sZOne;
-    LayerFECompositionState layerFECompositionState;
-    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
-                       layerFECompositionState);
-    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
-    mock::OutputLayer newOutputLayer;
-    mock::LayerFE newLayerFE;
-    OutputLayerCompositionState outputLayerCompositionStateTwo;
-    outputLayerCompositionStateTwo.z = sZTwo;
-    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
-                       layerFECompositionState);
-    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
-
-    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::ZOrder);
-
-    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
-    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
-}
-
 TEST_F(LayerStateTest, updateBufferTransform) {
     OutputLayerCompositionState outputLayerCompositionState;
     outputLayerCompositionState.bufferTransform = Hwc2::Transform::FLIP_H;
@@ -793,47 +756,6 @@
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
 }
 
-TEST_F(LayerStateTest, updateSurfaceDamage) {
-    OutputLayerCompositionState outputLayerCompositionState;
-    LayerFECompositionState layerFECompositionState;
-    layerFECompositionState.surfaceDamage = sRegionOne;
-    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
-                       layerFECompositionState);
-    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
-
-    mock::OutputLayer newOutputLayer;
-    mock::LayerFE newLayerFE;
-    OutputLayerCompositionState outputLayerCompositionStateTwo;
-    LayerFECompositionState layerFECompositionStateTwo;
-    layerFECompositionStateTwo.surfaceDamage = sRegionTwo;
-    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
-                       layerFECompositionStateTwo);
-    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
-    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SurfaceDamage), updates);
-}
-
-TEST_F(LayerStateTest, compareSurfaceDamage) {
-    OutputLayerCompositionState outputLayerCompositionState;
-    LayerFECompositionState layerFECompositionState;
-    layerFECompositionState.surfaceDamage = sRegionOne;
-    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
-                       layerFECompositionState);
-    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
-    mock::OutputLayer newOutputLayer;
-    mock::LayerFE newLayerFE;
-    OutputLayerCompositionState outputLayerCompositionStateTwo;
-    LayerFECompositionState layerFECompositionStateTwo;
-    layerFECompositionStateTwo.surfaceDamage = sRegionTwo;
-    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
-                       layerFECompositionStateTwo);
-    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
-
-    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::SurfaceDamage);
-
-    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
-    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
-}
-
 TEST_F(LayerStateTest, updateSidebandStream) {
     OutputLayerCompositionState outputLayerCompositionState;
     LayerFECompositionState layerFECompositionState;
@@ -912,6 +834,114 @@
     EXPECT_TRUE(otherLayerState->compare(*mLayerState));
 }
 
+TEST_F(LayerStateTest, updateBackgroundBlur) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.backgroundBlurRadius = sBgBlurRadiusOne;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.backgroundBlurRadius = sBgBlurRadiusTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BackgroundBlurRadius), updates);
+}
+
+TEST_F(LayerStateTest, compareBackgroundBlur) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.backgroundBlurRadius = sBgBlurRadiusOne;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.backgroundBlurRadius = sBgBlurRadiusTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState,
+                                   LayerStateField::BackgroundBlurRadius);
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateBlurRegions) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.blurRegions.push_back(sBlurRegionOne);
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.blurRegions.push_back(sBlurRegionTwo);
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlurRegions), updates);
+}
+
+TEST_F(LayerStateTest, compareBlurRegions) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.blurRegions.push_back(sBlurRegionOne);
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.blurRegions.push_back(sBlurRegionTwo);
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::BlurRegions);
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, hasBlurBehind_noBlur_returnsFalse) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_FALSE(mLayerState->hasBlurBehind());
+}
+
+TEST_F(LayerStateTest, hasBlurBehind_withBackgroundBlur_returnsTrue) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.backgroundBlurRadius = sBgBlurRadiusOne;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_TRUE(mLayerState->hasBlurBehind());
+}
+
+TEST_F(LayerStateTest, hasBlurBehind_withBlurRegion_returnsTrue) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.blurRegions.push_back(sBlurRegionOne);
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_TRUE(mLayerState->hasBlurBehind());
+}
+
 TEST_F(LayerStateTest, dumpDoesNotCrash) {
     OutputLayerCompositionState outputLayerCompositionState;
     LayerFECompositionState layerFECompositionState;
@@ -995,4 +1025,4 @@
 }
 
 } // namespace
-} // namespace android::compositionengine::impl::planner
\ No newline at end of file
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
index 43e119f..1492707 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
@@ -31,8 +31,6 @@
 const FloatRect sFloatRectTwo = FloatRect(400.f, 300.f, 200.f, 100.f);
 const Rect sRectOne = Rect(1, 2, 3, 4);
 const Rect sRectTwo = Rect(4, 3, 2, 1);
-const constexpr int32_t sZOne = 100;
-const constexpr int32_t sZTwo = 101;
 const constexpr float sAlphaOne = 0.25f;
 const constexpr float sAlphaTwo = 0.5f;
 const Region sRegionOne = Region(sRectOne);
@@ -194,11 +192,11 @@
             .displayFrame = sRectOne,
             .sourceCrop = sFloatRectOne,
             .dataspace = ui::Dataspace::SRGB,
-            .z = sZOne,
     };
     LayerFECompositionState layerFECompositionStateOne;
     layerFECompositionStateOne.alpha = sAlphaOne;
     layerFECompositionStateOne.colorTransformIsIdentity = true;
+    layerFECompositionStateOne.blendMode = hal::BlendMode::NONE;
     setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
                        layerFECompositionStateOne);
     LayerState layerStateOne(&outputLayerOne);
@@ -210,12 +208,12 @@
             .displayFrame = sRectTwo,
             .sourceCrop = sFloatRectTwo,
             .dataspace = ui::Dataspace::DISPLAY_P3,
-            .z = sZTwo,
     };
     LayerFECompositionState layerFECompositionStateTwo;
     layerFECompositionStateTwo.alpha = sAlphaTwo;
     layerFECompositionStateTwo.colorTransformIsIdentity = false;
     layerFECompositionStateTwo.colorTransform = sMat4One;
+    layerFECompositionStateTwo.blendMode = hal::BlendMode::PREMULTIPLIED;
     setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
                        layerFECompositionStateTwo);
     LayerState layerStateTwo(&outputLayerTwo);
@@ -264,7 +262,6 @@
             .displayFrame = sRectOne,
             .sourceCrop = sFloatRectOne,
             .dataspace = ui::Dataspace::SRGB,
-            .z = sZOne,
     };
     LayerFECompositionState layerFECompositionStateOne;
     layerFECompositionStateOne.buffer = new GraphicBuffer();
@@ -282,7 +279,6 @@
             .displayFrame = sRectTwo,
             .sourceCrop = sFloatRectTwo,
             .dataspace = ui::Dataspace::DISPLAY_P3,
-            .z = sZTwo,
     };
     LayerFECompositionState layerFECompositionStateTwo;
     layerFECompositionStateTwo.buffer = new GraphicBuffer();
@@ -346,6 +342,32 @@
     }
 };
 
+TEST_F(LayerStackTest, reorderingChangesNonBufferHash) {
+    mock::OutputLayer outputLayerOne;
+    mock::LayerFE layerFEOne;
+    OutputLayerCompositionState outputLayerCompositionStateOne{
+            .sourceCrop = sFloatRectOne,
+    };
+    LayerFECompositionState layerFECompositionStateOne;
+    setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+                       layerFECompositionStateOne);
+    LayerState layerStateOne(&outputLayerOne);
+
+    mock::OutputLayer outputLayerTwo;
+    mock::LayerFE layerFETwo;
+    OutputLayerCompositionState outputLayerCompositionStateTwo{
+            .sourceCrop = sFloatRectTwo,
+    };
+    LayerFECompositionState layerFECompositionStateTwo;
+    setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+                       layerFECompositionStateTwo);
+    LayerState layerStateTwo(&outputLayerTwo);
+
+    NonBufferHash hash = getNonBufferHash({&layerStateOne, &layerStateTwo});
+    NonBufferHash hashReverse = getNonBufferHash({&layerStateTwo, &layerStateOne});
+    EXPECT_NE(hash, hashReverse);
+}
+
 TEST_F(PredictionTest, constructPrediction) {
     Plan plan;
     plan.addLayerType(hal::Composition::DEVICE);
@@ -525,4 +547,4 @@
 }
 
 } // namespace
-} // namespace android::compositionengine::impl::planner
\ No newline at end of file
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index b7b2cc6..0f18235 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) {
@@ -136,6 +138,10 @@
     getCompositionDisplay()->setCompositionEnabled(mPowerMode != hal::PowerMode::OFF);
 }
 
+void DisplayDevice::enableLayerCaching(bool enable) {
+    getCompositionDisplay()->setLayerCachingEnabled(enable);
+}
+
 hal::PowerMode DisplayDevice::getPowerMode() const {
     return mPowerMode;
 }
@@ -266,8 +272,9 @@
     StringAppendF(&result, "+ %s\n", getDebugName().c_str());
     StringAppendF(&result, "   powerMode=%s (%d)\n", to_string(mPowerMode).c_str(),
                   static_cast<int32_t>(mPowerMode));
+    const auto activeMode = getActiveMode();
     StringAppendF(&result, "   activeMode=%s\n",
-                  mSupportedModes.size() ? to_string(*getActiveMode()).c_str() : "none");
+                  activeMode ? to_string(*activeMode).c_str() : "none");
 
     result.append("   supportedModes=\n");
 
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 68846d3..bf249cd 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -157,6 +157,9 @@
     void setPowerMode(hardware::graphics::composer::hal::PowerMode mode);
     bool isPoweredOn() const;
 
+    // Enables layer caching on this DisplayDevice
+    void enableLayerCaching(bool enable);
+
     ui::Dataspace getCompositionDataSpace() const;
 
     /* ------------------------------------------------------------------------
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 1bf43da..1cbcf59 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -20,18 +20,18 @@
 
 #undef LOG_TAG
 #define LOG_TAG "HwcComposer"
-
-#include <log/log.h>
-
-#include <algorithm>
-#include <cinttypes>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "ComposerHal.h"
 
 #include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
-#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 {
 
@@ -265,15 +265,15 @@
 Error Composer::createLayer(Display display, Layer* outLayer)
 {
     Error error = kDefaultError;
-    mClient->createLayer(display, BufferQueue::NUM_BUFFER_SLOTS,
-            [&](const auto& tmpError, const auto& tmpLayer) {
-                error = tmpError;
-                if (error != Error::NONE) {
-                    return;
-                }
+    mClient->createLayer(display, kMaxLayerBufferCount,
+                         [&](const auto& tmpError, const auto& tmpLayer) {
+                             error = tmpError;
+                             if (error != Error::NONE) {
+                                 return;
+                             }
 
-                *outLayer = tmpLayer;
-            });
+                             *outLayer = tmpLayer;
+                         });
 
     return error;
 }
@@ -492,6 +492,7 @@
 
 Error Composer::presentDisplay(Display display, int* outPresentFence)
 {
+    ATRACE_NAME("HwcPresentDisplay");
     mWriter.selectDisplay(display);
     mWriter.presentDisplay();
 
@@ -586,6 +587,7 @@
 Error Composer::validateDisplay(Display display, uint32_t* outNumTypes,
         uint32_t* outNumRequests)
 {
+    ATRACE_NAME("HwcValidateDisplay");
     mWriter.selectDisplay(display);
     mWriter.validateDisplay();
 
@@ -601,13 +603,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/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index c756d65..0619b8c 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -32,6 +32,7 @@
 #include <android/hardware/graphics/composer/2.4/IComposer.h>
 #include <android/hardware/graphics/composer/2.4/IComposerClient.h>
 #include <composer-command-buffer/2.4/ComposerCommandBuffer.h>
+#include <gui/BufferQueue.h>
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
 #include <ui/DisplayedFrameStats.h>
@@ -491,6 +492,11 @@
     // 64KiB minus a small space for metadata such as read/write pointers
     static constexpr size_t kWriterInitialSize =
         64 * 1024 / sizeof(uint32_t) - 16;
+    // Max number of buffers that may be cached for a given layer
+    // We obtain this number by:
+    // 1. Tightly coupling this cache to the max size of BufferQueue
+    // 2. Adding an additional slot for the layer caching feature in SurfaceFlinger (see: Planner.h)
+    static const constexpr uint32_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
     CommandWriter mWriter;
     CommandReader mReader;
 };
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index b73d032..dc4839e 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -471,6 +471,7 @@
 
 status_t HWComposer::getDeviceCompositionChanges(
         HalDisplayId displayId, bool frameUsesClientComposition,
+        std::chrono::steady_clock::time_point earliestPresentTime,
         std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
     ATRACE_CALL();
 
@@ -487,12 +488,14 @@
 
     hal::Error error = hal::Error::NONE;
 
-    // First try to skip validate altogether when there is no client
-    // composition.  When there is client composition, since we haven't
-    // rendered to the client target yet, we should not attempt to skip
-    // validate.
+    // First try to skip validate altogether when we passed the earliest time
+    // to present and there is no client. Otherwise, we may present a frame too
+    // early or in case of client composition we first need to render the
+    // client target buffer.
+    const bool canSkipValidate =
+            std::chrono::steady_clock::now() >= earliestPresentTime && !frameUsesClientComposition;
     displayData.validateWasSkipped = false;
-    if (!frameUsesClientComposition) {
+    if (canSkipValidate) {
         sp<Fence> outPresentFence;
         uint32_t state = UINT32_MAX;
         error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
@@ -556,7 +559,8 @@
     return fence->second;
 }
 
-status_t HWComposer::presentAndGetReleaseFences(HalDisplayId displayId) {
+status_t HWComposer::presentAndGetReleaseFences(
+        HalDisplayId displayId, std::chrono::steady_clock::time_point earliestPresentTime) {
     ATRACE_CALL();
 
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -572,6 +576,11 @@
         return NO_ERROR;
     }
 
+    {
+        ATRACE_NAME("wait for earliest present time");
+        std::this_thread::sleep_until(earliestPresentTime);
+    }
+
     auto error = hwcDisplay->present(&displayData.lastPresentFence);
     RETURN_IF_HWC_ERROR_FOR("present", error, displayId, UNKNOWN_ERROR);
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index f532e50..5bad529 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -129,13 +129,15 @@
     // expected.
     virtual status_t getDeviceCompositionChanges(
             HalDisplayId, bool frameUsesClientComposition,
+            std::chrono::steady_clock::time_point earliestPresentTime,
             std::optional<DeviceRequestedChanges>* outChanges) = 0;
 
     virtual status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
                                      const sp<GraphicBuffer>& target, ui::Dataspace) = 0;
 
     // Present layers to the display and read releaseFences.
-    virtual status_t presentAndGetReleaseFences(HalDisplayId) = 0;
+    virtual status_t presentAndGetReleaseFences(
+            HalDisplayId, std::chrono::steady_clock::time_point earliestPresentTime) = 0;
 
     // set power mode
     virtual status_t setPowerMode(PhysicalDisplayId, hal::PowerMode) = 0;
@@ -268,13 +270,15 @@
 
     status_t getDeviceCompositionChanges(
             HalDisplayId, bool frameUsesClientComposition,
+            std::chrono::steady_clock::time_point earliestPresentTime,
             std::optional<DeviceRequestedChanges>* outChanges) override;
 
     status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
                              const sp<GraphicBuffer>& target, ui::Dataspace) override;
 
     // Present layers to the display and read releaseFences.
-    status_t presentAndGetReleaseFences(HalDisplayId) override;
+    status_t presentAndGetReleaseFences(
+            HalDisplayId, std::chrono::steady_clock::time_point earliestPresentTime) override;
 
     // set power mode
     status_t setPowerMode(PhysicalDisplayId, hal::PowerMode mode) override;
diff --git a/services/surfaceflinger/FpsReporter.cpp b/services/surfaceflinger/FpsReporter.cpp
index 0bc2d3e..23db805 100644
--- a/services/surfaceflinger/FpsReporter.cpp
+++ b/services/surfaceflinger/FpsReporter.cpp
@@ -26,10 +26,18 @@
 
 namespace android {
 
-FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger)
-      : mFrameTimeline(frameTimeline), mFlinger(flinger) {}
+FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger,
+                         std::unique_ptr<Clock> clock)
+      : mFrameTimeline(frameTimeline), mFlinger(flinger), mClock(std::move(clock)) {
+    LOG_ALWAYS_FATAL_IF(mClock == nullptr, "Passed in null clock when constructing FpsReporter!");
+}
 
-void FpsReporter::dispatchLayerFps() const {
+void FpsReporter::dispatchLayerFps() {
+    const auto now = mClock->now();
+    if (now - mLastDispatch < kMinDispatchDuration) {
+        return;
+    }
+
     std::vector<TrackedListener> localListeners;
     {
         std::scoped_lock lock(mMutex);
@@ -71,6 +79,8 @@
 
         listener.listener->onFpsReported(mFrameTimeline.computeFps(layerIds));
     }
+
+    mLastDispatch = now;
 }
 
 void FpsReporter::binderDied(const wp<IBinder>& who) {
diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h
index 1cec295..bd7b9a5 100644
--- a/services/surfaceflinger/FpsReporter.h
+++ b/services/surfaceflinger/FpsReporter.h
@@ -22,6 +22,7 @@
 
 #include <unordered_map>
 
+#include "Clock.h"
 #include "FrameTimeline/FrameTimeline.h"
 
 namespace android {
@@ -31,12 +32,13 @@
 
 class FpsReporter : public IBinder::DeathRecipient {
 public:
-    FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger);
+    FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger,
+                std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>());
 
     // Dispatches updated layer fps values for the registered listeners
     // This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock
     // must be held when calling this method.
-    void dispatchLayerFps() const EXCLUDES(mMutex);
+    void dispatchLayerFps() EXCLUDES(mMutex);
 
     // Override for IBinder::DeathRecipient
     void binderDied(const wp<IBinder>&) override;
@@ -61,6 +63,10 @@
 
     frametimeline::FrameTimeline& mFrameTimeline;
     SurfaceFlinger& mFlinger;
+    static const constexpr std::chrono::steady_clock::duration kMinDispatchDuration =
+            std::chrono::milliseconds(500);
+    std::unique_ptr<Clock> mClock;
+    std::chrono::steady_clock::time_point mLastDispatch;
     std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex);
 };
 
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 6485265..f19e2a7 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -256,6 +256,10 @@
         protoJank |= FrameTimelineEvent::JANK_UNKNOWN;
         jankType &= ~JankType::Unknown;
     }
+    if (jankType & JankType::SurfaceFlingerStuffing) {
+        protoJank |= FrameTimelineEvent::JANK_SF_STUFFING;
+        jankType &= ~JankType::SurfaceFlingerStuffing;
+    }
 
     // jankType should be 0 if all types of jank were checked for.
     LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType);
@@ -653,8 +657,10 @@
             // If prediction is expired, we can't use the predicted start time. Instead, just use a
             // start time a little earlier than the end time so that we have some info about this
             // frame in the trace.
+            nsecs_t endTime =
+                    (mPresentState == PresentState::Dropped ? mDropTime : mActuals.endTime);
             packet->set_timestamp(
-                    static_cast<uint64_t>(mActuals.endTime - kPredictionExpiredStartTimeDelta));
+                    static_cast<uint64_t>(endTime - kPredictionExpiredStartTimeDelta));
         } else {
             packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime));
         }
@@ -724,9 +730,11 @@
 int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
     ATRACE_CALL();
     std::scoped_lock lock(mMutex);
+    while (mPredictions.size() >= kMaxTokens) {
+        mPredictions.erase(mPredictions.begin());
+    }
     const int64_t assignedToken = mCurrentToken++;
-    mPredictions[assignedToken] = {systemTime(), predictions};
-    flushTokens(systemTime());
+    mPredictions[assignedToken] = predictions;
     return assignedToken;
 }
 
@@ -734,32 +742,19 @@
     std::scoped_lock lock(mMutex);
     auto predictionsIterator = mPredictions.find(token);
     if (predictionsIterator != mPredictions.end()) {
-        return predictionsIterator->second.predictions;
+        return predictionsIterator->second;
     }
     return {};
 }
 
-void TokenManager::flushTokens(nsecs_t flushTime) {
-    for (auto it = mPredictions.begin(); it != mPredictions.end();) {
-        if (flushTime - it->second.timestamp >= kMaxRetentionTime) {
-            it = mPredictions.erase(it);
-        } else {
-            // Tokens are ordered by time. If i'th token is within the retention time, then the
-            // i+1'th token will also be within retention time.
-            break;
-        }
-    }
-}
-
 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() {
@@ -804,13 +799,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);
 }
@@ -830,13 +823,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();
@@ -874,25 +865,33 @@
     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) {
+void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
+                                               nsecs_t previousPresentTime) {
     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.
@@ -907,9 +906,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;
@@ -947,7 +944,15 @@
                 mJankType = JankType::Unknown;
             }
         } else if (mFramePresentMetadata == FramePresentMetadata::LatePresent) {
-            if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+            if (std::abs(mSurfaceFlingerPredictions.presentTime - previousPresentTime) <=
+                        mJankClassificationThresholds.presentThreshold ||
+                previousPresentTime > mSurfaceFlingerPredictions.presentTime) {
+                // The previous frame was either presented in the current frame's expected vsync or
+                // it was presented even later than the current frame's expected vsync.
+                mJankType = JankType::SurfaceFlingerStuffing;
+            }
+            if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish &&
+                !(mJankType & JankType::SurfaceFlingerStuffing)) {
                 // Finish on time, Present late
                 if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
                     deltaToVsync >= (mRefreshRate.getPeriodNsecs() -
@@ -961,12 +966,21 @@
                     mJankType = JankType::PredictionError;
                 }
             } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
-                if (mFrameStartMetadata == FrameStartMetadata::LateStart) {
-                    // Late start, Late finish, Late Present
-                    mJankType = JankType::SurfaceFlingerScheduling;
-                } else {
-                    // OnTime start, Finish late, Present late
-                    mJankType = JankType::SurfaceFlingerCpuDeadlineMissed;
+                if (!(mJankType & JankType::SurfaceFlingerStuffing) ||
+                    mSurfaceFlingerActuals.presentTime - previousPresentTime >
+                            mRefreshRate.getPeriodNsecs() +
+                                    mJankClassificationThresholds.presentThreshold) {
+                    // Classify CPU vs GPU if SF wasn't stuffed or if SF was stuffed but this frame
+                    // was presented more than a vsync late.
+                    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
@@ -979,11 +993,11 @@
     }
 }
 
-void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) {
+void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime, nsecs_t previousPresentTime) {
     mSurfaceFlingerActuals.presentTime = signalTime;
     nsecs_t deadlineDelta = 0;
     nsecs_t deltaToVsync = 0;
-    classifyJank(deadlineDelta, deltaToVsync);
+    classifyJank(deadlineDelta, deltaToVsync, previousPresentTime);
 
     for (auto& surfaceFrame : mSurfaceFrames) {
         surfaceFrame->onPresent(signalTime, mJankType, mRefreshRate, deadlineDelta, deltaToVsync);
@@ -1041,7 +1055,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));
     });
@@ -1148,8 +1162,9 @@
             }
         }
         auto& displayFrame = pendingPresentFence.second;
-        displayFrame->onPresent(signalTime);
+        displayFrame->onPresent(signalTime, mPreviousPresentTime);
         displayFrame->trace(mSurfaceFlingerPid);
+        mPreviousPresentTime = signalTime;
 
         mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
         --i;
@@ -1164,7 +1179,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..42be55a 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -92,11 +92,6 @@
     bool operator!=(const TimelineItem& other) const { return !(*this == other); }
 };
 
-struct TokenManagerPrediction {
-    nsecs_t timestamp = 0;
-    TimelineItem predictions;
-};
-
 struct JankClassificationThresholds {
     // The various thresholds for App and SF. If the actual timestamp falls within the threshold
     // compared to prediction, we treat it as on time.
@@ -293,11 +288,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
@@ -333,11 +329,10 @@
 
     void flushTokens(nsecs_t flushTime) REQUIRES(mMutex);
 
-    std::map<int64_t, TokenManagerPrediction> mPredictions GUARDED_BY(mMutex);
+    std::map<int64_t, TimelineItem> mPredictions GUARDED_BY(mMutex);
     int64_t mCurrentToken GUARDED_BY(mMutex);
     mutable std::mutex mMutex;
-    static constexpr nsecs_t kMaxRetentionTime =
-            std::chrono::duration_cast<std::chrono::nanoseconds>(120ms).count();
+    static constexpr size_t kMaxTokens = 500;
 };
 
 class FrameTimeline : public android::frametimeline::FrameTimeline {
@@ -355,7 +350,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.
@@ -369,14 +364,14 @@
         void onSfWakeUp(int64_t token, Fps refreshRate, std::optional<TimelineItem> predictions,
                         nsecs_t wakeUpTime);
         // Sets the appropriate metadata and classifies the jank.
-        void onPresent(nsecs_t signalTime);
+        void onPresent(nsecs_t signalTime, nsecs_t previousPresentTime);
         // Adds the provided SurfaceFrame to the current display frame.
         void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame);
 
         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.
@@ -397,7 +392,8 @@
         void dump(std::string& result, nsecs_t baseTime) const;
         void tracePredictions(pid_t surfaceFlingerPid) const;
         void traceActuals(pid_t surfaceFlingerPid) const;
-        void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync);
+        void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
+                          nsecs_t previousPresentTime);
 
         int64_t mToken = FrameTimelineInfo::INVALID_VSYNC_ID;
 
@@ -410,7 +406,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 +413,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 +431,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 +441,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;
@@ -481,12 +475,8 @@
     uint32_t mMaxDisplayFrames;
     std::shared_ptr<TimeStats> mTimeStats;
     const pid_t mSurfaceFlingerPid;
+    nsecs_t mPreviousPresentTime = 0;
     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/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 2fcd821..8b7dc4d 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -129,10 +129,10 @@
     mCurrentState.frameRateSelectionPriority = PRIORITY_UNSET;
     mCurrentState.metadata = args.metadata;
     mCurrentState.shadowRadius = 0.f;
-    mCurrentState.treeHasFrameRateVote = false;
     mCurrentState.fixedTransformHint = ui::Transform::ROT_INVALID;
     mCurrentState.frameTimelineInfo = {};
     mCurrentState.postTime = -1;
+    mCurrentState.destinationFrame.makeInvalid();
 
     if (args.flags & ISurfaceComposerClient::eNoColorFill) {
         // Set an invalid color so there is no color fill.
@@ -202,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;
@@ -234,21 +221,9 @@
 }
 
 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();
+    if (!mRemovedFromCurrentState) {
+        mRemovedFromCurrentState = true;
+        mFlinger->mScheduler->deregisterLayer(this);
     }
 
     mFlinger->markLayerPendingRemovalLocked(this);
@@ -274,7 +249,10 @@
 }
 
 void Layer::addToCurrentState() {
-    mRemovedFromCurrentState = false;
+    if (mRemovedFromCurrentState) {
+        mRemovedFromCurrentState = false;
+        mFlinger->mScheduler->registerLayer(this);
+    }
 
     for (const auto& child : mCurrentChildren) {
         child->addToCurrentState();
@@ -343,55 +321,6 @@
     return reduce(mBounds, activeTransparentRegion);
 }
 
-ui::Transform Layer::getBufferScaleTransform() const {
-    // If the layer is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g.
-    // it isFixedSize) then there may be additional scaling not accounted
-    // for in the layer transform.
-    if (!isFixedSize() || getBuffer() == nullptr) {
-        return {};
-    }
-
-    // If the layer is a buffer state layer, the active width and height
-    // could be infinite. In that case, return the effective transform.
-    const uint32_t activeWidth = getActiveWidth(getDrawingState());
-    const uint32_t activeHeight = getActiveHeight(getDrawingState());
-    if (activeWidth >= UINT32_MAX && activeHeight >= UINT32_MAX) {
-        return {};
-    }
-
-    int bufferWidth = getBuffer()->getWidth();
-    int bufferHeight = getBuffer()->getHeight();
-
-    if (getBufferTransform() & NATIVE_WINDOW_TRANSFORM_ROT_90) {
-        std::swap(bufferWidth, bufferHeight);
-    }
-
-    float sx = activeWidth / static_cast<float>(bufferWidth);
-    float sy = activeHeight / static_cast<float>(bufferHeight);
-
-    ui::Transform extraParentScaling;
-    extraParentScaling.set(sx, 0, 0, sy);
-    return extraParentScaling;
-}
-
-ui::Transform Layer::getTransformWithScale(const ui::Transform& bufferScaleTransform) const {
-    // We need to mirror this scaling to child surfaces or we will break the contract where WM can
-    // treat child surfaces as pixels in the parent surface.
-    if (!isFixedSize() || getBuffer() == nullptr) {
-        return mEffectiveTransform;
-    }
-    return mEffectiveTransform * bufferScaleTransform;
-}
-
-FloatRect Layer::getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const {
-    // We need the pre scaled layer bounds when computing child bounds to make sure the child is
-    // cropped to its parent layer after any buffer transform scaling is applied.
-    if (!isFixedSize() || getBuffer() == nullptr) {
-        return mBounds;
-    }
-    return bufferScaleTransform.inverse().transform(mBounds);
-}
-
 void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform,
                           float parentShadowRadius) {
     const State& s(getDrawingState());
@@ -428,11 +357,8 @@
     // don't pass it to its children.
     const float childShadowRadius = canDrawShadows() ? 0.f : mEffectiveShadowRadius;
 
-    // Add any buffer scaling to the layer's children.
-    ui::Transform bufferScaleTransform = getBufferScaleTransform();
     for (const sp<Layer>& child : mDrawingChildren) {
-        child->computeBounds(getBoundsPreScaling(bufferScaleTransform),
-                             getTransformWithScale(bufferScaleTransform), childShadowRadius);
+        child->computeBounds(mBounds, mEffectiveTransform, childShadowRadius);
     }
 }
 
@@ -559,7 +485,9 @@
             isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
 
     // Force client composition for special cases known only to the front-end.
-    if (isHdrY410() || usesRoundedCorners || drawShadows() || drawingState.blurRegions.size() > 0 ||
+    // Rounded corners no longer force client composition, since we may use a
+    // hole punch so that the layer will appear to have rounded corners.
+    if (isHdrY410() || drawShadows() || drawingState.blurRegions.size() > 0 ||
         compositionState->stretchEffect.hasEffect()) {
         compositionState->forceClientComposition = true;
     }
@@ -658,8 +586,10 @@
     if (!targetSettings.disableBlurs) {
         layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
         layerSettings.blurRegions = getBlurRegions();
+        layerSettings.blurRegionTransform =
+                getActiveTransform(getDrawingState()).inverse().asMatrix4();
     }
-    layerSettings.stretchEffect = getDrawingState().stretchEffect;
+    layerSettings.stretchEffect = getStretchEffect();
     // Record the name of the layer for debugging further down the stack.
     layerSettings.name = getName();
     return layerSettings;
@@ -698,23 +628,6 @@
         return {};
     }
 
-    float casterCornerRadius = shadowLayer.geometry.roundedCornersRadius;
-    const FloatRect& cornerRadiusCropRect = shadowLayer.geometry.roundedCornersCrop;
-    const FloatRect& casterRect = shadowLayer.geometry.boundaries;
-
-    // crop used to set the corner radius may be larger than the content rect. Adjust the corner
-    // radius accordingly.
-    if (casterCornerRadius > 0.f) {
-        float cropRectOffset = std::max(std::abs(cornerRadiusCropRect.top - casterRect.top),
-                                        std::abs(cornerRadiusCropRect.left - casterRect.left));
-        if (cropRectOffset > casterCornerRadius) {
-            casterCornerRadius = 0;
-        } else {
-            casterCornerRadius -= cropRectOffset;
-        }
-        shadowLayer.geometry.roundedCornersRadius = casterCornerRadius;
-    }
-
     return shadowLayer;
 }
 
@@ -775,21 +688,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
 // ----------------------------------------------------------------------------
@@ -808,132 +706,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());
 
@@ -1014,17 +786,21 @@
         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)) {
+    // Translates dest frame into scale and position updates. This helps align geometry calculations
+    // for BufferStateLayer with other layers. This should ideally happen in the client once client
+    // has the display orientation details from WM.
+    updateGeometry();
+
+    if (c.width != s.width || c.height != s.height || !(c.transform == s.transform)) {
         // invalidate and recompute the visible regions if needed
         flags |= Layer::eVisibleRegion;
     }
@@ -1050,11 +826,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;
@@ -1062,7 +837,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
@@ -1070,8 +849,15 @@
         // list.
         addSurfaceFrameDroppedForBuffer(bufferSurfaceFrame);
     }
+    const bool frameRateVoteChanged =
+            mDrawingState.frameRateForLayerTree != stateToCommit.frameRateForLayerTree;
     mDrawingState = stateToCommit;
 
+    if (frameRateVoteChanged) {
+        mFlinger->mScheduler->recordLayerHistory(this, systemTime(),
+                                                 LayerHistory::LayerUpdateType::SetFrameRate);
+    }
+
     // Set the present state for all bufferlessSurfaceFramesTX to Presented. The
     // bufferSurfaceFrameTX will be presented in latchBuffer.
     for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
@@ -1096,20 +882,18 @@
 }
 
 bool Layer::setPosition(float x, float y) {
-    if (mCurrentState.requested_legacy.transform.tx() == x &&
-        mCurrentState.requested_legacy.transform.ty() == y)
-        return false;
+    if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) return false;
     mCurrentState.sequence++;
 
     // We update the requested and active position simultaneously because
     // we want to apply the position portion of the transform matrix immediately,
     // but still delay scaling when resizing a SCALING_MODE_FREEZE layer.
-    mCurrentState.requested_legacy.transform.set(x, y);
+    mCurrentState.transform.set(x, y);
     // Here we directly update the active state
     // unlike other setters, because we store it within
     // the transform, but use different latching rules.
     // b/38182305
-    mCurrentState.active_legacy.transform.set(x, y);
+    mCurrentState.transform.set(x, y);
 
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
@@ -1228,6 +1012,7 @@
     setDefaultBufferSize(mCurrentState.requested_legacy.w, mCurrentState.requested_legacy.h);
     return true;
 }
+
 bool Layer::setAlpha(float alpha) {
     if (mCurrentState.color.a == alpha) return false;
     mCurrentState.sequence++;
@@ -1306,8 +1091,7 @@
         return false;
     }
     mCurrentState.sequence++;
-    mCurrentState.requested_legacy.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx,
-                                                 matrix.dsdy);
+    mCurrentState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
@@ -1476,8 +1260,7 @@
     };
 
     // update parents and children about the vote
-    // First traverse the tree and count how many layers has votes. In addition
-    // activate the layers in Scheduler's LayerHistory for it to check for changes
+    // First traverse the tree and count how many layers has votes.
     int layersWithVote = 0;
     traverseTree([&layersWithVote](Layer* layer) {
         const auto layerVotedWithDefaultCompatibility =
@@ -1497,20 +1280,11 @@
         }
     });
 
-    // Now update the other layers
+    // Now we can update the tree frame rate vote for each layer in the tree
+    const bool treeHasFrameRateVote = layersWithVote > 0;
     bool transactionNeeded = false;
-    traverseTree([layersWithVote, &transactionNeeded, this](Layer* layer) {
-        const bool treeHasFrameRateVote = layersWithVote > 0;
-        if (layer->mCurrentState.treeHasFrameRateVote != treeHasFrameRateVote) {
-            layer->mCurrentState.sequence++;
-            layer->mCurrentState.treeHasFrameRateVote = treeHasFrameRateVote;
-            layer->mCurrentState.modified = true;
-            layer->setTransactionFlags(eTransactionNeeded);
-            transactionNeeded = true;
-
-            mFlinger->mScheduler->recordLayerHistory(layer, systemTime(),
-                                                     LayerHistory::LayerUpdateType::SetFrameRate);
-        }
+    traverseTree([treeHasFrameRateVote, &transactionNeeded](Layer* layer) {
+        transactionNeeded = layer->updateFrameRateForLayerTree(treeHasFrameRateVote);
     });
 
     if (transactionNeeded) {
@@ -1639,52 +1413,43 @@
     return surfaceFrame;
 }
 
-Layer::FrameRate Layer::getFrameRateForLayerTree() const {
-    const auto frameRate = getDrawingState().frameRate;
+bool Layer::updateFrameRateForLayerTree(bool treeHasFrameRateVote) {
+    const auto updateCurrentState = [&](FrameRate frameRate) {
+        if (mCurrentState.frameRateForLayerTree == frameRate) {
+            return false;
+        }
+        mCurrentState.frameRateForLayerTree = frameRate;
+        mCurrentState.sequence++;
+        mCurrentState.modified = true;
+        setTransactionFlags(eTransactionNeeded);
+        return true;
+    };
+
+    const auto frameRate = mCurrentState.frameRate;
     if (frameRate.rate.isValid() || frameRate.type == FrameRateCompatibility::NoVote) {
-        return frameRate;
+        return updateCurrentState(frameRate);
     }
 
     // This layer doesn't have a frame rate. Check if its ancestors have a vote
-    if (sp<Layer> parent = getParent(); parent) {
-        if (const auto parentFrameRate = parent->getFrameRateForLayerTree();
-            parentFrameRate.rate.isValid()) {
-            return parentFrameRate;
+    for (sp<Layer> parent = getParent(); parent; parent = parent->getParent()) {
+        if (parent->mCurrentState.frameRate.rate.isValid()) {
+            return updateCurrentState(parent->mCurrentState.frameRate);
         }
     }
 
     // This layer and its ancestors don't have a frame rate. If one of successors
     // has a vote, return a NoVote for successors to set the vote
-    if (getDrawingState().treeHasFrameRateVote) {
-        return {Fps(0.0f), FrameRateCompatibility::NoVote};
+    if (treeHasFrameRateVote) {
+        return updateCurrentState(FrameRate(Fps(0.0f), FrameRateCompatibility::NoVote));
     }
 
-    return frameRate;
+    return updateCurrentState(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;
+Layer::FrameRate Layer::getFrameRateForLayerTree() const {
+    return getDrawingState().frameRateForLayerTree;
 }
 
-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...
-// ----------------------------------------------------------------------------
-
 bool Layer::isHiddenByPolicy() const {
     const State& s(mDrawingState);
     const auto& parent = mDrawingParent.promote();
@@ -1742,20 +1507,20 @@
     info.mVisibleRegion = getVisibleRegion(display);
     info.mSurfaceDamageRegion = surfaceDamageRegion;
     info.mLayerStack = getLayerStack();
-    info.mX = ds.active_legacy.transform.tx();
-    info.mY = ds.active_legacy.transform.ty();
+    info.mX = ds.transform.tx();
+    info.mY = ds.transform.ty();
     info.mZ = ds.z;
-    info.mWidth = ds.active_legacy.w;
-    info.mHeight = ds.active_legacy.h;
+    info.mWidth = ds.width;
+    info.mHeight = ds.height;
     info.mCrop = ds.crop;
     info.mColor = ds.color;
     info.mFlags = ds.flags;
     info.mPixelFormat = getPixelFormat();
     info.mDataSpace = static_cast<android_dataspace>(getDataSpace());
-    info.mMatrix[0][0] = ds.active_legacy.transform[0][0];
-    info.mMatrix[0][1] = ds.active_legacy.transform[0][1];
-    info.mMatrix[1][0] = ds.active_legacy.transform[1][0];
-    info.mMatrix[1][1] = ds.active_legacy.transform[1][1];
+    info.mMatrix[0][0] = ds.transform[0][0];
+    info.mMatrix[0][1] = ds.transform[0][1];
+    info.mMatrix[1][0] = ds.transform[1][0];
+    info.mMatrix[1][1] = ds.transform[1][1];
     {
         sp<const GraphicBuffer> buffer = getBuffer();
         if (buffer != 0) {
@@ -1949,37 +1714,10 @@
     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;
-        child->computeBounds(newParent->mBounds,
-                             newParent->getTransformWithScale(newParent->getBufferScaleTransform()),
+        child->computeBounds(newParent->mBounds, newParent->mEffectiveTransform,
                              newParent->mEffectiveShadowRadius);
     }
 }
@@ -2290,8 +2028,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 {
@@ -2388,14 +2131,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) {
@@ -2535,14 +2270,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()) {
@@ -2555,7 +2298,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;
@@ -2652,6 +2395,8 @@
     ui::Transform toPhysicalDisplay;
     if (display) {
         toPhysicalDisplay = display->getTransform();
+        info.displayWidth = display->getWidth();
+        info.displayHeight = display->getHeight();
     }
     fillInputFrameInfo(info, toPhysicalDisplay);
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 4a105eb..8139d8a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -166,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.
@@ -208,7 +203,7 @@
 
         Region transparentRegionHint;
 
-        sp<GraphicBuffer> buffer;
+        std::shared_ptr<renderengine::ExternalTexture> buffer;
         client_cache_t clientCacheId;
         sp<Fence> acquireFence;
         std::shared_ptr<FenceTime> acquireFenceTime;
@@ -244,8 +239,8 @@
 
         FrameRate frameRate;
 
-        // Indicates whether parents / children of this layer had set FrameRate
-        bool treeHasFrameRateVote;
+        // The combined frame rate of parents / children of this layer
+        FrameRate frameRateForLayerTree;
 
         // Set by window manager indicating the layer and all its children are
         // in a different orientation than the display. The hint suggests that
@@ -279,6 +274,9 @@
 
         // Stretch effect to apply to this layer
         StretchEffect stretchEffect;
+
+        Rect bufferCrop;
+        Rect destinationFrame;
     };
 
     /*
@@ -398,9 +396,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);
@@ -412,10 +407,11 @@
     // Used only to set BufferStateLayer state
     virtual bool setTransform(uint32_t /*transform*/) { return false; };
     virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { 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;
@@ -489,12 +485,9 @@
     // to avoid grabbing the lock again to avoid deadlock
     virtual bool isCreatedFromMainThread() const { return false; }
 
-    virtual Geometry getActiveGeometry(const Layer::State& s) const { return s.active_legacy; }
-    virtual uint32_t getActiveWidth(const Layer::State& s) const { return s.active_legacy.w; }
-    virtual uint32_t getActiveHeight(const Layer::State& s) const { return s.active_legacy.h; }
-    virtual ui::Transform getActiveTransform(const Layer::State& s) const {
-        return s.active_legacy.transform;
-    }
+    uint32_t getActiveWidth(const Layer::State& s) const { return s.width; }
+    uint32_t getActiveHeight(const Layer::State& s) const { return s.height; }
+    ui::Transform getActiveTransform(const Layer::State& s) const { return s.transform; }
     virtual Region getActiveTransparentRegion(const Layer::State& s) const {
         return s.activeTransparentRegion_legacy;
     }
@@ -578,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.
@@ -615,7 +600,8 @@
     // ignored.
     virtual RoundedCornerState getRoundedCornerState() const;
 
-    virtual void notifyAvailableFrames(nsecs_t /*expectedPresentTime*/) {}
+    bool hasRoundedCorners() const override { return getRoundedCornerState().radius > .0f; }
+
     virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
     /**
      * Return whether this layer needs an input info. For most layer types
@@ -637,8 +623,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
@@ -663,16 +647,6 @@
     // Compute bounds for the layer and cache the results.
     void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
 
-    // Returns the buffer scale transform if a scaling mode is set.
-    ui::Transform getBufferScaleTransform() const;
-
-    // Get effective layer transform, taking into account all its parent transform with any
-    // scaling if the parent scaling more is not NATIVE_WINDOW_SCALING_MODE_FREEZE.
-    ui::Transform getTransformWithScale(const ui::Transform& bufferScaleTransform) const;
-
-    // Returns the bounds of the layer without any buffer scaling.
-    FloatRect getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const;
-
     int32_t getSequence() const override { return sequence; }
 
     // For tracing.
@@ -728,7 +702,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
@@ -901,69 +875,13 @@
     bool setStretchEffect(const StretchEffect& effect);
     StretchEffect getStretchEffect() const;
 
+    virtual bool setBufferCrop(const Rect& /* bufferCrop */) { return false; }
+    virtual bool setDestinationFrame(const Rect& /* destinationFrame */) { return false; }
     virtual std::atomic<int32_t>* getPendingBufferCounter() { return nullptr; }
     virtual std::string getPendingBufferCounterName() { return ""; }
+    virtual void updateGeometry() {}
 
 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
@@ -982,7 +900,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>&) {}
 
@@ -1025,20 +942,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;
@@ -1049,14 +954,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;
@@ -1118,6 +1019,8 @@
     // Used in buffer stuffing analysis in FrameTimeline.
     nsecs_t mLastLatchTime = 0;
 
+    mutable bool mCurrentStateModified = false;
+
 private:
     virtual void setTransformHint(ui::Transform::RotationFlags) {}
 
@@ -1142,7 +1045,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.
@@ -1155,6 +1057,8 @@
     // Fills in the frame and transform info for the InputWindowInfo
     void fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay);
 
+    bool updateFrameRateForLayerTree(bool treeHasFrameRateVote);
+
     // Cached properties computed from drawing state
     // Effective transform taking into account parent transforms and any parent scaling, which is
     // a transform from the current layer coordinate space to display(screen) coordinate space.
@@ -1190,7 +1094,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 7a3e433..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];
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..653aca6 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -56,16 +56,14 @@
     noWorkNeeded,
     idleTimerWaiting,
     waitForQuietFrame,
-    waitForZeroPhase,
     waitForSamplePhase,
     sample
 };
 
-constexpr auto timeForRegionSampling = 5000000ns;
-constexpr auto maxRegionSamplingSkips = 10;
 constexpr auto defaultRegionSamplingWorkDuration = 3ms;
 constexpr auto defaultRegionSamplingPeriod = 100ms;
 constexpr auto defaultRegionSamplingTimerTimeout = 100ms;
+constexpr auto maxRegionSamplingDelay = 100ms;
 // TODO: (b/127403193) duration to string conversion could probably be constexpr
 template <typename Rep, typename Per>
 inline std::string toNsString(std::chrono::duration<Rep, Per> t) {
@@ -99,97 +97,22 @@
     }
 }
 
-struct SamplingOffsetCallback : VSyncSource::Callback {
-    SamplingOffsetCallback(RegionSamplingThread& samplingThread, Scheduler& scheduler,
-                           std::chrono::nanoseconds targetSamplingWorkDuration)
-          : mRegionSamplingThread(samplingThread),
-            mTargetSamplingWorkDuration(targetSamplingWorkDuration),
-            mVSyncSource(scheduler.makePrimaryDispSyncSource("SamplingThreadDispSyncListener", 0ns,
-                                                             0ns,
-                                                             /*traceVsync=*/false)) {
-        mVSyncSource->setCallback(this);
-    }
-
-    ~SamplingOffsetCallback() { stopVsyncListener(); }
-
-    SamplingOffsetCallback(const SamplingOffsetCallback&) = delete;
-    SamplingOffsetCallback& operator=(const SamplingOffsetCallback&) = delete;
-
-    void startVsyncListener() {
-        std::lock_guard lock(mMutex);
-        if (mVsyncListening) return;
-
-        mPhaseIntervalSetting = Phase::ZERO;
-        mVSyncSource->setVSyncEnabled(true);
-        mVsyncListening = true;
-    }
-
-    void stopVsyncListener() {
-        std::lock_guard lock(mMutex);
-        stopVsyncListenerLocked();
-    }
-
-private:
-    void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ {
-        if (!mVsyncListening) return;
-
-        mVSyncSource->setVSyncEnabled(false);
-        mVsyncListening = false;
-    }
-
-    void onVSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/,
-                      nsecs_t /*deadlineTimestamp*/) final {
-        std::unique_lock<decltype(mMutex)> lock(mMutex);
-
-        if (mPhaseIntervalSetting == Phase::ZERO) {
-            ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
-            mPhaseIntervalSetting = Phase::SAMPLING;
-            mVSyncSource->setDuration(mTargetSamplingWorkDuration, 0ns);
-            return;
-        }
-
-        if (mPhaseIntervalSetting == Phase::SAMPLING) {
-            mPhaseIntervalSetting = Phase::ZERO;
-            mVSyncSource->setDuration(0ns, 0ns);
-            stopVsyncListenerLocked();
-            lock.unlock();
-            mRegionSamplingThread.notifySamplingOffset();
-            return;
-        }
-    }
-
-    RegionSamplingThread& mRegionSamplingThread;
-    const std::chrono::nanoseconds mTargetSamplingWorkDuration;
-    mutable std::mutex mMutex;
-    enum class Phase {
-        ZERO,
-        SAMPLING
-    } mPhaseIntervalSetting /*GUARDED_BY(mMutex) macro doesnt work with unique_lock?*/
-            = Phase::ZERO;
-    bool mVsyncListening /*GUARDED_BY(mMutex)*/ = false;
-    std::unique_ptr<VSyncSource> mVSyncSource;
-};
-
-RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
-                                           const TimingTunables& tunables)
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, const TimingTunables& tunables)
       : mFlinger(flinger),
-        mScheduler(scheduler),
         mTunables(tunables),
         mIdleTimer(
                 "RegSampIdle",
                 std::chrono::duration_cast<std::chrono::milliseconds>(
                         mTunables.mSamplingTimerTimeout),
                 [] {}, [this] { checkForStaleLuma(); }),
-        mPhaseCallback(std::make_unique<SamplingOffsetCallback>(*this, mScheduler,
-                                                                tunables.mSamplingDuration)),
-        lastSampleTime(0ns) {
+        mLastSampleTime(0ns) {
     mThread = std::thread([this]() { threadMain(); });
     pthread_setname_np(mThread.native_handle(), "RegionSampling");
     mIdleTimer.start();
 }
 
-RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler)
-      : RegionSamplingThread(flinger, scheduler,
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger)
+      : RegionSamplingThread(flinger,
                              TimingTunables{defaultRegionSamplingWorkDuration,
                                             defaultRegionSamplingPeriod,
                                             defaultRegionSamplingTimerTimeout}) {}
@@ -224,48 +147,46 @@
 void RegionSamplingThread::checkForStaleLuma() {
     std::lock_guard lock(mThreadControlMutex);
 
-    if (mDiscardedFrames > 0) {
-        ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForZeroPhase));
-        mDiscardedFrames = 0;
-        mPhaseCallback->startVsyncListener();
+    if (mSampleRequestTime.has_value()) {
+        ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
+        mSampleRequestTime.reset();
+        mFlinger.scheduleRegionSamplingThread();
     }
 }
 
-void RegionSamplingThread::notifyNewContent() {
-    doSample();
+void RegionSamplingThread::onCompositionComplete(
+        std::optional<std::chrono::steady_clock::time_point> samplingDeadline) {
+    doSample(samplingDeadline);
 }
 
-void RegionSamplingThread::notifySamplingOffset() {
-    doSample();
-}
-
-void RegionSamplingThread::doSample() {
+void RegionSamplingThread::doSample(
+        std::optional<std::chrono::steady_clock::time_point> samplingDeadline) {
     std::lock_guard lock(mThreadControlMutex);
-    auto now = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC));
-    if (lastSampleTime + mTunables.mSamplingPeriod > now) {
+    const auto now = std::chrono::steady_clock::now();
+    if (mLastSampleTime + mTunables.mSamplingPeriod > now) {
+        // content changed, but we sampled not too long ago, so we need to sample some time in the
+        // future.
         ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting));
-        if (mDiscardedFrames == 0) mDiscardedFrames++;
+        mSampleRequestTime = now;
         return;
     }
-    if (mDiscardedFrames < maxRegionSamplingSkips) {
+    if (!mSampleRequestTime.has_value() || now - *mSampleRequestTime < maxRegionSamplingDelay) {
         // If there is relatively little time left for surfaceflinger
         // until the next vsync deadline, defer this sampling work
         // to a later frame, when hopefully there will be more time.
-        const DisplayStatInfo stats = mScheduler.getDisplayStatInfo(systemTime());
-        if (std::chrono::nanoseconds(stats.vsyncTime) - now < timeForRegionSampling) {
+        if (samplingDeadline.has_value() && now + mTunables.mSamplingDuration > *samplingDeadline) {
             ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame));
-            mDiscardedFrames++;
+            mSampleRequestTime = mSampleRequestTime.value_or(now);
             return;
         }
     }
 
     ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample));
 
-    mDiscardedFrames = 0;
-    lastSampleTime = now;
+    mSampleRequestTime.reset();
+    mLastSampleTime = now;
 
     mIdleTimer.reset();
-    mPhaseCallback->stopVsyncListener();
 
     mSampleRequested = true;
     mCondition.notify_one();
@@ -438,18 +359,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 +390,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 +402,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..2231853 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 {
@@ -61,9 +63,8 @@
     struct EnvironmentTimingTunables : TimingTunables {
         EnvironmentTimingTunables();
     };
-    explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
-                                  const TimingTunables& tunables);
-    explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler);
+    explicit RegionSamplingThread(SurfaceFlinger& flinger, const TimingTunables& tunables);
+    explicit RegionSamplingThread(SurfaceFlinger& flinger);
 
     ~RegionSamplingThread();
 
@@ -74,12 +75,11 @@
     // Remove the listener to stop receiving median luma notifications.
     void removeListener(const sp<IRegionSamplingListener>& listener);
 
-    // Notifies sampling engine that new content is available. This will trigger a sampling
-    // pass at some point in the future.
-    void notifyNewContent();
-
-    // Notifies the sampling engine that it has a good timing window in which to sample.
-    void notifySamplingOffset();
+    // Notifies sampling engine that composition is done and new content is
+    // available, and the deadline for the sampling work on the main thread to
+    // be completed without eating the budget of another frame.
+    void onCompositionComplete(
+            std::optional<std::chrono::steady_clock::time_point> samplingDeadline);
 
 private:
     struct Descriptor {
@@ -97,7 +97,7 @@
             const sp<GraphicBuffer>& buffer, const Point& leftTop,
             const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation);
 
-    void doSample();
+    void doSample(std::optional<std::chrono::steady_clock::time_point> samplingDeadline);
     void binderDied(const wp<IBinder>& who) override;
     void checkForStaleLuma();
 
@@ -105,24 +105,23 @@
     void threadMain();
 
     SurfaceFlinger& mFlinger;
-    Scheduler& mScheduler;
     const TimingTunables mTunables;
     scheduler::OneShotTimer mIdleTimer;
 
-    std::unique_ptr<SamplingOffsetCallback> const mPhaseCallback;
-
     std::thread mThread;
 
     std::mutex mThreadControlMutex;
     std::condition_variable_any mCondition;
     bool mRunning GUARDED_BY(mThreadControlMutex) = true;
     bool mSampleRequested GUARDED_BY(mThreadControlMutex) = false;
-    uint32_t mDiscardedFrames GUARDED_BY(mThreadControlMutex) = 0;
-    std::chrono::nanoseconds lastSampleTime GUARDED_BY(mThreadControlMutex);
+    std::optional<std::chrono::steady_clock::time_point> mSampleRequestTime
+            GUARDED_BY(mThreadControlMutex);
+    std::chrono::steady_clock::time_point mLastSampleTime GUARDED_BY(mThreadControlMutex);
 
     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/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index ce5c31a..50b38c9 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -60,8 +60,7 @@
                 mRegistration.schedule({.workDuration = mWorkDuration.count(),
                                         .readyDuration = mReadyDuration.count(),
                                         .earliestVsync = mLastCallTime.count()});
-        LOG_ALWAYS_FATAL_IF((scheduleResult != scheduler::ScheduleResult::Scheduled),
-                            "Error scheduling callback: rc %X", scheduleResult);
+        LOG_ALWAYS_FATAL_IF((!scheduleResult.has_value()), "Error scheduling callback");
     }
 
     void stop() {
@@ -100,8 +99,7 @@
                     mRegistration.schedule({.workDuration = mWorkDuration.count(),
                                             .readyDuration = mReadyDuration.count(),
                                             .earliestVsync = vsyncTime});
-            LOG_ALWAYS_FATAL_IF((scheduleResult != ScheduleResult::Scheduled),
-                                "Error rescheduling callback: rc %X", scheduleResult);
+            LOG_ALWAYS_FATAL_IF(!scheduleResult.has_value(), "Error rescheduling callback");
         }
     }
 
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 f4bc2a1..0563795 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -84,8 +84,11 @@
 LayerHistory::~LayerHistory() = default;
 
 void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
-    auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
     std::lock_guard lock(mLock);
+    for (const auto& info : mLayerInfos) {
+        LOG_ALWAYS_FATAL_IF(info.first == layer, "%s already registered", layer->getName().c_str());
+    }
+    auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
     mLayerInfos.emplace_back(layer, std::move(info));
 }
 
@@ -94,7 +97,7 @@
 
     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);
+    LOG_ALWAYS_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) {
@@ -111,7 +114,11 @@
 
     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);
+    if (it == mLayerInfos.end()) {
+        // Offscreen layer
+        ALOGV("LayerHistory::record: %s not registered", layer->getName().c_str());
+        return;
+    }
 
     const auto& info = it->second;
     const auto layerProps = LayerInfo::LayerProps{
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 3630ad8..989bf4e 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -235,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 ba03c89..34cc389 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -272,7 +272,7 @@
 
     // Used for sanitizing the heuristic data. If two frames are less than
     // this period apart from each other they'll be considered as duplicates.
-    static constexpr nsecs_t kMinPeriodBetweenFrames = Fps(120.f).getPeriodNsecs();
+    static constexpr nsecs_t kMinPeriodBetweenFrames = Fps(240.f).getPeriodNsecs();
     // Used for sanitizing the heuristic data. If two frames are more than
     // this period apart from each other, the interval between them won't be
     // taken into account when calculating average frame rate.
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 7ff0ddf..4d51125 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -23,7 +23,6 @@
 #include <utils/threads.h>
 
 #include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
 
 #include "EventThread.h"
 #include "FrameTimeline.h"
@@ -33,27 +32,32 @@
 namespace android::impl {
 
 void MessageQueue::Handler::dispatchRefresh() {
-    if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) {
+    if ((mEventMask.fetch_or(eventMaskRefresh) & eventMaskRefresh) == 0) {
         mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH));
     }
 }
 
 void MessageQueue::Handler::dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp) {
-    if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
+    if ((mEventMask.fetch_or(eventMaskInvalidate) & eventMaskInvalidate) == 0) {
         mVsyncId = vsyncId;
         mExpectedVSyncTime = expectedVSyncTimestamp;
         mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
     }
 }
 
+bool MessageQueue::Handler::invalidatePending() {
+    constexpr auto pendingMask = eventMaskInvalidate | eventMaskRefresh;
+    return (mEventMask.load() & pendingMask) != 0;
+}
+
 void MessageQueue::Handler::handleMessage(const Message& message) {
     switch (message.what) {
         case INVALIDATE:
-            android_atomic_and(~eventMaskInvalidate, &mEventMask);
+            mEventMask.fetch_and(~eventMaskInvalidate);
             mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
             break;
         case REFRESH:
-            android_atomic_and(~eventMaskRefresh, &mEventMask);
+            mEventMask.fetch_and(~eventMaskRefresh);
             mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
             break;
     }
@@ -106,7 +110,7 @@
     {
         std::lock_guard lock(mVsync.mutex);
         mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
-        mVsync.mScheduled = false;
+        mVsync.scheduled = false;
     }
     mHandler->dispatchInvalidate(mVsync.tokenManager->generateTokenForPredictions(
                                          {targetWakeupTime, readyTime, vsyncTime}),
@@ -131,9 +135,10 @@
     ATRACE_CALL();
     std::lock_guard lock(mVsync.mutex);
     mVsync.workDuration = workDuration;
-    if (mVsync.mScheduled) {
-        mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0,
-                                       mVsync.lastCallbackTime.count()});
+    if (mVsync.scheduled) {
+        mVsync.expectedWakeupTime = mVsync.registration->schedule(
+                {mVsync.workDuration.get().count(),
+                 /*readyDuration=*/0, mVsync.lastCallbackTime.count()});
     }
 }
 
@@ -176,10 +181,11 @@
     }
 
     std::lock_guard lock(mVsync.mutex);
-    mVsync.mScheduled = true;
-    mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
-                                   .readyDuration = 0,
-                                   .earliestVsync = mVsync.lastCallbackTime.count()});
+    mVsync.scheduled = true;
+    mVsync.expectedWakeupTime =
+            mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
+                                           .readyDuration = 0,
+                                           .earliestVsync = mVsync.lastCallbackTime.count()});
 }
 
 void MessageQueue::refresh() {
@@ -200,4 +206,19 @@
     }
 }
 
+std::optional<std::chrono::steady_clock::time_point> MessageQueue::nextExpectedInvalidate() {
+    if (mHandler->invalidatePending()) {
+        return std::chrono::steady_clock::now();
+    }
+
+    std::lock_guard lock(mVsync.mutex);
+    if (mVsync.scheduled) {
+        LOG_ALWAYS_FATAL_IF(!mVsync.expectedWakeupTime.has_value(), "callback was never scheduled");
+        const auto expectedWakeupTime = std::chrono::nanoseconds(*mVsync.expectedWakeupTime);
+        return std::optional<std::chrono::steady_clock::time_point>(expectedWakeupTime);
+    }
+
+    return std::nullopt;
+}
+
 } // namespace android::impl
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 2934af0..58ce9b9 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -72,6 +72,7 @@
     virtual void postMessage(sp<MessageHandler>&&) = 0;
     virtual void invalidate() = 0;
     virtual void refresh() = 0;
+    virtual std::optional<std::chrono::steady_clock::time_point> nextExpectedInvalidate() = 0;
 };
 
 // ---------------------------------------------------------------------------
@@ -81,9 +82,13 @@
 class MessageQueue : public android::MessageQueue {
 protected:
     class Handler : public MessageHandler {
-        enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 };
+        enum : uint32_t {
+            eventMaskInvalidate = 0x1,
+            eventMaskRefresh = 0x2,
+            eventMaskTransaction = 0x4
+        };
         MessageQueue& mQueue;
-        int32_t mEventMask;
+        std::atomic<uint32_t> mEventMask;
         std::atomic<int64_t> mVsyncId;
         std::atomic<nsecs_t> mExpectedVSyncTime;
 
@@ -92,6 +97,7 @@
         void handleMessage(const Message& message) override;
         virtual void dispatchRefresh();
         virtual void dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp);
+        virtual bool invalidatePending();
     };
 
     friend class Handler;
@@ -107,7 +113,8 @@
         TracedOrdinal<std::chrono::nanoseconds> workDuration
                 GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
         std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0};
-        bool mScheduled GUARDED_BY(mutex) = false;
+        bool scheduled GUARDED_BY(mutex) = false;
+        std::optional<nsecs_t> expectedWakeupTime GUARDED_BY(mutex);
         TracedOrdinal<int> value = {"VSYNC-sf", 0};
     };
 
@@ -141,6 +148,8 @@
 
     // sends REFRESH message at next VSYNC
     void refresh() override;
+
+    std::optional<std::chrono::steady_clock::time_point> nextExpectedInvalidate() override;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
index ce3b0c6..16f041a 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -15,7 +15,6 @@
  */
 
 #include "OneShotTimer.h"
-
 #include <utils/Log.h>
 #include <utils/Timers.h>
 #include <chrono>
@@ -42,11 +41,14 @@
 
 OneShotTimer::OneShotTimer(std::string name, const Interval& interval,
                            const ResetCallback& resetCallback,
-                           const TimeoutCallback& timeoutCallback)
-      : mName(std::move(name)),
+                           const TimeoutCallback& timeoutCallback, std::unique_ptr<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 +114,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 +130,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..09265bb 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -20,6 +20,7 @@
 #include <chrono>
 #include <condition_variable>
 #include <thread>
+#include "../Clock.h"
 
 #include <android-base/thread_annotations.h>
 
@@ -37,7 +38,8 @@
     using TimeoutCallback = std::function<void()>;
 
     OneShotTimer(std::string name, const Interval& interval, const ResetCallback& resetCallback,
-                 const TimeoutCallback& timeoutCallback);
+                 const TimeoutCallback& timeoutCallback,
+                 std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>());
     ~OneShotTimer();
 
     // Initializes and turns on the idle timer.
@@ -78,6 +80,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/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 8426737..fac2c65 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -248,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));
 }
 
@@ -444,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);
@@ -563,13 +583,9 @@
 
     scheduler::LayerHistory::LayerVoteType voteType;
 
-    if (layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
+    if (!mOptions.useContentDetection ||
+        layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
         voteType = scheduler::LayerHistory::LayerVoteType::NoVote;
-    } else if (!mOptions.useContentDetection) {
-        // If the content detection feature is off, all layers are registered at Max. We still keep
-        // the layer history, since we use it for other features (like Frame Rate API), so layers
-        // still need to be registered.
-        voteType = scheduler::LayerHistory::LayerVoteType::Max;
     } else if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) {
         // Running Wallpaper at Min is considered as part of content detection.
         voteType = scheduler::LayerHistory::LayerVoteType::Min;
@@ -577,6 +593,9 @@
         voteType = scheduler::LayerHistory::LayerVoteType::Heuristic;
     }
 
+    // If the content detection feature is off, we still keep the layer history,
+    // since we use it for other features (like Frame Rate API), so layers
+    // still need to be registered.
     mLayerHistory->registerLayer(layer, voteType);
 }
 
@@ -907,4 +926,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 d4932e7..49d3d93 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -149,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;
@@ -240,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/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 9d71103..b52706f 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -16,8 +16,10 @@
 
 #pragma once
 
+#include <utils/Log.h>
 #include <utils/Timers.h>
 #include <functional>
+#include <optional>
 #include <string>
 
 #include "StrongTyping.h"
@@ -26,7 +28,8 @@
 class TimeKeeper;
 class VSyncTracker;
 
-enum class ScheduleResult { Scheduled, CannotSchedule, Error };
+using ScheduleResult = std::optional<nsecs_t>;
+
 enum class CancelResult { Cancelled, TooLate, Error };
 
 /*
@@ -121,11 +124,8 @@
      *
      * \param [in] token           The callback to schedule.
      * \param [in] scheduleTiming  The timing information for this schedule call
-     * \return                     A ScheduleResult::Scheduled if callback was scheduled.
-     *                             A ScheduleResult::CannotSchedule
-     *                             if (workDuration + readyDuration - earliestVsync) is in the past,
-     * or if a callback was dispatched for the predictedVsync already. A ScheduleResult::Error if
-     * there was another error.
+     * \return                     The expected callback time if a callback was scheduled.
+     *                             std::nullopt if the callback is not registered.
      */
     virtual ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) = 0;
 
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index ca6ea27..28be962 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -26,6 +26,20 @@
 namespace android::scheduler {
 using base::StringAppendF;
 
+namespace {
+nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime,
+                                const VSyncDispatch::ScheduleTiming& timing) {
+    return nextVsyncTime - timing.readyDuration - timing.workDuration;
+}
+
+nsecs_t getExpectedCallbackTime(VSyncTracker& tracker, nsecs_t now,
+                                const VSyncDispatch::ScheduleTiming& timing) {
+    const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
+            std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
+    return getExpectedCallbackTime(nextVsyncTime, timing);
+}
+} // namespace
+
 VSyncDispatch::~VSyncDispatch() = default;
 VSyncTracker::~VSyncTracker() = default;
 TimeKeeper::~TimeKeeper() = default;
@@ -74,7 +88,7 @@
     bool const wouldSkipAVsyncTarget =
             mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
     if (wouldSkipAVsyncTarget) {
-        return ScheduleResult::Scheduled;
+        return getExpectedCallbackTime(nextVsyncTime, timing);
     }
 
     bool const alreadyDispatchedForVsync = mLastDispatchTime &&
@@ -89,7 +103,7 @@
     auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
     mScheduleTiming = timing;
     mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
-    return ScheduleResult::Scheduled;
+    return getExpectedCallbackTime(nextVsyncTime, timing);
 }
 
 void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) {
@@ -317,7 +331,7 @@
 
 ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
                                                  ScheduleTiming scheduleTiming) {
-    auto result = ScheduleResult::Error;
+    ScheduleResult result;
     {
         std::lock_guard lock(mMutex);
 
@@ -333,11 +347,11 @@
         auto const rearmImminent = now > mIntendedWakeupTime;
         if (CC_UNLIKELY(rearmImminent)) {
             callback->addPendingWorkloadUpdate(scheduleTiming);
-            return ScheduleResult::Scheduled;
+            return getExpectedCallbackTime(mTracker, now, scheduleTiming);
         }
 
         result = callback->schedule(scheduleTiming, mTracker, now);
-        if (result == ScheduleResult::CannotSchedule) {
+        if (!result.has_value()) {
             return result;
         }
 
@@ -416,7 +430,7 @@
 
 ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
     if (!mValidToken) {
-        return ScheduleResult::Error;
+        return std::nullopt;
     }
     return mDispatch.get().schedule(mToken, scheduleTiming);
 }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1b7158b..a5b7107 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>
@@ -288,6 +287,8 @@
 const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
 const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS");
 const String16 sDump("android.permission.DUMP");
+const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT");
+
 const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
 
 // ---------------------------------------------------------------------------
@@ -307,20 +308,7 @@
 Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
 ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
 bool SurfaceFlinger::useFrameRateApi;
-
-std::string getHwcServiceName() {
-    char value[PROPERTY_VALUE_MAX] = {};
-    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";
-}
+bool SurfaceFlinger::enableSdrDimming;
 
 std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
     switch(displayColorSetting) {
@@ -344,8 +332,6 @@
             PermissionCache::checkPermission(sRotateSurfaceFlinger, pid, uid);
 }
 
-SurfaceFlingerBE::SurfaceFlingerBE() : mHwcServiceName(getHwcServiceName()) {}
-
 SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
       : mFactory(factory),
         mInterceptor(mFactory.createSurfaceInterceptor()),
@@ -354,8 +340,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(); });
 }
 
@@ -395,6 +384,12 @@
     mColorSpaceAgnosticDataspace =
             static_cast<ui::Dataspace>(color_space_agnostic_dataspace(Dataspace::UNKNOWN));
 
+    mLayerCachingEnabled = [] {
+        const bool enable =
+                android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false);
+        return base::GetBoolProperty(std::string("debug.sf.enable_layer_caching"), enable);
+    }();
+
     useContextPriority = use_context_priority(true);
 
     using Values = SurfaceFlingerProperties::primary_display_orientation_values;
@@ -469,12 +464,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);
     }
 
@@ -484,6 +480,9 @@
     base::SetProperty(KERNEL_IDLE_TIMER_PROP, mKernelIdleTimerEnabled ? "true" : "false");
 
     mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
+
+    // Debug property overrides ro. property
+    enableSdrDimming = property_get_bool("debug.sf.enable_sdr_dimming", enable_sdr_dimming(false));
 }
 
 SurfaceFlinger::~SurfaceFlinger() = default;
@@ -639,7 +638,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 +691,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 +735,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();
@@ -1306,6 +1309,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,
@@ -1484,8 +1492,13 @@
     }
 
     return ftl::chain(schedule([=]() MAIN_THREAD {
-               if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
-                   return getHwComposer().setDisplayBrightness(*displayId,
+               if (const auto display = getDisplayDeviceLocked(displayToken)) {
+                   if (enableSdrDimming) {
+                       display->getCompositionDisplay()
+                               ->setDisplayBrightness(brightness.sdrWhitePointNits,
+                                                      brightness.displayBrightnessNits);
+                   }
+                   return getHwComposer().setDisplayBrightness(display->getPhysicalId(),
                                                                brightness.displayBrightness);
                } else {
                    ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
@@ -1707,11 +1720,11 @@
 }
 
 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
-    return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? mPreviousPresentFences[0]
-                                                          : mPreviousPresentFences[1];
+    const auto now = systemTime();
+    const auto vsyncPeriod = mScheduler->getDisplayStatInfo(now).vsyncPeriod;
+    const bool expectedPresentTimeIsTheNextVsync = mExpectedPresentTime - now <= vsyncPeriod;
+    return expectedPresentTimeIsTheNextVsync ? mPreviousPresentFences[0]
+                                             : mPreviousPresentFences[1];
 }
 
 bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
@@ -1745,12 +1758,6 @@
 }
 
 void SurfaceFlinger::onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime) {
-    const auto vsyncIn = [&] {
-        if (!ATRACE_ENABLED()) return 0.f;
-        return (expectedVSyncTime - systemTime()) / 1e6f;
-    }();
-
-    ATRACE_FORMAT("onMessageReceived %" PRId64 " vsyncIn %.2fms", vsyncId, vsyncIn);
     switch (what) {
         case MessageQueue::INVALIDATE: {
             onMessageInvalidate(vsyncId, expectedVSyncTime);
@@ -1764,8 +1771,6 @@
 }
 
 void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime) {
-    ATRACE_CALL();
-
     const nsecs_t frameStart = systemTime();
     // calculate the expected present time once and use the cached
     // value throughout this frame to make sure all layers are
@@ -1780,6 +1785,13 @@
     const nsecs_t lastScheduledPresentTime = mScheduledPresentTime;
     mScheduledPresentTime = expectedVSyncTime;
 
+    const auto vsyncIn = [&] {
+        if (!ATRACE_ENABLED()) return 0.f;
+        return (mExpectedPresentTime - systemTime()) / 1e6f;
+    }();
+    ATRACE_FORMAT("onMessageInvalidate %" PRId64 " vsyncIn %.2fms%s", vsyncId, vsyncIn,
+                  mExpectedPresentTime == expectedVSyncTime ? "" : " (adjusted)");
+
     // When Backpressure propagation is enabled we want to give a small grace period
     // for the present fence to fire instead of just giving up on this frame to handle cases
     // where present fence is just about to get signaled.
@@ -1814,10 +1826,6 @@
     if (frameMissed) {
         mFrameMissedCount++;
         mTimeStats->incrementMissedFrames();
-        if (mMissedFrameJankCount == 0) {
-            mMissedFrameJankStart = systemTime();
-        }
-        mMissedFrameJankCount++;
     }
 
     if (hwcFrameMissed) {
@@ -1849,37 +1857,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;
@@ -1926,7 +1903,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
@@ -1944,6 +1920,7 @@
         mRefreshPending = true;
         onMessageRefresh();
     }
+    notifyRegionSamplingThread();
 }
 
 bool SurfaceFlinger::handleMessageTransaction() {
@@ -2012,6 +1989,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
@@ -2072,6 +2051,9 @@
     ATRACE_CALL();
     bool refreshNeeded = handlePageFlip();
 
+    // Send on commit callbacks
+    mTransactionCallbackInvoker.sendCallbacks();
+
     if (mVisibleRegionsDirty) {
         computeLayerBounds();
     }
@@ -2173,7 +2155,7 @@
     // 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(), mPreviousPresentFences[0].fenceTime,
-                                 glCompositionDoneFenceTime != FenceTime::NO_FENCE);
+                                 glCompositionDoneFenceTime);
 
     nsecs_t dequeueReadyTime = systemTime();
     for (const auto& layer : mLayersWithQueuedFrames) {
@@ -2226,20 +2208,10 @@
         mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
             if (layer->isVisible() &&
                 compositionDisplay->belongsInOutput(layer->getCompositionEngineLayerFE())) {
-                bool isHdr = false;
-                switch (layer->getDataSpace()) {
-                    case ui::Dataspace::BT2020:
-                    case ui::Dataspace::BT2020_HLG:
-                    case ui::Dataspace::BT2020_PQ:
-                    case ui::Dataspace::BT2020_ITU:
-                    case ui::Dataspace::BT2020_ITU_HLG:
-                    case ui::Dataspace::BT2020_ITU_PQ:
-                        isHdr = true;
-                        break;
-                    default:
-                        isHdr = false;
-                        break;
-                }
+                const Dataspace transfer =
+                        static_cast<Dataspace>(layer->getDataSpace() & Dataspace::TRANSFER_MASK);
+                const bool isHdr = (transfer == Dataspace::TRANSFER_ST2084 ||
+                                    transfer == Dataspace::TRANSFER_HLG);
 
                 if (isHdr) {
                     info.numberOfHdrLayers++;
@@ -2302,14 +2274,6 @@
     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;
     }
@@ -2349,10 +2313,6 @@
         }
     }
 
-    if (mLumaSampling && mRegionSamplingThread) {
-        mRegionSamplingThread->notifyNewContent();
-    }
-
     // Even though ATRACE_INT64 already checks if tracing is enabled, it doesn't prevent the
     // side-effect of getTotalSize(), so we check that again here
     if (ATRACE_ENABLED()) {
@@ -2686,6 +2646,7 @@
     builder.setGpuVirtualDisplayIdGenerator(mGpuVirtualDisplayIdGenerator);
     builder.setName(state.displayName);
     const auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
+    compositionDisplay->setLayerCachingEnabled(mLayerCachingEnabled);
 
     sp<compositionengine::DisplaySurface> displaySurface;
     sp<IGraphicBufferProducer> producer;
@@ -2802,6 +2763,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) {
@@ -2859,23 +2821,19 @@
 }
 
 void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) {
-    const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
+    // Commit display transactions
+    const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded;
+    if (displayTransactionNeeded) {
+        processDisplayChangesLocked();
+        processDisplayHotplugEventsLocked();
+    }
 
-    // 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)
-     */
-
-    if ((transactionFlags & eTraversalNeeded) || mForceTraversal) {
-        mForceTraversal = false;
+    // Commit layer transactions. This needs to happen after display transactions are
+    // committed because some geometry logic relies on display orientation.
+    if ((transactionFlags & eTraversalNeeded) || mForceTraversal || displayTransactionNeeded) {
         mCurrentState.traverse([&](Layer* layer) {
             uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
-            if (!trFlags) return;
+            if (!trFlags && !displayTransactionNeeded) return;
 
             const uint32_t flags = layer->doTransaction(0);
             if (flags & Layer::eVisibleRegion)
@@ -2887,15 +2845,7 @@
         });
     }
 
-    /*
-     * Perform display own transactions if needed
-     */
-
-    if (transactionFlags & eDisplayTransactionNeeded) {
-        processDisplayChangesLocked();
-        processDisplayHotplugEventsLocked();
-    }
-
+    // Update transform hint
     if (transactionFlags & (eTransformHintUpdateNeeded | eDisplayTransactionNeeded)) {
         // The transform hint might have changed for some layers
         // (either because a display has changed, or because a layer
@@ -3115,8 +3065,7 @@
                            configs.late.sfWorkDuration);
 
     mRegionSamplingThread =
-            new RegionSamplingThread(*this, *mScheduler,
-                                     RegionSamplingThread::EnvironmentTimingTunables());
+            new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables());
     mFpsReporter = new FpsReporter(*mFrameTimeline, *this);
     // Dispatch a mode change request for the primary display on scheduler
     // initialization, so that the EventThreads always contain a reference to a
@@ -3154,7 +3103,7 @@
 
 void SurfaceFlinger::commitTransaction() {
     commitTransactionLocked();
-    signalSynchronousTransactions();
+    signalSynchronousTransactions(CountDownLatch::eSyncTransaction);
     mAnimTransactionPending = false;
 }
 
@@ -3315,66 +3264,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) {
@@ -3604,7 +3525,9 @@
     // Generate a CountDownLatch pending state if this is a synchronous transaction.
     if ((state.flags & eSynchronous) || state.inputWindowCommands.syncInputWindows) {
         state.transactionCommittedSignal = std::make_shared<CountDownLatch>(
-                (state.inputWindowCommands.syncInputWindows ? 2 : 1));
+                (state.inputWindowCommands.syncInputWindows
+                         ? (CountDownLatch::eSyncInputWindows | CountDownLatch::eSyncTransaction)
+                         : CountDownLatch::eSyncTransaction));
     }
 
     mTransactionQueue.emplace(state);
@@ -3629,10 +3552,10 @@
     }
 }
 
-void SurfaceFlinger::signalSynchronousTransactions() {
+void SurfaceFlinger::signalSynchronousTransactions(const uint32_t flag) {
     for (auto it = mTransactionCommittedSignals.begin();
          it != mTransactionCommittedSignals.end();) {
-        if ((*it)->countDown() == 0) {
+        if ((*it)->countDown(flag)) {
             it = mTransactionCommittedSignals.erase(it);
         } else {
             it++;
@@ -3747,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
@@ -3844,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");
@@ -3869,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();
@@ -4013,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;
     }
@@ -4104,6 +4033,16 @@
             flags |= eTraversalNeeded;
         }
     }
+    if (what & layer_state_t::eBufferCropChanged) {
+        if (layer->setBufferCrop(s.bufferCrop)) {
+            flags |= eTraversalNeeded;
+        }
+    }
+    if (what & layer_state_t::eDestinationFrameChanged) {
+        if (layer->setDestinationFrame(s.destinationFrame)) {
+            flags |= eTraversalNeeded;
+        }
+    }
     // This has to happen after we reparent children because when we reparent to null we remove
     // child layers from current state and remove its relative z. If the children are reparented in
     // the same transaction, then we have to make sure we reparent the children first so we do not
@@ -4121,30 +4060,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;
@@ -4227,10 +4159,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);
@@ -4354,14 +4282,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;
 
@@ -4449,7 +4370,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};
@@ -5071,6 +4992,13 @@
         result.append("\n");
     }
 
+    {
+        DumpArgs plannerArgs;
+        plannerArgs.add(); // first argument is ignored
+        plannerArgs.add(String16("--layers"));
+        dumpPlannerInfo(plannerArgs, result);
+    }
+
     /*
      * Dump HWComposer state
      */
@@ -5120,13 +5048,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:
@@ -5137,7 +5068,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:
@@ -5201,17 +5131,15 @@
         // captureLayers and captureDisplay will handle the permission check in the function
         case CAPTURE_LAYERS:
         case CAPTURE_DISPLAY:
-        case SET_DISPLAY_BRIGHTNESS:
         case SET_FRAME_TIMELINE_INFO:
         case GET_GPU_CONTEXT_PRIORITY:
         case GET_EXTRA_BUFFER_COUNT: {
             // This is not sensitive information, so should not require permission control.
             return OK;
         }
+        case SET_DISPLAY_BRIGHTNESS:
         case ADD_HDR_LAYER_INFO_LISTENER:
         case REMOVE_HDR_LAYER_INFO_LISTENER: {
-            // TODO (b/183985553): Should getting & setting brightness be part of this...?
-            // codes that require permission check
             IPCThreadState* ipc = IPCThreadState::self();
             const int pid = ipc->getCallingPid();
             const int uid = ipc->getCallingUid();
@@ -5246,6 +5174,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
@@ -5256,9 +5191,9 @@
         code == IBinder::SYSPROPS_TRANSACTION) {
         return OK;
     }
-    // Numbers from 1000 to 1038 are currently used for backdoors. The code
+    // Numbers from 1000 to 1040 are currently used for backdoors. The code
     // in onTransact verifies that the user is root, and has access to use SF.
-    if (code >= 1000 && code <= 1039) {
+    if (code >= 1000 && code <= 1040) {
         ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
         return OK;
     }
@@ -5575,9 +5510,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;
                 }
 
@@ -5640,6 +5590,36 @@
                 mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
                 return NO_ERROR;
             }
+            // Toggle caching feature
+            // First argument is an int32 - nonzero enables caching and zero disables caching
+            // Second argument is an optional uint64 - if present, then limits enabling/disabling
+            // caching to a particular physical display
+            case 1040: {
+                n = data.readInt32();
+                std::optional<PhysicalDisplayId> inputId = std::nullopt;
+                if (uint64_t inputDisplayId; data.readUint64(&inputDisplayId) == NO_ERROR) {
+                    const auto token =
+                            getPhysicalDisplayToken(static_cast<PhysicalDisplayId>(inputDisplayId));
+                    if (!token) {
+                        ALOGE("No display with id: %" PRIu64, inputDisplayId);
+                        return NAME_NOT_FOUND;
+                    }
+
+                    inputId = std::make_optional<PhysicalDisplayId>(inputDisplayId);
+                }
+                {
+                    Mutex::Autolock lock(mStateLock);
+                    mLayerCachingEnabled = n != 0;
+                    for (const auto& [_, display] : mDisplays) {
+                        if (!inputId || *inputId == display->getPhysicalId()) {
+                            display->enableLayerCaching(mLayerCachingEnabled);
+                        }
+                    }
+                }
+                invalidateHwcGeometry();
+                repaintEverything();
+                return NO_ERROR;
+            }
         }
     }
     return err;
@@ -5735,6 +5715,14 @@
     }
 }
 
+static bool hasCaptureBlackoutContentPermission() {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int pid = ipc->getCallingPid();
+    const int uid = ipc->getCallingUid();
+    return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
+            PermissionCache::checkPermission(sCaptureBlackoutContent, pid, uid);
+}
+
 static status_t validateScreenshotPermissions(const CaptureArgs& captureArgs) {
     IPCThreadState* ipc = IPCThreadState::self();
     const int pid = ipc->getCallingPid();
@@ -5905,6 +5893,10 @@
     Rect layerStackSpaceRect;
     ui::Dataspace dataspace;
     bool captureSecureLayers;
+
+    // Call this before holding mStateLock to avoid any deadlocking.
+    bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
+
     {
         Mutex::Autolock lock(mStateLock);
 
@@ -5914,9 +5906,8 @@
             return NAME_NOT_FOUND;
         }
 
-        const int uid = IPCThreadState::self()->getCallingUid();
-        const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
-        if (!forSystem && parent->getCurrentState().flags & layer_state_t::eLayerSecure) {
+        if (!canCaptureBlackoutContent &&
+            parent->getCurrentState().flags & layer_state_t::eLayerSecure) {
             ALOGW("Attempting to capture secure layer: PERMISSION_DENIED");
             return PERMISSION_DENIED;
         }
@@ -6048,15 +6039,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) {
@@ -6064,8 +6057,7 @@
         return BAD_VALUE;
     }
 
-    const int uid = IPCThreadState::self()->getCallingUid();
-    const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
+    bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
 
     static_cast<void>(schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable {
         if (mRefreshPending) {
@@ -6085,19 +6077,11 @@
 
         status_t result = NO_ERROR;
         renderArea->render([&] {
-            result = renderScreenImplLocked(*renderArea, traverseLayers, buffer, forSystem,
-                                            regionSampling, grayscale, captureResults);
+            result = renderScreenImplLocked(*renderArea, traverseLayers, buffer,
+                                            canCaptureBlackoutContent, 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);
     }));
@@ -6105,11 +6089,11 @@
     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 canCaptureBlackoutContent, bool regionSampling, bool grayscale,
+        ScreenCaptureResults& captureResults) {
     ATRACE_CALL();
 
     traverseLayers([&](Layer* layer) {
@@ -6117,17 +6101,17 @@
                 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
     // the impetus on WindowManager to not persist them.
-    if (captureResults.capturedSecureLayers && !forSystem) {
+    if (captureResults.capturedSecureLayers && !canCaptureBlackoutContent) {
         ALOGW("FB is protected: PERMISSION_DENIED");
         return PERMISSION_DENIED;
     }
 
-    captureResults.buffer = buffer;
+    captureResults.buffer = buffer->getBuffer();
     captureResults.capturedDataspace = renderArea.getReqDataSpace();
 
     const auto reqWidth = renderArea.getReqWidth();
@@ -6218,11 +6202,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));
@@ -6240,7 +6222,7 @@
 
 void SurfaceFlinger::setInputWindowsFinished() {
     Mutex::Autolock _l(mStateLock);
-    signalSynchronousTransactions();
+    signalSynchronousTransactions(CountDownLatch::eSyncInputWindows);
 }
 
 // ---------------------------------------------------------------------------
@@ -6290,6 +6272,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
@@ -6316,11 +6303,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);
@@ -6464,13 +6446,17 @@
 
 void SurfaceFlinger::onLayerFirstRef(Layer* layer) {
     mNumLayers++;
-    mScheduler->registerLayer(layer);
+    if (!layer->isRemovedFromCurrentState()) {
+        mScheduler->registerLayer(layer);
+    }
 }
 
 void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
-    mScheduler->deregisterLayer(layer);
     mNumLayers--;
     removeFromOffscreenLayers(layer);
+    if (!layer->isRemovedFromCurrentState()) {
+        mScheduler->deregisterLayer(layer);
+    }
 }
 
 // WARNING: ONLY CALL THIS FROM LAYER DTOR
@@ -6487,10 +6473,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) {
@@ -6702,6 +6684,100 @@
     }
 }
 
+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;
+}
+
+void SurfaceFlinger::scheduleRegionSamplingThread() {
+    static_cast<void>(schedule([&] { notifyRegionSamplingThread(); }));
+}
+
+void SurfaceFlinger::notifyRegionSamplingThread() {
+    if (!mLumaSampling || !mRegionSamplingThread) {
+        return;
+    }
+
+    mRegionSamplingThread->onCompositionComplete(mEventQueue->nextExpectedInvalidate());
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c7601fa..cb8d312 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 {
@@ -278,6 +271,10 @@
 
     static constexpr SkipInitializationTag SkipInitialization;
 
+    // Whether or not SDR layers should be dimmed to the desired SDR white point instead of
+    // being treated as native display brightness
+    static bool enableSdrDimming;
+
     // must be called before clients can connect
     void init() ANDROID_API;
 
@@ -332,9 +329,6 @@
     wp<Layer> fromHandle(const sp<IBinder>& handle);
     wp<Layer> fromHandleLocked(const sp<IBinder>& handle) const REQUIRES(mStateLock);
 
-    // Inherit from ClientCache::ErasedRecipient
-    void bufferErased(const client_cache_t& clientCacheId) override;
-
     // If set, disables reusing client composition buffers. This can be set by
     // debug.sf.disable_client_composition_cache
     bool mDisableClientCompositionCache = false;
@@ -477,24 +471,31 @@
 
     class CountDownLatch {
     public:
-        explicit CountDownLatch(int32_t count) : mCount(count) {}
+        enum {
+            eSyncTransaction = 1 << 0,
+            eSyncInputWindows = 1 << 1,
+        };
+        explicit CountDownLatch(uint32_t flags) : mFlags(flags) {}
 
-        int32_t countDown() {
+        // True if there is no waiting condition after count down.
+        bool countDown(uint32_t flag) {
             std::unique_lock<std::mutex> lock(mMutex);
-            if (mCount == 0) {
-                return 0;
+            if (mFlags == 0) {
+                return true;
             }
-            if (--mCount == 0) {
+            mFlags &= ~flag;
+            if (mFlags == 0) {
                 mCountDownComplete.notify_all();
+                return true;
             }
-            return mCount;
+            return false;
         }
 
         // Return true if triggered.
         bool wait_until(const std::chrono::seconds& timeout) const {
             std::unique_lock<std::mutex> lock(mMutex);
             const auto untilTime = std::chrono::system_clock::now() + timeout;
-            while (mCount != 0) {
+            while (mFlags != 0) {
                 // Conditional variables can be woken up sporadically, so we check count
                 // to verify the wakeup was triggered by |countDown|.
                 if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) {
@@ -505,7 +506,7 @@
         }
 
     private:
-        int32_t mCount;
+        uint32_t mFlags;
         mutable std::condition_variable mCountDownComplete;
         mutable std::mutex mMutex;
     };
@@ -647,6 +648,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;
@@ -907,11 +909,13 @@
     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,
+                                    const std::shared_ptr<renderengine::ExternalTexture>&,
+                                    bool canCaptureBlackoutContent, bool regionSampling,
                                     bool grayscale, ScreenCaptureResults&);
 
     sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
@@ -1131,7 +1135,7 @@
     // Add transaction to the Transaction Queue
     void queueTransaction(TransactionState& state) EXCLUDES(mQueueLock);
     void waitForSynchronousTransaction(const CountDownLatch& transactionCommittedSignal);
-    void signalSynchronousTransactions();
+    void signalSynchronousTransactions(const uint32_t flag);
 
     /*
      * Generic Layer Metadata
@@ -1236,6 +1240,7 @@
     int mDebugRegion = 0;
     bool mDebugDisableHWC = false;
     bool mDebugDisableTransformHint = false;
+    bool mLayerCachingEnabled = false;
     volatile nsecs_t mDebugInTransaction = 0;
     bool mForceFullDamage = false;
     bool mPropagateBackpressureClientComposition = false;
@@ -1323,6 +1328,10 @@
     SurfaceFlingerBE mBE;
     std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
 
+    const std::string mHwcServiceName;
+
+    bool hasMockHwc() const { return mHwcServiceName == "mock"; }
+
     /*
      * Scheduler
      */
@@ -1387,13 +1396,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;
@@ -1402,6 +1404,38 @@
 
     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;
+
+    void scheduleRegionSamplingThread();
+    void notifyRegionSamplingThread();
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index b3dca78..4a69c8f 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -321,6 +321,10 @@
     return defaultValue;
 }
 
+bool enable_sdr_dimming(bool defaultValue) {
+    return SurfaceFlingerProperties::enable_sdr_dimming().value_or(defaultValue);
+}
+
 int32_t display_update_imminent_timeout_ms(int32_t defaultValue) {
     auto temp = SurfaceFlingerProperties::display_update_imminent_timeout_ms();
     if (temp.has_value()) {
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index b19d216..039d316 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -102,6 +102,8 @@
 
 bool enable_layer_caching(bool defaultValue);
 
+bool enable_sdr_dimming(bool defaultValue);
+
 } // namespace sysprop
 } // namespace android
 #endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 8a3be9f..c5f1598 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -130,8 +130,8 @@
     transaction->set_animation(layerFlags & BnSurfaceComposer::eAnimation);
 
     const int32_t layerId(getLayerId(layer));
-    addPositionLocked(transaction, layerId, layer->mCurrentState.active_legacy.transform.tx(),
-                      layer->mCurrentState.active_legacy.transform.ty());
+    addPositionLocked(transaction, layerId, layer->mCurrentState.transform.tx(),
+                      layer->mCurrentState.transform.ty());
     addDepthLocked(transaction, layerId, layer->mCurrentState.z);
     addAlphaLocked(transaction, layerId, layer->mCurrentState.color.a);
     addTransparentRegionLocked(transaction, layerId,
@@ -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 2094972..d6a0787 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,147 +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,
-        TimeStats::SetFrameRateVote::FrameRateCompatibility frameRateCompatibility,
-        TimeStats::SetFrameRateVote::Seamlessness 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 */,
-                static_cast<int>(frameRateCompatibility));
-    proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, static_cast<int>(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;
@@ -198,69 +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(layer->setFrameRateVote.frameRate,
-                                               layer->setFrameRateVote.frameRateCompatibility,
-                                               layer->setFrameRateVote.seamlessness);
-        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;
     }
@@ -270,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) {
@@ -393,12 +351,16 @@
             std::max(mTimeStats.displayEventConnectionsCountLegacy, count);
 }
 
+static int32_t toMs(nsecs_t nanos) {
+    int64_t millis =
+            std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::nanoseconds(nanos))
+                    .count();
+    millis = std::clamp(millis, int64_t(INT32_MIN), int64_t(INT32_MAX));
+    return static_cast<int32_t>(millis);
+}
+
 static int32_t msBetween(nsecs_t start, nsecs_t end) {
-    int64_t delta = std::chrono::duration_cast<std::chrono::milliseconds>(
-                            std::chrono::nanoseconds(end - start))
-                            .count();
-    delta = std::clamp(delta, int64_t(INT32_MIN), int64_t(INT32_MAX));
-    return static_cast<int32_t>(delta);
+    return toMs(end - start);
 }
 
 void TimeStats::recordFrameDuration(nsecs_t startTime, nsecs_t endTime) {
@@ -469,8 +431,8 @@
     return true;
 }
 
-static int32_t clampToSmallestBucket(Fps fps, size_t bucketWidth) {
-    return (fps.getIntValue() / bucketWidth) * bucketWidth;
+static int32_t clampToNearestBucket(Fps fps, size_t bucketWidth) {
+    return std::round(fps.getValue() / bucketWidth) * bucketWidth;
 }
 
 void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
@@ -483,10 +445,10 @@
     TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
     std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
     const int32_t refreshRateBucket =
-            clampToSmallestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH);
+            clampToNearestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH);
     const int32_t renderRateBucket =
-            clampToSmallestBucket(renderRate ? *renderRate : displayRefreshRate,
-                                  RENDER_RATE_BUCKET_WIDTH);
+            clampToNearestBucket(renderRate ? *renderRate : displayRefreshRate,
+                                 RENDER_RATE_BUCKET_WIDTH);
     while (!timeRecords.empty()) {
         if (!recordReadyLocked(layerId, &timeRecords[0])) break;
         ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerId,
@@ -787,7 +749,7 @@
 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) {
@@ -813,9 +775,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++;
     }
 }
 
@@ -839,10 +803,10 @@
     static const std::string kDefaultLayerName = "none";
 
     const int32_t refreshRateBucket =
-            clampToSmallestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH);
+            clampToNearestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH);
     const int32_t renderRateBucket =
-            clampToSmallestBucket(info.renderRate ? *info.renderRate : info.refreshRate,
-                                  RENDER_RATE_BUCKET_WIDTH);
+            clampToNearestBucket(info.renderRate ? *info.renderRate : info.refreshRate,
+                                 RENDER_RATE_BUCKET_WIDTH);
     const TimeStatsHelper::TimelineStatsKey timelineKey = {refreshRateBucket, renderRateBucket};
 
     if (!mTimeStats.stats.count(timelineKey)) {
@@ -869,10 +833,9 @@
         // TimeStats Histograms only retain positive values, so we don't need to check if these
         // deadlines were really missed if we know that the frame had jank, since deadlines
         // that were met will be dropped.
-        timelineStats.displayDeadlineDeltas.insert(static_cast<int32_t>(info.displayDeadlineDelta));
-        timelineStats.displayPresentDeltas.insert(static_cast<int32_t>(info.displayPresentJitter));
-        timeStatsLayer.deltas["appDeadlineDeltas"].insert(
-                static_cast<int32_t>(info.appDeadlineDelta));
+        timelineStats.displayDeadlineDeltas.insert(toMs(info.displayDeadlineDelta));
+        timelineStats.displayPresentDeltas.insert(toMs(info.displayPresentJitter));
+        timeStatsLayer.deltas["appDeadlineDeltas"].insert(toMs(info.appDeadlineDelta));
     }
 }
 
@@ -1061,6 +1024,7 @@
 
 void TimeStats::clearAll() {
     std::lock_guard<std::mutex> lock(mMutex);
+    mTimeStats.stats.clear();
     clearGlobalLocked();
     clearLayersLocked();
 }
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index a87b7cb..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>
@@ -54,9 +51,8 @@
 
     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;
@@ -232,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;
@@ -332,11 +281,8 @@
     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,
@@ -366,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/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/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index ee5542d..78f8a2f 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -463,3 +463,12 @@
     access: Readonly
     prop_name: "ro.surface_flinger.enable_layer_caching"
 }
+
+# Enables SDR layer dimming
+prop {
+    api_name: "enable_sdr_dimming"
+    type: Boolean
+    scope: Public
+    access: Readonly
+    prop_name: "ro.surface_flinger.enable_sdr_dimming"
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 47e14f6..9c567d6 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -53,6 +53,10 @@
     prop_name: "ro.surface_flinger.protected_contents"
   }
   prop {
+    api_name: "enable_sdr_dimming"
+    prop_name: "ro.surface_flinger.enable_sdr_dimming"
+  }
+  prop {
     api_name: "force_hwc_copy_for_virtual_displays"
     prop_name: "ro.surface_flinger.force_hwc_copy_for_virtual_displays"
   }
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/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 011ff70..965aac3 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -767,7 +767,8 @@
     EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
 }
 
-TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame) {
+// TODO (b/183181768): Fix & re-enable
+TEST_F(LayerCallbackTest, DISABLED_MultipleTransactions_SingleFrame) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
 
@@ -938,7 +939,8 @@
     EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
 }
 
-TEST_F(LayerCallbackTest, DesiredPresentTime_OutOfOrder) {
+// TODO (b/183181768): Fix & re-enable
+TEST_F(LayerCallbackTest, DISABLED_DesiredPresentTime_OutOfOrder) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
 
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 53d230a..c8eeac6 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;
@@ -607,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;
@@ -901,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));
@@ -1076,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(
@@ -1467,7 +1336,8 @@
                                        Color::GREEN, true /* filtered */);
 }
 
-TEST_P(LayerRenderTypeTransactionTest, SetFenceBasic_BufferState) {
+// TODO (b/186543004): Fix & re-enable
+TEST_P(LayerRenderTypeTransactionTest, DISABLED_SetFenceBasic_BufferState) {
     sp<SurfaceControl> layer;
     Transaction transaction;
     ASSERT_NO_FATAL_FAILURE(
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index edf55ea..43d957c 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;
@@ -278,6 +273,198 @@
     }
 }
 
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusBufferRotationTransform) {
+    sp<SurfaceControl> layer;
+    sp<SurfaceControl> parent;
+    ASSERT_NO_FATAL_FAILURE(
+            parent = LayerTransactionTest::createLayer("parent", 0, 0,
+                                                       ISurfaceComposerClient::eFXSurfaceEffect));
+
+    const uint32_t bufferWidth = 1500;
+    const uint32_t bufferHeight = 300;
+
+    const uint32_t layerWidth = 300;
+    const uint32_t layerHeight = 1500;
+
+    const uint32_t testArea = 4;
+    const float cornerRadius = 120.0f;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", bufferWidth, bufferHeight));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, bufferWidth, bufferHeight));
+
+    Transaction()
+            .reparent(layer, parent)
+            .setColor(parent, half3(0, 1, 0))
+            .setCrop(parent, Rect(0, 0, layerWidth, layerHeight))
+            .setCornerRadius(parent, cornerRadius)
+
+            .setTransform(layer, ui::Transform::ROT_90)
+            .setDestinationFrame(layer, Rect(0, 0, layerWidth, layerHeight))
+            .apply();
+    {
+        auto shot = getScreenCapture();
+        // Corners are transparent
+        // top-left
+        shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+        // top-right
+        shot->expectColor(Rect(layerWidth - testArea, 0, layerWidth, testArea), Color::BLACK);
+        // bottom-left
+        shot->expectColor(Rect(0, layerHeight - testArea, testArea, layerHeight), Color::BLACK);
+        // bottom-right
+        shot->expectColor(Rect(layerWidth - testArea, layerHeight - testArea, layerWidth,
+                               layerHeight),
+                          Color::BLACK);
+
+        // Area after corner radius is solid
+        // top-left to top-right under the corner
+        shot->expectColor(Rect(0, cornerRadius, layerWidth, cornerRadius + testArea), Color::RED);
+        // bottom-left to bottom-right above the corner
+        shot->expectColor(Rect(0, layerHeight - cornerRadius - testArea, layerWidth,
+                               layerHeight - cornerRadius),
+                          Color::RED);
+        // left side after the corner
+        shot->expectColor(Rect(cornerRadius, 0, cornerRadius + testArea, layerHeight), Color::RED);
+        // right side before the corner
+        shot->expectColor(Rect(layerWidth - cornerRadius - testArea, 0, layerWidth - cornerRadius,
+                               layerHeight),
+                          Color::RED);
+    }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusBufferCropTransform) {
+    sp<SurfaceControl> layer;
+    sp<SurfaceControl> parent;
+    ASSERT_NO_FATAL_FAILURE(
+            parent = LayerTransactionTest::createLayer("parent", 0, 0,
+                                                       ISurfaceComposerClient::eFXSurfaceEffect));
+
+    const uint32_t bufferWidth = 150 * 2;
+    const uint32_t bufferHeight = 750 * 2;
+
+    const Rect bufferCrop(0, 0, 150, 750);
+
+    const uint32_t layerWidth = 300;
+    const uint32_t layerHeight = 1500;
+
+    const uint32_t testArea = 4;
+    const float cornerRadius = 120.0f;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", bufferWidth, bufferHeight));
+    ASSERT_NO_FATAL_FAILURE(fillLayerQuadrant(layer, bufferWidth, bufferHeight, Color::RED,
+                                              Color::BLACK, Color::GREEN, Color::BLUE));
+
+    Transaction()
+            .reparent(layer, parent)
+            .setColor(parent, half3(0, 1, 0))
+            .setCrop(parent, Rect(0, 0, layerWidth, layerHeight))
+            .setCornerRadius(parent, cornerRadius)
+
+            .setBufferCrop(layer, bufferCrop)
+            .setDestinationFrame(layer, Rect(0, 0, layerWidth, layerHeight))
+            .apply();
+    {
+        auto shot = getScreenCapture();
+        // Corners are transparent
+        // top-left
+        shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+        // top-right
+        shot->expectColor(Rect(layerWidth - testArea, 0, layerWidth, testArea), Color::BLACK);
+        // bottom-left
+        shot->expectColor(Rect(0, layerHeight - testArea, testArea, layerHeight), Color::BLACK);
+        // bottom-right
+        shot->expectColor(Rect(layerWidth - testArea, layerHeight - testArea, layerWidth,
+                               layerHeight),
+                          Color::BLACK);
+
+        // Area after corner radius is solid
+        // since the buffer is scaled, there will blending so adjust some of the bounds when
+        // checking.
+        float adjustedCornerRadius = cornerRadius + 15;
+        float adjustedLayerHeight = layerHeight - 15;
+        float adjustedLayerWidth = layerWidth - 15;
+
+        // top-left to top-right under the corner
+        shot->expectColor(Rect(15, adjustedCornerRadius, adjustedLayerWidth,
+                               adjustedCornerRadius + testArea),
+                          Color::RED);
+        // bottom-left to bottom-right above the corner
+        shot->expectColor(Rect(15, adjustedLayerHeight - adjustedCornerRadius - testArea,
+                               adjustedLayerWidth, adjustedLayerHeight - adjustedCornerRadius),
+                          Color::RED);
+        // left side after the corner
+        shot->expectColor(Rect(adjustedCornerRadius, 15, adjustedCornerRadius + testArea,
+                               adjustedLayerHeight),
+                          Color::RED);
+        // right side before the corner
+        shot->expectColor(Rect(adjustedLayerWidth - adjustedCornerRadius - testArea, 15,
+                               adjustedLayerWidth - adjustedCornerRadius, adjustedLayerHeight),
+                          Color::RED);
+    }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildBufferRotationTransform) {
+    sp<SurfaceControl> layer;
+    sp<SurfaceControl> parent;
+    ASSERT_NO_FATAL_FAILURE(
+            parent = LayerTransactionTest::createLayer("parent", 0, 0,
+                                                       ISurfaceComposerClient::eFXSurfaceEffect));
+
+    const uint32_t bufferWidth = 1500;
+    const uint32_t bufferHeight = 300;
+
+    const uint32_t layerWidth = 300;
+    const uint32_t layerHeight = 1500;
+
+    const uint32_t testArea = 4;
+    const float cornerRadius = 120.0f;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", bufferWidth, bufferHeight));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::BLUE, bufferWidth, bufferHeight));
+
+    sp<SurfaceControl> child;
+    ASSERT_NO_FATAL_FAILURE(child = createLayer("child", bufferWidth, bufferHeight));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::RED, bufferWidth, bufferHeight));
+
+    Transaction()
+            .reparent(layer, parent)
+            .reparent(child, layer)
+            .setColor(parent, half3(0, 1, 0))
+            .setCrop(parent, Rect(0, 0, layerWidth, layerHeight))
+            .setCornerRadius(parent, cornerRadius) /* */
+
+            .setTransform(layer, ui::Transform::ROT_90)
+            .setDestinationFrame(layer, Rect(0, 0, layerWidth, layerHeight))
+
+            .setTransform(child, ui::Transform::ROT_90)
+            .setDestinationFrame(child, Rect(0, 0, layerWidth, layerHeight))
+            .apply();
+    {
+        auto shot = getScreenCapture();
+        // Corners are transparent
+        // top-left
+        shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+        // top-right
+        shot->expectColor(Rect(layerWidth - testArea, 0, layerWidth, testArea), Color::BLACK);
+        // bottom-left
+        shot->expectColor(Rect(0, layerHeight - testArea, testArea, layerHeight), Color::BLACK);
+        // bottom-right
+        shot->expectColor(Rect(layerWidth - testArea, layerHeight - testArea, layerWidth,
+                               layerHeight),
+                          Color::BLACK);
+
+        // Area after corner radius is solid
+        // top-left to top-right under the corner
+        shot->expectColor(Rect(0, cornerRadius, layerWidth, cornerRadius + testArea), Color::RED);
+        // bottom-left to bottom-right above the corner
+        shot->expectColor(Rect(0, layerHeight - cornerRadius - testArea, layerWidth,
+                               layerHeight - cornerRadius),
+                          Color::RED);
+        // left side after the corner
+        shot->expectColor(Rect(cornerRadius, 0, cornerRadius + testArea, layerHeight), Color::RED);
+        // right side before the corner
+        shot->expectColor(Rect(layerWidth - cornerRadius - testArea, 0, layerWidth - cornerRadius,
+                               layerHeight),
+                          Color::RED);
+    }
+}
+
 TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusSimple) {
     if (!deviceSupportsBlurs()) GTEST_SKIP();
     if (!deviceUsesSkiaRenderEngine()) GTEST_SKIP();
@@ -324,7 +511,6 @@
             .setLayer(blurLayer, mLayerZBase + 3)
             .setBackgroundBlurRadius(blurLayer, blurRadius)
             .setCrop(blurLayer, blurRect)
-            .setSize(blurLayer, blurRect.getWidth(), blurRect.getHeight())
             .setAlpha(blurLayer, 0.0f)
             .apply();
 
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
index 4753362..34c9182 100644
--- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -19,6 +19,7 @@
 #pragma clang diagnostic ignored "-Wconversion"
 
 #include <gui/BufferItemConsumer.h>
+#include <private/android_filesystem_config.h>
 #include "TransactionTestHarnesses.h"
 
 namespace android {
@@ -170,7 +171,11 @@
     args.displayToken = mDisplay;
 
     ScreenCaptureResults captureResults;
-    ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(args, captureResults));
+    {
+        // Ensure the UID is not root because root has all permissions
+        UIDFaker f(AID_APP_START);
+        ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(args, captureResults));
+    }
 
     Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true);
     ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, captureResults));
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index e5c2ec8..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;
 
@@ -462,38 +399,6 @@
     }
 }
 
-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);
-    }
-}
-
 TEST_F(ChildLayerTest, ChildrenSurviveParentDestruction) {
     sp<SurfaceControl> mGrandChild =
             createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
@@ -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/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index b0753c8..6912fcf 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -84,7 +84,11 @@
 
     Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);
 
-    ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+    {
+        // Ensure the UID is not root because root has all permissions
+        UIDFaker f(AID_APP_START);
+        ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+    }
 
     UIDFaker f(AID_SYSTEM);
 
@@ -350,7 +354,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 +368,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 +439,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 +474,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 +502,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.
@@ -518,15 +532,16 @@
     ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
 }
 
-TEST_F(ScreenCaptureTest, CaputureSecureLayer) {
-    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+TEST_F(ScreenCaptureTest, CaptureSecureLayer) {
+    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()
@@ -541,8 +556,12 @@
     args.childrenOnly = false;
     ScreenCaptureResults captureResults;
 
-    // Call from outside system with secure layers will result in permission denied
-    ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(args, captureResults));
+    {
+        // Ensure the UID is not root because root has all permissions
+        UIDFaker f(AID_APP_START);
+        // Call from outside system with secure layers will result in permission denied
+        ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(args, captureResults));
+    }
 
     UIDFaker f(AID_SYSTEM);
 
@@ -874,4 +893,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 9f94c73..88fb811 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -115,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",
@@ -126,6 +127,7 @@
         "librenderengine",
         "libserviceutils",
         "libtimestats",
+        "libtimestats_atoms_proto",
         "libtimestats_proto",
         "libtrace_proto",
         "perfetto_trace_protos",
@@ -137,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",
@@ -152,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/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index 54f4c7c..a9ad249 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -66,7 +66,7 @@
             ALOGD("schedule: %zu", token.value());
             if (mCallbacks.count(token) == 0) {
                 ALOGD("schedule: callback %zu not registered", token.value());
-                return scheduler::ScheduleResult::Error;
+                return scheduler::ScheduleResult{};
             }
 
             auto& callback = mCallbacks.at(token);
@@ -75,7 +75,7 @@
             callback.targetWakeupTime =
                     timing.earliestVsync - timing.workDuration - timing.readyDuration;
             ALOGD("schedule: callback %zu scheduled", token.value());
-            return scheduler::ScheduleResult::Scheduled;
+            return scheduler::ScheduleResult{callback.targetWakeupTime};
         });
 
         ON_CALL(*this, cancel).WillByDefault([this](CallbackToken token) {
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/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
index a2291b2..dec0ff5 100644
--- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -28,6 +28,7 @@
 #include "FpsReporter.h"
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
+#include "fake/FakeClock.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockFrameTimeline.h"
@@ -91,7 +92,9 @@
     sp<Layer> mUnrelated;
 
     sp<TestableFpsListener> mFpsListener;
-    sp<FpsReporter> mFpsReporter = new FpsReporter(mFrameTimeline, *(mFlinger.flinger()));
+    fake::FakeClock* mClock = new fake::FakeClock();
+    sp<FpsReporter> mFpsReporter =
+            new FpsReporter(mFrameTimeline, *(mFlinger.flinger()), std::unique_ptr<Clock>(mClock));
 };
 
 FpsReporterTest::FpsReporterTest() {
@@ -178,6 +181,7 @@
             .WillOnce(Return(expectedFps));
 
     mFpsReporter->addListener(mFpsListener, kTaskId);
+    mClock->advanceTime(600ms);
     mFpsReporter->dispatchLayerFps();
     EXPECT_EQ(expectedFps, mFpsListener->lastReportedFps);
     mFpsReporter->removeListener(mFpsListener);
@@ -187,5 +191,34 @@
     mFpsReporter->dispatchLayerFps();
 }
 
+TEST_F(FpsReporterTest, rateLimits) {
+    const constexpr int32_t kTaskId = 12;
+    LayerMetadata targetMetadata;
+    targetMetadata.setInt32(METADATA_TASK_ID, kTaskId);
+    mTarget = createBufferStateLayer(targetMetadata);
+    mFlinger.mutableCurrentState().layersSortedByZ.add(mTarget);
+
+    float firstFps = 44.0;
+    float secondFps = 53.0;
+
+    EXPECT_CALL(mFrameTimeline, computeFps(UnorderedElementsAre(mTarget->getSequence())))
+            .WillOnce(Return(firstFps))
+            .WillOnce(Return(secondFps));
+
+    mFpsReporter->addListener(mFpsListener, kTaskId);
+    mClock->advanceTime(600ms);
+    mFpsReporter->dispatchLayerFps();
+    EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
+    mClock->advanceTime(200ms);
+    mFpsReporter->dispatchLayerFps();
+    EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
+    mClock->advanceTime(200ms);
+    mFpsReporter->dispatchLayerFps();
+    EXPECT_EQ(firstFps, mFpsListener->lastReportedFps);
+    mClock->advanceTime(200ms);
+    mFpsReporter->dispatchLayerFps();
+    EXPECT_EQ(secondFps, mFpsListener->lastReportedFps);
+}
+
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 982252f..c6a4115 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -68,12 +68,12 @@
     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;
         maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
-        maxTokenRetentionTime = mTokenManager->kMaxRetentionTime;
+        maxTokens = mTokenManager->kMaxTokens;
     }
 
     // Each tracing session can be used for a single block of Start -> Stop.
@@ -111,9 +111,11 @@
         mFrameTimeline->setSfPresent(2500, presentFence1);
     }
 
-    void flushTokens(nsecs_t flushTime) {
-        std::lock_guard<std::mutex> lock(mTokenManager->mMutex);
-        mTokenManager->flushTokens(flushTime);
+    void flushTokens() {
+        for (size_t i = 0; i < maxTokens; i++) {
+            mTokenManager->generateTokenForPredictions({});
+        }
+        EXPECT_EQ(getPredictions().size(), maxTokens);
     }
 
     SurfaceFrame& getSurfaceFrame(size_t displayFrameIdx, size_t surfaceFrameIdx) {
@@ -132,7 +134,7 @@
                 a.presentTime == b.presentTime;
     }
 
-    const std::map<int64_t, TokenManagerPrediction>& getPredictions() const {
+    const std::map<int64_t, TimelineItem>& getPredictions() const {
         return mTokenManager->mPredictions;
     }
 
@@ -155,7 +157,7 @@
     TraceCookieCounter* mTraceCookieCounter;
     FenceToFenceTimeMap fenceFactory;
     uint32_t* maxDisplayFrames;
-    nsecs_t maxTokenRetentionTime;
+    size_t maxTokens;
     static constexpr pid_t kSurfaceFlingerPid = 666;
     static constexpr nsecs_t kPresentThreshold = std::chrono::nanoseconds(2ns).count();
     static constexpr nsecs_t kDeadlineThreshold = std::chrono::nanoseconds(2ns).count();
@@ -163,7 +165,6 @@
     static constexpr JankClassificationThresholds kTestThresholds{kPresentThreshold,
                                                                   kDeadlineThreshold,
                                                                   kStartThreshold};
-    static constexpr nsecs_t kHwcDuration = std::chrono::nanoseconds(3ns).count();
 };
 
 static const std::string sLayerNameOne = "layer1";
@@ -178,12 +179,11 @@
 TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
     int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
     EXPECT_EQ(getPredictions().size(), 1u);
-    flushTokens(systemTime() + maxTokenRetentionTime);
+    flushTokens();
     int64_t token2 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     std::optional<TimelineItem> predictions = mTokenManager->getPredictionsForToken(token1);
 
     // token1 should have expired
-    EXPECT_EQ(getPredictions().size(), 1u);
     EXPECT_EQ(predictions.has_value(), false);
 
     predictions = mTokenManager->getPredictionsForToken(token2);
@@ -213,7 +213,7 @@
 
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) {
     int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
-    flushTokens(systemTime() + maxTokenRetentionTime);
+    flushTokens();
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
                                                        sLayerIdOne, sLayerNameOne, sLayerNameOne,
@@ -483,7 +483,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 +503,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 +538,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 +562,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 +589,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 +614,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 +640,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 +668,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});
@@ -671,7 +697,7 @@
                 incrementJankyFrames(
                         TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne, sLayerNameOne,
                                                    JankType::Unknown | JankType::AppDeadlineMissed,
-                                                   -1, -1, 25}));
+                                                   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});
@@ -682,7 +708,7 @@
                                                        sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(45);
     // Trigger a prediction expiry
-    flushTokens(systemTime() + maxTokenRetentionTime);
+    flushTokens();
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
 
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1040,7 +1066,7 @@
     tracingSession->StartBlocking();
     int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({10, 25, 30});
     // Flush the token so that it would expire
-    flushTokens(systemTime() + maxTokenRetentionTime);
+    flushTokens();
 
     // Set up the display frame
     mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11));
@@ -1258,7 +1284,7 @@
             mTokenManager->generateTokenForPredictions({appStartTime, appEndTime, appPresentTime});
 
     // Flush the token so that it would expire
-    flushTokens(systemTime() + maxTokenRetentionTime);
+    flushTokens();
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
                                                        sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
@@ -1322,6 +1348,81 @@
     validateTraceEvent(actualSurfaceFrameEnd, protoActualSurfaceFrameEnd);
 }
 
+TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDroppedFramesTracedProperly) {
+    auto tracingSession = getTracingSessionForTest();
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+
+    tracingSession->StartBlocking();
+    constexpr nsecs_t appStartTime = std::chrono::nanoseconds(10ms).count();
+    constexpr nsecs_t appEndTime = std::chrono::nanoseconds(20ms).count();
+    constexpr nsecs_t appPresentTime = std::chrono::nanoseconds(30ms).count();
+    int64_t surfaceFrameToken =
+            mTokenManager->generateTokenForPredictions({appStartTime, appEndTime, appPresentTime});
+
+    // Flush the token so that it would expire
+    flushTokens();
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
+                                                       sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne, /*isBuffer*/ true);
+
+    constexpr nsecs_t sfStartTime = std::chrono::nanoseconds(22ms).count();
+    constexpr nsecs_t sfEndTime = std::chrono::nanoseconds(30ms).count();
+    constexpr nsecs_t sfPresentTime = std::chrono::nanoseconds(30ms).count();
+    int64_t displayFrameToken =
+            mTokenManager->generateTokenForPredictions({sfStartTime, sfEndTime, sfPresentTime});
+
+    // First 2 cookies will be used by the DisplayFrame
+    int64_t traceCookie = snoopCurrentTraceCookie() + 2;
+
+    auto protoActualSurfaceFrameStart =
+            createProtoActualSurfaceFrameStart(traceCookie + 1, surfaceFrameToken,
+                                               displayFrameToken, sPidOne, sLayerNameOne,
+                                               FrameTimelineEvent::PRESENT_DROPPED, false, false,
+                                               FrameTimelineEvent::JANK_NONE,
+                                               FrameTimelineEvent::PREDICTION_EXPIRED);
+    auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
+
+    // Set up the display frame
+    mFrameTimeline->setSfWakeUp(displayFrameToken, sfStartTime, Fps::fromPeriodNsecs(11));
+    surfaceFrame1->setDropTime(sfStartTime);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    mFrameTimeline->setSfPresent(sfEndTime, presentFence1);
+    presentFence1->signalForTest(sfPresentTime);
+
+    addEmptyDisplayFrame();
+    flushTrace();
+    tracingSession->StopBlocking();
+
+    auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
+    // Display Frame 4 packets + SurfaceFrame 2 packets
+    ASSERT_EQ(packets.size(), 6u);
+
+    // Packet - 4 : ActualSurfaceFrameStart
+    const auto& packet4 = packets[4];
+    ASSERT_TRUE(packet4.has_timestamp());
+    EXPECT_EQ(packet4.timestamp(),
+              static_cast<uint64_t>(sfStartTime - SurfaceFrame::kPredictionExpiredStartTimeDelta));
+    ASSERT_TRUE(packet4.has_frame_timeline_event());
+
+    const auto& event4 = packet4.frame_timeline_event();
+    ASSERT_TRUE(event4.has_actual_surface_frame_start());
+    const auto& actualSurfaceFrameStart = event4.actual_surface_frame_start();
+    validateTraceEvent(actualSurfaceFrameStart, protoActualSurfaceFrameStart);
+
+    // Packet - 5 : FrameEnd (ActualSurfaceFrame)
+    const auto& packet5 = packets[5];
+    ASSERT_TRUE(packet5.has_timestamp());
+    EXPECT_EQ(packet5.timestamp(), static_cast<uint64_t>(sfStartTime));
+    ASSERT_TRUE(packet5.has_frame_timeline_event());
+
+    const auto& event5 = packet5.frame_timeline_event();
+    ASSERT_TRUE(event5.has_frame_end());
+    const auto& actualSurfaceFrameEnd = event5.frame_end();
+    validateTraceEvent(actualSurfaceFrameEnd, protoActualSurfaceFrameEnd);
+}
+
 // Tests for Jank classification
 TEST_F(FrameTimelineTest, jankClassification_presentOnTimeDoesNotClassify) {
     // Layer specific increment
@@ -1458,46 +1559,84 @@
 }
 
 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
+     * Case 3 - previous frame ran longer -> sf_stuffing
+     * Case 4 - Long cpu under SF stuffing -> cpudeadlinemissed
+     */
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto presentFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto presentFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto gpuFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto gpuFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto gpuFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto gpuFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+    int64_t sfToken3 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+    int64_t sfToken4 = mTokenManager->generateTokenForPredictions({112, 120, 120});
+
+    // 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 = 30
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(30));
+    mFrameTimeline->setSfPresent(56, presentFence2, gpuFence2);
+    auto displayFrame1 = getDisplayFrame(1);
+    gpuFence2->signalForTest(76);
+    presentFence2->signalForTest(90);
+
+    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);
+
+    // case 3 - cpu time = 86 - 82 = 4, vsync period = 30
+    mFrameTimeline->setSfWakeUp(sfToken3, 106, Fps::fromPeriodNsecs(30));
+    mFrameTimeline->setSfPresent(112, presentFence3, gpuFence3);
+    auto displayFrame2 = getDisplayFrame(2);
+    gpuFence3->signalForTest(116);
+    presentFence3->signalForTest(120);
+
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+    // Fences have flushed for second displayFrame, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 90);
+    EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
+
+    // case 4 - cpu time = 86 - 82 = 4, vsync period = 30
+    mFrameTimeline->setSfWakeUp(sfToken4, 120, Fps::fromPeriodNsecs(30));
+    mFrameTimeline->setSfPresent(140, presentFence4, gpuFence4);
+    auto displayFrame3 = getDisplayFrame(3);
+    gpuFence4->signalForTest(156);
+    presentFence4->signalForTest(180);
+
+    EXPECT_EQ(displayFrame3->getActuals().presentTime, 0);
+    // Fences have flushed for third displayFrame, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 120);
+    EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerStuffing);
 
     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);
-}
-
-TEST_F(FrameTimelineTest, jankClassification_displayFrameLateStartLateFinishLatePresent) {
-    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
-    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
-    mFrameTimeline->setSfWakeUp(sfToken1, 26, Fps::fromPeriodNsecs(11));
-    mFrameTimeline->setSfPresent(36, presentFence1);
-    auto displayFrame = getDisplayFrame(0);
-    presentFence1->signalForTest(52);
-
-    // Fences haven't been flushed yet, so it should be 0
-    EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
-
-    addEmptyDisplayFrame();
-    displayFrame = getDisplayFrame(0);
-
-    // Fences have flushed, so the present timestamps should be updated
-    EXPECT_EQ(displayFrame->getActuals().presentTime, 52);
-    EXPECT_EQ(displayFrame->getFrameStartMetadata(), FrameStartMetadata::LateStart);
-    EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
-    EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
-    EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+    // Fences have flushed for third displayFrame, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame3->getActuals().presentTime, 180);
+    EXPECT_EQ(displayFrame3->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame3->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(displayFrame3->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
 }
 
 TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresent) {
@@ -1562,7 +1701,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();
@@ -1642,7 +1781,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();
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
index 325fb8f..d6ce5e2 100644
--- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -126,7 +126,7 @@
     std::deque<FrameTimeData> frameTimes;
     constexpr auto kExpectedFps = Fps(50.0f);
     constexpr auto kExpectedPeriod = kExpectedFps.getPeriodNsecs();
-    constexpr auto kSmallPeriod = Fps(150.0f).getPeriodNsecs();
+    constexpr auto kSmallPeriod = Fps(250.0f).getPeriodNsecs();
     constexpr int kNumIterations = 10;
     for (int i = 1; i <= kNumIterations; i++) {
         frameTimes.push_back(FrameTimeData{.presentTime = kExpectedPeriod * i,
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 8208b3f..dbd51fe 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -104,8 +104,12 @@
     const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
                                                                  .readyDuration = 0,
                                                                  .earliestVsync = 0};
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_FALSE(mEventQueue.nextExpectedInvalidate().has_value());
+
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+    EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+    EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
 }
 
 TEST_F(MessageQueueTest, invalidateTwice) {
@@ -114,11 +118,15 @@
                                                                  .readyDuration = 0,
                                                                  .earliestVsync = 0};
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+    EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+    EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+    EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+    EXPECT_EQ(4567, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
 }
 
 TEST_F(MessageQueueTest, invalidateTwiceWithCallback) {
@@ -127,8 +135,10 @@
                                                                  .readyDuration = 0,
                                                                  .earliestVsync = 0};
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+    EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+    EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
 
     const auto startTime = 100;
     const auto endTime = startTime + mDuration.count();
@@ -141,12 +151,14 @@
     EXPECT_CALL(*mHandler, dispatchInvalidate(vsyncId, presentTime)).Times(1);
     EXPECT_NO_FATAL_FAILURE(mEventQueue.triggerVsyncCallback(presentTime, startTime, endTime));
 
+    EXPECT_FALSE(mEventQueue.nextExpectedInvalidate().has_value());
+
     const auto timingAfterCallback =
             scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
                                                      .readyDuration = 0,
                                                      .earliestVsync = presentTime};
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).Times(1);
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
 }
 
@@ -158,7 +170,7 @@
                                                      .readyDuration = 0,
                                                      .earliestVsync = 0};
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
 }
 
diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
index cfbb3f5..6916764 100644
--- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
@@ -19,9 +19,11 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <utils/Log.h>
+#include <utils/Timers.h>
 
 #include "AsyncCallRecorder.h"
 #include "Scheduler/OneShotTimer.h"
+#include "fake/FakeClock.h"
 
 using namespace std::chrono_literals;
 
@@ -33,16 +35,6 @@
     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 +48,179 @@
 
 namespace {
 TEST_F(OneShotTimerTest, createAndDestroyTest) {
+    fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>(
-            "TestTimer", 3ms, [] {}, [] {});
+            "TestTimer", 3ms, [] {}, [] {}, std::unique_ptr<fake::FakeClock>(clock));
 }
 
 TEST_F(OneShotTimerTest, startStopTest) {
-    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 30ms,
+    fake::FakeClock* clock = new fake::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<fake::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,
+    fake::FakeClock* clock = new fake::FakeClock();
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
-                                                           mExpiredTimerCallback.getInvocable());
+                                                           mExpiredTimerCallback.getInvocable(),
+                                                           std::unique_ptr<fake::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,
+    fake::FakeClock* clock = new fake::FakeClock();
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
-                                                           mExpiredTimerCallback.getInvocable());
+                                                           mExpiredTimerCallback.getInvocable(),
+                                                           std::unique_ptr<fake::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,
+    fake::FakeClock* clock = new fake::FakeClock();
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
-                                                           mExpiredTimerCallback.getInvocable());
+                                                           mExpiredTimerCallback.getInvocable(),
+                                                           std::unique_ptr<fake::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,
+    fake::FakeClock* clock = new fake::FakeClock();
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
-                                                           mExpiredTimerCallback.getInvocable());
+                                                           mExpiredTimerCallback.getInvocable(),
+                                                           std::unique_ptr<fake::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,
+    fake::FakeClock* clock = new fake::FakeClock();
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
-                                                           mExpiredTimerCallback.getInvocable());
+                                                           mExpiredTimerCallback.getInvocable(),
+                                                           std::unique_ptr<fake::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,
+    fake::FakeClock* clock = new fake::FakeClock();
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
-                                                           mExpiredTimerCallback.getInvocable());
+                                                           mExpiredTimerCallback.getInvocable(),
+                                                           std::unique_ptr<fake::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,
+    fake::FakeClock* clock = new fake::FakeClock();
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
-                                                           mExpiredTimerCallback.getInvocable());
+                                                           mExpiredTimerCallback.getInvocable(),
+                                                           std::unique_ptr<fake::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/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 7e602a2..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>(),
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 c1f0c4e..3e4e130 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>
 
@@ -156,44 +156,8 @@
     }
 
     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) {
@@ -278,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());
@@ -948,24 +897,24 @@
     EXPECT_THAT(result, HasSubstr("compositionStrategyChanges = 0"));
     EXPECT_THAT(result, HasSubstr("averageFrameDuration = 0.000 ms"));
     EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 0.000 ms"));
-    std::string expectedResult = "totalTimelineFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "jankyFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "sfLongCpuJankyFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "sfLongGpuJankyFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "sfUnattributedJankyFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "appUnattributedJankyFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "sfSchedulingJankyFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "sfPredictionErrorJankyFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
-    expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(0);
-    EXPECT_THAT(result, HasSubstr(expectedResult));
+    std::string expectedResult = "totalTimelineFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+    expectedResult = "jankyFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+    expectedResult = "sfLongCpuJankyFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+    expectedResult = "sfLongGpuJankyFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+    expectedResult = "sfUnattributedJankyFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+    expectedResult = "appUnattributedJankyFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+    expectedResult = "sfSchedulingJankyFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+    expectedResult = "sfPredictionErrorJankyFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
+    expectedResult = "appBufferStuffingJankyFrames = ";
+    EXPECT_THAT(result, Not(HasSubstr(expectedResult)));
 }
 
 TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
@@ -1012,61 +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,
-        TimeStats::SetFrameRateVote::FrameRateCompatibility frameRateCompatibility,
-        TimeStats::SetFrameRateVote::Seamlessness 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 */,
-                static_cast<int>(frameRateCompatibility));
-    proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, static_cast<int>(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) {
@@ -1074,8 +1011,10 @@
     constexpr size_t MISSED_FRAMES = 4;
     constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
     constexpr size_t DISPLAY_EVENT_CONNECTIONS = 14;
+    constexpr nsecs_t DISPLAY_DEADLINE_DELTA = 1'000'000;
+    constexpr nsecs_t DISPLAY_PRESENT_JITTER = 2'000'000;
+    constexpr nsecs_t APP_DEADLINE_DELTA = 3'000'000;
 
-    mTimeStats->onBootFinished();
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
     for (size_t i = 0; i < TOTAL_FRAMES; i++) {
@@ -1100,86 +1039,65 @@
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
 
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
+                                      JankType::SurfaceFlingerCpuDeadlineMissed,
+                                      DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+                                      APP_DEADLINE_DELTA});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
+                                      JankType::SurfaceFlingerGpuDeadlineMissed,
+                                      DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+                                      APP_DEADLINE_DELTA});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::DisplayHAL, 1, 2, 3});
+                                      JankType::DisplayHAL, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::AppDeadlineMissed, 1, 2, 3});
+                                      JankType::AppDeadlineMissed, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::SurfaceFlingerScheduling, 1, 2, 3});
+                                      JankType::SurfaceFlingerScheduling, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::PredictionError, 1, 2, 3});
+                                      JankType::PredictionError, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2,
-                                      3});
+                                      JankType::AppDeadlineMissed | JankType::BufferStuffing,
+                                      DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+                                      APP_DEADLINE_DELTA});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::None, 1, 2, 3});
+                                      JankType::BufferStuffing, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::None, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
 
-    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)));
@@ -1194,7 +1112,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));
@@ -1226,17 +1144,19 @@
     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));
 }
 
 TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) {
     constexpr size_t LATE_ACQUIRE_FRAMES = 2;
     constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 3;
+    constexpr nsecs_t DISPLAY_DEADLINE_DELTA = 1'000'000;
+    constexpr nsecs_t DISPLAY_PRESENT_JITTER = 2'000'000;
+    constexpr nsecs_t APP_DEADLINE_DELTA_2MS = 2'000'000;
+    constexpr nsecs_t APP_DEADLINE_DELTA_3MS = 3'000'000;
     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);
@@ -1253,116 +1173,69 @@
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60);
 
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
+                                      JankType::SurfaceFlingerCpuDeadlineMissed,
+                                      DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+                                      APP_DEADLINE_DELTA_3MS});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
+                                      JankType::SurfaceFlingerGpuDeadlineMissed,
+                                      DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+                                      APP_DEADLINE_DELTA_3MS});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::DisplayHAL, 1, 2, 3});
+                                      JankType::DisplayHAL, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::AppDeadlineMissed, 1, 2, 3});
+                                      JankType::AppDeadlineMissed, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::SurfaceFlingerScheduling, 1, 2, 2});
+                                      JankType::SurfaceFlingerScheduling, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_2MS});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::PredictionError, 1, 2, 2});
+                                      JankType::PredictionError, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_2MS});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2,
-                                      2});
+                                      JankType::AppDeadlineMissed | JankType::BufferStuffing,
+                                      DISPLAY_DEADLINE_DELTA, APP_DEADLINE_DELTA_2MS,
+                                      APP_DEADLINE_DELTA_2MS});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::None, 1, 2, 3});
+                                      JankType::None, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
 
-    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(frameRate60.frameRate,
-                                           frameRate60.frameRateCompatibility,
-                                           frameRate60.seamlessness);
-    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)));
@@ -1398,37 +1271,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);
@@ -1438,102 +1300,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) {
@@ -1563,6 +1376,30 @@
     }
 }
 
+TEST_F(TimeStatsTest, refreshRateIsClampedToNearestBucket) {
+    // this stat is not in the proto so verify by checking the string dump
+    const auto verifyRefreshRateBucket = [&](Fps fps, int32_t bucket) {
+        EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
+        EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+        insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+        mTimeStats->incrementJankyFrames(
+                {fps, std::nullopt, UID_0, genLayerName(LAYER_ID_0), JankType::None, 0, 0, 0});
+        const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+        std::string expectedResult = "displayRefreshRate = " + std::to_string(bucket) + " fps";
+        EXPECT_THAT(result, HasSubstr(expectedResult)) << "failed for " << fps;
+    };
+
+    verifyRefreshRateBucket(Fps(91.f), 90);
+    verifyRefreshRateBucket(Fps(89.f), 90);
+
+    verifyRefreshRateBucket(Fps(61.f), 60);
+    verifyRefreshRateBucket(Fps(59.f), 60);
+
+    verifyRefreshRateBucket(Fps(31.f), 30);
+    verifyRefreshRateBucket(Fps(29.f), 30);
+}
+
 } // namespace
 } // namespace android
 
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/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index b64cce9..d59d64b 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -232,11 +232,12 @@
         VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
                                           mVsyncMoveThreshold};
         CountingCallback cb(mDispatch);
-        EXPECT_EQ(mDispatch.schedule(cb,
-                                     {.workDuration = 100,
-                                      .readyDuration = 0,
-                                      .earliestVsync = 1000}),
-                  ScheduleResult::Scheduled);
+        const auto result = mDispatch.schedule(cb,
+                                               {.workDuration = 100,
+                                                .readyDuration = 0,
+                                                .earliestVsync = 1000});
+        EXPECT_TRUE(result.has_value());
+        EXPECT_EQ(900, *result);
     }
 }
 
@@ -245,11 +246,13 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 900));
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 100,
-                                  .readyDuration = 0,
-                                  .earliestVsync = intended}),
-              ScheduleResult::Scheduled);
+    const auto result = mDispatch.schedule(cb,
+                                           {.workDuration = 100,
+                                            .readyDuration = 0,
+                                            .earliestVsync = intended});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(900, *result);
+
     advanceToNextCallback();
 
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -277,11 +280,12 @@
     EXPECT_CALL(mMockClock, alarmAt(_, mPeriod));
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = workDuration,
-                                  .readyDuration = 0,
-                                  .earliestVsync = mPeriod}),
-              ScheduleResult::Scheduled);
+    const auto result = mDispatch.schedule(cb,
+                                           {.workDuration = workDuration,
+                                            .readyDuration = 0,
+                                            .earliestVsync = mPeriod});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(mPeriod, *result);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) {
@@ -289,11 +293,11 @@
     EXPECT_CALL(mMockClock, alarmCancel());
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 100,
-                                  .readyDuration = 0,
-                                  .earliestVsync = mPeriod}),
-              ScheduleResult::Scheduled);
+    const auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(mPeriod - 100, *result);
     EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled);
 }
 
@@ -302,11 +306,11 @@
     EXPECT_CALL(mMockClock, alarmCancel());
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 100,
-                                  .readyDuration = 0,
-                                  .earliestVsync = mPeriod}),
-              ScheduleResult::Scheduled);
+    const auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(mPeriod - 100, *result);
     mMockClock.advanceBy(950);
     EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
 }
@@ -316,11 +320,11 @@
     EXPECT_CALL(mMockClock, alarmCancel());
 
     PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 100,
-                                  .readyDuration = 0,
-                                  .earliestVsync = mPeriod}),
-              ScheduleResult::Scheduled);
+    const auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(mPeriod - 100, *result);
 
     std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
     EXPECT_TRUE(cb.waitForPause());
@@ -337,11 +341,11 @@
 
     PausingCallback cb(mDispatch, 50ms);
     cb.stashResource(resource);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 100,
-                                  .readyDuration = 0,
-                                  .earliestVsync = mPeriod}),
-              ScheduleResult::Scheduled);
+    const auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(mPeriod - 100, *result);
 
     std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
     EXPECT_TRUE(cb.waitForPause());
@@ -535,21 +539,25 @@
     std::optional<nsecs_t> lastTarget;
     tmp = mDispatch.registerCallback(
             [&](auto timestamp, auto, auto) {
-                EXPECT_EQ(mDispatch.schedule(tmp,
-                                             {.workDuration = 400,
-                                              .readyDuration = 0,
-                                              .earliestVsync = timestamp - mVsyncMoveThreshold}),
-                          ScheduleResult::Scheduled);
-                EXPECT_EQ(mDispatch.schedule(tmp,
-                                             {.workDuration = 400,
-                                              .readyDuration = 0,
-                                              .earliestVsync = timestamp}),
-                          ScheduleResult::Scheduled);
-                EXPECT_EQ(mDispatch.schedule(tmp,
-                                             {.workDuration = 400,
-                                              .readyDuration = 0,
-                                              .earliestVsync = timestamp + mVsyncMoveThreshold}),
-                          ScheduleResult::Scheduled);
+                auto result =
+                        mDispatch.schedule(tmp,
+                                           {.workDuration = 400,
+                                            .readyDuration = 0,
+                                            .earliestVsync = timestamp - mVsyncMoveThreshold});
+                EXPECT_TRUE(result.has_value());
+                EXPECT_EQ(mPeriod + timestamp - 400, *result);
+                result = mDispatch.schedule(tmp,
+                                            {.workDuration = 400,
+                                             .readyDuration = 0,
+                                             .earliestVsync = timestamp});
+                EXPECT_TRUE(result.has_value());
+                EXPECT_EQ(mPeriod + timestamp - 400, *result);
+                result = mDispatch.schedule(tmp,
+                                            {.workDuration = 400,
+                                             .readyDuration = 0,
+                                             .earliestVsync = timestamp + mVsyncMoveThreshold});
+                EXPECT_TRUE(result.has_value());
+                EXPECT_EQ(mPeriod + timestamp - 400, *result);
                 lastTarget = timestamp;
             },
             "oo");
@@ -627,36 +635,41 @@
 
 TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) {
     VSyncDispatch::CallbackToken token(100);
-    EXPECT_THAT(mDispatch.schedule(token,
-                                   {.workDuration = 100,
-                                    .readyDuration = 0,
-                                    .earliestVsync = 1000}),
-                Eq(ScheduleResult::Error));
+    EXPECT_FALSE(mDispatch
+                         .schedule(token,
+                                   {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000})
+                         .has_value());
     EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
     CountingCallback cb0(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb0,
+                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(500, *result);
+    result = mDispatch.schedule(cb0,
+                                {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(900, *result);
 }
 
 // b/1450138150
 TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) {
     EXPECT_CALL(mMockClock, alarmAt(_, 500));
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(500, *result);
     mMockClock.advanceBy(400);
 
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb,
+                                {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1200, *result);
     advanceToNextCallback();
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
 }
@@ -667,24 +680,30 @@
             .WillOnce(Return(1000))
             .WillOnce(Return(1002));
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(500, *result);
     mMockClock.advanceBy(400);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb,
+                                {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(602, *result);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
     CountingCallback cb0(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb0,
+                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(500, *result);
     advanceToNextCallback();
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb0,
+                                {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(900, *result);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
@@ -692,26 +711,32 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
     EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq);
     CountingCallback cb0(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb0,
+                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(500, *result);
     advanceToNextCallback();
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb0,
+                                {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1100, *result);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
     EXPECT_CALL(mMockClock, alarmAt(_, 600));
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
 
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb,
+                                {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
 
     advanceToNextCallback();
 }
@@ -754,16 +779,19 @@
     CountingCallback cb1(mDispatch);
     CountingCallback cb2(mDispatch);
 
-    EXPECT_EQ(mDispatch.schedule(cb1,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb1,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
 
-    EXPECT_EQ(mDispatch.schedule(cb2,
-                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb2,
+                                {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1900, *result);
     mMockClock.advanceBy(80);
 
     EXPECT_THAT(cb1.mCalls.size(), Eq(1));
@@ -779,16 +807,19 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 1630)).InSequence(seq);
     CountingCallback cb(mDispatch);
 
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
 
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb,
+                                {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1630, *result);
     mMockClock.advanceBy(80);
 
     EXPECT_THAT(cb.mCalls.size(), Eq(1));
@@ -802,12 +833,15 @@
     CountingCallback cb1(mDispatch);
     CountingCallback cb2(mDispatch);
 
-    EXPECT_EQ(mDispatch.schedule(cb1,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
-    EXPECT_EQ(mDispatch.schedule(cb2,
-                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb1,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
+    result = mDispatch.schedule(cb2,
+                                {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1900, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
@@ -828,12 +862,15 @@
     CountingCallback cb1(mDispatch);
     CountingCallback cb2(mDispatch);
 
-    EXPECT_EQ(mDispatch.schedule(cb1,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
-    EXPECT_EQ(mDispatch.schedule(cb2,
-                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb1,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
+    result = mDispatch.schedule(cb2,
+                                {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1900, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
@@ -861,12 +898,15 @@
             .InSequence(seq)
             .WillOnce(Return(1000));
 
-    EXPECT_EQ(mDispatch.schedule(cb1,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
-    EXPECT_EQ(mDispatch.schedule(cb2,
-                                 {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb1,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
+    result = mDispatch.schedule(cb2,
+                                {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(610, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(700);
@@ -886,11 +926,12 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 900));
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 70,
-                                  .readyDuration = 30,
-                                  .earliestVsync = intended}),
-              ScheduleResult::Scheduled);
+    const auto result = mDispatch.schedule(cb,
+                                           {.workDuration = 70,
+                                            .readyDuration = 30,
+                                            .earliestVsync = intended});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(900, *result);
     advanceToNextCallback();
 
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -922,9 +963,9 @@
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(900));
@@ -944,9 +985,9 @@
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
-    EXPECT_THAT(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
-                               mStubTracker, now),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
+                               mStubTracker, now)
+                        .has_value());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(9500));
@@ -967,9 +1008,9 @@
             },
             mVsyncMoveThreshold);
 
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(900));
@@ -1002,9 +1043,9 @@
     entry.update(mStubTracker, 0);
     EXPECT_FALSE(entry.wakeupTime());
 
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     auto wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(wakeup, Eq(900));
@@ -1018,9 +1059,9 @@
 TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
     VSyncDispatchTimerQueueEntry entry(
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     entry.update(mStubTracker, 0);
 
     auto const wakeup = entry.wakeupTime();
@@ -1031,26 +1072,26 @@
 TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
     VSyncDispatchTimerQueueEntry entry(
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     entry.executing(); // 1000 is executing
     // had 1000 not been executing, this could have been scheduled for time 800.
-    EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
 
-    EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
 
-    EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
+                               mStubTracker, 0)
+                        .has_value());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
 }
@@ -1071,32 +1112,32 @@
             .InSequence(seq)
             .WillOnce(Return(2000));
 
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
 
     entry.executing(); // 1000 is executing
 
-    EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
     VSyncDispatchTimerQueueEntry entry(
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
-    EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
-    EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
-    EXPECT_THAT(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
+    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
+    EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
+    EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) {
@@ -1128,9 +1169,9 @@
             },
             mVsyncMoveThreshold);
 
-    EXPECT_THAT(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(900));
diff --git a/services/surfaceflinger/tests/unittests/fake/FakeClock.h b/services/surfaceflinger/tests/unittests/fake/FakeClock.h
new file mode 100644
index 0000000..6d9c764
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/fake/FakeClock.h
@@ -0,0 +1,34 @@
+/*
+ * 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 "../../Clock.h"
+
+namespace android::fake {
+
+class FakeClock : public 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;
+};
+
+} // namespace android::fake
\ No newline at end of file
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/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index 453c93a..0e7b320 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -39,6 +39,7 @@
                  void(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
                       std::chrono::nanoseconds));
     MOCK_METHOD1(setDuration, void(std::chrono::nanoseconds workDuration));
+    MOCK_METHOD0(nextExpectedInvalidate, std::optional<std::chrono::steady_clock::time_point>());
 };
 
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 37b74ed..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());
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 eb24a22..80e9a3c 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -106,42 +106,6 @@
     ],
 }
 
-cc_binary {
-    name: "vr_hwc",
-    enabled: false,
-    system_ext_specific: true,
-    vintf_fragments: ["manifest_vr_hwc.xml"],
-    srcs: [
-        "vr_hardware_composer_service.cpp",
-    ],
-    static_libs: [
-        "libvr_hwc-impl",
-        // NOTE: This needs to be included after the *-impl lib otherwise the
-        // symbols in the *-binder library get optimized out.
-        "libvr_hwc-binder",
-    ],
-    shared_libs: [
-        "android.frameworks.vr.composer@2.0",
-        "android.hardware.graphics.composer@2.3",
-        "libbase",
-        "libbinder",
-        "liblog",
-        "libhardware",
-        "libhidlbase",
-        "libui",
-        "libutils",
-        "libvr_hwc-hal",
-    ],
-    cflags: [
-        "-DLOG_TAG=\"vr_hwc\"",
-        "-Wall",
-        "-Werror",
-    ],
-    init_rc: [
-        "vr_hwc.rc",
-    ],
-}
-
 cc_test {
     name: "vr_hwc_test",
     gtest: true,
diff --git a/services/vr/hardware_composer/manifest_vr_hwc.xml b/services/vr/hardware_composer/manifest_vr_hwc.xml
deleted file mode 100644
index 1068cac..0000000
--- a/services/vr/hardware_composer/manifest_vr_hwc.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<manifest version="1.0" type="framework">
-    <hal>
-      <name>android.hardware.graphics.composer</name>
-      <transport>hwbinder</transport>
-      <version>2.1</version>
-      <interface>
-          <name>IComposer</name>
-          <instance>vr</instance>
-      </interface>
-    </hal>
-</manifest>
diff --git a/services/vr/hardware_composer/vr_hardware_composer_service.cpp b/services/vr/hardware_composer/vr_hardware_composer_service.cpp
deleted file mode 100644
index 7701847..0000000
--- a/services/vr/hardware_composer/vr_hardware_composer_service.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-#include <hwbinder/IPCThreadState.h>
-#include <impl/vr_hwc.h>
-#include <inttypes.h>
-
-#include "vr_composer.h"
-
-int main() {
-  android::ProcessState::self()->startThreadPool();
-
-  // Register the hwbinder HWC HAL service used by SurfaceFlinger while in VR
-  // mode.
-  android::sp<android::dvr::VrHwc> service = new android::dvr::VrHwc();
-
-  LOG_ALWAYS_FATAL_IF(!service.get(), "Failed to get service");
-  LOG_ALWAYS_FATAL_IF(service->isRemote(), "Service is remote");
-
-  const char instance[] = "vr";
-  LOG_ALWAYS_FATAL_IF(service->registerAsService(instance) != android::OK,
-                      "Failed to register service");
-
-  android::sp<android::dvr::VrComposer> composer =
-      new android::dvr::VrComposer(service.get());
-
-  android::sp<android::IServiceManager> sm(android::defaultServiceManager());
-
-  // Register the binder service used by VR Window Manager service to receive
-  // frame information from VR HWC HAL.
-  android::status_t status = sm->addService(
-      android::dvr::VrComposer::SERVICE_NAME(), composer.get(),
-      false /* allowIsolated */);
-  LOG_ALWAYS_FATAL_IF(status != android::OK,
-                      "VrDisplay service failed to start: %" PRId32, status);
-
-  android::hardware::ProcessState::self()->startThreadPool();
-  android::hardware::IPCThreadState::self()->joinThreadPool();
-
-  return 0;
-}
diff --git a/services/vr/hardware_composer/vr_hwc.rc b/services/vr/hardware_composer/vr_hwc.rc
deleted file mode 100644
index 645ab80..0000000
--- a/services/vr/hardware_composer/vr_hwc.rc
+++ /dev/null
@@ -1,6 +0,0 @@
-service vr_hwc /system/bin/vr_hwc
-  class hal animation
-  user system
-  group system graphics
-  onrestart restart surfaceflinger
-  writepid /dev/cpuset/system-background/tasks
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 67cd875..440c5b1 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -29,17 +29,14 @@
     unversioned_until: "current",
 }
 
-llndk_library {
-    name: "libvulkan.llndk",
-    symbol_file: "libvulkan.map.txt",
-    export_llndk_headers: [
-        "vulkan_headers_llndk",
-    ],
-}
-
 cc_library_shared {
     name: "libvulkan",
-    llndk_stubs: "libvulkan.llndk",
+    llndk: {
+        symbol_file: "libvulkan.map.txt",
+        export_llndk_headers: [
+            "vulkan_headers",
+        ],
+    },
     clang: true,
     sanitize: {
         misc_undefined: ["integer"],
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);