Merge "Support building libjnigraphics on the host"
diff --git a/aidl/gui/android/view/LayerMetadataKey.aidl b/aidl/gui/android/view/LayerMetadataKey.aidl
index 491c629..a1d8ce5 100644
--- a/aidl/gui/android/view/LayerMetadataKey.aidl
+++ b/aidl/gui/android/view/LayerMetadataKey.aidl
@@ -25,4 +25,5 @@
     METADATA_MOUSE_CURSOR = 4,
     METADATA_ACCESSIBILITY_ID = 5,
     METADATA_OWNER_PID = 6,
+    METADATA_DEQUEUE_TIME = 7,
 }
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index bfcc058..ba25a5a 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -39,8 +39,13 @@
     std::string calling_package;
 };
 
-static binder::Status exception(uint32_t code, const std::string& msg) {
-    MYLOGE("%s (%d) ", msg.c_str(), code);
+static binder::Status exception(uint32_t code, const std::string& msg,
+                                const std::string& extra_msg = "") {
+    if (extra_msg.empty()) {
+        MYLOGE("%s (%d) ", msg.c_str(), code);
+    } else {
+        MYLOGE("%s %s (%d) ", msg.c_str(), extra_msg.c_str(), code);
+    }
     return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
 }
 
@@ -60,7 +65,7 @@
 
 }  // namespace
 
-DumpstateService::DumpstateService() : ds_(nullptr) {
+DumpstateService::DumpstateService() : ds_(nullptr), calling_uid_(-1), calling_package_() {
 }
 
 char const* DumpstateService::getServiceName() {
@@ -131,6 +136,10 @@
     ds_->SetOptions(std::move(options));
     ds_->listener_ = listener;
 
+    // Track caller info for cancellation purposes.
+    calling_uid_ = calling_uid;
+    calling_package_ = calling_package;
+
     DumpstateInfo* ds_info = new DumpstateInfo();
     ds_info->ds = ds_;
     ds_info->calling_uid = calling_uid;
@@ -149,8 +158,20 @@
     return binder::Status::ok();
 }
 
-binder::Status DumpstateService::cancelBugreport() {
+binder::Status DumpstateService::cancelBugreport(int32_t calling_uid,
+                                                 const std::string& calling_package) {
     std::lock_guard<std::mutex> lock(lock_);
+    if (calling_uid != calling_uid_ || calling_package != calling_package_) {
+        // Note: we use a SecurityException to prevent BugreportManagerServiceImpl from killing the
+        // report in progress (from another caller).
+        return exception(
+            binder::Status::EX_SECURITY,
+            StringPrintf("Cancellation requested by %d/%s does not match report in "
+                         "progress",
+                         calling_uid, calling_package.c_str()),
+            // Sharing the owner of the BR is a (minor) leak, so leave it out of the app's exception
+            StringPrintf("started by %d/%s", calling_uid_, calling_package_.c_str()));
+    }
     ds_->Cancel();
     return binder::Status::ok();
 }
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index ac8d3ac..3ec8471 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -44,8 +44,7 @@
                                   const sp<IDumpstateListener>& listener,
                                   bool is_screenshot_requested) override;
 
-    // No-op
-    binder::Status cancelBugreport();
+    binder::Status cancelBugreport(int32_t calling_uid, const std::string& calling_package);
 
   private:
     // Dumpstate object which contains all the bugreporting logic.
@@ -53,6 +52,8 @@
     // one bugreport.
     // This service does not own this object.
     Dumpstate* ds_;
+    int32_t calling_uid_;
+    std::string calling_package_;
     std::mutex lock_;
 };
 
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index eeaa5a3..c833d0e 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -124,6 +124,12 @@
     return *this;
 }
 
+CommandOptions::CommandOptionsBuilder&
+CommandOptions::CommandOptionsBuilder::CloseAllFileDescriptorsOnExec() {
+    values.close_all_fds_on_exec_ = true;
+    return *this;
+}
+
 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Log(
     const std::string& message) {
     values.logging_message_ = message;
@@ -137,6 +143,7 @@
 CommandOptions::CommandOptionsValues::CommandOptionsValues(int64_t timeout_ms)
     : timeout_ms_(timeout_ms),
       always_(false),
+      close_all_fds_on_exec_(false),
       account_mode_(DONT_DROP_ROOT),
       output_mode_(NORMAL_OUTPUT),
       logging_message_("") {
@@ -157,6 +164,10 @@
     return values.always_;
 }
 
+bool CommandOptions::ShouldCloseAllFileDescriptorsOnExec() const {
+    return values.close_all_fds_on_exec_;
+}
+
 PrivilegeMode CommandOptions::PrivilegeMode() const {
     return values.account_mode_;
 }
@@ -277,7 +288,8 @@
         MYLOGI(logging_message.c_str(), command_string.c_str());
     }
 
-    bool silent = (options.OutputMode() == REDIRECT_TO_STDERR);
+    bool silent = (options.OutputMode() == REDIRECT_TO_STDERR ||
+                   options.ShouldCloseAllFileDescriptorsOnExec());
     bool redirecting_to_fd = STDOUT_FILENO != fd;
 
     if (PropertiesHelper::IsDryRun() && !options.Always()) {
@@ -314,7 +326,27 @@
             return -1;
         }
 
-        if (silent) {
+        if (options.ShouldCloseAllFileDescriptorsOnExec()) {
+            int devnull_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
+            TEMP_FAILURE_RETRY(dup2(devnull_fd, STDIN_FILENO));
+            close(devnull_fd);
+            devnull_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY));
+            TEMP_FAILURE_RETRY(dup2(devnull_fd, STDOUT_FILENO));
+            TEMP_FAILURE_RETRY(dup2(devnull_fd, STDERR_FILENO));
+            close(devnull_fd);
+            // This is to avoid leaking FDs that, accidentally, have not been
+            // marked as O_CLOEXEC. Leaking FDs across exec can cause failures
+            // when execing a process that has a SELinux auto_trans rule.
+            // Here we assume that the dumpstate process didn't open more than
+            // 1000 FDs. In theory we could iterate through /proc/self/fd/, but
+            // doing that in a fork-safe way is too complex and not worth it
+            // (opendir()/readdir() do heap allocations and take locks).
+            for (int i = 0; i < 1000; i++) {
+                if (i != STDIN_FILENO && i!= STDOUT_FILENO && i != STDERR_FILENO) {
+                    close(i);
+                }
+            }
+        } else if (silent) {
             // Redirects stdout to stderr
             TEMP_FAILURE_RETRY(dup2(STDERR_FILENO, STDOUT_FILENO));
         } else if (redirecting_to_fd) {
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
index b099443..b00c46e 100644
--- a/cmds/dumpstate/DumpstateUtil.h
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -80,6 +80,7 @@
 
         int64_t timeout_ms_;
         bool always_;
+        bool close_all_fds_on_exec_;
         PrivilegeMode account_mode_;
         OutputMode output_mode_;
         std::string logging_message_;
@@ -112,6 +113,13 @@
         CommandOptionsBuilder& DropRoot();
         /* Sets the command's OutputMode as `REDIRECT_TO_STDERR` */
         CommandOptionsBuilder& RedirectStderr();
+        /* Closes all file descriptors before exec-ing the target process. This
+         * includes also stdio pipes, which are dup-ed on /dev/null. It prevents
+         * leaking opened FDs to the target process, which in turn can hit
+         * selinux denials in presence of auto_trans rules.
+         */
+        CommandOptionsBuilder& CloseAllFileDescriptorsOnExec();
+
         /* When not empty, logs a message before executing the command.
          * Must contain a `%s`, which will be replaced by the full command line, and end on `\n`. */
         CommandOptionsBuilder& Log(const std::string& message);
@@ -130,6 +138,8 @@
     int64_t TimeoutInMs() const;
     /* Checks whether the command should always be run, even on dry-run mode. */
     bool Always() const;
+    /* Checks whether all FDs should be closed prior to the exec() calls. */
+    bool ShouldCloseAllFileDescriptorsOnExec() const;
     /** Gets the PrivilegeMode of the command. */
     PrivilegeMode PrivilegeMode() const;
     /** Gets the OutputMode of the command. */
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index ba008bb..0793f0b 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -1,4 +1,4 @@
-/**
+/*
  * Copyright (c) 2016, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,9 +19,9 @@
 import android.os.IDumpstateListener;
 
 /**
-  * Binder interface for the currently running dumpstate process.
-  * {@hide}
-  */
+ * Binder interface for the currently running dumpstate process.
+ * {@hide}
+ */
 interface IDumpstate {
 
     // NOTE: If you add to or change these modes, please also change the corresponding enums
@@ -49,10 +49,10 @@
     // Default mode.
     const int BUGREPORT_MODE_DEFAULT = 6;
 
-    /*
+    /**
      * Starts a bugreport in the background.
      *
-     *<p>Shows the user a dialog to get consent for sharing the bugreport with the calling
+     * <p>Shows the user a dialog to get consent for sharing the bugreport with the calling
      * application. If they deny {@link IDumpstateListener#onError} will be called. If they
      * consent and bugreport generation is successful artifacts will be copied to the given fds and
      * {@link IDumpstateListener#onFinished} will be called. If there
@@ -71,8 +71,15 @@
                         int bugreportMode, IDumpstateListener listener,
                         boolean isScreenshotRequested);
 
-    /*
+    /**
      * Cancels the bugreport currently in progress.
+     *
+     * <p>The caller must match the original caller of {@link #startBugreport} in order for the
+     * report to actually be cancelled. A {@link SecurityException} is reported if a mismatch is
+     * detected.
+     *
+     * @param callingUid UID of the original application that requested the cancellation.
+     * @param callingPackage package of the original application that requested the cancellation.
      */
-    void cancelBugreport();
+    void cancelBugreport(int callingUid, @utf8InCpp String callingPackage);
 }
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index afa0b4d..4a4a510 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -28,6 +28,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/mount.h>
 #include <sys/poll.h>
 #include <sys/prctl.h>
 #include <sys/resource.h>
@@ -174,6 +175,7 @@
 #define SNAPSHOTCTL_LOG_DIR "/data/misc/snapshotctl_log"
 #define LINKERCONFIG_DIR "/linkerconfig"
 #define PACKAGE_DEX_USE_LIST "/data/system/package-dex-usage.list"
+#define SYSTEM_TRACE_SNAPSHOT "/data/misc/perfetto-traces/bugreport/systrace.pftrace"
 
 // TODO(narayan): Since this information has to be kept in sync
 // with tombstoned, we should just put it in a common header.
@@ -227,7 +229,6 @@
 static const std::string DUMP_HALS_TASK = "DUMP HALS";
 static const std::string DUMP_BOARD_TASK = "dumpstate_board()";
 static const std::string DUMP_CHECKINS_TASK = "DUMP CHECKINS";
-static const std::string DUMP_APP_INFOS_TASK = "DUMP APP INFOS";
 
 namespace android {
 namespace os {
@@ -1053,6 +1054,24 @@
     }
 }
 
+static void MaybeAddSystemTraceToZip() {
+    // This function copies into the .zip the system trace that was snapshotted
+    // by the early call to MaybeSnapshotSystemTrace(), if any background
+    // tracing was happening.
+    if (!ds.IsZipping()) {
+        MYLOGD("Not dumping system trace because it's not a zipped bugreport\n");
+        return;
+    }
+    if (!ds.has_system_trace_) {
+        // No background trace was happening at the time dumpstate was invoked.
+        return;
+    }
+    ds.AddZipEntry(
+            ZIP_ROOT_DIR + SYSTEM_TRACE_SNAPSHOT,
+            SYSTEM_TRACE_SNAPSHOT);
+    android::os::UnlinkAndLogOnError(SYSTEM_TRACE_SNAPSHOT);
+}
+
 static void DumpVisibleWindowViews() {
     if (!ds.IsZipping()) {
         MYLOGD("Not dumping visible views because it's not a zipped bugreport\n");
@@ -1550,7 +1569,7 @@
     dprintf(out_fd, "========================================================\n");
 
     RunDumpsys("APP PROVIDERS PLATFORM", {"activity", "provider", "all-platform"},
-            DUMPSYS_COMPONENTS_OPTIONS, out_fd);
+            DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
 
     dprintf(out_fd, "========================================================\n");
     dprintf(out_fd, "== Running Application Providers (non-platform)\n");
@@ -1577,7 +1596,6 @@
         ds.dump_pool_->enqueueTask(DUMP_INCIDENT_REPORT_TASK, &DumpIncidentReport);
         ds.dump_pool_->enqueueTaskWithFd(DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
         ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1);
-        ds.dump_pool_->enqueueTaskWithFd(DUMP_APP_INFOS_TASK, &DumpAppInfos, _1);
     }
 
     // Dump various things. Note that anything that takes "long" (i.e. several seconds) should
@@ -1651,6 +1669,8 @@
 
     AddAnrTraceFiles();
 
+    MaybeAddSystemTraceToZip();
+
     // NOTE: tombstones are always added as separate entries in the zip archive
     // and are not interspersed with the main report.
     const bool tombstones_dumped = AddDumps(ds.tombstone_data_.begin(), ds.tombstone_data_.end(),
@@ -1730,11 +1750,7 @@
         RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_CHECKINS_TASK, DumpCheckins);
     }
 
-    if (ds.dump_pool_) {
-        WAIT_TASK_WITH_CONSENT_CHECK(DUMP_APP_INFOS_TASK, ds.dump_pool_);
-    } else {
-        RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_APP_INFOS_TASK, DumpAppInfos);
-    }
+    RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpAppInfos);
 
     printf("========================================================\n");
     printf("== Dropbox crashes\n");
@@ -2163,6 +2179,22 @@
         return;
     }
 
+    /*
+     * 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.
+     */
+    auto api_level = android::base::GetIntProperty("ro.product.first_api_level", 0);
+    bool mount_debugfs = !PropertiesHelper::IsUserBuild() && api_level >= 31;
+
+    if (mount_debugfs) {
+        RunCommand("mount debugfs", {"mount", "-t", "debugfs", "debugfs", "/sys/kernel/debug"},
+                   AS_ROOT_20);
+        RunCommand("chmod debugfs", {"chmod", "0755", "/sys/kernel/debug"},
+                   AS_ROOT_20);
+    }
+
     std::vector<std::string> paths;
     std::vector<android::base::ScopeGuard<std::function<void()>>> remover;
     for (int i = 0; i < NUM_OF_DUMPS; i++) {
@@ -2262,6 +2294,10 @@
                "there might be racing in content\n", killing_timeout_sec);
     }
 
+    if (mount_debugfs) {
+        RunCommand("unmount debugfs", {"umount", "/sys/kernel/debug"}, AS_ROOT_20);
+    }
+
     auto file_sizes = std::make_unique<ssize_t[]>(paths.size());
     for (size_t i = 0; i < paths.size(); i++) {
         struct stat s;
@@ -2872,6 +2908,13 @@
         RunDumpsysCritical();
     }
     MaybeTakeEarlyScreenshot();
+
+    if (!is_dumpstate_restricted) {
+        // Snapshot the system trace now (if running) to avoid that dumpstate's
+        // own activity pushes out interesting data from the trace ring buffer.
+        // The trace file is added to the zip by MaybeAddSystemTraceToZip().
+        MaybeSnapshotSystemTrace();
+    }
     onUiIntensiveBugreportDumpsFinished(calling_uid);
     MaybeCheckUserConsent(calling_uid, calling_package);
     if (options_->telephony_only) {
@@ -2962,6 +3005,26 @@
     TakeScreenshot();
 }
 
+void Dumpstate::MaybeSnapshotSystemTrace() {
+    // If a background system trace is happening and is marked as "suitable for
+    // bugreport" (i.e. bugreport_score > 0 in the trace config), this command
+    // will stop it and serialize into SYSTEM_TRACE_SNAPSHOT. In the (likely)
+    // case that no trace is ongoing, this command is a no-op.
+    // Note: this should not be enqueued as we need to freeze the trace before
+    // dumpstate starts. Otherwise the trace ring buffers will contain mostly
+    // the dumpstate's own activity which is irrelevant.
+    int res = RunCommand(
+        "SERIALIZE PERFETTO TRACE",
+        {"perfetto", "--save-for-bugreport"},
+        CommandOptions::WithTimeout(10)
+            .DropRoot()
+            .CloseAllFileDescriptorsOnExec()
+            .Build());
+    has_system_trace_ = res == 0;
+    // MaybeAddSystemTraceToZip() will take care of copying the trace in the zip
+    // file in the later stages.
+}
+
 void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) {
     if (calling_uid == AID_SHELL || !CalledByApi()) {
         return;
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 255243f..f83968b 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -458,6 +458,11 @@
     // Whether it should take an screenshot earlier in the process.
     bool do_early_screenshot_ = false;
 
+    // This is set to true when the trace snapshot request in the early call to
+    // MaybeSnapshotSystemTrace(). When this is true, the later stages of
+    // dumpstate will append the trace to the zip archive.
+    bool has_system_trace_ = false;
+
     std::unique_ptr<Progress> progress_;
 
     // When set, defines a socket file-descriptor use to report progress to bugreportz
@@ -543,6 +548,7 @@
     RunStatus DumpstateDefaultAfterCritical();
 
     void MaybeTakeEarlyScreenshot();
+    void MaybeSnapshotSystemTrace();
 
     void onUiIntensiveBugreportDumpsFinished(int32_t calling_uid);
 
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index fe6a34a..0e366cb 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -319,6 +319,16 @@
  */
 class BugreportSectionTest : public Test {
   public:
+    ZipArchiveHandle handle;
+
+    void SetUp() {
+        ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath().c_str(), &handle), 0);
+    }
+
+    void TearDown() {
+        CloseArchive(handle);
+    }
+
     static void SetUpTestCase() {
         ParseSections(ZippedBugreportGenerationTest::getZipFilePath().c_str(),
                       ZippedBugreportGenerationTest::sections.get());
@@ -343,6 +353,19 @@
         }
         FAIL() << sectionName << " not found.";
     }
+
+    /**
+     * Whether or not the content of the section is injected by other commands.
+     */
+    bool IsContentInjectedByOthers(const std::string& line) {
+        // Command header such as `------ APP ACTIVITIES (/system/bin/dumpsys activity -v) ------`.
+        static const std::regex kCommandHeader = std::regex{"------ .+ \\(.+\\) ------"};
+        std::smatch match;
+        if (std::regex_match(line, match, kCommandHeader)) {
+          return true;
+        }
+        return false;
+    }
 };
 
 TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) {
@@ -400,6 +423,28 @@
     SectionExists("wifi", /* bytes= */ 100000);
 }
 
+TEST_F(BugreportSectionTest, NoInjectedContentByOtherCommand) {
+    // Extract the main entry to a temp file
+    TemporaryFile tmp_binary;
+    ASSERT_NE(-1, tmp_binary.fd);
+    ExtractBugreport(&handle, tmp_binary.fd);
+
+    // Read line by line and identify sections
+    std::ifstream ifs(tmp_binary.path, std::ifstream::in);
+    std::string line;
+    std::string current_section_name;
+    while (std::getline(ifs, line)) {
+        std::string section_name;
+        if (IsSectionStart(line, &section_name)) {
+            current_section_name = section_name;
+        } else if (IsSectionEnd(line)) {
+            current_section_name = "";
+        } else if (!current_section_name.empty()) {
+            EXPECT_FALSE(IsContentInjectedByOthers(line));
+        }
+    }
+}
+
 class DumpstateBinderTest : public Test {
   protected:
     void SetUp() override {
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 1327cfd..a017246 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -427,7 +427,7 @@
                  << strerror(errno) << std::endl;
             status = -errno;
             break;
-        } else if (rc == 0) {
+        } else if (rc == 0 || time_left_ms() == 0) {
             status = TIMED_OUT;
             break;
         }
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 64bfdf9..643b3ca 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -15,7 +15,7 @@
 cc_defaults {
     name: "idlcli-defaults",
     shared_libs: [
-        "android.hardware.vibrator-ndk_platform",
+        "android.hardware.vibrator-unstable-ndk_platform",
         "android.hardware.vibrator@1.0",
         "android.hardware.vibrator@1.1",
         "android.hardware.vibrator@1.2",
diff --git a/data/etc/android.software.translation.xml b/data/etc/android.software.translation.xml
new file mode 100644
index 0000000..3b361e5
--- /dev/null
+++ b/data/etc/android.software.translation.xml
@@ -0,0 +1,19 @@
+<?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 dc6963f..2c34047 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -52,6 +52,7 @@
     <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 e878f86..873b5b7 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -52,6 +52,7 @@
     <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/imagedecoder.h b/include/android/imagedecoder.h
index fcc0b9b..c7a8939 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -727,7 +727,7 @@
  * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The AImageDecoder
  *   is null.
  */
-int32_t AImageDecoder_getRepeatCount(AImageDecoder* _Nonnull decoder);
+int32_t AImageDecoder_getRepeatCount(AImageDecoder* _Nonnull decoder)
         __INTRODUCED_IN(31);
 
 /**
diff --git a/include/android/permission_manager.h b/include/android/permission_manager.h
index 7817126..2d5d060 100644
--- a/include/android/permission_manager.h
+++ b/include/android/permission_manager.h
@@ -64,8 +64,6 @@
     PERMISSION_MANAGER_STATUS_SERVICE_UNAVAILABLE = -2,
 };
 
-#if __ANDROID_API__ >= 31
-
 /**
  * Checks whether the package with the given pid/uid has been granted a permission.
  *
@@ -84,8 +82,6 @@
                                            uid_t uid,
                                            int32_t* outResult) __INTRODUCED_IN(31);
 
-#endif // __ANDROID_API__ >= 31
-
 __END_DECLS
 
 #endif  // ANDROID_PERMISSION_MANAGER_H
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 83582d6..eb81534 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -53,7 +53,7 @@
 #include <sys/types.h>
 
 #if !defined(__INTRODUCED_IN)
-#define __INTRODUCED_IN(30) /* Introduced in API level 30 */
+#define __INTRODUCED_IN(__api_level) /* nothing */
 #endif
 
 #ifdef __cplusplus
@@ -181,6 +181,51 @@
 
 #endif  //  __ANDROID_API__ >= 30
 
+#if __ANDROID_API__ >= 31
+
+/**
+ * Provides an estimate of how much thermal headroom the device currently has before
+ * hitting severe throttling.
+ *
+ * Note that this only attempts to track the headroom of slow-moving sensors, such as
+ * the skin temperature sensor. This means that there is no benefit to calling this function
+ * more frequently than about once per second, and attempted to call significantly
+ * more frequently may result in the function returning {@code NaN}.
+ *
+ * In addition, in order to be able to provide an accurate forecast, the system does
+ * not attempt to forecast until it has multiple temperature samples from which to
+ * extrapolate. This should only take a few seconds from the time of the first call,
+ * but during this time, no forecasting will occur, and the current headroom will be
+ * returned regardless of the value of {@code forecastSeconds}.
+ *
+ * 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
+ * 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.
+ *
+ * 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
+ * linearly with temperature, though temperature changes over time are typically not linear.
+ * Negative values will be clamped to 0.0 before returning.
+ *
+ * Available since API level 31.
+ *
+ * @param manager The manager instance to use.
+ *                Acquired via {@link AThermal_acquireManager}.
+ * @param forecastSeconds how many seconds into the future to forecast. Given that device
+ *                        conditions may change at any time, forecasts from further in the
+ *                        future will likely be less accurate than forecasts in the near future.
+ * @return a value greater than equal to 0.0, where 1.0 indicates the SEVERE throttling threshold,
+ *         as described above. Returns NaN if the device does not support this functionality or
+ *         if this function is called significantly faster than once per second.
+  */
+float AThermal_getThermalHeadroom(AThermalManager *manager,
+        int forecastSeconds) __INTRODUCED_IN(31);
+
+#endif  //  __ANDROID_API__ >= 31
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h
index 639df7a..4aa2f60 100644
--- a/include/audiomanager/AudioManager.h
+++ b/include/audiomanager/AudioManager.h
@@ -37,6 +37,7 @@
     PLAYER_STATE_STARTED  = 2,
     PLAYER_STATE_PAUSED   = 3,
     PLAYER_STATE_STOPPED  = 4,
+    PLAYER_UPDATE_DEVICE_ID = 5,
 } player_state_t;
 
 // must be kept in sync with definitions in AudioManager.java
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h
index 2f5ccb8..7d1f38f 100644
--- a/include/audiomanager/IAudioManager.h
+++ b/include/audiomanager/IAudioManager.h
@@ -49,7 +49,8 @@
                 audio_content_type_t content, const sp<IBinder>& player) = 0;
     /*oneway*/ virtual status_t playerAttributes(audio_unique_id_t piid, audio_usage_t usage,
                 audio_content_type_t content)= 0;
-    /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) = 0;
+    /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event,
+                audio_port_handle_t deviceId) = 0;
     /*oneway*/ virtual status_t releasePlayer(audio_unique_id_t piid) = 0;
     virtual audio_unique_id_t trackRecorder(const sp<IBinder>& recorder) = 0;
     /*oneway*/ virtual status_t recorderEvent(audio_unique_id_t riid, recorder_state_t event) = 0;
diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp
index dae6eeb..15bd5c3 100644
--- a/libs/adbd_auth/adbd_auth.cpp
+++ b/libs/adbd_auth/adbd_auth.cpp
@@ -282,9 +282,8 @@
             LOG(FATAL) << "adbd_auth: unhandled packet type?";
         }
 
-        output_queue_.pop_front();
-
         ssize_t rc = writev(framework_fd_.get(), iovs, iovcnt);
+        output_queue_.pop_front();
         if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
             PLOG(ERROR) << "adbd_auth: failed to write to framework fd";
             ReplaceFrameworkFd(unique_fd());
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index feaea63..9ea9732 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -179,15 +179,26 @@
         "--header-filter=^.*frameworks/native/libs/binder/.*.h$",
     ],
     tidy_checks_as_errors: [
-        "*",
+        // Explicitly list the checks that should not occur in this module.
+        "abseil-*",
+        "android-*",
+        "bugprone-*",
+        "cert-*",
+        "clang-analyzer-*",
         "-clang-analyzer-core.CallAndMessage",
         "-clang-analyzer-core.uninitialized.Assign",
-        "-clang-analyzer-unix.Malloc,",
+        "-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*",
+        "portability*",
     ],
 }
 
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 87eab52..440c98c 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1477,6 +1477,31 @@
     goto data_sorted;
 }
 
+status_t Parcel::readVectorSizeWithCoarseBoundCheck(int32_t *size) const {
+    int32_t requestedSize;
+    const status_t status = readInt32(&requestedSize);
+    if (status != NO_ERROR) return status;
+
+    // We permit negative sizes, which indicate presence of a nullable vector,
+    // i.e. a vector embedded in std::optional, std::unique_ptr, or std::shared_ptr.
+    if (requestedSize > 0) {
+        // Check if there are fewer bytes than vector elements.
+        // A lower bound is 1 byte per element, satisfied by some enum and int8_t and uint8_t.
+        const size_t availableBytes = dataAvail();
+        if (static_cast<size_t>(requestedSize) > availableBytes) {
+            // We have a size that is greater than the number of bytes available.
+            // On bounds failure we do not 'rewind' position by 4 bytes of the size already read.
+            ALOGW("%s: rejecting out of bounds vector size (requestedSize):%d "
+                    "Parcel{dataAvail:%zu mDataSize:%zu mDataPos:%zu mDataCapacity:%zu}",
+                    __func__, requestedSize, availableBytes, mDataSize, mDataPos, mDataCapacity);
+            return BAD_VALUE;
+        }
+    }
+
+    *size = requestedSize;
+    return NO_ERROR;
+}
+
 status_t Parcel::read(void* outData, size_t len) const
 {
     if (len > INT32_MAX) {
@@ -1699,7 +1724,7 @@
 status_t Parcel::readBoolVector(std::optional<std::vector<bool>>* val) const {
     const int32_t start = dataPosition();
     int32_t size;
-    status_t status = readInt32(&size);
+    status_t status = readVectorSizeWithCoarseBoundCheck(&size);
     val->reset();
 
     if (status != OK || size < 0) {
@@ -1721,7 +1746,7 @@
 status_t Parcel::readBoolVector(std::unique_ptr<std::vector<bool>>* val) const {
     const int32_t start = dataPosition();
     int32_t size;
-    status_t status = readInt32(&size);
+    status_t status = readVectorSizeWithCoarseBoundCheck(&size);
     val->reset();
 
     if (status != OK || size < 0) {
@@ -1742,7 +1767,7 @@
 
 status_t Parcel::readBoolVector(std::vector<bool>* val) const {
     int32_t size;
-    status_t status = readInt32(&size);
+    status_t status = readVectorSizeWithCoarseBoundCheck(&size);
 
     if (status != OK) {
         return status;
diff --git a/libs/binder/ParcelableHolder.cpp b/libs/binder/ParcelableHolder.cpp
index b2b8671..2e86b74 100644
--- a/libs/binder/ParcelableHolder.cpp
+++ b/libs/binder/ParcelableHolder.cpp
@@ -37,7 +37,7 @@
         size_t sizePos = p->dataPosition();
         RETURN_ON_FAILURE(p->writeInt32(0));
         size_t dataStartPos = p->dataPosition();
-        RETURN_ON_FAILURE(p->writeUtf8AsUtf16(this->mParcelableName));
+        RETURN_ON_FAILURE(p->writeString16(this->mParcelableName));
         this->mParcelable->writeToParcel(p);
         size_t dataSize = p->dataPosition() - dataStartPos;
 
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 97e282e..00a14f4 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -10,6 +10,9 @@
       "name": "binderAllocationLimits"
     },
     {
+      "name": "binderClearBufTest"
+    },
+    {
       "name": "binderDriverInterfaceTest"
     },
     {
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 988508e..e2a0f87 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -232,7 +232,6 @@
   "android.gui.IGraphicBufferConsumer",
   "android.gui.IRegionSamplingListener",
   "android.gui.ITransactionComposerListener",
-  "android.gui.IScreenCaptureListener",
   "android.gui.SensorEventConnection",
   "android.gui.SensorServer",
   "android.hardware.ICamera",
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index b49951b..54c49e4 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -517,6 +517,11 @@
     void                initState();
     void                scanForFds() const;
     status_t            validateReadData(size_t len) const;
+
+    // Reads an int32 size and does a coarse bounds check against the number
+    // of available bytes in the Parcel.
+    status_t            readVectorSizeWithCoarseBoundCheck(int32_t *size) const;
+
     void                updateWorkSourceRequestHeaderPosition() const;
 
     status_t            finishFlattenBinder(const sp<IBinder>& binder);
@@ -787,6 +792,7 @@
 template<typename T>
 status_t Parcel::resizeOutVector(std::vector<T>* val) const {
     int32_t size;
+    // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here
     status_t err = readInt32(&size);
     if (err != NO_ERROR) {
         return err;
@@ -802,6 +808,7 @@
 template<typename T>
 status_t Parcel::resizeOutVector(std::optional<std::vector<T>>* val) const {
     int32_t size;
+    // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here
     status_t err = readInt32(&size);
     if (err != NO_ERROR) {
         return err;
@@ -818,6 +825,7 @@
 template<typename T>
 status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const {
     int32_t size;
+    // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here
     status_t err = readInt32(&size);
     if (err != NO_ERROR) {
         return err;
@@ -834,7 +842,7 @@
 template<typename T>
 status_t Parcel::reserveOutVector(std::vector<T>* val, size_t* size) const {
     int32_t read_size;
-    status_t err = readInt32(&read_size);
+    status_t err = readVectorSizeWithCoarseBoundCheck(&read_size);
     if (err != NO_ERROR) {
         return err;
     }
@@ -850,7 +858,7 @@
 template<typename T>
 status_t Parcel::reserveOutVector(std::optional<std::vector<T>>* val, size_t* size) const {
     int32_t read_size;
-    status_t err = readInt32(&read_size);
+    status_t err = readVectorSizeWithCoarseBoundCheck(&read_size);
     if (err != NO_ERROR) {
         return err;
     }
@@ -870,7 +878,7 @@
 status_t Parcel::reserveOutVector(std::unique_ptr<std::vector<T>>* val,
                                   size_t* size) const {
     int32_t read_size;
-    status_t err = readInt32(&read_size);
+    status_t err = readVectorSizeWithCoarseBoundCheck(&read_size);
     if (err != NO_ERROR) {
         return err;
     }
@@ -923,7 +931,7 @@
         std::vector<T>* val,
         status_t(Parcel::*read_func)(U*) const) const {
     int32_t size;
-    status_t status = this->readInt32(&size);
+    status_t status = this->readVectorSizeWithCoarseBoundCheck(&size);
 
     if (status != OK) {
         return status;
@@ -965,7 +973,7 @@
                                          status_t(Parcel::*read_func)(T*) const) const {
     const size_t start = dataPosition();
     int32_t size;
-    status_t status = readInt32(&size);
+    status_t status = readVectorSizeWithCoarseBoundCheck(&size);
     val->reset();
 
     if (status != OK || size < 0) {
@@ -989,7 +997,7 @@
                                          status_t(Parcel::*read_func)(T*) const) const {
     const size_t start = dataPosition();
     int32_t size;
-    status_t status = readInt32(&size);
+    status_t status = readVectorSizeWithCoarseBoundCheck(&size);
     val->reset();
 
     if (status != OK || size < 0) {
@@ -1093,7 +1101,7 @@
 status_t Parcel::readParcelableVector(std::optional<std::vector<std::optional<T>>>* val) const {
     const size_t start = dataPosition();
     int32_t size;
-    status_t status = readInt32(&size);
+    status_t status = readVectorSizeWithCoarseBoundCheck(&size);
     val->reset();
 
     if (status != OK || size < 0) {
@@ -1117,7 +1125,7 @@
 status_t Parcel::readParcelableVector(std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const {
     const size_t start = dataPosition();
     int32_t size;
-    status_t status = readInt32(&size);
+    status_t status = readVectorSizeWithCoarseBoundCheck(&size);
     val->reset();
 
     if (status != OK || size < 0) {
diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h
index 7024a4b..9e4475c 100644
--- a/libs/binder/include/binder/ParcelableHolder.h
+++ b/libs/binder/include/binder/ParcelableHolder.h
@@ -18,6 +18,7 @@
 
 #include <binder/Parcel.h>
 #include <binder/Parcelable.h>
+#include <utils/String16.h>
 #include <mutex>
 #include <optional>
 #include <tuple>
@@ -52,53 +53,59 @@
     }
 
     template <typename T>
-    bool setParcelable(T&& p) {
+    status_t setParcelable(T&& p) {
         using Tt = typename std::decay<T>::type;
         return setParcelable<Tt>(std::make_shared<Tt>(std::forward<T>(p)));
     }
 
     template <typename T>
-    bool setParcelable(std::shared_ptr<T> p) {
+    status_t setParcelable(std::shared_ptr<T> p) {
         static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
         if (p && this->getStability() > p->getStability()) {
-            return false;
+            return android::BAD_VALUE;
         }
         this->mParcelable = p;
         this->mParcelableName = T::getParcelableDescriptor();
         this->mParcelPtr = nullptr;
-        return true;
+        return android::OK;
     }
 
     template <typename T>
-    std::shared_ptr<T> getParcelable() const {
+    status_t getParcelable(std::shared_ptr<T>* ret) const {
         static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
-        const std::string& parcelableDesc = T::getParcelableDescriptor();
+        const String16& parcelableDesc = T::getParcelableDescriptor();
         if (!this->mParcelPtr) {
             if (!this->mParcelable || !this->mParcelableName) {
                 ALOGD("empty ParcelableHolder");
-                return nullptr;
+                *ret = nullptr;
+                return android::OK;
             } else if (parcelableDesc != *mParcelableName) {
                 ALOGD("extension class name mismatch expected:%s actual:%s",
-                      mParcelableName->c_str(), parcelableDesc.c_str());
-                return nullptr;
+                      String8(*mParcelableName).c_str(), String8(parcelableDesc).c_str());
+                *ret = nullptr;
+                return android::BAD_VALUE;
             }
-            return std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+            *ret = std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+            return android::OK;
         }
         this->mParcelPtr->setDataPosition(0);
-        status_t status = this->mParcelPtr->readUtf8FromUtf16(&this->mParcelableName);
+        status_t status = this->mParcelPtr->readString16(&this->mParcelableName);
         if (status != android::OK || parcelableDesc != this->mParcelableName) {
             this->mParcelableName = std::nullopt;
-            return nullptr;
+            *ret = nullptr;
+            return status;
         }
         this->mParcelable = std::make_shared<T>();
         status = mParcelable.get()->readFromParcel(this->mParcelPtr.get());
         if (status != android::OK) {
             this->mParcelableName = std::nullopt;
             this->mParcelable = nullptr;
-            return nullptr;
+            *ret = nullptr;
+            return status;
         }
         this->mParcelPtr = nullptr;
-        return std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+        *ret = std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+        return android::OK;
     }
 
     Stability getStability() const override { return mStability; }
@@ -124,7 +131,7 @@
 
 private:
     mutable std::shared_ptr<Parcelable> mParcelable;
-    mutable std::optional<std::string> mParcelableName;
+    mutable std::optional<String16> mParcelableName;
     mutable std::unique_ptr<Parcel> mParcelPtr;
     Stability mStability;
 };
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index bdb74dc..82f3882 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -104,15 +104,28 @@
         "--header-filter=^.*frameworks/native/libs/binder/.*.h$",
     ],
     tidy_checks_as_errors: [
-        "*",
+        // Explicitly list the checks that should not occur in this module.
+        "abseil-*",
+        "android-*",
+        "bugprone-*",
+        "cert-*",
+        "clang-analyzer-*",
         "-clang-analyzer-core.CallAndMessage",
         "-clang-analyzer-core.uninitialized.Assign",
-        "-clang-analyzer-unix.Malloc,",
+        "-clang-analyzer-unix.Malloc",
         "-clang-analyzer-deadcode.DeadStores",
         "-clang-analyzer-optin.cplusplus.UninitializedObject",
+        "google-*",
+        "-google-readability-*",
+        "-google-runtime-references",
+        "misc-*",
         "-misc-no-recursion",
+        "-misc-non-private-member-variables-in-classes",
         "-misc-redundant-expression",
+        "-misc-unused-parameters",
         "-misc-unused-using-decls",
+        "performance*",
+        "portability*",
     ],
 }
 
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 350c658..454fbd0 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -301,6 +301,26 @@
     return binder.get();
 }
 
+AIBinder_Weak* AIBinder_Weak_clone(const AIBinder_Weak* weak) {
+    if (weak == nullptr) {
+        return nullptr;
+    }
+
+    return new AIBinder_Weak{weak->binder};
+}
+
+bool AIBinder_lt(const AIBinder* lhs, const AIBinder* rhs) {
+    if (lhs == nullptr || rhs == nullptr) return lhs < rhs;
+
+    return const_cast<AIBinder*>(lhs)->getBinder() < const_cast<AIBinder*>(rhs)->getBinder();
+}
+
+bool AIBinder_Weak_lt(const AIBinder_Weak* lhs, const AIBinder_Weak* rhs) {
+    if (lhs == nullptr || rhs == nullptr) return lhs < rhs;
+
+    return lhs->binder < rhs->binder;
+}
+
 AIBinder_Class::AIBinder_Class(const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate,
                                AIBinder_Class_onDestroy onDestroy,
                                AIBinder_Class_onTransact onTransact)
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index a4f4441..a1102e2 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -83,7 +83,8 @@
     template <class T, class... Args>
     static std::shared_ptr<T> make(Args&&... args) {
         T* t = new T(std::forward<Args>(args)...);
-        return t->template ref<T>();
+        // warning: Potential leak of memory pointed to by 't' [clang-analyzer-unix.Malloc]
+        return t->template ref<T>();  // NOLINT(clang-analyzer-unix.Malloc)
     }
 
     static void operator delete(void* p) { std::free(p); }
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index e1d6c34..6636a41 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -82,34 +82,37 @@
     }
 
     template <typename T>
-    bool setParcelable(const T& p) {
+    binder_status_t setParcelable(const T& p) {
         if (this->mStability > T::_aidl_stability) {
-            return false;
+            return STATUS_BAD_VALUE;
         }
         AParcel_reset(mParcel.get());
         AParcel_writeString(mParcel.get(), T::descriptor, strlen(T::descriptor));
         p.writeToParcel(mParcel.get());
-        return true;
+        return STATUS_OK;
     }
 
     template <typename T>
-    std::unique_ptr<T> getParcelable() const {
+    binder_status_t getParcelable(std::optional<T>* ret) const {
         const std::string parcelableDesc(T::descriptor);
         AParcel_setDataPosition(mParcel.get(), 0);
         if (AParcel_getDataSize(mParcel.get()) == 0) {
-            return nullptr;
+            *ret = std::nullopt;
+            return STATUS_OK;
         }
         std::string parcelableDescInParcel;
         binder_status_t status = AParcel_readString(mParcel.get(), &parcelableDescInParcel);
         if (status != STATUS_OK || parcelableDesc != parcelableDescInParcel) {
-            return nullptr;
+            *ret = std::nullopt;
+            return status;
         }
-        std::unique_ptr<T> ret = std::make_unique<T>();
-        status = ret->readFromParcel(this->mParcel.get());
+        *ret = std::make_optional<T>();
+        status = (*ret)->readFromParcel(this->mParcel.get());
         if (status != STATUS_OK) {
-            return nullptr;
+            *ret = std::nullopt;
+            return status;
         }
-        return std::move(ret);
+        return STATUS_OK;
     }
 
     void reset() { AParcel_reset(mParcel.get()); }
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 5e1ed46..0ca3a07 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -657,6 +657,68 @@
  */
 const char* AIBinder_Class_getDescriptor(const AIBinder_Class* clazz) __INTRODUCED_IN(31);
 
+/**
+ * Whether AIBinder is less than another.
+ *
+ * This provides a per-process-unique total ordering of binders determined by
+ * an underlying allocation address where a null AIBinder* is considered to be
+ * ordered before all other binders.
+ *
+ * AIBinder* pointers themselves actually also create a per-process-unique total
+ * ordering. However, this ordering is inconsistent with AIBinder_Weak_lt for
+ * remote binders.
+ *
+ * Available since API level 31.
+ *
+ * \param lhs comparison object
+ * \param rhs comparison object
+ *
+ * \return whether "lhs < rhs" is true
+ */
+bool AIBinder_lt(const AIBinder* lhs, const AIBinder* rhs);
+
+/**
+ * Clone an AIBinder_Weak. Useful because even if a weak binder promotes to a
+ * null value, after further binder transactions, it may no longer promote to a
+ * null value.
+ *
+ * Available since API level 31.
+ *
+ * \param weak Object to clone
+ *
+ * \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);
+
+/**
+ * Whether AIBinder_Weak is less than another.
+ *
+ * This provides a per-process-unique total ordering of binders which is exactly
+ * the same as AIBinder_lt. Similarly, a null AIBinder_Weak* is considered to be
+ * ordered before all other weak references.
+ *
+ * If you have many AIBinder_Weak* objects which are all references to distinct
+ * binder objects which happen to have the same underlying address (as ordered
+ * by AIBinder_lt), these AIBinder_Weak* objects will retain the same order with
+ * respect to all other AIBinder_Weak* pointers with different underlying
+ * addresses and are also guaranteed to have a per-process-unique ordering. That
+ * is, even though multiple AIBinder* instances may happen to be allocated at
+ * the same underlying address, this function will still correctly distinguish
+ * that these are weak pointers to different binder objects.
+ *
+ * Unlike AIBinder*, the AIBinder_Weak* addresses themselves have nothing to do
+ * with the underlying binder.
+ *
+ * Available since API level 31.
+ *
+ * \param lhs comparison object
+ * \param rhs comparison object
+ *
+ * \return whether "lhs < rhs" is true
+ */
+bool AIBinder_Weak_lt(const AIBinder_Weak* lhs, const AIBinder_Weak* rhs);
+
 #endif  //__ANDROID_API__ >= 31
 
 __END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index e233ffd..9a93bf3 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -122,6 +122,9 @@
     AServiceManager_waitForService; # apex llndk
 
     AIBinder_Class_getDescriptor;
+    AIBinder_Weak_clone;
+    AIBinder_Weak_lt;
+    AIBinder_lt;
     AParcel_appendFrom;
     AParcel_create;
     AParcel_getDataSize;
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 87f1d45..988f7f3 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -157,6 +157,24 @@
     require_root: true,
 }
 
+cc_test {
+    name: "binderClearBufTest",
+    defaults: ["binder_test_defaults"],
+    srcs: [
+        "binderClearBufTest.cpp",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "liblog",
+        "libutils",
+    ],
+
+    test_suites: ["general-tests"],
+    require_root: true,
+}
+
 aidl_interface {
     name: "binderStabilityTestIface",
     unstable: true,
diff --git a/libs/binder/tests/binderClearBufTest.cpp b/libs/binder/tests/binderClearBufTest.cpp
new file mode 100644
index 0000000..a565e72
--- /dev/null
+++ b/libs/binder/tests/binderClearBufTest.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/Stability.h>
+#include <gtest/gtest.h>
+
+#include <sys/prctl.h>
+#include <thread>
+
+using namespace android;
+
+const String16 kServerName = String16("binderClearBuf");
+
+std::string hexString(const void* bytes, size_t len) {
+    if (bytes == nullptr) return "<null>";
+
+    const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
+    char chars[] = "0123456789abcdef";
+    std::string result;
+    result.resize(len * 2);
+
+    for (size_t i = 0; i < len; i++) {
+        result[2 * i] = chars[bytes8[i] >> 4];
+        result[2 * i + 1] = chars[bytes8[i] & 0xf];
+    }
+
+    return result;
+}
+
+class FooBar : public BBinder {
+ public:
+    enum {
+        TRANSACTION_REPEAT_STRING = IBinder::FIRST_CALL_TRANSACTION,
+    };
+
+    std::mutex foo;
+    std::string last;
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+        // not checking data, since there is no hook at the time this test is
+        // written to check values there are set to zero. Instead, we only check
+        // the reply parcel.
+
+        switch (code) {
+            case TRANSACTION_REPEAT_STRING: {
+                const char* str = data.readCString();
+                return reply->writeCString(str == nullptr ? "<null>" : str);
+            }
+        }
+        return BBinder::onTransact(code, data, reply, flags);
+    }
+    static std::string RepeatString(const sp<IBinder> binder,
+                                    const std::string& repeat,
+                                    std::string* outBuffer) {
+        Parcel data;
+        data.writeCString(repeat.c_str());
+        std::string result;
+        const uint8_t* lastReply;
+        size_t lastReplySize;
+        {
+            Parcel reply;
+            binder->transact(TRANSACTION_REPEAT_STRING, data, &reply, FLAG_CLEAR_BUF);
+            result = reply.readCString();
+            lastReply = reply.data();
+            lastReplySize = reply.dataSize();
+        }
+        IPCThreadState::self()->flushCommands();
+        *outBuffer = hexString(lastReply, lastReplySize);
+        return result;
+    }
+};
+
+TEST(BinderClearBuf, ClearKernelBuffer) {
+    sp<IBinder> binder = defaultServiceManager()->getService(kServerName);
+    ASSERT_NE(nullptr, binder);
+
+    std::string replyBuffer;
+    std::string result = FooBar::RepeatString(binder, "foo", &replyBuffer);
+    EXPECT_EQ("foo", result);
+
+    // the buffer must have at least some length for the string, but we will
+    // just check it has some length, to avoid assuming anything about the
+    // format
+    EXPECT_GT(replyBuffer.size(), 0);
+
+    for (size_t i = 0; i < replyBuffer.size(); i++) {
+        EXPECT_EQ(replyBuffer[i], '0') << "reply buffer at " << i;
+    }
+}
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    if (fork() == 0) {
+        prctl(PR_SET_PDEATHSIG, SIGHUP);
+
+        sp<IBinder> server = new FooBar;
+        android::defaultServiceManager()->addService(kServerName, server);
+
+        IPCThreadState::self()->joinThreadPool(true);
+        exit(1);  // should not reach
+    }
+
+    // This is not racey. Just giving these services some time to register before we call
+    // getService which sleeps for much longer. One alternative would be to
+    // start a threadpool + use waitForService, but we want to have as few
+    // binder things going on in this test as possible, since we are checking
+    // memory is zero'd which the kernel has a right to change.
+    usleep(100000);
+
+    return RUN_ALL_TESTS();
+}
diff --git a/libs/binder/tests/fuzzers/Android.bp b/libs/binder/tests/fuzzers/Android.bp
index c465bed..5531296 100644
--- a/libs/binder/tests/fuzzers/Android.bp
+++ b/libs/binder/tests/fuzzers/Android.bp
@@ -69,3 +69,18 @@
     defaults: ["binder_fuzz_defaults"],
     srcs: ["TextOutputFuzz.cpp"],
 }
+
+cc_fuzz {
+    name: "binder_bufferedTextOutputFuzz",
+    include_dirs: [
+        "frameworks/native/libs/binder",
+    ],
+    defaults: ["binder_fuzz_defaults"],
+    srcs: ["BufferedTextOutputFuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "binder_memoryDealerFuzz",
+    defaults: ["binder_fuzz_defaults"],
+    srcs: ["MemoryDealerFuzz.cpp"],
+}
diff --git a/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp b/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp
new file mode 100644
index 0000000..09cb216
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <string>
+#include <vector>
+#include "BufferedTextOutput.h"
+
+namespace android {
+
+class FuzzBufferedTextOutput : public BufferedTextOutput {
+public:
+    FuzzBufferedTextOutput(uint32_t flags) : BufferedTextOutput(flags) {}
+    virtual status_t writeLines(const struct iovec& buf, size_t) {
+        size_t len = buf.iov_len;
+        void* tmp_buf = malloc(len);
+
+        if (tmp_buf == NULL) {
+            return status_t();
+        }
+
+        // This will attempt to read data from iov_base to ensure valid params were passed.
+        memcpy(tmp_buf, buf.iov_base, len);
+        free(tmp_buf);
+        return status_t();
+    }
+};
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider fdp(data, size);
+    uint32_t flags = fdp.ConsumeIntegral<uint32_t>();
+    size_t push_count = 0;
+    std::shared_ptr<BufferedTextOutput> bTextOutput(new FuzzBufferedTextOutput(flags));
+
+    while (fdp.remaining_bytes() > 0) {
+        fdp.PickValueInArray<std::function<void()>>({
+                [&]() -> void {
+                    bTextOutput->pushBundle();
+                    push_count++;
+                },
+                [&]() -> void {
+                    std::string txt = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+                    size_t len = fdp.ConsumeIntegralInRange<size_t>(0, txt.length());
+                    bTextOutput->print(txt.c_str(), len);
+                },
+                [&]() -> void {
+                    if (push_count == 0) return;
+
+                    bTextOutput->popBundle();
+                    push_count--;
+                },
+        })();
+    }
+
+    return 0;
+}
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp b/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp
new file mode 100644
index 0000000..f9dda8c
--- /dev/null
+++ b/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/MemoryDealer.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <string>
+#include <unordered_set>
+
+namespace android {
+
+static constexpr size_t kMaxBufferSize = 10000;
+static constexpr size_t kMaxDealerSize = 1024 * 512;
+static constexpr size_t kMaxAllocSize = 1024;
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    if (size > kMaxBufferSize) {
+        return 0;
+    }
+
+    FuzzedDataProvider fdp(data, size);
+    size_t dSize = fdp.ConsumeIntegralInRange<size_t>(0, kMaxDealerSize);
+    std::string name = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+    uint32_t flags = fdp.ConsumeIntegral<uint32_t>();
+    sp<MemoryDealer> dealer = new MemoryDealer(dSize, name.c_str(), flags);
+
+    // This is used to track offsets that have been freed already to avoid an expected fatal log.
+    std::unordered_set<size_t> free_list;
+
+    while (fdp.remaining_bytes() > 0) {
+        fdp.PickValueInArray<std::function<void()>>({
+                [&]() -> void { dealer->getAllocationAlignment(); },
+                [&]() -> void { dealer->getMemoryHeap(); },
+                [&]() -> void {
+                    size_t offset = fdp.ConsumeIntegral<size_t>();
+
+                    // Offset has already been freed, so return instead.
+                    if (free_list.find(offset) != free_list.end()) return;
+
+                    dealer->deallocate(offset);
+                    free_list.insert(offset);
+                },
+                [&]() -> void {
+                    std::string randString = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+                    dealer->dump(randString.c_str());
+                },
+                [&]() -> void {
+                    size_t allocSize = fdp.ConsumeIntegralInRange<size_t>(0, kMaxAllocSize);
+                    sp<IMemory> allocated = dealer->allocate(allocSize);
+                    // If the allocation was successful, try to write to it
+                    if (allocated != nullptr && allocated->unsecurePointer() != nullptr) {
+                        memset(allocated->unsecurePointer(), 1, allocated->size());
+
+                        // Clear the address from freelist since it has been allocated over again.
+                        free_list.erase(allocated->offset());
+                    }
+                },
+        })();
+    }
+
+    return 0;
+}
+} // namespace android
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 4209dc5..2e72cc4 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -56,6 +56,7 @@
 static std::vector<std::vector<uint32_t>> gPolicyFreqs;
 static std::vector<std::vector<uint32_t>> gPolicyCpus;
 static std::set<uint32_t> gAllFreqs;
+static unique_fd gTisTotalMapFd;
 static unique_fd gTisMapFd;
 static unique_fd gConcurrentMapFd;
 static unique_fd gUidLastUpdateMapFd;
@@ -129,6 +130,10 @@
         gPolicyCpus.emplace_back(*cpus);
     }
 
+    gTisTotalMapFd =
+            unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_total_time_in_state_map")};
+    if (gTisTotalMapFd < 0) return false;
+
     gTisMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")};
     if (gTisMapFd < 0) return false;
 
@@ -239,6 +244,31 @@
     return gPolicyFreqs;
 }
 
+std::optional<std::vector<std::vector<uint64_t>>> getTotalCpuFreqTimes() {
+    if (!gInitialized && !initGlobals()) return {};
+
+    std::vector<std::vector<uint64_t>> out;
+    uint32_t maxFreqCount = 0;
+    for (const auto &freqList : gPolicyFreqs) {
+        if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size();
+        out.emplace_back(freqList.size(), 0);
+    }
+
+    std::vector<uint64_t> vals(gNCpus);
+    const uint32_t freqCount = maxFreqCount <= MAX_FREQS_FOR_TOTAL ? maxFreqCount :
+            MAX_FREQS_FOR_TOTAL;
+    for (uint32_t freqIdx = 0; freqIdx < freqCount; ++freqIdx) {
+        if (findMapEntry(gTisTotalMapFd, &freqIdx, vals.data())) return {};
+        for (uint32_t policyIdx = 0; policyIdx < gNPolicies; ++policyIdx) {
+            if (freqIdx >= gPolicyFreqs[policyIdx].size()) continue;
+            for (const auto &cpu : gPolicyCpus[policyIdx]) {
+                out[policyIdx][freqIdx] += vals[cpu];
+            }
+        }
+    }
+
+    return out;
+}
 // Retrieve the times in ns that uid spent running at each CPU frequency.
 // Return contains no value on error, otherwise it contains a vector of vectors using the format:
 // [[t0_0, t0_1, ...],
diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h
index 87a328a..46de669 100644
--- a/libs/cputimeinstate/cputimeinstate.h
+++ b/libs/cputimeinstate/cputimeinstate.h
@@ -23,6 +23,7 @@
 namespace bpf {
 
 bool startTrackingUidTimes();
+std::optional<std::vector<std::vector<uint64_t>>> getTotalCpuFreqTimes();
 std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid);
 std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>>
     getUidsCpuFreqTimes();
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
index 519689b..d25b2e9 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -40,6 +40,12 @@
 
 using std::vector;
 
+TEST(TimeInStateTest, TotalTimeInState) {
+    auto times = getTotalCpuFreqTimes();
+    ASSERT_TRUE(times.has_value());
+    EXPECT_FALSE(times->empty());
+}
+
 TEST(TimeInStateTest, SingleUidTimeInState) {
     auto times = getUidCpuFreqTimes(0);
     ASSERT_TRUE(times.has_value());
@@ -186,6 +192,31 @@
     }
 }
 
+TEST(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) {
+    auto allUid = getUidsCpuFreqTimes();
+    auto total = getTotalCpuFreqTimes();
+
+    ASSERT_TRUE(allUid.has_value() && total.has_value());
+
+    // Check the number of policies.
+    ASSERT_EQ(allUid->at(0).size(), total->size());
+
+    for (uint32_t policyIdx = 0; policyIdx < total->size(); ++policyIdx) {
+        std::vector<uint64_t> totalTimes = total->at(policyIdx);
+        uint32_t totalFreqsCount = totalTimes.size();
+        std::vector<uint64_t> allUidTimes(totalFreqsCount, 0);
+        for (auto const &[uid, uidTimes]: *allUid) {
+            for (uint32_t freqIdx = 0; freqIdx < uidTimes[policyIdx].size(); ++freqIdx) {
+                allUidTimes[std::min(freqIdx, totalFreqsCount - 1)] += uidTimes[policyIdx][freqIdx];
+            }
+        }
+
+        for (uint32_t freqIdx = 0; freqIdx < totalFreqsCount; ++freqIdx) {
+            ASSERT_LE(allUidTimes[freqIdx], totalTimes[freqIdx]);
+        }
+    }
+}
+
 TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) {
     uint64_t zero = 0;
     auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
@@ -292,6 +323,22 @@
     ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf());
 }
 
+TEST(TimeInStateTest, TotalTimeInStateMonotonic) {
+    auto before = getTotalCpuFreqTimes();
+    ASSERT_TRUE(before.has_value());
+    sleep(1);
+    auto after = getTotalCpuFreqTimes();
+    ASSERT_TRUE(after.has_value());
+
+    for (uint32_t policyIdx = 0; policyIdx < after->size(); ++policyIdx) {
+        auto timesBefore = before->at(policyIdx);
+        auto timesAfter = after->at(policyIdx);
+        for (uint32_t freqIdx = 0; freqIdx < timesAfter.size(); ++freqIdx) {
+            ASSERT_NO_FATAL_FAILURE(TestCheckDelta(timesBefore[freqIdx], timesAfter[freqIdx]));
+        }
+    }
+}
+
 TEST(TimeInStateTest, AllUidTimeInStateMonotonic) {
     auto map1 = getUidsCpuFreqTimes();
     ASSERT_TRUE(map1.has_value());
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index af9ef06..38ae353 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -38,9 +38,10 @@
 
 cc_library_shared {
     name: "libgui",
-    vendor_available: false,
+    vendor_available: true,
     vndk: {
         enabled: true,
+        private: true,
     },
     double_loadable: true,
 
@@ -68,7 +69,6 @@
         "IGraphicBufferProducer.cpp",
         "IProducerListener.cpp",
         "IRegionSamplingListener.cpp",
-        "IScreenCaptureListener.cpp",
         "ISurfaceComposer.cpp",
         "ISurfaceComposerClient.cpp",
         "ITransactionCompletedListener.cpp",
@@ -77,6 +77,7 @@
         "LayerState.cpp",
         "OccupancyTracker.cpp",
         "StreamSplitter.cpp",
+        "ScreenCaptureResults.cpp",
         "Surface.cpp",
         "SurfaceControl.cpp",
         "SurfaceComposerClient.cpp",
@@ -161,6 +162,7 @@
 filegroup {
     name: "libgui_bufferqueue_sources",
     srcs: [
+        "BatchBufferOps.cpp",
         "BufferItem.cpp",
         "BufferQueue.cpp",
         "BufferQueueConsumer.cpp",
@@ -171,7 +173,7 @@
         "FrameTimestamps.cpp",
         "GLConsumerUtils.cpp",
         "HdrMetadata.cpp",
-        "QueueBufferInputOutput.cpp",
+        "IGraphicBufferProducerFlattenables.cpp",
         "bufferqueue/1.0/Conversion.cpp",
         "bufferqueue/1.0/H2BProducerListener.cpp",
         "bufferqueue/1.0/WProducerListener.cpp",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index ee5552f..f4b5a26 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -354,6 +354,16 @@
         t->setAutoRefresh(mSurfaceControl, bufferItem.mAutoRefresh);
         mAutoRefresh = bufferItem.mAutoRefresh;
     }
+    {
+        std::unique_lock _lock{mTimestampMutex};
+        auto dequeueTime = mDequeueTimestamps.find(buffer->getId());
+        if (dequeueTime != mDequeueTimestamps.end()) {
+            Parcel p;
+            p.writeInt64(dequeueTime->second);
+            t->setMetadata(mSurfaceControl, METADATA_DEQUEUE_TIME, p);
+            mDequeueTimestamps.erase(dequeueTime);
+        }
+    }
 
     auto mergeTransaction =
             [&t, currentFrameNumber = bufferItem.mFrameNumber](
@@ -371,7 +381,7 @@
                                mPendingTransactions.end());
 
     if (applyTransaction) {
-        t->apply();
+        t->setApplyToken(mApplyToken).apply();
     }
 
     BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
@@ -412,6 +422,16 @@
     // Do nothing since we are not storing unacquired buffer items locally.
 }
 
+void BLASTBufferQueue::onFrameDequeued(const uint64_t bufferId) {
+    std::unique_lock _lock{mTimestampMutex};
+    mDequeueTimestamps[bufferId] = systemTime();
+};
+
+void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) {
+    std::unique_lock _lock{mTimestampMutex};
+    mDequeueTimestamps.erase(bufferId);
+};
+
 void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
     std::lock_guard _lock{mMutex};
     mNextTransaction = t;
@@ -600,6 +620,14 @@
         return BufferQueueProducer::connect(new AsyncProducerListener(listener), api,
                                             producerControlledByApp, output);
     }
+
+    int query(int what, int* value) override {
+        if (what == NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER) {
+            *value = 1;
+            return NO_ERROR;
+        }
+        return BufferQueueProducer::query(what, value);
+    }
 };
 
 // Similar to BufferQueue::createBufferQueue but creates an adapter specific bufferqueue producer.
diff --git a/libs/gui/BatchBufferOps.cpp b/libs/gui/BatchBufferOps.cpp
new file mode 100644
index 0000000..60aceb1
--- /dev/null
+++ b/libs/gui/BatchBufferOps.cpp
@@ -0,0 +1,125 @@
+/*
+ * 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 <inttypes.h>
+
+#define LOG_TAG "IGBPBatchOps"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+/**
+ * Default implementation of batched buffer operations. These default
+ * implementations call into the non-batched version of the same operation.
+ */
+
+status_t IGraphicBufferProducer::requestBuffers(
+        const std::vector<int32_t>& slots,
+        std::vector<RequestBufferOutput>* outputs) {
+    outputs->clear();
+    outputs->reserve(slots.size());
+    for (int32_t slot : slots) {
+        RequestBufferOutput& output = outputs->emplace_back();
+        output.result = requestBuffer(static_cast<int>(slot),
+                                      &output.buffer);
+    }
+    return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::dequeueBuffers(
+        const std::vector<DequeueBufferInput>& inputs,
+        std::vector<DequeueBufferOutput>* outputs) {
+    outputs->clear();
+    outputs->reserve(inputs.size());
+    for (const DequeueBufferInput& input : inputs) {
+        DequeueBufferOutput& output = outputs->emplace_back();
+        output.result = dequeueBuffer(
+                &output.slot,
+                &output.fence,
+                input.width,
+                input.height,
+                input.format,
+                input.usage,
+                &output.bufferAge,
+                input.getTimestamps ? &output.timestamps.emplace() : nullptr);
+    }
+    return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::detachBuffers(
+        const std::vector<int32_t>& slots,
+        std::vector<status_t>* results) {
+    results->clear();
+    results->reserve(slots.size());
+    for (int32_t slot : slots) {
+        results->emplace_back(detachBuffer(slot));
+    }
+    return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::attachBuffers(
+        const std::vector<sp<GraphicBuffer>>& buffers,
+        std::vector<AttachBufferOutput>* outputs) {
+    outputs->clear();
+    outputs->reserve(buffers.size());
+    for (const sp<GraphicBuffer>& buffer : buffers) {
+        AttachBufferOutput& output = outputs->emplace_back();
+        output.result = attachBuffer(&output.slot, buffer);
+    }
+    return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::queueBuffers(
+        const std::vector<QueueBufferInput>& inputs,
+        std::vector<QueueBufferOutput>* outputs) {
+    outputs->clear();
+    outputs->reserve(inputs.size());
+    for (const QueueBufferInput& input : inputs) {
+        QueueBufferOutput& output = outputs->emplace_back();
+        output.result = queueBuffer(input.slot, input, &output);
+    }
+    return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::cancelBuffers(
+        const std::vector<CancelBufferInput>& inputs,
+        std::vector<status_t>* results) {
+    results->clear();
+    results->reserve(inputs.size());
+    for (const CancelBufferInput& input : inputs) {
+        results->emplace_back() = cancelBuffer(input.slot, input.fence);
+    }
+    return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::query(const std::vector<int32_t> inputs,
+                                       std::vector<QueryOutput>* outputs) {
+    outputs->clear();
+    outputs->reserve(inputs.size());
+    for (int32_t input : inputs) {
+        QueryOutput& output = outputs->emplace_back();
+        int value{};
+        output.result = static_cast<status_t>(
+                query(static_cast<int>(input), &value));
+        output.value = static_cast<int64_t>(value);
+    }
+    return NO_ERROR;
+}
+
+} // namespace android
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index ad00939..c1f9b85 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -74,6 +74,13 @@
     GET_CONSUMER_USAGE,
     SET_LEGACY_BUFFER_DROP,
     SET_AUTO_PREROTATION,
+    REQUEST_BUFFERS,
+    DEQUEUE_BUFFERS,
+    DETACH_BUFFERS,
+    ATTACH_BUFFERS,
+    QUEUE_BUFFERS,
+    CANCEL_BUFFERS,
+    QUERY_MULTIPLE,
 };
 
 class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -90,7 +97,7 @@
         Parcel data, reply;
         data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
         data.writeInt32(bufferIdx);
-        status_t result =remote()->transact(REQUEST_BUFFER, data, &reply);
+        status_t result = remote()->transact(REQUEST_BUFFER, data, &reply);
         if (result != NO_ERROR) {
             return result;
         }
@@ -107,6 +114,27 @@
         return result;
     }
 
+    virtual status_t requestBuffers(
+            const std::vector<int32_t>& slots,
+            std::vector<RequestBufferOutput>* outputs) override {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        data.writeInt32Vector(slots);
+        status_t result = remote()->transact(REQUEST_BUFFERS, data, &reply);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.resizeOutVector(outputs);
+        for (RequestBufferOutput& output : *outputs) {
+            if (result != NO_ERROR) {
+                return result;
+            }
+            result = reply.read(output);
+        }
+
+        return result;
+    }
+
     virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) {
         Parcel data, reply;
         data.writeInterfaceToken(
@@ -183,6 +211,29 @@
         return result;
     }
 
+    virtual status_t dequeueBuffers(
+            const std::vector<DequeueBufferInput>& inputs,
+            std::vector<DequeueBufferOutput>* outputs) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        data.writeVectorSize(inputs);
+        for (const auto& input : inputs) {
+            data.write(input);
+        }
+        status_t result = remote()->transact(DEQUEUE_BUFFERS, data, &reply);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.resizeOutVector(outputs);
+        for (auto& output : *outputs) {
+            if (result != NO_ERROR) {
+                return result;
+            }
+            result = reply.read(output);
+        }
+        return result;
+    }
+
     virtual status_t detachBuffer(int slot) {
         Parcel data, reply;
         data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -195,6 +246,19 @@
         return result;
     }
 
+    virtual status_t detachBuffers(const std::vector<int32_t>& slots,
+                                   std::vector<status_t>* results) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        data.writeInt32Vector(slots);
+        status_t result = remote()->transact(DETACH_BUFFERS, data, &reply);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.readInt32Vector(results);
+        return result;
+    }
+
     virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence) {
         if (outBuffer == nullptr) {
@@ -256,6 +320,39 @@
         return result;
     }
 
+    virtual status_t attachBuffers(
+            const std::vector<sp<GraphicBuffer>>& buffers,
+            std::vector<AttachBufferOutput>* outputs) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        data.writeVectorSize(buffers);
+        for (const sp<GraphicBuffer>& buffer : buffers) {
+            data.write(*buffer.get());
+        }
+        status_t result = remote()->transact(ATTACH_BUFFERS, data, &reply);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.resizeOutVector(outputs);
+        for (AttachBufferOutput& output : *outputs) {
+            if (result != NO_ERROR) {
+                return result;
+            }
+            result = reply.read(output);
+        }
+        if (result == NO_ERROR) {
+            for (AttachBufferOutput& output : *outputs) {
+                if (output.result == NO_ERROR && output.slot < 0) {
+                    ALOGE("attachBuffers returned invalid slot %d",
+                          output.slot);
+                    android_errorWriteLog(0x534e4554, "37478824");
+                    output.result = UNKNOWN_ERROR;
+                }
+            }
+        }
+        return result;
+    }
+
     virtual status_t queueBuffer(int buf,
             const QueueBufferInput& input, QueueBufferOutput* output) {
         Parcel data, reply;
@@ -278,6 +375,28 @@
         return result;
     }
 
+    virtual status_t queueBuffers(const std::vector<QueueBufferInput>& inputs,
+                                  std::vector<QueueBufferOutput>* outputs) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        data.writeVectorSize(inputs);
+        for (const QueueBufferInput& input : inputs) {
+            data.write(input);
+        }
+        status_t result = remote()->transact(QUEUE_BUFFERS, data, &reply);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.resizeOutVector(outputs);
+        for (QueueBufferOutput& output : *outputs) {
+            if (result != NO_ERROR) {
+                return result;
+            }
+            result = reply.read(output);
+        }
+        return result;
+    }
+
     virtual status_t cancelBuffer(int buf, const sp<Fence>& fence) {
         Parcel data, reply;
         data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -291,6 +410,23 @@
         return result;
     }
 
+    virtual status_t cancelBuffers(
+            const std::vector<CancelBufferInput>& inputs,
+            std::vector<status_t>* results) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        data.writeVectorSize(inputs);
+        for (const CancelBufferInput& input : inputs) {
+            data.write(input);
+        }
+        status_t result = remote()->transact(CANCEL_BUFFERS, data, &reply);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.readInt32Vector(results);
+        return result;
+    }
+
     virtual int query(int what, int* value) {
         Parcel data, reply;
         data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -304,6 +440,25 @@
         return result;
     }
 
+    virtual status_t query(const std::vector<int32_t> inputs,
+                           std::vector<QueryOutput>* outputs) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        data.writeInt32Vector(inputs);
+        status_t result = remote()->transact(QUERY_MULTIPLE, data, &reply);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.resizeOutVector(outputs);
+        for (QueryOutput& output : *outputs) {
+            if (result != NO_ERROR) {
+                return result;
+            }
+            result = reply.read(output);
+        }
+        return result;
+    }
+
     virtual status_t connect(const sp<IProducerListener>& listener,
             int api, bool producerControlledByApp, QueueBufferOutput* output) {
         Parcel data, reply;
@@ -576,6 +731,12 @@
         return mBase->requestBuffer(slot, buf);
     }
 
+    status_t requestBuffers(
+            const std::vector<int32_t>& slots,
+            std::vector<RequestBufferOutput>* outputs) override {
+        return mBase->requestBuffers(slots, outputs);
+    }
+
     status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override {
         return mBase->setMaxDequeuedBufferCount(maxDequeuedBuffers);
     }
@@ -590,10 +751,21 @@
         return mBase->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge, outTimestamps);
     }
 
+    status_t dequeueBuffers(
+            const std::vector<DequeueBufferInput>& inputs,
+            std::vector<DequeueBufferOutput>* outputs) override {
+        return mBase->dequeueBuffers(inputs, outputs);
+    }
+
     status_t detachBuffer(int slot) override {
         return mBase->detachBuffer(slot);
     }
 
+    status_t detachBuffers(const std::vector<int32_t>& slots,
+                           std::vector<status_t>* results) override {
+        return mBase->detachBuffers(slots, results);
+    }
+
     status_t detachNextBuffer(
             sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override {
         return mBase->detachNextBuffer(outBuffer, outFence);
@@ -604,6 +776,12 @@
         return mBase->attachBuffer(outSlot, buffer);
     }
 
+    status_t attachBuffers(
+            const std::vector<sp<GraphicBuffer>>& buffers,
+            std::vector<AttachBufferOutput>* outputs) override {
+        return mBase->attachBuffers(buffers, outputs);
+    }
+
     status_t queueBuffer(
             int slot,
             const QueueBufferInput& input,
@@ -611,14 +789,30 @@
         return mBase->queueBuffer(slot, input, output);
     }
 
+    status_t queueBuffers(const std::vector<QueueBufferInput>& inputs,
+                          std::vector<QueueBufferOutput>* outputs) override {
+        return mBase->queueBuffers(inputs, outputs);
+    }
+
     status_t cancelBuffer(int slot, const sp<Fence>& fence) override {
         return mBase->cancelBuffer(slot, fence);
     }
 
+    status_t cancelBuffers(
+            const std::vector<CancelBufferInput>& inputs,
+            std::vector<status_t>* results) override {
+        return mBase->cancelBuffers(inputs, results);
+    }
+
     int query(int what, int* value) override {
         return mBase->query(what, value);
     }
 
+    status_t query(const std::vector<int32_t> inputs,
+                   std::vector<QueryOutput>* outputs) override {
+        return mBase->query(inputs, outputs);
+    }
+
     status_t connect(
             const sp<IProducerListener>& listener,
             int api, bool producerControlledByApp,
@@ -789,7 +983,7 @@
     switch(code) {
         case REQUEST_BUFFER: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
-            int bufferIdx   = data.readInt32();
+            int bufferIdx = data.readInt32();
             sp<GraphicBuffer> buffer;
             int result = requestBuffer(bufferIdx, &buffer);
             reply->writeInt32(buffer != nullptr);
@@ -799,6 +993,24 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
+        case REQUEST_BUFFERS: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            std::vector<int32_t> slots;
+            std::vector<RequestBufferOutput> outputs;
+            status_t result = data.readInt32Vector(&slots);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            (void)requestBuffers(slots, &outputs);
+            result = reply->writeVectorSize(outputs);
+            for (const RequestBufferOutput& output : outputs) {
+                if (result != NO_ERROR) {
+                    return result;
+                }
+                result = reply->write(output);
+            }
+            return result;
+        }
         case SET_MAX_DEQUEUED_BUFFER_COUNT: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             int maxDequeuedBuffers = data.readInt32();
@@ -841,6 +1053,30 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
+        case DEQUEUE_BUFFERS: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            std::vector<DequeueBufferInput> inputs;
+            std::vector<DequeueBufferOutput> outputs;
+            status_t result = data.resizeOutVector(&inputs);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            for (DequeueBufferInput& input : inputs) {
+                result = data.read(input);
+                if (result != NO_ERROR) {
+                    return result;
+                }
+            }
+            (void)dequeueBuffers(inputs, &outputs);
+            result = reply->writeVectorSize(outputs);
+            for (const DequeueBufferOutput& output : outputs) {
+                if (result != NO_ERROR) {
+                    return result;
+                }
+                result = reply->write(output);
+            }
+            return result;
+        }
         case DETACH_BUFFER: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             int slot = data.readInt32();
@@ -848,6 +1084,17 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
+        case DETACH_BUFFERS: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            std::vector<int32_t> slots;
+            std::vector<status_t> results;
+            status_t result = data.readInt32Vector(&slots);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            (void)detachBuffers(slots, &results);
+            return reply->writeInt32Vector(results);
+        }
         case DETACH_NEXT_BUFFER: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             sp<GraphicBuffer> buffer;
@@ -878,6 +1125,31 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
+        case ATTACH_BUFFERS: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            std::vector<sp<GraphicBuffer>> buffers;
+            status_t result = data.resizeOutVector(&buffers);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            for (sp<GraphicBuffer>& buffer : buffers) {
+                buffer = new GraphicBuffer();
+                result = data.read(*buffer.get());
+                if (result != NO_ERROR) {
+                    return result;
+                }
+            }
+            std::vector<AttachBufferOutput> outputs;
+            (void)attachBuffers(buffers, &outputs);
+            result = reply->writeVectorSize(outputs);
+            for (const AttachBufferOutput& output : outputs) {
+                if (result != NO_ERROR) {
+                    return result;
+                }
+                result = reply->write(output);
+            }
+            return result;
+        }
         case QUEUE_BUFFER: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
 
@@ -890,6 +1162,30 @@
 
             return NO_ERROR;
         }
+        case QUEUE_BUFFERS: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            std::vector<QueueBufferInput> inputs;
+            status_t result = data.resizeOutVector(&inputs);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            for (QueueBufferInput& input : inputs) {
+                result = data.read(input);
+                if (result != NO_ERROR) {
+                    return result;
+                }
+            }
+            std::vector<QueueBufferOutput> outputs;
+            (void)queueBuffers(inputs, &outputs);
+            result = reply->writeVectorSize(outputs);
+            for (const QueueBufferOutput& output : outputs) {
+                if (result != NO_ERROR) {
+                    return result;
+                }
+                result = reply->write(output);
+            }
+            return result;
+        }
         case CANCEL_BUFFER: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             int buf = data.readInt32();
@@ -901,6 +1197,26 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
+        case CANCEL_BUFFERS: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            std::vector<CancelBufferInput> inputs;
+            status_t result = data.resizeOutVector(&inputs);
+            for (CancelBufferInput& input : inputs) {
+                if (result != NO_ERROR) {
+                    return result;
+                }
+                result = data.read(input);
+            }
+            if (result != NO_ERROR) {
+                return result;
+            }
+            std::vector<status_t> results;
+            result = cancelBuffers(inputs, &results);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            return reply->writeInt32Vector(results);
+        }
         case QUERY: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             int value = 0;
@@ -910,6 +1226,27 @@
             reply->writeInt32(res);
             return NO_ERROR;
         }
+        case QUERY_MULTIPLE: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            std::vector<int32_t> inputs;
+            status_t result = data.readInt32Vector(&inputs);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            std::vector<QueryOutput> outputs;
+            result = query(inputs, &outputs);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            result = reply->writeVectorSize(outputs);
+            for (const QueryOutput& output : outputs) {
+                if (result != NO_ERROR) {
+                    return result;
+                }
+                result = reply->write(output);
+            }
+            return result;
+        }
         case CONNECT: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             sp<IProducerListener> listener;
@@ -1083,11 +1420,4 @@
     return BBinder::onTransact(code, data, reply, flags);
 }
 
-// ----------------------------------------------------------------------------
-
-IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) {
-    parcel.read(*this);
-}
-
-
 }; // namespace android
diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp
new file mode 100644
index 0000000..c8b9b67
--- /dev/null
+++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp
@@ -0,0 +1,413 @@
+/*
+ * 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 <inttypes.h>
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
+    return sizeof(timestamp) +
+            sizeof(isAutoTimestamp) +
+            sizeof(dataSpace) +
+            sizeof(crop) +
+            sizeof(scalingMode) +
+            sizeof(transform) +
+            sizeof(stickyTransform) +
+            sizeof(getFrameTimestamps) +
+            sizeof(slot);
+}
+
+size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
+    return minFlattenedSize() +
+            fence->getFlattenedSize() +
+            surfaceDamage.getFlattenedSize() +
+            hdrMetadata.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
+    return fence->getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferInput::flatten(
+        void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, timestamp);
+    FlattenableUtils::write(buffer, size, isAutoTimestamp);
+    FlattenableUtils::write(buffer, size, dataSpace);
+    FlattenableUtils::write(buffer, size, crop);
+    FlattenableUtils::write(buffer, size, scalingMode);
+    FlattenableUtils::write(buffer, size, transform);
+    FlattenableUtils::write(buffer, size, stickyTransform);
+    FlattenableUtils::write(buffer, size, getFrameTimestamps);
+
+    status_t result = fence->flatten(buffer, size, fds, count);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    result = surfaceDamage.flatten(buffer, size);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
+    result = hdrMetadata.flatten(buffer, size);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    FlattenableUtils::advance(buffer, size, hdrMetadata.getFlattenedSize());
+    FlattenableUtils::write(buffer, size, slot);
+    return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
+        void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+    if (size < minFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, timestamp);
+    FlattenableUtils::read(buffer, size, isAutoTimestamp);
+    FlattenableUtils::read(buffer, size, dataSpace);
+    FlattenableUtils::read(buffer, size, crop);
+    FlattenableUtils::read(buffer, size, scalingMode);
+    FlattenableUtils::read(buffer, size, transform);
+    FlattenableUtils::read(buffer, size, stickyTransform);
+    FlattenableUtils::read(buffer, size, getFrameTimestamps);
+
+    fence = new Fence();
+    status_t result = fence->unflatten(buffer, size, fds, count);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    result = surfaceDamage.unflatten(buffer, size);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
+    result =  hdrMetadata.unflatten(buffer, size);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    FlattenableUtils::advance(buffer, size, hdrMetadata.getFlattenedSize());
+    FlattenableUtils::read(buffer, size, slot);
+    return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
+    return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) +
+            sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount) +
+            sizeof(result);
+}
+size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
+    return minFlattenedSize() + frameTimestamps.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
+    return frameTimestamps.getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
+        void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, width);
+    FlattenableUtils::write(buffer, size, height);
+    FlattenableUtils::write(buffer, size, transformHint);
+    FlattenableUtils::write(buffer, size, numPendingBuffers);
+    FlattenableUtils::write(buffer, size, nextFrameNumber);
+    FlattenableUtils::write(buffer, size, bufferReplaced);
+    FlattenableUtils::write(buffer, size, maxBufferCount);
+
+    status_t result = frameTimestamps.flatten(buffer, size, fds, count);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    FlattenableUtils::write(buffer, size, result);
+    return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
+        void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+    if (size < minFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, width);
+    FlattenableUtils::read(buffer, size, height);
+    FlattenableUtils::read(buffer, size, transformHint);
+    FlattenableUtils::read(buffer, size, numPendingBuffers);
+    FlattenableUtils::read(buffer, size, nextFrameNumber);
+    FlattenableUtils::read(buffer, size, bufferReplaced);
+    FlattenableUtils::read(buffer, size, maxBufferCount);
+
+    status_t result = frameTimestamps.unflatten(buffer, size, fds, count);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    FlattenableUtils::read(buffer, size, result);
+    return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+constexpr size_t IGraphicBufferProducer::RequestBufferOutput::minFlattenedSize() {
+    return sizeof(result) +
+            sizeof(int32_t); // IsBufferNull
+}
+
+size_t IGraphicBufferProducer::RequestBufferOutput::getFlattenedSize() const {
+    return minFlattenedSize() + (buffer == nullptr ? 0 : buffer->getFlattenedSize());
+}
+
+size_t IGraphicBufferProducer::RequestBufferOutput::getFdCount() const {
+    return (buffer == nullptr ? 0 : buffer->getFdCount());
+}
+
+status_t IGraphicBufferProducer::RequestBufferOutput::flatten(
+        void*& fBuffer, size_t& size, int*& fds, size_t& count) const {
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(fBuffer, size, result);
+    const int32_t isBufferNull = (buffer == nullptr ? 1 : 0);
+    FlattenableUtils::write(fBuffer, size, isBufferNull);
+
+    if (!isBufferNull) {
+        status_t status = buffer->flatten(fBuffer, size, fds, count);
+        if (status != NO_ERROR) {
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::RequestBufferOutput::unflatten(
+        void const*& fBuffer, size_t& size, int const*& fds, size_t& count) {
+    if (size < minFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(fBuffer, size, result);
+    int32_t isBufferNull = 0;
+    FlattenableUtils::read(fBuffer, size, isBufferNull);
+    buffer = new GraphicBuffer();
+    if (!isBufferNull) {
+        status_t status = buffer->unflatten(fBuffer, size, fds, count);
+        if (status != NO_ERROR) {
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+size_t IGraphicBufferProducer::DequeueBufferInput::getFlattenedSize() const {
+    return sizeof(width) + sizeof(height) + sizeof(format) + sizeof(usage) +
+            sizeof(int32_t/*getTimestamps*/);
+}
+
+status_t IGraphicBufferProducer::DequeueBufferInput::flatten(void* buffer, size_t size) const {
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+    FlattenableUtils::write(buffer, size, width);
+    FlattenableUtils::write(buffer, size, height);
+    FlattenableUtils::write(buffer, size, format);
+    FlattenableUtils::write(buffer, size, usage);
+    const int32_t getTimestampsInt = (getTimestamps ? 1 : 0);
+    FlattenableUtils::write(buffer, size, getTimestampsInt);
+
+    return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::DequeueBufferInput::unflatten(void const* buffer, size_t size) {
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, width);
+    FlattenableUtils::read(buffer, size, height);
+    FlattenableUtils::read(buffer, size, format);
+    FlattenableUtils::read(buffer, size, usage);
+    int32_t getTimestampsInt = 0;
+    FlattenableUtils::read(buffer, size, getTimestampsInt);
+    getTimestamps = (getTimestampsInt == 1);
+
+    return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+constexpr size_t IGraphicBufferProducer::DequeueBufferOutput::minFlattenedSize() {
+    return sizeof(result) + sizeof(slot) + sizeof(bufferAge) + sizeof(int32_t/*hasTimestamps*/);
+}
+
+size_t IGraphicBufferProducer::DequeueBufferOutput::getFlattenedSize() const {
+    return minFlattenedSize() +
+            fence->getFlattenedSize() +
+            (timestamps.has_value() ? timestamps->getFlattenedSize() : 0);
+}
+
+size_t IGraphicBufferProducer::DequeueBufferOutput::getFdCount() const {
+    return fence->getFdCount() +
+            (timestamps.has_value() ? timestamps->getFdCount() : 0);
+}
+
+status_t IGraphicBufferProducer::DequeueBufferOutput::flatten(
+        void*& buffer, size_t& size, int*& fds, size_t& count) const {
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, result);
+    FlattenableUtils::write(buffer, size, slot);
+    FlattenableUtils::write(buffer, size, bufferAge);
+    status_t status = fence->flatten(buffer, size, fds, count);
+    if (status != NO_ERROR) {
+        return result;
+    }
+    const int32_t hasTimestamps = timestamps.has_value() ? 1 : 0;
+    FlattenableUtils::write(buffer, size, hasTimestamps);
+    if (timestamps.has_value()) {
+        status = timestamps->flatten(buffer, size, fds, count);
+    }
+    return status;
+}
+
+status_t IGraphicBufferProducer::DequeueBufferOutput::unflatten(
+        void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+    if (size < minFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, result);
+    FlattenableUtils::read(buffer, size, slot);
+    FlattenableUtils::read(buffer, size, bufferAge);
+
+    fence = new Fence();
+    status_t status = fence->unflatten(buffer, size, fds, count);
+    if (status != NO_ERROR) {
+        return status;
+    }
+    int32_t hasTimestamps = 0;
+    FlattenableUtils::read(buffer, size, hasTimestamps);
+    if (hasTimestamps) {
+        timestamps.emplace();
+        status = timestamps->unflatten(buffer, size, fds, count);
+    }
+    return status;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+size_t IGraphicBufferProducer::AttachBufferOutput::getFlattenedSize() const {
+    return sizeof(result) + sizeof(slot);
+}
+
+status_t IGraphicBufferProducer::AttachBufferOutput::flatten(void* buffer, size_t size) const {
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+    FlattenableUtils::write(buffer, size, result);
+    FlattenableUtils::write(buffer, size, slot);
+
+    return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::AttachBufferOutput::unflatten(void const* buffer, size_t size) {
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+    FlattenableUtils::read(buffer, size, result);
+    FlattenableUtils::read(buffer, size, slot);
+
+    return NO_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////
+
+constexpr size_t IGraphicBufferProducer::CancelBufferInput::minFlattenedSize() {
+    return sizeof(slot);
+}
+
+size_t IGraphicBufferProducer::CancelBufferInput::getFlattenedSize() const {
+    return minFlattenedSize() + fence->getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::CancelBufferInput::getFdCount() const {
+    return fence->getFdCount();
+}
+
+status_t IGraphicBufferProducer::CancelBufferInput::flatten(
+        void*& buffer, size_t& size, int*& fds, size_t& count) const {
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, slot);
+    return fence->flatten(buffer, size, fds, count);
+}
+
+status_t IGraphicBufferProducer::CancelBufferInput::unflatten(
+        void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+    if (size < minFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, slot);
+
+    fence = new Fence();
+    return fence->unflatten(buffer, size, fds, count);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+size_t IGraphicBufferProducer::QueryOutput::getFlattenedSize() const {
+    return sizeof(result) + sizeof(value);
+}
+
+status_t IGraphicBufferProducer::QueryOutput::flatten(void* buffer, size_t size) const {
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+    FlattenableUtils::write(buffer, size, result);
+    FlattenableUtils::write(buffer, size, value);
+
+    return NO_ERROR;
+}
+
+status_t IGraphicBufferProducer::QueryOutput::unflatten(void const* buffer, size_t size) {
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+    FlattenableUtils::read(buffer, size, result);
+    FlattenableUtils::read(buffer, size, value);
+
+    return NO_ERROR;
+}
+
+} // namespace android
diff --git a/libs/gui/IScreenCaptureListener.cpp b/libs/gui/IScreenCaptureListener.cpp
deleted file mode 100644
index 0635e9c..0000000
--- a/libs/gui/IScreenCaptureListener.cpp
+++ /dev/null
@@ -1,70 +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.
- */
-
-#include <gui/IScreenCaptureListener.h>
-#include <gui/LayerState.h>
-
-namespace android {
-
-namespace { // Anonymous
-
-enum class Tag : uint32_t {
-    ON_SCREEN_CAPTURE_COMPLETE = IBinder::FIRST_CALL_TRANSACTION,
-    LAST = ON_SCREEN_CAPTURE_COMPLETE,
-};
-
-} // Anonymous namespace
-
-class BpScreenCaptureListener : public SafeBpInterface<IScreenCaptureListener> {
-public:
-    explicit BpScreenCaptureListener(const sp<IBinder>& impl)
-          : SafeBpInterface<IScreenCaptureListener>(impl, "BpScreenCaptureListener") {}
-
-    ~BpScreenCaptureListener() override;
-
-    status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override {
-        Parcel data, reply;
-        data.writeInterfaceToken(IScreenCaptureListener::getInterfaceDescriptor());
-
-        SAFE_PARCEL(captureResults.write, data);
-        return remote()->transact(static_cast<uint32_t>(Tag::ON_SCREEN_CAPTURE_COMPLETE), data,
-                                  &reply, IBinder::FLAG_ONEWAY);
-    }
-};
-
-// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
-// clang warning -Wweak-vtables)
-BpScreenCaptureListener::~BpScreenCaptureListener() = default;
-
-IMPLEMENT_META_INTERFACE(ScreenCaptureListener, "android.gui.IScreenCaptureListener");
-
-status_t BnScreenCaptureListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-                                             uint32_t flags) {
-    auto tag = static_cast<Tag>(code);
-    switch (tag) {
-        case Tag::ON_SCREEN_CAPTURE_COMPLETE: {
-            CHECK_INTERFACE(IScreenCaptureListener, data, reply);
-            ScreenCaptureResults captureResults;
-            SAFE_PARCEL(captureResults.read, data);
-            return onScreenCaptureComplete(captureResults);
-        }
-        default: {
-            return BBinder::onTransact(code, data, reply, flags);
-        }
-    }
-}
-
-} // namespace android
\ No newline at end of file
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
index 30c9b37..634d8b7 100644
--- a/libs/gui/LayerMetadata.cpp
+++ b/libs/gui/LayerMetadata.cpp
@@ -17,6 +17,7 @@
 #include <android-base/stringprintf.h>
 #include <binder/Parcel.h>
 #include <gui/LayerMetadata.h>
+#include <inttypes.h>
 
 #include "android/view/LayerMetadataKey.h"
 
@@ -113,6 +114,15 @@
     memcpy(data.data(), p.data(), p.dataSize());
 }
 
+std::optional<int64_t> LayerMetadata::getInt64(uint32_t key) const {
+    if (!has(key)) return std::nullopt;
+    const std::vector<uint8_t>& data = mMap.at(key);
+    if (data.size() < sizeof(int64_t)) return std::nullopt;
+    Parcel p;
+    p.setData(data.data(), data.size());
+    return p.readInt64();
+}
+
 std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const {
     if (!has(key)) return std::string();
     switch (static_cast<view::LayerMetadataKey>(key)) {
@@ -124,6 +134,8 @@
             return StringPrintf("taskId%s%d", separator, getInt32(key, 0));
         case view::LayerMetadataKey::METADATA_OWNER_PID:
             return StringPrintf("ownerPID%s%d", separator, getInt32(key, 0));
+        case view::LayerMetadataKey::METADATA_DEQUEUE_TIME:
+            return StringPrintf("dequeueTime%s%" PRId64, separator, *getInt64(key));
         default:
             return StringPrintf("%d%s%dbytes", key, separator,
                                 static_cast<int>(mMap.at(key).size()));
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 7d2c7b8..63be3ed 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -714,33 +714,4 @@
     return NO_ERROR;
 }
 
-status_t ScreenCaptureResults::write(Parcel& output) const {
-    if (buffer != nullptr) {
-        SAFE_PARCEL(output.writeBool, true);
-        SAFE_PARCEL(output.write, *buffer);
-    } else {
-        SAFE_PARCEL(output.writeBool, false);
-    }
-    SAFE_PARCEL(output.writeBool, capturedSecureLayers);
-    SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(capturedDataspace));
-    SAFE_PARCEL(output.writeInt32, result);
-    return NO_ERROR;
-}
-
-status_t ScreenCaptureResults::read(const Parcel& input) {
-    bool hasGraphicBuffer;
-    SAFE_PARCEL(input.readBool, &hasGraphicBuffer);
-    if (hasGraphicBuffer) {
-        buffer = new GraphicBuffer();
-        SAFE_PARCEL(input.read, *buffer);
-    }
-
-    SAFE_PARCEL(input.readBool, &capturedSecureLayers);
-    uint32_t dataspace = 0;
-    SAFE_PARCEL(input.readUint32, &dataspace);
-    capturedDataspace = static_cast<ui::Dataspace>(dataspace);
-    SAFE_PARCEL(input.readInt32, &result);
-    return NO_ERROR;
-}
-
 }; // namespace android
diff --git a/libs/gui/QueueBufferInputOutput.cpp b/libs/gui/QueueBufferInputOutput.cpp
deleted file mode 100644
index 30f0ef6..0000000
--- a/libs/gui/QueueBufferInputOutput.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <inttypes.h>
-
-#define LOG_TAG "QueueBufferInputOutput"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-//#define LOG_NDEBUG 0
-
-#include <gui/IGraphicBufferProducer.h>
-
-namespace android {
-
-constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
-    return sizeof(timestamp) +
-            sizeof(isAutoTimestamp) +
-            sizeof(dataSpace) +
-            sizeof(crop) +
-            sizeof(scalingMode) +
-            sizeof(transform) +
-            sizeof(stickyTransform) +
-            sizeof(getFrameTimestamps);
-}
-
-IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) {
-    parcel.read(*this);
-}
-
-size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
-    return minFlattenedSize() +
-            fence->getFlattenedSize() +
-            surfaceDamage.getFlattenedSize() +
-            hdrMetadata.getFlattenedSize();
-}
-
-size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
-    return fence->getFdCount();
-}
-
-status_t IGraphicBufferProducer::QueueBufferInput::flatten(
-        void*& buffer, size_t& size, int*& fds, size_t& count) const
-{
-    if (size < getFlattenedSize()) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::write(buffer, size, timestamp);
-    FlattenableUtils::write(buffer, size, isAutoTimestamp);
-    FlattenableUtils::write(buffer, size, dataSpace);
-    FlattenableUtils::write(buffer, size, crop);
-    FlattenableUtils::write(buffer, size, scalingMode);
-    FlattenableUtils::write(buffer, size, transform);
-    FlattenableUtils::write(buffer, size, stickyTransform);
-    FlattenableUtils::write(buffer, size, getFrameTimestamps);
-
-    status_t result = fence->flatten(buffer, size, fds, count);
-    if (result != NO_ERROR) {
-        return result;
-    }
-    result = surfaceDamage.flatten(buffer, size);
-    if (result != NO_ERROR) {
-        return result;
-    }
-    FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
-    return hdrMetadata.flatten(buffer, size);
-}
-
-status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
-        void const*& buffer, size_t& size, int const*& fds, size_t& count)
-{
-    if (size < minFlattenedSize()) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::read(buffer, size, timestamp);
-    FlattenableUtils::read(buffer, size, isAutoTimestamp);
-    FlattenableUtils::read(buffer, size, dataSpace);
-    FlattenableUtils::read(buffer, size, crop);
-    FlattenableUtils::read(buffer, size, scalingMode);
-    FlattenableUtils::read(buffer, size, transform);
-    FlattenableUtils::read(buffer, size, stickyTransform);
-    FlattenableUtils::read(buffer, size, getFrameTimestamps);
-
-    fence = new Fence();
-    status_t result = fence->unflatten(buffer, size, fds, count);
-    if (result != NO_ERROR) {
-        return result;
-    }
-    result = surfaceDamage.unflatten(buffer, size);
-    if (result != NO_ERROR) {
-        return result;
-    }
-    FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
-    return hdrMetadata.unflatten(buffer, size);
-}
-
-////////////////////////////////////////////////////////////////////////
-constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
-    return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) +
-            sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount);
-}
-size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
-    return minFlattenedSize() + frameTimestamps.getFlattenedSize();
-}
-
-size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
-    return frameTimestamps.getFdCount();
-}
-
-status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
-        void*& buffer, size_t& size, int*& fds, size_t& count) const
-{
-    if (size < getFlattenedSize()) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::write(buffer, size, width);
-    FlattenableUtils::write(buffer, size, height);
-    FlattenableUtils::write(buffer, size, transformHint);
-    FlattenableUtils::write(buffer, size, numPendingBuffers);
-    FlattenableUtils::write(buffer, size, nextFrameNumber);
-    FlattenableUtils::write(buffer, size, bufferReplaced);
-    FlattenableUtils::write(buffer, size, maxBufferCount);
-
-    return frameTimestamps.flatten(buffer, size, fds, count);
-}
-
-status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
-        void const*& buffer, size_t& size, int const*& fds, size_t& count)
-{
-    if (size < minFlattenedSize()) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::read(buffer, size, width);
-    FlattenableUtils::read(buffer, size, height);
-    FlattenableUtils::read(buffer, size, transformHint);
-    FlattenableUtils::read(buffer, size, numPendingBuffers);
-    FlattenableUtils::read(buffer, size, nextFrameNumber);
-    FlattenableUtils::read(buffer, size, bufferReplaced);
-    FlattenableUtils::read(buffer, size, maxBufferCount);
-
-    return frameTimestamps.unflatten(buffer, size, fds, count);
-}
-
-} // namespace android
diff --git a/libs/gui/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp
new file mode 100644
index 0000000..2b29487
--- /dev/null
+++ b/libs/gui/ScreenCaptureResults.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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 <gui/ScreenCaptureResults.h>
+
+namespace android::gui {
+
+status_t ScreenCaptureResults::writeToParcel(android::Parcel* parcel) const {
+    if (buffer != nullptr) {
+        SAFE_PARCEL(parcel->writeBool, true);
+        SAFE_PARCEL(parcel->write, *buffer);
+    } else {
+        SAFE_PARCEL(parcel->writeBool, false);
+    }
+    SAFE_PARCEL(parcel->writeBool, capturedSecureLayers);
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(capturedDataspace));
+    SAFE_PARCEL(parcel->writeInt32, result);
+    return NO_ERROR;
+}
+
+status_t ScreenCaptureResults::readFromParcel(const android::Parcel* parcel) {
+    bool hasGraphicBuffer;
+    SAFE_PARCEL(parcel->readBool, &hasGraphicBuffer);
+    if (hasGraphicBuffer) {
+        buffer = new GraphicBuffer();
+        SAFE_PARCEL(parcel->read, *buffer);
+    }
+
+    SAFE_PARCEL(parcel->readBool, &capturedSecureLayers);
+    uint32_t dataspace = 0;
+    SAFE_PARCEL(parcel->readUint32, &dataspace);
+    capturedDataspace = static_cast<ui::Dataspace>(dataspace);
+    SAFE_PARCEL(parcel->readInt32, &result);
+    return NO_ERROR;
+}
+
+} // namespace android::gui
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 94390aa..e82f0cc 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -618,29 +618,31 @@
     std::mutex mMutex;
 };
 
+void Surface::getDequeueBufferInputLocked(
+        IGraphicBufferProducer::DequeueBufferInput* dequeueInput) {
+    LOG_ALWAYS_FATAL_IF(dequeueInput == nullptr, "input is null");
+
+    dequeueInput->width = mReqWidth ? mReqWidth : mUserWidth;
+    dequeueInput->height = mReqHeight ? mReqHeight : mUserHeight;
+
+    dequeueInput->format = mReqFormat;
+    dequeueInput->usage = mReqUsage;
+
+    dequeueInput->getTimestamps = mEnableFrameTimestamps;
+}
+
 int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
     ATRACE_CALL();
     ALOGV("Surface::dequeueBuffer");
 
-    uint32_t reqWidth;
-    uint32_t reqHeight;
-    PixelFormat reqFormat;
-    uint64_t reqUsage;
-    bool enableFrameTimestamps;
-
+    IGraphicBufferProducer::DequeueBufferInput dqInput;
     {
         Mutex::Autolock lock(mMutex);
         if (mReportRemovedBuffers) {
             mRemovedBuffers.clear();
         }
 
-        reqWidth = mReqWidth ? mReqWidth : mUserWidth;
-        reqHeight = mReqHeight ? mReqHeight : mUserHeight;
-
-        reqFormat = mReqFormat;
-        reqUsage = mReqUsage;
-
-        enableFrameTimestamps = mEnableFrameTimestamps;
+        getDequeueBufferInputLocked(&dqInput);
 
         if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
                 BufferItem::INVALID_BUFFER_SLOT) {
@@ -658,16 +660,17 @@
     nsecs_t startTime = systemTime();
 
     FrameEventHistoryDelta frameTimestamps;
-    status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,
-                                                            reqFormat, reqUsage, &mBufferAge,
-                                                            enableFrameTimestamps ? &frameTimestamps
-                                                                                  : nullptr);
+    status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, dqInput.width,
+                                                            dqInput.height, dqInput.format,
+                                                            dqInput.usage, &mBufferAge,
+                                                            dqInput.getTimestamps ?
+                                                                    &frameTimestamps : nullptr);
     mLastDequeueDuration = systemTime() - startTime;
 
     if (result < 0) {
         ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer"
                 "(%d, %d, %d, %#" PRIx64 ") failed: %d",
-                reqWidth, reqHeight, reqFormat, reqUsage, result);
+                dqInput.width, dqInput.height, dqInput.format, dqInput.usage, result);
         return result;
     }
 
@@ -696,7 +699,7 @@
         freeAllBuffers();
     }
 
-    if (enableFrameTimestamps) {
+    if (dqInput.getTimestamps) {
          mFrameEventHistory->applyDelta(frameTimestamps);
     }
 
@@ -739,6 +742,176 @@
     return OK;
 }
 
+int Surface::dequeueBuffers(std::vector<BatchBuffer>* buffers) {
+    using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput;
+    using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput;
+    using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput;
+    using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput;
+
+    ATRACE_CALL();
+    ALOGV("Surface::dequeueBuffers");
+
+    if (buffers->size() == 0) {
+        ALOGE("%s: must dequeue at least 1 buffer!", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    if (mSharedBufferMode) {
+        ALOGE("%s: batch operation is not supported in shared buffer mode!",
+                __FUNCTION__);
+        return INVALID_OPERATION;
+    }
+
+    size_t numBufferRequested = buffers->size();
+    DequeueBufferInput input;
+
+    {
+        Mutex::Autolock lock(mMutex);
+        if (mReportRemovedBuffers) {
+            mRemovedBuffers.clear();
+        }
+
+        getDequeueBufferInputLocked(&input);
+    } // Drop the lock so that we can still touch the Surface while blocking in IGBP::dequeueBuffers
+
+    std::vector<DequeueBufferInput> dequeueInput(numBufferRequested, input);
+    std::vector<DequeueBufferOutput> dequeueOutput;
+
+    nsecs_t startTime = systemTime();
+
+    status_t result = mGraphicBufferProducer->dequeueBuffers(dequeueInput, &dequeueOutput);
+
+    mLastDequeueDuration = systemTime() - startTime;
+
+    if (result < 0) {
+        ALOGV("%s: IGraphicBufferProducer::dequeueBuffers"
+                "(%d, %d, %d, %#" PRIx64 ") failed: %d",
+                __FUNCTION__, input.width, input.height, input.format, input.usage, result);
+        return result;
+    }
+
+    std::vector<CancelBufferInput> cancelBufferInputs(numBufferRequested);
+    std::vector<status_t> cancelBufferOutputs;
+    for (size_t i = 0; i < numBufferRequested; i++) {
+        cancelBufferInputs[i].slot = dequeueOutput[i].slot;
+        cancelBufferInputs[i].fence = dequeueOutput[i].fence;
+    }
+
+    for (const auto& output : dequeueOutput) {
+        if (output.result < 0) {
+            mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+            ALOGV("%s: IGraphicBufferProducer::dequeueBuffers"
+                    "(%d, %d, %d, %#" PRIx64 ") failed: %d",
+                    __FUNCTION__, input.width, input.height, input.format, input.usage,
+                    output.result);
+            return output.result;
+        }
+
+        if (output.slot < 0 || output.slot >= NUM_BUFFER_SLOTS) {
+            mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+            ALOGE("%s: IGraphicBufferProducer returned invalid slot number %d",
+                    __FUNCTION__, output.slot);
+            android_errorWriteLog(0x534e4554, "36991414"); // SafetyNet logging
+            return FAILED_TRANSACTION;
+        }
+
+        if (input.getTimestamps && !output.timestamps.has_value()) {
+            mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+            ALOGE("%s: no frame timestamp returns!", __FUNCTION__);
+            return FAILED_TRANSACTION;
+        }
+
+        // this should never happen
+        ALOGE_IF(output.fence == nullptr,
+                "%s: received null Fence! slot=%d", __FUNCTION__, output.slot);
+    }
+
+    Mutex::Autolock lock(mMutex);
+
+    // Write this while holding the mutex
+    mLastDequeueStartTime = startTime;
+
+    std::vector<int32_t> requestBufferSlots;
+    requestBufferSlots.reserve(numBufferRequested);
+    // handle release all buffers and request buffers
+    for (const auto& output : dequeueOutput) {
+        if (output.result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
+            ALOGV("%s: RELEASE_ALL_BUFFERS during batch operation", __FUNCTION__);
+            freeAllBuffers();
+            break;
+        }
+    }
+
+    for (const auto& output : dequeueOutput) {
+        // Collect slots that needs requesting buffer
+        sp<GraphicBuffer>& gbuf(mSlots[output.slot].buffer);
+        if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {
+            if (mReportRemovedBuffers && (gbuf != nullptr)) {
+                mRemovedBuffers.push_back(gbuf);
+            }
+            requestBufferSlots.push_back(output.slot);
+        }
+    }
+
+    // Batch request Buffer
+    std::vector<RequestBufferOutput> reqBufferOutput;
+    if (requestBufferSlots.size() > 0) {
+        result = mGraphicBufferProducer->requestBuffers(requestBufferSlots, &reqBufferOutput);
+        if (result != NO_ERROR) {
+            ALOGE("%s: IGraphicBufferProducer::requestBuffers failed: %d",
+                    __FUNCTION__, result);
+            mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+            return result;
+        }
+
+        // Check if we have any single failure
+        for (size_t i = 0; i < requestBufferSlots.size(); i++) {
+            if (reqBufferOutput[i].result != OK) {
+                ALOGE("%s: IGraphicBufferProducer::requestBuffers failed at %zu-th buffer, slot %d",
+                        __FUNCTION__, i, requestBufferSlots[i]);
+                mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+                return reqBufferOutput[i].result;
+            }
+        }
+
+        // Fill request buffer results to mSlots
+        for (size_t i = 0; i < requestBufferSlots.size(); i++) {
+            mSlots[requestBufferSlots[i]].buffer = reqBufferOutput[i].buffer;
+        }
+    }
+
+    for (size_t batchIdx = 0; batchIdx < numBufferRequested; batchIdx++) {
+        const auto& output = dequeueOutput[batchIdx];
+        int slot = output.slot;
+        sp<GraphicBuffer>& gbuf(mSlots[slot].buffer);
+
+        if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) {
+            static FenceMonitor hwcReleaseThread("HWC release");
+            hwcReleaseThread.queueFence(output.fence);
+        }
+
+        if (input.getTimestamps) {
+             mFrameEventHistory->applyDelta(output.timestamps.value());
+        }
+
+        if (output.fence->isValid()) {
+            buffers->at(batchIdx).fenceFd = output.fence->dup();
+            if (buffers->at(batchIdx).fenceFd == -1) {
+                ALOGE("%s: error duping fence: %d", __FUNCTION__, errno);
+                // dup() should never fail; something is badly wrong. Soldier on
+                // and hope for the best; the worst that should happen is some
+                // visible corruption that lasts until the next frame.
+            }
+        } else {
+            buffers->at(batchIdx).fenceFd = -1;
+        }
+
+        buffers->at(batchIdx).buffer = gbuf.get();
+        mDequeuedSlots.insert(slot);
+    }
+    return OK;
+}
+
 int Surface::cancelBuffer(android_native_buffer_t* buffer,
         int fenceFd) {
     ATRACE_CALL();
@@ -769,15 +942,65 @@
     return OK;
 }
 
+int Surface::cancelBuffers(const std::vector<BatchBuffer>& buffers) {
+    using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput;
+    ATRACE_CALL();
+    ALOGV("Surface::cancelBuffers");
+
+    if (mSharedBufferMode) {
+        ALOGE("%s: batch operation is not supported in shared buffer mode!",
+                __FUNCTION__);
+        return INVALID_OPERATION;
+    }
+
+    size_t numBuffers = buffers.size();
+    std::vector<CancelBufferInput> cancelBufferInputs(numBuffers);
+    std::vector<status_t> cancelBufferOutputs;
+    size_t numBuffersCancelled = 0;
+    int badSlotResult = 0;
+    for (size_t i = 0; i < numBuffers; i++) {
+        int slot = getSlotFromBufferLocked(buffers[i].buffer);
+        int fenceFd = buffers[i].fenceFd;
+        if (slot < 0) {
+            if (fenceFd >= 0) {
+                close(fenceFd);
+            }
+            ALOGE("%s: cannot find slot number for cancelled buffer", __FUNCTION__);
+            badSlotResult = slot;
+        } else {
+            sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+            cancelBufferInputs[numBuffersCancelled].slot = slot;
+            cancelBufferInputs[numBuffersCancelled++].fence = fence;
+        }
+    }
+    cancelBufferInputs.resize(numBuffersCancelled);
+    mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
+
+
+    for (size_t i = 0; i < numBuffersCancelled; i++) {
+        mDequeuedSlots.erase(cancelBufferInputs[i].slot);
+    }
+
+    if (badSlotResult != 0) {
+        return badSlotResult;
+    }
+    return OK;
+}
+
 int Surface::getSlotFromBufferLocked(
         android_native_buffer_t* buffer) const {
+    if (buffer == nullptr) {
+        ALOGE("%s: input buffer is null!", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
     for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
         if (mSlots[i].buffer != nullptr &&
                 mSlots[i].buffer->handle == buffer->handle) {
             return i;
         }
     }
-    ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle);
+    ALOGE("%s: unknown buffer: %p", __FUNCTION__, buffer->handle);
     return BAD_VALUE;
 }
 
@@ -787,42 +1010,22 @@
     return OK;
 }
 
-int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
-    ATRACE_CALL();
-    ALOGV("Surface::queueBuffer");
-    Mutex::Autolock lock(mMutex);
-    int64_t timestamp;
+void Surface::getQueueBufferInputLocked(android_native_buffer_t* buffer, int fenceFd,
+        nsecs_t timestamp, IGraphicBufferProducer::QueueBufferInput* out) {
     bool isAutoTimestamp = false;
 
-    if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
+    if (timestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
         timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
         isAutoTimestamp = true;
         ALOGV("Surface::queueBuffer making up timestamp: %.2f ms",
             timestamp / 1000000.0);
-    } else {
-        timestamp = mTimestamp;
     }
-    int i = getSlotFromBufferLocked(buffer);
-    if (i < 0) {
-        if (fenceFd >= 0) {
-            close(fenceFd);
-        }
-        return i;
-    }
-    if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) {
-        if (fenceFd >= 0) {
-            close(fenceFd);
-        }
-        return OK;
-    }
-
 
     // Make sure the crop rectangle is entirely inside the buffer.
     Rect crop(Rect::EMPTY_RECT);
     mCrop.intersect(Rect(buffer->width, buffer->height), &crop);
 
     sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
-    IGraphicBufferProducer::QueueBufferOutput output;
     IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
             static_cast<android_dataspace>(mDataSpace), crop, mScalingMode,
             mTransform ^ mStickyTransform, fence, mStickyTransform,
@@ -893,15 +1096,12 @@
 
         input.setSurfaceDamage(flippedRegion);
     }
+    *out = input;
+}
 
-    nsecs_t now = systemTime();
-    status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
-    mLastQueueDuration = systemTime() - now;
-    if (err != OK)  {
-        ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
-    }
-
-    mDequeuedSlots.erase(i);
+void Surface::onBufferQueuedLocked(int slot, sp<Fence> fence,
+        const IGraphicBufferProducer::QueueBufferOutput& output) {
+    mDequeuedSlots.erase(slot);
 
     if (mEnableFrameTimestamps) {
         mFrameEventHistory->applyDelta(output.frameTimestamps);
@@ -935,7 +1135,7 @@
         mDirtyRegion = Region::INVALID_REGION;
     }
 
-    if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == i) {
+    if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == slot) {
         mSharedBufferHasBeenQueued = true;
     }
 
@@ -945,6 +1145,89 @@
         static FenceMonitor gpuCompletionThread("GPU completion");
         gpuCompletionThread.queueFence(fence);
     }
+}
+
+int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
+    ATRACE_CALL();
+    ALOGV("Surface::queueBuffer");
+    Mutex::Autolock lock(mMutex);
+
+    int i = getSlotFromBufferLocked(buffer);
+    if (i < 0) {
+        if (fenceFd >= 0) {
+            close(fenceFd);
+        }
+        return i;
+    }
+    if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) {
+        if (fenceFd >= 0) {
+            close(fenceFd);
+        }
+        return OK;
+    }
+
+    IGraphicBufferProducer::QueueBufferOutput output;
+    IGraphicBufferProducer::QueueBufferInput input;
+    getQueueBufferInputLocked(buffer, fenceFd, mTimestamp, &input);
+    sp<Fence> fence = input.fence;
+
+    nsecs_t now = systemTime();
+    status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
+    mLastQueueDuration = systemTime() - now;
+    if (err != OK)  {
+        ALOGE("queueBuffer: error queuing buffer, %d", err);
+    }
+
+    onBufferQueuedLocked(i, fence, output);
+    return err;
+}
+
+int Surface::queueBuffers(const std::vector<BatchQueuedBuffer>& buffers) {
+    ATRACE_CALL();
+    ALOGV("Surface::queueBuffers");
+    Mutex::Autolock lock(mMutex);
+
+    if (mSharedBufferMode) {
+        ALOGE("%s: batched operation is not supported in shared buffer mode", __FUNCTION__);
+        return INVALID_OPERATION;
+    }
+
+    size_t numBuffers = buffers.size();
+    std::vector<IGraphicBufferProducer::QueueBufferInput> queueBufferInputs(numBuffers);
+    std::vector<IGraphicBufferProducer::QueueBufferOutput> queueBufferOutputs;
+    std::vector<int> bufferSlots(numBuffers, -1);
+    std::vector<sp<Fence>> bufferFences(numBuffers);
+
+    for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) {
+        int i = getSlotFromBufferLocked(buffers[batchIdx].buffer);
+        if (i < 0) {
+            if (buffers[batchIdx].fenceFd >= 0) {
+                close(buffers[batchIdx].fenceFd);
+            }
+            return i;
+        }
+        bufferSlots[batchIdx] = i;
+
+        IGraphicBufferProducer::QueueBufferInput input;
+        getQueueBufferInputLocked(
+                buffers[batchIdx].buffer, buffers[batchIdx].fenceFd, buffers[batchIdx].timestamp,
+                &input);
+        bufferFences[batchIdx] = input.fence;
+        queueBufferInputs[batchIdx] = input;
+    }
+
+    nsecs_t now = systemTime();
+    status_t err = mGraphicBufferProducer->queueBuffers(queueBufferInputs, &queueBufferOutputs);
+    mLastQueueDuration = systemTime() - now;
+    if (err != OK)  {
+        ALOGE("%s: error queuing buffer, %d", __FUNCTION__, err);
+    }
+
+
+    for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) {
+        onBufferQueuedLocked(bufferSlots[batchIdx], bufferFences[batchIdx],
+                queueBufferOutputs[batchIdx]);
+    }
 
     return err;
 }
@@ -985,6 +1268,10 @@
                 }
                 break;
             case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: {
+                status_t err = mGraphicBufferProducer->query(what, value);
+                if (err == NO_ERROR) {
+                    return NO_ERROR;
+                }
                 if (composerService()->authenticateSurfaceTexture(
                         mGraphicBufferProducer)) {
                     *value = 1;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 5570d99..97c2693 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -396,7 +396,8 @@
         mContainsBuffer(other.mContainsBuffer),
         mDesiredPresentTime(other.mDesiredPresentTime),
         mIsAutoTimestamp(other.mIsAutoTimestamp),
-        mFrameTimelineVsyncId(other.mFrameTimelineVsyncId) {
+        mFrameTimelineVsyncId(other.mFrameTimelineVsyncId),
+        mApplyToken(other.mApplyToken) {
     mDisplayStates = other.mDisplayStates;
     mComposerStates = other.mComposerStates;
     mInputWindowCommands = other.mInputWindowCommands;
@@ -427,7 +428,8 @@
     const int64_t desiredPresentTime = parcel->readInt64();
     const bool isAutoTimestamp = parcel->readBool();
     const int64_t frameTimelineVsyncId = parcel->readInt64();
-
+    sp<IBinder> applyToken;
+    parcel->readNullableStrongBinder(&applyToken);
     size_t count = static_cast<size_t>(parcel->readUint32());
     if (count > parcel->dataSize()) {
         return BAD_VALUE;
@@ -505,6 +507,7 @@
     mListenerCallbacks = listenerCallbacks;
     mComposerStates = composerStates;
     mInputWindowCommands = inputWindowCommands;
+    mApplyToken = applyToken;
     return NO_ERROR;
 }
 
@@ -532,6 +535,7 @@
     parcel->writeInt64(mDesiredPresentTime);
     parcel->writeBool(mIsAutoTimestamp);
     parcel->writeInt64(mFrameTimelineVsyncId);
+    parcel->writeStrongBinder(mApplyToken);
     parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
     for (auto const& displayState : mDisplayStates) {
         displayState.write(*parcel);
@@ -607,6 +611,7 @@
     mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup;
     mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart;
     mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd;
+    mApplyToken = other.mApplyToken;
 
     // When merging vsync Ids we take the oldest one
     if (mFrameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID &&
@@ -635,6 +640,7 @@
     mDesiredPresentTime = 0;
     mIsAutoTimestamp = true;
     mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
+    mApplyToken = nullptr;
 }
 
 void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -763,7 +769,10 @@
         flags |= ISurfaceComposer::eExplicitEarlyWakeupEnd;
     }
 
-    sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    sp<IBinder> applyToken = mApplyToken
+            ? mApplyToken
+            : IInterface::asBinder(TransactionCompletedListener::getIInstance());
+
     sf->setTransactionState(mFrameTimelineVsyncId, composerStates, displayStates, flags, applyToken,
                             mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
                             {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
@@ -1579,6 +1588,12 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApplyToken(
+        const sp<IBinder>& applyToken) {
+    mApplyToken = applyToken;
+    return *this;
+}
+
 // ---------------------------------------------------------------------------
 
 DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
diff --git a/libs/gui/aidl/android/gui/IScreenCaptureListener.aidl b/libs/gui/aidl/android/gui/IScreenCaptureListener.aidl
new file mode 100644
index 0000000..f490118
--- /dev/null
+++ b/libs/gui/aidl/android/gui/IScreenCaptureListener.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+package android.gui;
+
+import android.gui.ScreenCaptureResults;
+
+/** @hide */
+oneway interface IScreenCaptureListener {
+    void onScreenCaptureComplete(in ScreenCaptureResults captureResults);
+}
\ No newline at end of file
diff --git a/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl
new file mode 100644
index 0000000..9908edd
--- /dev/null
+++ b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+package android.gui;
+
+parcelable ScreenCaptureResults cpp_header "gui/ScreenCaptureResults.h";
\ No newline at end of file
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index c4cdb65..0fbcbdc 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -78,6 +78,8 @@
     void onBufferFreed(const wp<GraphicBuffer>&/* graphicBuffer*/) override { /* TODO */ }
     void onFrameReplaced(const BufferItem& item) override;
     void onFrameAvailable(const BufferItem& item) override;
+    void onFrameDequeued(const uint64_t) override;
+    void onFrameCancelled(const uint64_t) override;
 
     void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
             const std::vector<SurfaceControlStats>& stats);
@@ -165,6 +167,18 @@
 
     std::function<void(int64_t)> mTransactionCompleteCallback GUARDED_BY(mMutex) = nullptr;
     uint64_t mTransactionCompleteFrameNumber GUARDED_BY(mMutex){0};
+
+    // Queues up transactions using this token in SurfaceFlinger. This prevents queued up
+    // transactions from other parts of the client from blocking this transaction.
+    const sp<IBinder> mApplyToken GUARDED_BY(mMutex) = new BBinder();
+
+    // Guards access to mDequeueTimestamps since we cannot hold to mMutex in onFrameDequeued or
+    // we will deadlock.
+    std::mutex mTimestampMutex;
+    // Tracks buffer dequeue times by the client. This info is sent to SurfaceFlinger which uses
+    // it for debugging purposes.
+    std::unordered_map<uint64_t /* bufferId */, nsecs_t> mDequeueTimestamps
+            GUARDED_BY(mTimestampMutex);
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 45e0a13..c3b9262 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -38,6 +38,9 @@
 #include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
 #include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h>
 
+#include <optional>
+#include <vector>
+
 namespace android {
 // ----------------------------------------------------------------------------
 
@@ -289,8 +292,9 @@
             const sp<GraphicBuffer>& buffer) = 0;
 
     struct QueueBufferInput : public Flattenable<QueueBufferInput> {
-        friend class Flattenable<QueueBufferInput>;
-        explicit inline QueueBufferInput(const Parcel& parcel);
+        explicit inline QueueBufferInput(const Parcel& parcel) {
+            parcel.read(*this);
+        }
 
         // timestamp - a monotonically increasing value in nanoseconds
         // isAutoTimestamp - if the timestamp was synthesized at queue time
@@ -304,21 +308,29 @@
         //          camera mode).
         // getFrameTimestamps - whether or not the latest frame timestamps
         //                      should be retrieved from the consumer.
+        // slot - the slot index to queue. This is used only by queueBuffers().
+        //        queueBuffer() ignores this value and uses the argument `slot`
+        //        instead.
         inline QueueBufferInput(int64_t _timestamp, bool _isAutoTimestamp,
                 android_dataspace _dataSpace, const Rect& _crop,
                 int _scalingMode, uint32_t _transform, const sp<Fence>& _fence,
-                uint32_t _sticky = 0, bool _getFrameTimestamps = false)
+                uint32_t _sticky = 0, bool _getFrameTimestamps = false,
+                int _slot = -1)
                 : timestamp(_timestamp), isAutoTimestamp(_isAutoTimestamp),
                   dataSpace(_dataSpace), crop(_crop), scalingMode(_scalingMode),
-                  transform(_transform), stickyTransform(_sticky), fence(_fence),
-                  surfaceDamage(), getFrameTimestamps(_getFrameTimestamps) { }
+                  transform(_transform), stickyTransform(_sticky),
+                  fence(_fence), surfaceDamage(),
+                  getFrameTimestamps(_getFrameTimestamps), slot(_slot) { }
+
+        QueueBufferInput() = default;
 
         inline void deflate(int64_t* outTimestamp, bool* outIsAutoTimestamp,
                 android_dataspace* outDataSpace,
                 Rect* outCrop, int* outScalingMode,
                 uint32_t* outTransform, sp<Fence>* outFence,
                 uint32_t* outStickyTransform = nullptr,
-                bool* outGetFrameTimestamps = nullptr) const {
+                bool* outGetFrameTimestamps = nullptr,
+                int* outSlot = nullptr) const {
             *outTimestamp = timestamp;
             *outIsAutoTimestamp = bool(isAutoTimestamp);
             *outDataSpace = dataSpace;
@@ -332,6 +344,9 @@
             if (outGetFrameTimestamps) {
                 *outGetFrameTimestamps = getFrameTimestamps;
             }
+            if (outSlot) {
+                *outSlot = slot;
+            }
         }
 
         // Flattenable protocol
@@ -357,6 +372,7 @@
         sp<Fence> fence;
         Region surfaceDamage;
         bool getFrameTimestamps{false};
+        int slot{-1};
         HdrMetadata hdrMetadata;
     };
 
@@ -385,6 +401,7 @@
         FrameEventHistoryDelta frameTimestamps;
         bool bufferReplaced{false};
         int maxBufferCount{0};
+        status_t result{NO_ERROR};
     };
 
     // queueBuffer indicates that the client has finished filling in the
@@ -404,6 +421,10 @@
     // Upon success, the output will be filled with meaningful values
     // (refer to the documentation below).
     //
+    // Note: QueueBufferInput::slot was added to QueueBufferInput to be used by
+    // queueBuffers(), the batched version of queueBuffer(). The non-batched
+    // method (queueBuffer()) uses `slot` and ignores `input.slot`.
+    //
     // Return of a value other than NO_ERROR means an error has occurred:
     // * NO_INIT - the buffer queue has been abandoned or the producer is not
     //             connected.
@@ -639,6 +660,147 @@
     // the width and height used for dequeueBuffer will be additionally swapped.
     virtual status_t setAutoPrerotation(bool autoPrerotation);
 
+    struct RequestBufferOutput : public Flattenable<RequestBufferOutput> {
+        RequestBufferOutput() = default;
+
+        // Flattenable protocol
+        static constexpr size_t minFlattenedSize();
+        size_t getFlattenedSize() const;
+        size_t getFdCount() const;
+        status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+        status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+        status_t result;
+        sp<GraphicBuffer> buffer;
+    };
+
+    // Batched version of requestBuffer().
+    // This method behaves like a sequence of requestBuffer() calls.
+    // The return value of the batched method will only be about the
+    // transaction. For a local call, the return value will always be NO_ERROR.
+    virtual status_t requestBuffers(
+            const std::vector<int32_t>& slots,
+            std::vector<RequestBufferOutput>* outputs);
+
+    struct DequeueBufferInput : public LightFlattenable<DequeueBufferInput> {
+        DequeueBufferInput() = default;
+
+        // LightFlattenable protocol
+        inline bool isFixedSize() const { return true; }
+        size_t getFlattenedSize() const;
+        status_t flatten(void* buffer, size_t size) const;
+        status_t unflatten(void const* buffer, size_t size);
+
+        uint32_t width;
+        uint32_t height;
+        PixelFormat format;
+        uint64_t usage;
+        bool getTimestamps;
+    };
+
+    struct DequeueBufferOutput : public Flattenable<DequeueBufferOutput> {
+        DequeueBufferOutput() = default;
+
+        // Flattenable protocol
+        static constexpr size_t minFlattenedSize();
+        size_t getFlattenedSize() const;
+        size_t getFdCount() const;
+        status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+        status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+        status_t result;
+        int slot = -1;
+        sp<Fence> fence = Fence::NO_FENCE;
+        uint64_t bufferAge;
+        std::optional<FrameEventHistoryDelta> timestamps;
+    };
+
+    // Batched version of dequeueBuffer().
+    // This method behaves like a sequence of dequeueBuffer() calls.
+    // The return value of the batched method will only be about the
+    // transaction. For a local call, the return value will always be NO_ERROR.
+    virtual status_t dequeueBuffers(
+            const std::vector<DequeueBufferInput>& inputs,
+            std::vector<DequeueBufferOutput>* outputs);
+
+    // Batched version of detachBuffer().
+    // This method behaves like a sequence of detachBuffer() calls.
+    // The return value of the batched method will only be about the
+    // transaction. For a local call, the return value will always be NO_ERROR.
+    virtual status_t detachBuffers(const std::vector<int32_t>& slots,
+                                   std::vector<status_t>* results);
+
+
+    struct AttachBufferOutput : public LightFlattenable<AttachBufferOutput> {
+        AttachBufferOutput() = default;
+
+        // LightFlattenable protocol
+        inline bool isFixedSize() const { return true; }
+        size_t getFlattenedSize() const;
+        status_t flatten(void* buffer, size_t size) const;
+        status_t unflatten(void const* buffer, size_t size);
+
+        status_t result;
+        int slot;
+    };
+    // Batched version of attachBuffer().
+    // This method behaves like a sequence of attachBuffer() calls.
+    // The return value of the batched method will only be about the
+    // transaction. For a local call, the return value will always be NO_ERROR.
+    virtual status_t attachBuffers(
+            const std::vector<sp<GraphicBuffer>>& buffers,
+            std::vector<AttachBufferOutput>* outputs);
+
+    // Batched version of queueBuffer().
+    // This method behaves like a sequence of queueBuffer() calls.
+    // The return value of the batched method will only be about the
+    // transaction. For a local call, the return value will always be NO_ERROR.
+    //
+    // Note: QueueBufferInput::slot was added to QueueBufferInput to include the
+    // `slot` input argument of the non-batched method queueBuffer().
+    virtual status_t queueBuffers(const std::vector<QueueBufferInput>& inputs,
+                                  std::vector<QueueBufferOutput>* outputs);
+
+    struct CancelBufferInput : public Flattenable<CancelBufferInput> {
+        CancelBufferInput() = default;
+
+        // Flattenable protocol
+        static constexpr size_t minFlattenedSize();
+        size_t getFlattenedSize() const;
+        size_t getFdCount() const;
+        status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+        status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+        int slot;
+        sp<Fence> fence;
+    };
+    // Batched version of cancelBuffer().
+    // This method behaves like a sequence of cancelBuffer() calls.
+    // The return value of the batched method will only be about the
+    // transaction. For a local call, the return value will always be NO_ERROR.
+    virtual status_t cancelBuffers(
+            const std::vector<CancelBufferInput>& inputs,
+            std::vector<status_t>* results);
+
+    struct QueryOutput : public LightFlattenable<QueryOutput> {
+        QueryOutput() = default;
+
+        // LightFlattenable protocol
+        inline bool isFixedSize() const { return true; }
+        size_t getFlattenedSize() const;
+        status_t flatten(void* buffer, size_t size) const;
+        status_t unflatten(void const* buffer, size_t size);
+
+        status_t result;
+        int64_t value;
+    };
+    // Batched version of query().
+    // This method behaves like a sequence of query() calls.
+    // The return value of the batched method will only be about the
+    // transaction. For a local call, the return value will always be NO_ERROR.
+    virtual status_t query(const std::vector<int32_t> inputs,
+                           std::vector<QueryOutput>* outputs);
+
 #ifndef NO_BINDER
     // Static method exports any IGraphicBufferProducer object to a parcel. It
     // handles null producer as well.
diff --git a/libs/gui/include/gui/IScreenCaptureListener.h b/libs/gui/include/gui/IScreenCaptureListener.h
deleted file mode 100644
index a2ddc9f..0000000
--- a/libs/gui/include/gui/IScreenCaptureListener.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <binder/Binder.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-#include <binder/SafeInterface.h>
-
-namespace android {
-
-struct ScreenCaptureResults;
-
-// TODO(b/166271443): Convert to AIDL
-class IScreenCaptureListener : public IInterface {
-public:
-    DECLARE_META_INTERFACE(ScreenCaptureListener)
-
-    virtual status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) = 0;
-};
-
-class BnScreenCaptureListener : public SafeBnInterface<IScreenCaptureListener> {
-public:
-    BnScreenCaptureListener()
-          : SafeBnInterface<IScreenCaptureListener>("BnScreenCaptureListener") {}
-
-    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-                        uint32_t flags = 0) override;
-};
-
-} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 40316db..86f3c60 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -22,8 +22,8 @@
 #include <binder/IBinder.h>
 #include <binder/IInterface.h>
 
+#include <android/gui/IScreenCaptureListener.h>
 #include <android/gui/ITransactionTraceListener.h>
-#include <gui/IScreenCaptureListener.h>
 #include <gui/ITransactionCompletedListener.h>
 
 #include <input/Flags.h>
@@ -59,7 +59,6 @@
 struct DisplayState;
 struct InputWindowCommands;
 struct LayerCaptureArgs;
-struct ScreenCaptureResults;
 class LayerDebugInfo;
 class HdrCapabilities;
 class IDisplayEventConnection;
@@ -69,6 +68,8 @@
 class Rect;
 enum class FrameEvent;
 
+using gui::IScreenCaptureListener;
+
 namespace ui {
 
 struct DisplayState;
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index ac48aef..41982c2 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -29,6 +29,7 @@
     METADATA_MOUSE_CURSOR = 4,
     METADATA_ACCESSIBILITY_ID = 5,
     METADATA_OWNER_PID = 6,
+    METADATA_DEQUEUE_TIME = 7
 };
 
 struct LayerMetadata : public Parcelable {
@@ -51,6 +52,8 @@
     bool has(uint32_t key) const;
     int32_t getInt32(uint32_t key, int32_t fallback) const;
     void setInt32(uint32_t key, int32_t value);
+    std::optional<int64_t> getInt64(uint32_t key) const;
+    void setInt64(uint32_t key, int64_t value);
 
     std::string itemToString(uint32_t key, const char* separator) const;
 };
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index f1c5d67..4a291ae 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -362,16 +362,6 @@
     status_t read(const Parcel& input) override;
 };
 
-struct ScreenCaptureResults {
-    sp<GraphicBuffer> buffer;
-    bool capturedSecureLayers{false};
-    ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB};
-    status_t result = OK;
-
-    status_t write(Parcel& output) const;
-    status_t read(const Parcel& input);
-};
-
 }; // namespace android
 
 #endif // ANDROID_SF_LAYER_STATE_H
diff --git a/libs/gui/include/gui/ScreenCaptureResults.h b/libs/gui/include/gui/ScreenCaptureResults.h
new file mode 100644
index 0000000..fdb4b69
--- /dev/null
+++ b/libs/gui/include/gui/ScreenCaptureResults.h
@@ -0,0 +1,47 @@
+/*
+ * 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
+
+#define SAFE_PARCEL(FUNC, ...)                                                            \
+    {                                                                                     \
+        status_t error = FUNC(__VA_ARGS__);                                               \
+        if (error) {                                                                      \
+            ALOGE("ERROR(%d). Failed to call parcel %s(%s)", error, #FUNC, #__VA_ARGS__); \
+            return error;                                                                 \
+        }                                                                                 \
+    }
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android::gui {
+
+struct ScreenCaptureResults : public Parcelable {
+public:
+    ScreenCaptureResults() = default;
+    virtual ~ScreenCaptureResults() = default;
+    status_t writeToParcel(android::Parcel* parcel) const override;
+    status_t readFromParcel(const android::Parcel* parcel) override;
+
+    sp<GraphicBuffer> buffer;
+    bool capturedSecureLayers{false};
+    ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB};
+    status_t result = OK;
+};
+
+} // namespace android::gui
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 82bc5c9..43b5dcd 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -344,6 +344,23 @@
     static status_t attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer,
                                                       ui::Dataspace dataspace);
 
+    // Batch version of dequeueBuffer, cancelBuffer and queueBuffer
+    // Note that these batched operations are not supported when shared buffer mode is being used.
+    struct BatchBuffer {
+        ANativeWindowBuffer* buffer = nullptr;
+        int fenceFd = -1;
+    };
+    virtual int dequeueBuffers(std::vector<BatchBuffer>* buffers);
+    virtual int cancelBuffers(const std::vector<BatchBuffer>& buffers);
+
+    struct BatchQueuedBuffer {
+        ANativeWindowBuffer* buffer = nullptr;
+        int fenceFd = -1;
+        nsecs_t timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
+    };
+    virtual int queueBuffers(
+            const std::vector<BatchQueuedBuffer>& buffers);
+
 protected:
     enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS };
     enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
@@ -373,6 +390,14 @@
     void freeAllBuffers();
     int getSlotFromBufferLocked(android_native_buffer_t* buffer) const;
 
+    void getDequeueBufferInputLocked(IGraphicBufferProducer::DequeueBufferInput* dequeueInput);
+
+    void getQueueBufferInputLocked(android_native_buffer_t* buffer, int fenceFd, nsecs_t timestamp,
+            IGraphicBufferProducer::QueueBufferInput* out);
+
+    void onBufferQueuedLocked(int slot, sp<Fence> fence,
+            const IGraphicBufferProducer::QueueBufferOutput& output);
+
     struct BufferSlot {
         sp<GraphicBuffer> buffer;
         Region dirtyRegion;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 0abe72c..48bc5d5 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -388,6 +388,10 @@
         // The vsync Id provided by Choreographer.getVsyncId
         int64_t mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
 
+        // If not null, transactions will be queued up using this token otherwise a common token
+        // per process will be used.
+        sp<IBinder> mApplyToken = nullptr;
+
         InputWindowCommands mInputWindowCommands;
         int mStatus = NO_ERROR;
 
@@ -555,6 +559,11 @@
         // in shared buffer mode.
         Transaction& setAutoRefresh(const sp<SurfaceControl>& sc, bool autoRefresh);
 
+        // Queues up transactions using this token in SurfaceFlinger.  By default, all transactions
+        // from a client are placed on the same queue. This can be used to prevent multiple
+        // transactions from blocking each other.
+        Transaction& setApplyToken(const sp<IBinder>& token);
+
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
 
diff --git a/libs/gui/include/gui/SyncScreenCaptureListener.h b/libs/gui/include/gui/SyncScreenCaptureListener.h
index 2857996..8620b77 100644
--- a/libs/gui/include/gui/SyncScreenCaptureListener.h
+++ b/libs/gui/include/gui/SyncScreenCaptureListener.h
@@ -16,16 +16,19 @@
 
 #pragma once
 
+#include <android/gui/BnScreenCaptureListener.h>
 #include <gui/SurfaceComposerClient.h>
 #include <future>
 
 namespace android {
 
-class SyncScreenCaptureListener : public BnScreenCaptureListener {
+using gui::ScreenCaptureResults;
+
+struct SyncScreenCaptureListener : gui::BnScreenCaptureListener {
 public:
-    status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override {
+    binder::Status onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override {
         resultsPromise.set_value(captureResults);
-        return NO_ERROR;
+        return binder::Status::ok();
     }
 
     ScreenCaptureResults waitForResults() {
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 17f8b97..d69b7c3 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -24,6 +24,7 @@
 #include <gui/FrameTimestamps.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/IProducerListener.h>
+#include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 #include <gui/SyncScreenCaptureListener.h>
 #include <private/gui/ComposerService.h>
@@ -220,6 +221,32 @@
         return captureResults.result;
     }
 
+    void queueBuffer(sp<IGraphicBufferProducer> igbp, uint8_t r, uint8_t g, uint8_t b,
+                     nsecs_t presentTimeDelay) {
+        int slot;
+        sp<Fence> fence;
+        sp<GraphicBuffer> buf;
+        auto ret = igbp->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                       PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                       nullptr, nullptr);
+        ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+        ASSERT_EQ(OK, igbp->requestBuffer(slot, &buf));
+
+        uint32_t* bufData;
+        buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+                  reinterpret_cast<void**>(&bufData));
+        fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight() / 2), buf->getStride(), r, g, b);
+        buf->unlock();
+
+        IGraphicBufferProducer::QueueBufferOutput qbOutput;
+        nsecs_t timestampNanos = systemTime() + presentTimeDelay;
+        IGraphicBufferProducer::QueueBufferInput input(timestampNanos, false, HAL_DATASPACE_UNKNOWN,
+                                                       Rect(mDisplayWidth, mDisplayHeight / 2),
+                                                       NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+                                                       Fence::NO_FENCE);
+        igbp->queueBuffer(slot, input, &qbOutput);
+    }
+
     sp<SurfaceComposerClient> mClient;
     sp<ISurfaceComposer> mComposer;
 
@@ -515,6 +542,53 @@
     adapter.waitForCallbacks();
 }
 
+TEST_F(BLASTBufferQueueTest, QueryNativeWindowQueuesToWindowComposer) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+    sp<android::Surface> surface = new Surface(adapter.getIGraphicBufferProducer());
+    ANativeWindow* nativeWindow = (ANativeWindow*)(surface.get());
+    int queuesToNativeWindow = 0;
+    int err = nativeWindow->query(nativeWindow, NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER,
+                                  &queuesToNativeWindow);
+    ASSERT_EQ(NO_ERROR, err);
+    ASSERT_EQ(queuesToNativeWindow, 1);
+}
+
+TEST_F(BLASTBufferQueueTest, OutOfOrderTransactionTest) {
+    sp<SurfaceControl> bgSurface =
+            mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceBufferState);
+    ASSERT_NE(nullptr, bgSurface.get());
+    Transaction t;
+    t.setLayerStack(bgSurface, 0)
+            .show(bgSurface)
+            .setDataspace(bgSurface, ui::Dataspace::V0_SRGB)
+            .setFrame(bgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+            .setLayer(bgSurface, std::numeric_limits<int32_t>::max() - 1)
+            .apply();
+
+    BLASTBufferQueueHelper slowAdapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> slowIgbProducer;
+    setUpProducer(slowAdapter, slowIgbProducer);
+    nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
+    queueBuffer(slowIgbProducer, 0 /* r */, 0 /* g */, 0 /* b */, presentTimeDelay);
+
+    BLASTBufferQueueHelper fastAdapter(bgSurface, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> fastIgbProducer;
+    setUpProducer(fastAdapter, fastIgbProducer);
+    uint8_t r = 255;
+    uint8_t g = 0;
+    uint8_t b = 0;
+    queueBuffer(fastIgbProducer, r, g, b, 0 /* presentTimeDelay */);
+    fastAdapter.waitForCallbacks();
+
+    // capture screen and verify that it is red
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
 class BLASTBufferQueueTransformTest : public BLASTBufferQueueTest {
 public:
     void test(uint32_t tr) {
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 15bd32d..2af2fe1 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -70,6 +70,9 @@
     const int QUEUE_BUFFER_INPUT_SCALING_MODE = 0;
     const int QUEUE_BUFFER_INPUT_TRANSFORM = 0;
     const sp<Fence> QUEUE_BUFFER_INPUT_FENCE = Fence::NO_FENCE;
+    const uint32_t QUEUE_BUFFER_INPUT_STICKY_TRANSFORM = 0;
+    const bool QUEUE_BUFFER_INPUT_GET_TIMESTAMPS = 0;
+    const int QUEUE_BUFFER_INPUT_SLOT = -1;
 
     // Enums to control which IGraphicBufferProducer backend to test.
     enum IGraphicBufferProducerTestCode {
@@ -156,6 +159,9 @@
            scalingMode = QUEUE_BUFFER_INPUT_SCALING_MODE;
            transform = QUEUE_BUFFER_INPUT_TRANSFORM;
            fence = QUEUE_BUFFER_INPUT_FENCE;
+           stickyTransform = QUEUE_BUFFER_INPUT_STICKY_TRANSFORM;
+           getTimestamps = QUEUE_BUFFER_INPUT_GET_TIMESTAMPS;
+           slot = QUEUE_BUFFER_INPUT_SLOT;
         }
 
         IGraphicBufferProducer::QueueBufferInput build() {
@@ -166,7 +172,10 @@
                     crop,
                     scalingMode,
                     transform,
-                    fence);
+                    fence,
+                    stickyTransform,
+                    getTimestamps,
+                    slot);
         }
 
         QueueBufferInputBuilder& setTimestamp(int64_t timestamp) {
@@ -204,6 +213,21 @@
             return *this;
         }
 
+        QueueBufferInputBuilder& setStickyTransform(uint32_t stickyTransform) {
+            this->stickyTransform = stickyTransform;
+            return *this;
+        }
+
+        QueueBufferInputBuilder& setGetTimestamps(bool getTimestamps) {
+            this->getTimestamps = getTimestamps;
+            return *this;
+        }
+
+        QueueBufferInputBuilder& setSlot(int slot) {
+            this->slot = slot;
+            return *this;
+        }
+
     private:
         int64_t timestamp;
         bool isAutoTimestamp;
@@ -212,17 +236,17 @@
         int scalingMode;
         uint32_t transform;
         sp<Fence> fence;
+        uint32_t stickyTransform;
+        bool getTimestamps;
+        int slot;
     }; // struct QueueBufferInputBuilder
 
-    // To easily store dequeueBuffer results into containers
-    struct DequeueBufferResult {
-        int slot;
-        sp<Fence> fence;
-    };
-
-    status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage, DequeueBufferResult* result) {
-        return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage,
-                                        nullptr, nullptr);
+    status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage,
+                           IGraphicBufferProducer::DequeueBufferOutput* result) {
+        result->result =
+            mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage,
+                                     &result->bufferAge, nullptr);
+        return result->result;
     }
 
     void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence,
@@ -336,6 +360,27 @@
     EXPECT_OK(mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
     EXPECT_EQ(DEFAULT_CONSUMER_USAGE_BITS, value);
 
+    { // Test the batched version
+        std::vector<int32_t> inputs = {
+                NATIVE_WINDOW_WIDTH,
+                NATIVE_WINDOW_HEIGHT,
+                NATIVE_WINDOW_FORMAT,
+                NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+                NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND,
+                NATIVE_WINDOW_CONSUMER_USAGE_BITS };
+        using QueryOutput = IGraphicBufferProducer::QueryOutput;
+        std::vector<QueryOutput> outputs;
+        EXPECT_OK(mProducer->query(inputs, &outputs));
+        EXPECT_EQ(DEFAULT_WIDTH, static_cast<uint32_t>(outputs[0].value));
+        EXPECT_EQ(DEFAULT_HEIGHT, static_cast<uint32_t>(outputs[1].value));
+        EXPECT_EQ(DEFAULT_FORMAT, outputs[2].value);
+        EXPECT_LE(0, outputs[3].value);
+        EXPECT_FALSE(outputs[4].value);
+        EXPECT_EQ(DEFAULT_CONSUMER_USAGE_BITS, outputs[5].value);
+        for (const QueryOutput& output : outputs) {
+            EXPECT_OK(output.result);
+        }
+    }
 }
 
 TEST_P(IGraphicBufferProducerTest, Query_ReturnsError) {
@@ -358,6 +403,24 @@
     EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_TRANSFORM_HINT, &value));
     // TODO: Consider documented the above enums as unsupported or make a new enum for IGBP
 
+    { // Test the batched version
+        std::vector<int32_t> inputs = {
+                -1,
+                static_cast<int32_t>(0xDEADBEEF),
+                NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE,
+                NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER,
+                NATIVE_WINDOW_CONCRETE_TYPE,
+                NATIVE_WINDOW_DEFAULT_WIDTH,
+                NATIVE_WINDOW_DEFAULT_HEIGHT,
+                NATIVE_WINDOW_TRANSFORM_HINT};
+        using QueryOutput = IGraphicBufferProducer::QueryOutput;
+        std::vector<QueryOutput> outputs;
+        EXPECT_OK(mProducer->query(inputs, &outputs));
+        for (const QueryOutput& output : outputs) {
+            EXPECT_EQ(BAD_VALUE, output.result);
+        }
+    }
+
     // Value was NULL
     EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/nullptr));
 
@@ -416,29 +479,113 @@
 
     // Buffer was not in the dequeued state
     EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+    { // Test batched methods
+        constexpr size_t BATCH_SIZE = 4;
+
+        ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE));
+        // Dequeue
+        using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput;
+        using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput;
+        DequeueBufferInput dequeueInput;
+        dequeueInput.width = DEFAULT_WIDTH;
+        dequeueInput.height = DEFAULT_HEIGHT;
+        dequeueInput.format = DEFAULT_FORMAT;
+        dequeueInput.usage = TEST_PRODUCER_USAGE_BITS;
+        dequeueInput.getTimestamps = false;
+        std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput);
+        std::vector<DequeueBufferOutput> dequeueOutputs;
+        EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs));
+        ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size());
+
+        // Request
+        std::vector<int32_t> requestInputs;
+        requestInputs.reserve(BATCH_SIZE);
+        for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+            ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                      dequeueOutput.result);
+            requestInputs.emplace_back(dequeueOutput.slot);
+        }
+        using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput;
+        std::vector<RequestBufferOutput> requestOutputs;
+        EXPECT_OK(mProducer->requestBuffers(requestInputs, &requestOutputs));
+        ASSERT_EQ(requestInputs.size(), requestOutputs.size());
+        for (const RequestBufferOutput& requestOutput : requestOutputs) {
+            EXPECT_OK(requestOutput.result);
+        }
+
+        // Queue
+        using QueueBufferInput = IGraphicBufferProducer::QueueBufferInput;
+        using QueueBufferOutput = IGraphicBufferProducer::QueueBufferOutput;
+        std::vector<QueueBufferInput> queueInputs;
+        queueInputs.reserve(BATCH_SIZE);
+        for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+            queueInputs.emplace_back(CreateBufferInput()).slot =
+                dequeueOutput.slot;
+        }
+        std::vector<QueueBufferOutput> queueOutputs;
+        EXPECT_OK(mProducer->queueBuffers(queueInputs, &queueOutputs));
+        ASSERT_EQ(queueInputs.size(), queueOutputs.size());
+        for (const QueueBufferOutput& queueOutput : queueOutputs) {
+            EXPECT_OK(queueOutput.result);
+        }
+
+        // Re-queue
+        EXPECT_OK(mProducer->queueBuffers(queueInputs, &queueOutputs));
+        ASSERT_EQ(queueInputs.size(), queueOutputs.size());
+        for (const QueueBufferOutput& queueOutput : queueOutputs) {
+            EXPECT_EQ(BAD_VALUE, queueOutput.result);
+        }
+    }
 }
 
 TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) {
     ASSERT_NO_FATAL_FAILURE(ConnectProducer());
 
+    using QueueBufferInput = IGraphicBufferProducer::QueueBufferInput;
+    using QueueBufferOutput = IGraphicBufferProducer::QueueBufferOutput;
     // Invalid slot number
     {
         // A generic "valid" input
-        IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
-        IGraphicBufferProducer::QueueBufferOutput output;
+        QueueBufferInput input = CreateBufferInput();
+        QueueBufferOutput output;
 
         EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/-1, input, &output));
         EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/0xDEADBEEF, input, &output));
         EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(BufferQueue::NUM_BUFFER_SLOTS,
                                                     input, &output));
+
+        { // Test with the batched version
+            constexpr size_t BATCH_SIZE = 16;
+            input.slot = -1;
+            std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+            std::vector<QueueBufferOutput> outputs;
+            EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+            ASSERT_EQ(inputs.size(), outputs.size());
+            for (const QueueBufferOutput& output : outputs) {
+                EXPECT_EQ(BAD_VALUE, output.result);
+            }
+        }
     }
 
     // Slot was not in the dequeued state (all slots start out in Free state)
     {
-        IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
-        IGraphicBufferProducer::QueueBufferOutput output;
+        QueueBufferInput input = CreateBufferInput();
+        QueueBufferOutput output;
 
         EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/0, input, &output));
+
+        { // Test with the batched version
+            constexpr size_t BATCH_SIZE = 16;
+            input.slot = 0;
+            std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+            std::vector<QueueBufferOutput> outputs;
+            EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+            ASSERT_EQ(inputs.size(), outputs.size());
+            for (const QueueBufferOutput& output : outputs) {
+                EXPECT_EQ(BAD_VALUE, output.result);
+            }
+        }
     }
 
     // Put the slot into the "dequeued" state for the rest of the test
@@ -453,10 +600,22 @@
 
     // Slot was enqueued without requesting a buffer
     {
-        IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
-        IGraphicBufferProducer::QueueBufferOutput output;
+        QueueBufferInput input = CreateBufferInput();
+        QueueBufferOutput output;
 
         EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+        { // Test with the batched version
+            constexpr size_t BATCH_SIZE = 16;
+            input.slot = dequeuedSlot;
+            std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+            std::vector<QueueBufferOutput> outputs;
+            EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+            ASSERT_EQ(inputs.size(), outputs.size());
+            for (const QueueBufferOutput& output : outputs) {
+                EXPECT_EQ(BAD_VALUE, output.result);
+            }
+        }
     }
 
     // Request the buffer so that the rest of the tests don't fail on earlier checks.
@@ -467,11 +626,23 @@
     {
         sp<Fence> nullFence = nullptr;
 
-        IGraphicBufferProducer::QueueBufferInput input =
+        QueueBufferInput input =
                 QueueBufferInputBuilder().setFence(nullFence).build();
-        IGraphicBufferProducer::QueueBufferOutput output;
+        QueueBufferOutput output;
 
         EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+        { // Test with the batched version
+            constexpr size_t BATCH_SIZE = 16;
+            input.slot = dequeuedSlot;
+            std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+            std::vector<QueueBufferOutput> outputs;
+            EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+            ASSERT_EQ(inputs.size(), outputs.size());
+            for (const QueueBufferOutput& output : outputs) {
+                EXPECT_EQ(BAD_VALUE, output.result);
+            }
+        }
     }
 
     // Scaling mode was unknown
@@ -482,9 +653,33 @@
 
         EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
 
+        { // Test with the batched version
+            constexpr size_t BATCH_SIZE = 16;
+            input.slot = dequeuedSlot;
+            std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+            std::vector<QueueBufferOutput> outputs;
+            EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+            ASSERT_EQ(inputs.size(), outputs.size());
+            for (const QueueBufferOutput& output : outputs) {
+                EXPECT_EQ(BAD_VALUE, output.result);
+            }
+        }
+
         input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build();
 
         EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+        { // Test with the batched version
+            constexpr size_t BATCH_SIZE = 16;
+            input.slot = dequeuedSlot;
+            std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+            std::vector<QueueBufferOutput> outputs;
+            EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+            ASSERT_EQ(inputs.size(), outputs.size());
+            for (const QueueBufferOutput& output : outputs) {
+                EXPECT_EQ(BAD_VALUE, output.result);
+            }
+        }
     }
 
     // Crop rect is out of bounds of the buffer dimensions
@@ -495,6 +690,18 @@
         IGraphicBufferProducer::QueueBufferOutput output;
 
         EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+        { // Test with the batched version
+            constexpr size_t BATCH_SIZE = 16;
+            input.slot = dequeuedSlot;
+            std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+            std::vector<QueueBufferOutput> outputs;
+            EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+            ASSERT_EQ(inputs.size(), outputs.size());
+            for (const QueueBufferOutput& output : outputs) {
+                EXPECT_EQ(BAD_VALUE, output.result);
+            }
+        }
     }
 
     // Abandon the buffer queue so that the last test fails
@@ -507,6 +714,18 @@
 
         // TODO(b/73267953): Make BufferHub honor producer and consumer connection.
         EXPECT_EQ(NO_INIT, mProducer->queueBuffer(dequeuedSlot, input, &output));
+
+        { // Test with the batched version
+            constexpr size_t BATCH_SIZE = 16;
+            input.slot = dequeuedSlot;
+            std::vector<QueueBufferInput> inputs(BATCH_SIZE, input);
+            std::vector<QueueBufferOutput> outputs;
+            EXPECT_OK(mProducer->queueBuffers(inputs, &outputs));
+            ASSERT_EQ(inputs.size(), outputs.size());
+            for (const QueueBufferOutput& output : outputs) {
+                EXPECT_EQ(NO_INIT, output.result);
+            }
+        }
     }
 }
 
@@ -525,6 +744,44 @@
     // No return code, but at least test that it doesn't blow up...
     // TODO: add a return code
     mProducer->cancelBuffer(dequeuedSlot, dequeuedFence);
+
+    { // Test batched methods
+        constexpr size_t BATCH_SIZE = 4;
+        ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE));
+
+        // Dequeue
+        using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput;
+        using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput;
+        DequeueBufferInput dequeueInput;
+        dequeueInput.width = DEFAULT_WIDTH;
+        dequeueInput.height = DEFAULT_HEIGHT;
+        dequeueInput.format = DEFAULT_FORMAT;
+        dequeueInput.usage = TEST_PRODUCER_USAGE_BITS;
+        dequeueInput.getTimestamps = false;
+        std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput);
+        std::vector<DequeueBufferOutput> dequeueOutputs;
+        EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs));
+        ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size());
+
+        // Cancel
+        using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput;
+        std::vector<CancelBufferInput> cancelInputs;
+        cancelInputs.reserve(BATCH_SIZE);
+        for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+            ASSERT_EQ(OK,
+                      ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                      dequeueOutput.result);
+            CancelBufferInput& cancelInput = cancelInputs.emplace_back();
+            cancelInput.slot = dequeueOutput.slot;
+            cancelInput.fence = dequeueOutput.fence;
+        }
+        std::vector<status_t> cancelOutputs;
+        EXPECT_OK(mProducer->cancelBuffers(cancelInputs, &cancelOutputs));
+        ASSERT_EQ(cancelInputs.size(), cancelOutputs.size());
+        for (status_t result : cancelOutputs) {
+            EXPECT_OK(result);
+        }
+    }
 }
 
 TEST_P(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) {
@@ -541,11 +798,11 @@
             << "bufferCount: " << minBuffers;
 
     // Should now be able to dequeue up to minBuffers times
-    DequeueBufferResult result;
+    IGraphicBufferProducer::DequeueBufferOutput result;
     for (int i = 0; i < minBuffers; ++i) {
         EXPECT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
                 (dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                              TEST_PRODUCER_USAGE_BITS, &result)))
+                               TEST_PRODUCER_USAGE_BITS, &result)))
                 << "iteration: " << i << ", slot: " << result.slot;
     }
 
@@ -558,7 +815,6 @@
     ASSERT_OK(mProducer->requestBuffer(result.slot, &buffer));
     ASSERT_OK(mProducer->queueBuffer(result.slot, input, &output));
 
-
     // Should now be able to dequeue up to maxBuffers times
     int dequeuedSlot = -1;
     sp<Fence> dequeuedFence;
@@ -794,6 +1050,71 @@
 
     EXPECT_OK(mProducer->attachBuffer(&slot, buffer));
     EXPECT_OK(buffer->initCheck());
+
+    ASSERT_OK(mProducer->detachBuffer(slot));
+
+    { // Test batched methods
+        constexpr size_t BATCH_SIZE = 4;
+        ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE));
+
+        // Dequeue
+        using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput;
+        using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput;
+        DequeueBufferInput dequeueInput;
+        dequeueInput.width = DEFAULT_WIDTH;
+        dequeueInput.height = DEFAULT_HEIGHT;
+        dequeueInput.format = DEFAULT_FORMAT;
+        dequeueInput.usage = TEST_PRODUCER_USAGE_BITS;
+        dequeueInput.getTimestamps = false;
+        std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput);
+        std::vector<DequeueBufferOutput> dequeueOutputs;
+        EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs));
+        ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size());
+
+        // Request
+        std::vector<int32_t> requestInputs;
+        requestInputs.reserve(BATCH_SIZE);
+        for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+            ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
+                      dequeueOutput.result);
+            requestInputs.emplace_back(dequeueOutput.slot);
+        }
+        using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput;
+        std::vector<RequestBufferOutput> requestOutputs;
+        EXPECT_OK(mProducer->requestBuffers(requestInputs, &requestOutputs));
+        ASSERT_EQ(requestInputs.size(), requestOutputs.size());
+        for (const RequestBufferOutput& requestOutput : requestOutputs) {
+            EXPECT_OK(requestOutput.result);
+        }
+
+        // Detach
+        std::vector<int32_t> detachInputs;
+        detachInputs.reserve(BATCH_SIZE);
+        for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) {
+            detachInputs.emplace_back(dequeueOutput.slot);
+        }
+        std::vector<status_t> detachOutputs;
+        EXPECT_OK(mProducer->detachBuffers(detachInputs, &detachOutputs));
+        ASSERT_EQ(detachInputs.size(), detachOutputs.size());
+        for (status_t result : detachOutputs) {
+            EXPECT_OK(result);
+        }
+
+        // Attach
+        using AttachBufferOutput = IGraphicBufferProducer::AttachBufferOutput;
+        std::vector<sp<GraphicBuffer>> attachInputs;
+        attachInputs.reserve(BATCH_SIZE);
+        for (const RequestBufferOutput& requestOutput : requestOutputs) {
+            attachInputs.emplace_back(requestOutput.buffer);
+        }
+        std::vector<AttachBufferOutput> attachOutputs;
+        EXPECT_OK(mProducer->attachBuffers(attachInputs, &attachOutputs));
+        ASSERT_EQ(attachInputs.size(), attachOutputs.size());
+        for (const AttachBufferOutput& attachOutput : attachOutputs) {
+            EXPECT_OK(attachOutput.result);
+            EXPECT_NE(-1, attachOutput.slot);
+        }
+    }
 }
 
 #if USE_BUFFER_HUB_AS_BUFFER_QUEUE
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index fa98cd4c..63db9a7 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -2024,4 +2024,86 @@
     EXPECT_EQ(BufferQueueDefs::NUM_BUFFER_SLOTS, count);
 }
 
+TEST_F(SurfaceTest, BatchOperations) {
+    const int BUFFER_COUNT = 16;
+    const int BATCH_SIZE = 8;
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+
+    sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
+    sp<Surface> surface = new Surface(producer);
+    sp<ANativeWindow> window(surface);
+    sp<StubProducerListener> listener = new StubProducerListener();
+
+    ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener,
+            /*reportBufferRemoval*/false));
+
+    ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
+
+    std::vector<Surface::BatchBuffer> buffers(BATCH_SIZE);
+
+    // Batch dequeued buffers can be queued individually
+    ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers));
+    for (size_t i = 0; i < BATCH_SIZE; i++) {
+        ANativeWindowBuffer* buffer = buffers[i].buffer;
+        int fence = buffers[i].fenceFd;
+        ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence));
+    }
+
+    // Batch dequeued buffers can be canceled individually
+    ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers));
+    for (size_t i = 0; i < BATCH_SIZE; i++) {
+        ANativeWindowBuffer* buffer = buffers[i].buffer;
+        int fence = buffers[i].fenceFd;
+        ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
+    }
+
+    // Batch dequeued buffers can be batch cancelled
+    ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers));
+    ASSERT_EQ(NO_ERROR, surface->cancelBuffers(buffers));
+
+    // Batch dequeued buffers can be batch queued
+    ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers));
+    std::vector<Surface::BatchQueuedBuffer> queuedBuffers(BATCH_SIZE);
+    for (size_t i = 0; i < BATCH_SIZE; i++) {
+        queuedBuffers[i].buffer = buffers[i].buffer;
+        queuedBuffers[i].fenceFd = buffers[i].fenceFd;
+        queuedBuffers[i].timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
+    }
+    ASSERT_EQ(NO_ERROR, surface->queueBuffers(queuedBuffers));
+
+    ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
+}
+
+TEST_F(SurfaceTest, BatchIllegalOperations) {
+    const int BUFFER_COUNT = 16;
+    const int BATCH_SIZE = 8;
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+
+    sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
+    sp<Surface> surface = new Surface(producer);
+    sp<ANativeWindow> window(surface);
+    sp<StubProducerListener> listener = new StubProducerListener();
+
+    ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener,
+            /*reportBufferRemoval*/false));
+
+    ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
+
+    std::vector<Surface::BatchBuffer> buffers(BATCH_SIZE);
+    std::vector<Surface::BatchQueuedBuffer> queuedBuffers(BATCH_SIZE);
+
+    // Batch operations are invalid in shared buffer mode
+    surface->setSharedBufferMode(true);
+    ASSERT_EQ(INVALID_OPERATION, surface->dequeueBuffers(&buffers));
+    ASSERT_EQ(INVALID_OPERATION, surface->cancelBuffers(buffers));
+    ASSERT_EQ(INVALID_OPERATION, surface->queueBuffers(queuedBuffers));
+    surface->setSharedBufferMode(false);
+
+    ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
+}
+
 } // namespace android
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 3e65d9a..45db31c 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -41,6 +41,9 @@
     if (strcmp(prop, "skiagl") == 0) {
         renderEngineType = RenderEngineType::SKIA_GL;
     }
+    if (strcmp(prop, "skiaglthreaded") == 0) {
+        renderEngineType = RenderEngineType::SKIA_GL_THREADED;
+    }
 
     switch (renderEngineType) {
         case RenderEngineType::THREADED:
@@ -49,6 +52,10 @@
                     [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); });
         case RenderEngineType::SKIA_GL:
             return renderengine::skia::SkiaGLRenderEngine::create(args);
+        case RenderEngineType::SKIA_GL_THREADED:
+            return renderengine::threaded::RenderEngineThreaded::create([args]() {
+                return android::renderengine::skia::SkiaGLRenderEngine::create(args);
+            });
         case RenderEngineType::GLES:
         default:
             ALOGD("RenderEngine with GLES Backend");
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 9157066..506f81e 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -82,6 +82,7 @@
         GLES = 1,
         THREADED = 2,
         SKIA_GL = 3,
+        SKIA_GL_THREADED = 4,
     };
 
     static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index d126c27..2ffb547 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -72,6 +72,7 @@
 
 AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer,
                                        bool isRender) {
+    ATRACE_CALL();
     AHardwareBuffer_Desc desc;
     AHardwareBuffer_describe(buffer, &desc);
     bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 3d6b7af..d9495a9 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -19,25 +19,10 @@
 #define LOG_TAG "RenderEngine"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <cstdint>
-#include <memory>
-
-#include "SkImageInfo.h"
-#include "log/log_main.h"
-#include "system/graphics-base-v1.0.h"
+#include "SkiaGLRenderEngine.h"
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
-#include <sync/sync.h>
-#include <ui/BlurRegion.h>
-#include <ui/GraphicBuffer.h>
-#include <utils/Trace.h>
-#include "../gl/GLExtensions.h"
-#include "SkiaGLRenderEngine.h"
-#include "filters/BlurFilter.h"
-#include "filters/LinearEffect.h"
-#include "skia/debug/SkiaCapture.h"
-
 #include <GrContextOptions.h>
 #include <SkCanvas.h>
 #include <SkColorFilter.h>
@@ -48,8 +33,23 @@
 #include <SkShadowUtils.h>
 #include <SkSurface.h>
 #include <gl/GrGLInterface.h>
+#include <sync/sync.h>
+#include <ui/BlurRegion.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Trace.h>
 
 #include <cmath>
+#include <cstdint>
+#include <memory>
+
+#include "../gl/GLExtensions.h"
+#include "SkBlendMode.h"
+#include "SkImageInfo.h"
+#include "filters/BlurFilter.h"
+#include "filters/LinearEffect.h"
+#include "log/log_main.h"
+#include "skia/debug/SkiaCapture.h"
+#include "system/graphics-base-v1.0.h"
 
 bool checkGlError(const char* op, int lineNumber);
 
@@ -418,6 +418,32 @@
     mProtectedTextureCache.erase(bufferId);
 }
 
+sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> shader,
+                                                              const LayerSettings* layer,
+                                                              const DisplaySettings& display,
+                                                              bool undoPremultipliedAlpha) {
+    if (mUseColorManagement &&
+        needsLinearEffect(layer->colorTransform, layer->sourceDataspace, display.outputDataspace)) {
+        LinearEffect effect = LinearEffect{.inputDataspace = layer->sourceDataspace,
+                                           .outputDataspace = display.outputDataspace,
+                                           .undoPremultipliedAlpha = undoPremultipliedAlpha};
+
+        auto effectIter = mRuntimeEffects.find(effect);
+        sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
+        if (effectIter == mRuntimeEffects.end()) {
+            runtimeEffect = buildRuntimeEffect(effect);
+            mRuntimeEffects.insert({effect, runtimeEffect});
+        } else {
+            runtimeEffect = effectIter->second;
+        }
+        return createLinearEffectShader(shader, effect, runtimeEffect, layer->colorTransform,
+                                        display.maxLuminance,
+                                        layer->source.buffer.maxMasteringLuminance,
+                                        layer->source.buffer.maxContentLuminance);
+    }
+    return shader;
+}
+
 status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
                                         const std::vector<const LayerSettings*>& layers,
                                         const sp<GraphicBuffer>& buffer,
@@ -624,44 +650,53 @@
                 shader = image->makeShader(SkSamplingOptions(), matrix);
             }
 
-            if (mUseColorManagement &&
-                needsLinearEffect(layer->colorTransform, layer->sourceDataspace,
-                                  display.outputDataspace)) {
-                LinearEffect effect = LinearEffect{.inputDataspace = layer->sourceDataspace,
-                                                   .outputDataspace = display.outputDataspace,
-                                                   .undoPremultipliedAlpha = !item.isOpaque &&
-                                                           item.usePremultipliedAlpha};
+            paint.setShader(
+                    createRuntimeEffectShader(shader, layer, display,
+                                              !item.isOpaque && item.usePremultipliedAlpha));
 
-                auto effectIter = mRuntimeEffects.find(effect);
-                sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
-                if (effectIter == mRuntimeEffects.end()) {
-                    runtimeEffect = buildRuntimeEffect(effect);
-                    mRuntimeEffects.insert({effect, runtimeEffect});
-                } else {
-                    runtimeEffect = effectIter->second;
-                }
-
-                paint.setShader(createLinearEffectShader(shader, effect, runtimeEffect,
-                                                         layer->colorTransform,
-                                                         display.maxLuminance,
-                                                         layer->source.buffer.maxMasteringLuminance,
-                                                         layer->source.buffer.maxContentLuminance));
-            } else {
-                paint.setShader(shader);
-            }
             // Make sure to take into the account the alpha set on the layer.
             paint.setAlphaf(layer->alpha);
         } else {
             ATRACE_NAME("DrawColor");
             const auto color = layer->source.solidColor;
-            paint.setShader(SkShaders::Color(SkColor4f{.fR = color.r,
-                                                       .fG = color.g,
-                                                       .fB = color.b,
-                                                       layer->alpha},
-                                             nullptr));
+            sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r,
+                                                                .fG = color.g,
+                                                                .fB = color.b,
+                                                                layer->alpha},
+                                                      nullptr);
+            paint.setShader(createRuntimeEffectShader(shader, layer, display,
+                                                      /* undoPremultipliedAlpha */ false));
         }
 
-        paint.setColorFilter(SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform)));
+        sk_sp<SkColorFilter> filter =
+                SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform));
+
+        // Handle opaque images - it's a little nonstandard how we do this.
+        // Fundamentally we need to support SurfaceControl.Builder#setOpaque:
+        // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean)
+        // The important language is that when isOpaque is set, opacity is not sampled from the
+        // alpha channel, but blending may still be supported on a transaction via setAlpha. So,
+        // here's the conundrum:
+        // 1. We can't force the SkImage alpha type to kOpaque_SkAlphaType, because it's treated as
+        // an internal hint - composition is undefined when there are alpha bits present.
+        // 2. We can try to lie about the pixel layout, but that only works for RGBA8888 buffers,
+        // i.e., treating them as RGBx8888 instead. But we can't do the same for RGBA1010102 because
+        // RGBx1010102 is not supported as a pixel layout for SkImages. It's also not clear what to
+        // use for F16 either, and lying about the pixel layout is a bit of a hack anyways.
+        // 3. We can't change the blendmode to src, because while this satisfies the requirement for
+        // ignoring the alpha channel, it doesn't quite satisfy the blending requirement because
+        // src always clobbers the destination content.
+        //
+        // So, what we do here instead is an additive blend mode where we compose the input image
+        // with a solid black. This might need to be reassess if this does not support FP16
+        // incredibly well, but FP16 end-to-end isn't well supported anyway at the moment.
+        if (layer->source.buffer.buffer && layer->source.buffer.isOpaque) {
+            filter = SkColorFilters::Compose(filter,
+                                             SkColorFilters::Blend(SK_ColorBLACK,
+                                                                   SkBlendMode::kPlus));
+        }
+
+        paint.setColorFilter(filter);
 
         for (const auto effectRegion : layer->blurRegions) {
             drawBlurRegion(canvas, effectRegion, layerRect, cachedBlurs[effectRegion.blurRadius]);
@@ -672,13 +707,17 @@
                     ? getSkRect(layer->geometry.roundedCornersCrop)
                     : dest;
             drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
+        } else {
+            // Shadows are assumed to live only on their own layer - it's not valid
+            // to draw the boundary retangles when there is already a caster shadow
+            // TODO(b/175915334): consider relaxing this restriction to enable more flexible
+            // composition - using a well-defined invalid color is long-term less error-prone.
+            // Push the clipRRect onto the clip stack. Draw the image. Pop the clip.
+            if (layer->geometry.roundedCornersRadius > 0) {
+                canvas->clipRRect(getRoundedRect(layer), true);
+            }
+            canvas->drawRect(dest, paint);
         }
-
-        // Push the clipRRect onto the clip stack. Draw the image. Pop the clip.
-        if (layer->geometry.roundedCornersRadius > 0) {
-            canvas->clipRRect(getRoundedRect(layer), true);
-        }
-        canvas->drawRect(dest, paint);
         canvas->restore();
     }
     canvas->restore();
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 3d8c693..5384ec8 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -91,6 +91,11 @@
     void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& layerRect,
                         sk_sp<SkSurface> blurredSurface);
     SkMatrix getBlurShaderTransform(const SkCanvas* canvas, const SkRect& layerRect);
+    // If mUseColorManagement is correct and layer needsLinearEffect, it returns a linear runtime
+    // shader. Otherwise it returns the input shader.
+    sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, const LayerSettings* layer,
+                                              const DisplaySettings& display,
+                                              bool undoPremultipliedAlpha);
 
     EGLDisplay mEGLDisplay;
     EGLContext mEGLContext;
diff --git a/libs/renderengine/skia/debug/SkiaCapture.cpp b/libs/renderengine/skia/debug/SkiaCapture.cpp
index 8006a11..e9cfead 100644
--- a/libs/renderengine/skia/debug/SkiaCapture.cpp
+++ b/libs/renderengine/skia/debug/SkiaCapture.cpp
@@ -129,8 +129,14 @@
         // SkDocuments don't take ownership of the streams they write.
         // we need to keep it until after mMultiPic.close()
         // procs is passed as a pointer, but just as a method of having an optional default.
-        // procs doesn't need to outlive this Make call.
-        mMultiPic = SkMakeMultiPictureDocument(mOpenMultiPicStream.get(), &procs);
+        // procs doesn't need to outlive this Make call
+        // The last argument is a callback for the endPage behavior.
+        // See SkSharingProc.h for more explanation of this callback.
+        mMultiPic = SkMakeMultiPictureDocument(
+                mOpenMultiPicStream.get(), &procs,
+                [sharingCtx = mSerialContext.get()](const SkPicture* pic) {
+                    SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx);
+                });
         mCaptureRunning = true;
         return true;
     } else {
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 47b8cad..2b8063e 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -306,6 +306,26 @@
                           backgroundColor.a);
     }
 
+    void expectShadowColorWithoutCaster(const FloatRect& casterBounds,
+                                        const renderengine::ShadowSettings& shadow,
+                                        const ubyte4& backgroundColor) {
+        const float shadowInset = shadow.length * -1.0f;
+        const Rect casterRect(casterBounds);
+        const Rect shadowRect =
+                Rect(casterRect).inset(shadowInset, shadowInset, shadowInset, shadowInset);
+
+        const Region backgroundRegion =
+                Region(fullscreenRect()).subtractSelf(casterRect).subtractSelf(shadowRect);
+
+        expectAlpha(shadowRect, 255);
+        // (0, 0, 0) fill on the bounds of the layer should be ignored.
+        expectBufferColor(casterRect, 255, 255, 255, 255, 254);
+
+        // verify background
+        expectBufferColor(backgroundRegion, backgroundColor.r, backgroundColor.g, backgroundColor.b,
+                          backgroundColor.a);
+    }
+
     static renderengine::ShadowSettings getShadowSettings(const vec2& casterPos, float shadowLength,
                                                           bool casterIsTranslucent) {
         renderengine::ShadowSettings shadow;
@@ -447,6 +467,10 @@
                     const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
                     const ubyte4& backgroundColor);
 
+    void drawShadowWithoutCaster(const FloatRect& castingBounds,
+                                 const renderengine::ShadowSettings& shadow,
+                                 const ubyte4& backgroundColor);
+
     std::unique_ptr<renderengine::gl::GLESRenderEngine> mRE;
 
     // Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
@@ -1119,6 +1143,37 @@
     invokeDraw(settings, layers, mBuffer);
 }
 
+void RenderEngineTest::drawShadowWithoutCaster(const FloatRect& castingBounds,
+                                               const renderengine::ShadowSettings& shadow,
+                                               const ubyte4& backgroundColor) {
+    renderengine::DisplaySettings settings;
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+
+    std::vector<const renderengine::LayerSettings*> layers;
+
+    // add background layer
+    renderengine::LayerSettings bgLayer;
+    bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    bgLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+    ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
+                                  backgroundColor.b / 255.0f, this);
+    bgLayer.alpha = backgroundColor.a / 255.0f;
+    layers.push_back(&bgLayer);
+
+    // add shadow layer
+    renderengine::LayerSettings shadowLayer;
+    shadowLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    shadowLayer.geometry.boundaries = castingBounds;
+    shadowLayer.alpha = 1.0f;
+    ColorSourceVariant::fillColor(shadowLayer, 0, 0, 0, this);
+    shadowLayer.shadow = shadow;
+    layers.push_back(&shadowLayer);
+
+    invokeDraw(settings, layers, mBuffer);
+}
+
 INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest,
                          testing::Values(std::make_shared<GLESRenderEngineFactory>(),
                                          std::make_shared<GLESCMRenderEngineFactory>(),
@@ -1131,6 +1186,37 @@
     drawEmptyLayers();
 }
 
+TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
+    renderengine::DisplaySettings settings;
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+
+    // 255, 255, 255, 255 is full opaque white.
+    const ubyte4 backgroundColor(255.f, 255.f, 255.f, 255.f);
+    // Create layer with given color.
+    renderengine::LayerSettings bgLayer;
+    bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    bgLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+    bgLayer.source.solidColor = half3(backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
+                                      backgroundColor.b / 255.0f);
+    bgLayer.alpha = backgroundColor.a / 255.0f;
+    // Transform the red color.
+    bgLayer.colorTransform = mat4(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+
+    std::vector<const renderengine::LayerSettings*> layers;
+    layers.push_back(&bgLayer);
+
+    invokeDraw(settings, layers, mBuffer);
+
+    // Expect to see full opaque pixel (with inverted red from the transform).
+    expectBufferColor(Rect(0, 0, 1, 1), 0.f, backgroundColor.g, backgroundColor.b,
+                      backgroundColor.a);
+}
+
 TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) {
     const auto& renderEngineFactory = GetParam();
     mRE = renderEngineFactory->createRenderEngine();
@@ -1609,6 +1695,22 @@
     EXPECT_FALSE(mRE->isImageCachedForTesting(bufferId));
 }
 
+TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
+    const ubyte4 backgroundColor(255, 255, 255, 255);
+    const float shadowLength = 5.0f;
+    Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+    casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+    renderengine::ShadowSettings settings =
+            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+                              false /* casterIsTranslucent */);
+
+    drawShadowWithoutCaster(casterBounds.toFloatRect(), settings, backgroundColor);
+    expectShadowColorWithoutCaster(casterBounds.toFloatRect(), settings, backgroundColor);
+}
+
 TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
     const auto& renderEngineFactory = GetParam();
     mRE = renderEngineFactory->createRenderEngine();
diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp
index f973cba..7bddee6 100644
--- a/libs/sensorprivacy/SensorPrivacyManager.cpp
+++ b/libs/sensorprivacy/SensorPrivacyManager.cpp
@@ -64,6 +64,15 @@
     }
 }
 
+void SensorPrivacyManager::addIndividualSensorPrivacyListener(int userId, int sensor,
+        const sp<hardware::ISensorPrivacyListener>& listener)
+{
+    sp<hardware::ISensorPrivacyManager> service = getService();
+    if (service != nullptr) {
+        service->addIndividualSensorPrivacyListener(userId, sensor, listener);
+    }
+}
+
 void SensorPrivacyManager::removeSensorPrivacyListener(
         const sp<hardware::ISensorPrivacyListener>& listener)
 {
@@ -85,6 +94,18 @@
     return false;
 }
 
+bool SensorPrivacyManager::isIndividualSensorPrivacyEnabled(int userId, int sensor)
+{
+    sp<hardware::ISensorPrivacyManager> service = getService();
+    if (service != nullptr) {
+        bool result;
+        service->isIndividualSensorPrivacyEnabled(userId, sensor, &result);
+        return result;
+    }
+    // if the SensorPrivacyManager is not available then assume sensor privacy is disabled
+    return false;
+}
+
 status_t SensorPrivacyManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient)
 {
     sp<hardware::ISensorPrivacyManager> service = getService();
diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
index 4c2d5db..629b8c2 100644
--- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
+++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
@@ -22,9 +22,17 @@
 interface ISensorPrivacyManager {
     void addSensorPrivacyListener(in ISensorPrivacyListener listener);
 
+    void addIndividualSensorPrivacyListener(int userId, int sensor, in ISensorPrivacyListener listener);
+
     void removeSensorPrivacyListener(in ISensorPrivacyListener listener);
 
     boolean isSensorPrivacyEnabled();
 
+    boolean isIndividualSensorPrivacyEnabled(int userId, int sensor);
+
     void setSensorPrivacy(boolean enable);
+
+    void setIndividualSensorPrivacy(int userId, int sensor, boolean enable);
+
+    void setIndividualSensorPrivacyForProfileGroup(int userId, int sensor, boolean enable);
 }
diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
index 2546a68..bd7c726 100644
--- a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
+++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
@@ -28,11 +28,19 @@
 class SensorPrivacyManager
 {
 public:
+    enum {
+        INDIVIDUAL_SENSOR_MICROPHONE = 1,
+        INDIVIDUAL_SENSOR_CAMERA = 2
+    };
+
     SensorPrivacyManager();
 
     void addSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener);
+    void addIndividualSensorPrivacyListener(int userId, int sensor,
+            const sp<hardware::ISensorPrivacyListener>& listener);
     void removeSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener);
     bool isSensorPrivacyEnabled();
+    bool isIndividualSensorPrivacyEnabled(int userId, int sensor);
 
     status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
     status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient);
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 48abdce..8b94f61 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -54,6 +54,7 @@
 
 cc_library_headers {
     name: "gl_headers",
+    host_supported: true,
     vendor_available: true,
     export_include_dirs: ["include"],
 }
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 398efc0..f576660 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -128,6 +128,7 @@
         "EGL_KHR_stream_producer_eglsurface "
         "EGL_KHR_surfaceless_context "
         "EGL_KHR_wait_sync "                    // strongly recommended
+        "EGL_NV_context_priority_realtime "
         "EGL_NV_system_time "
         ;
 
diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
index 6235f06..0d17265 100644
--- a/services/audiomanager/IAudioManager.cpp
+++ b/services/audiomanager/IAudioManager.cpp
@@ -84,11 +84,13 @@
         return remote()->transact(PLAYER_ATTRIBUTES, data, &reply, IBinder::FLAG_ONEWAY);
     }
 
-    virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) {
+    virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event,
+            audio_port_handle_t deviceId) {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
         data.writeInt32((int32_t) piid);
         data.writeInt32((int32_t) event);
+        data.writeInt32((int32_t) deviceId);
         return remote()->transact(PLAYER_EVENT, data, &reply, IBinder::FLAG_ONEWAY);
     }
 
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
index c5f8859..e916221 100644
--- a/services/gpuservice/tests/unittests/GpuMemTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp
@@ -59,7 +59,6 @@
     }
 
     void SetUp() override {
-        SKIP_IF_BPF_NOT_SUPPORTED;
         bpf::setrlimitForTest();
 
         mGpuMem = std::make_unique<GpuMem>();
@@ -87,8 +86,6 @@
 };
 
 TEST_F(GpuMemTest, validGpuMemTotalBpfPaths) {
-    SKIP_IF_BPF_NOT_SUPPORTED;
-
     EXPECT_EQ(mTestableGpuMem.getGpuMemTraceGroup(), "gpu_mem");
     EXPECT_EQ(mTestableGpuMem.getGpuMemTotalTracepoint(), "gpu_mem_total");
     EXPECT_EQ(mTestableGpuMem.getGpuMemTotalProgPath(),
@@ -97,20 +94,16 @@
 }
 
 TEST_F(GpuMemTest, bpfInitializationFailed) {
-    SKIP_IF_BPF_NOT_SUPPORTED;
-
     EXPECT_EQ(dumpsys(), "Failed to initialize GPU memory eBPF\n");
 }
 
 TEST_F(GpuMemTest, gpuMemTotalMapEmpty) {
-    SKIP_IF_BPF_NOT_SUPPORTED;
     mTestableGpuMem.setGpuMemTotalMap(mTestMap);
 
     EXPECT_EQ(dumpsys(), "GPU memory total usage map is empty\n");
 }
 
 TEST_F(GpuMemTest, globalMemTotal) {
-    SKIP_IF_BPF_NOT_SUPPORTED;
     ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY));
     mTestableGpuMem.setGpuMemTotalMap(mTestMap);
 
@@ -118,7 +111,6 @@
 }
 
 TEST_F(GpuMemTest, missingGlobalMemTotal) {
-    SKIP_IF_BPF_NOT_SUPPORTED;
     ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
     mTestableGpuMem.setGpuMemTotalMap(mTestMap);
 
@@ -126,7 +118,6 @@
 }
 
 TEST_F(GpuMemTest, procMemTotal) {
-    SKIP_IF_BPF_NOT_SUPPORTED;
     ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
     ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY));
     mTestableGpuMem.setGpuMemTotalMap(mTestMap);
@@ -146,7 +137,6 @@
 }
 
 TEST_F(GpuMemTest, traverseGpuMemTotals) {
-    SKIP_IF_BPF_NOT_SUPPORTED;
     ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY));
     ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
     ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY));
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 4e55872..887bdd4 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -55,12 +55,18 @@
         ALOGE("There is no focused window for %s", applicationHandle->getName().c_str());
     }
 
-    void notifyConnectionUnresponsive(const sp<IBinder>& connectionToken,
-                                      const std::string& reason) override {
-        ALOGE("Connection is not responding: %s", reason.c_str());
+    void notifyWindowUnresponsive(const sp<IBinder>& connectionToken,
+                                  const std::string& reason) override {
+        ALOGE("Window is not responding: %s", reason.c_str());
     }
 
-    void notifyConnectionResponsive(const sp<IBinder>& connectionToken) override {}
+    void notifyWindowResponsive(const sp<IBinder>& connectionToken) override {}
+
+    void notifyMonitorUnresponsive(int32_t pid, const std::string& reason) override {
+        ALOGE("Monitor is not responding: %s", reason.c_str());
+    }
+
+    void notifyMonitorResponsive(int32_t pid) override {}
 
     void notifyInputChannelBroken(const sp<IBinder>&) override {}
 
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 3a860f0..26b641d 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -271,6 +271,7 @@
     sp<IBinder> newToken;
     std::string obscuringPackage;
     bool enabled;
+    int32_t pid;
 };
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index f3d969e..cdc74f3 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -453,6 +453,20 @@
     return event;
 }
 
+static std::optional<int32_t> findMonitorPidByToken(
+        const std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay,
+        const sp<IBinder>& token) {
+    for (const auto& it : monitorsByDisplay) {
+        const std::vector<Monitor>& monitors = it.second;
+        for (const Monitor& monitor : monitors) {
+            if (monitor.inputChannel->getConnectionToken() == token) {
+                return monitor.pid;
+            }
+        }
+    }
+    return std::nullopt;
+}
+
 // --- InputDispatcher ---
 
 InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
@@ -615,7 +629,7 @@
     connection->responsive = false;
     // Stop waking up for this unresponsive connection
     mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
-    onAnrLocked(*connection);
+    onAnrLocked(connection);
     return LONG_LONG_MIN;
 }
 
@@ -1266,14 +1280,20 @@
         // Disable Pointer Capture
         token = mWindowTokenWithPointerCapture;
         mWindowTokenWithPointerCapture = nullptr;
-        mFocusedWindowRequestedPointerCapture = false;
+        if (mFocusedWindowRequestedPointerCapture) {
+            mFocusedWindowRequestedPointerCapture = false;
+            setPointerCaptureLocked(false);
+        }
     }
 
     auto channel = getInputChannelLocked(token);
     if (channel == nullptr) {
         // Window has gone away, clean up Pointer Capture state.
         mWindowTokenWithPointerCapture = nullptr;
-        mFocusedWindowRequestedPointerCapture = false;
+        if (mFocusedWindowRequestedPointerCapture) {
+            mFocusedWindowRequestedPointerCapture = false;
+            setPointerCaptureLocked(false);
+        }
         return;
     }
     InputTarget target;
@@ -5148,6 +5168,14 @@
     return std::nullopt;
 }
 
+std::optional<int32_t> InputDispatcher::findMonitorPidByTokenLocked(const sp<IBinder>& token) {
+    std::optional<int32_t> gesturePid = findMonitorPidByToken(mGestureMonitorsByDisplay, token);
+    if (gesturePid.has_value()) {
+        return gesturePid;
+    }
+    return findMonitorPidByToken(mGlobalMonitorsByDisplay, token);
+}
+
 sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) const {
     if (inputConnectionToken == nullptr) {
         return nullptr;
@@ -5208,12 +5236,15 @@
     postCommandLocked(std::move(commandEntry));
 }
 
-void InputDispatcher::onAnrLocked(const Connection& connection) {
+void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
+    if (connection == nullptr) {
+        LOG_ALWAYS_FATAL("Caller must check for nullness");
+    }
     // Since we are allowing the policy to extend the timeout, maybe the waitQueue
     // is already healthy again. Don't raise ANR in this situation
-    if (connection.waitQueue.empty()) {
+    if (connection->waitQueue.empty()) {
         ALOGI("Not raising ANR because the connection %s has recovered",
-              connection.inputChannel->getName().c_str());
+              connection->inputChannel->getName().c_str());
         return;
     }
     /**
@@ -5224,21 +5255,20 @@
      * processes the events linearly. So providing information about the oldest entry seems to be
      * most useful.
      */
-    DispatchEntry* oldestEntry = *connection.waitQueue.begin();
+    DispatchEntry* oldestEntry = *connection->waitQueue.begin();
     const nsecs_t currentWait = now() - oldestEntry->deliveryTime;
     std::string reason =
             android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",
-                                        connection.inputChannel->getName().c_str(),
+                                        connection->inputChannel->getName().c_str(),
                                         ns2ms(currentWait),
                                         oldestEntry->eventEntry->getDescription().c_str());
-    sp<IBinder> connectionToken = connection.inputChannel->getConnectionToken();
+    sp<IBinder> connectionToken = connection->inputChannel->getConnectionToken();
     updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);
 
-    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
-            &InputDispatcher::doNotifyConnectionUnresponsiveLockedInterruptible);
-    commandEntry->connectionToken = connectionToken;
-    commandEntry->reason = std::move(reason);
-    postCommandLocked(std::move(commandEntry));
+    processConnectionUnresponsiveLocked(*connection, std::move(reason));
+
+    // Stop waking up for events on this connection, it is already unresponsive
+    cancelEventsForAnrLocked(connection);
 }
 
 void InputDispatcher::onAnrLocked(std::shared_ptr<InputApplicationHandle> application) {
@@ -5323,26 +5353,34 @@
     mLock.lock();
 }
 
-void InputDispatcher::doNotifyConnectionUnresponsiveLockedInterruptible(
-        CommandEntry* commandEntry) {
+void InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {
     mLock.unlock();
 
-    mPolicy->notifyConnectionUnresponsive(commandEntry->connectionToken, commandEntry->reason);
+    mPolicy->notifyWindowUnresponsive(commandEntry->connectionToken, commandEntry->reason);
 
     mLock.lock();
-
-    // stop waking up for events in this connection, it is already not responding
-    sp<Connection> connection = getConnectionLocked(commandEntry->connectionToken);
-    if (connection == nullptr) {
-        return;
-    }
-    cancelEventsForAnrLocked(connection);
 }
 
-void InputDispatcher::doNotifyConnectionResponsiveLockedInterruptible(CommandEntry* commandEntry) {
+void InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {
     mLock.unlock();
 
-    mPolicy->notifyConnectionResponsive(commandEntry->connectionToken);
+    mPolicy->notifyMonitorUnresponsive(commandEntry->pid, commandEntry->reason);
+
+    mLock.lock();
+}
+
+void InputDispatcher::doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) {
+    mLock.unlock();
+
+    mPolicy->notifyWindowResponsive(commandEntry->connectionToken);
+
+    mLock.lock();
+}
+
+void InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) {
+    mLock.unlock();
+
+    mPolicy->notifyMonitorResponsive(commandEntry->pid);
 
     mLock.lock();
 }
@@ -5447,13 +5485,8 @@
         if (!connection->responsive) {
             connection->responsive = isConnectionResponsive(*connection);
             if (connection->responsive) {
-                // The connection was unresponsive, and now it's responsive. Tell the policy
-                // about it so that it can stop ANR.
-                std::unique_ptr<CommandEntry> connectionResponsiveCommand =
-                        std::make_unique<CommandEntry>(
-                                &InputDispatcher::doNotifyConnectionResponsiveLockedInterruptible);
-                connectionResponsiveCommand->connectionToken = connectionToken;
-                postCommandLocked(std::move(connectionResponsiveCommand));
+                // The connection was unresponsive, and now it's responsive.
+                processConnectionResponsiveLocked(*connection);
             }
         }
         traceWaitQueueLength(connection);
@@ -5469,6 +5502,82 @@
     startDispatchCycleLocked(now(), connection);
 }
 
+void InputDispatcher::sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) {
+    std::unique_ptr<CommandEntry> monitorUnresponsiveCommand = std::make_unique<CommandEntry>(
+            &InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible);
+    monitorUnresponsiveCommand->pid = pid;
+    monitorUnresponsiveCommand->reason = std::move(reason);
+    postCommandLocked(std::move(monitorUnresponsiveCommand));
+}
+
+void InputDispatcher::sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken,
+                                                          std::string reason) {
+    std::unique_ptr<CommandEntry> windowUnresponsiveCommand = std::make_unique<CommandEntry>(
+            &InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible);
+    windowUnresponsiveCommand->connectionToken = std::move(connectionToken);
+    windowUnresponsiveCommand->reason = std::move(reason);
+    postCommandLocked(std::move(windowUnresponsiveCommand));
+}
+
+void InputDispatcher::sendMonitorResponsiveCommandLocked(int32_t pid) {
+    std::unique_ptr<CommandEntry> monitorResponsiveCommand = std::make_unique<CommandEntry>(
+            &InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible);
+    monitorResponsiveCommand->pid = pid;
+    postCommandLocked(std::move(monitorResponsiveCommand));
+}
+
+void InputDispatcher::sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) {
+    std::unique_ptr<CommandEntry> windowResponsiveCommand = std::make_unique<CommandEntry>(
+            &InputDispatcher::doNotifyWindowResponsiveLockedInterruptible);
+    windowResponsiveCommand->connectionToken = std::move(connectionToken);
+    postCommandLocked(std::move(windowResponsiveCommand));
+}
+
+/**
+ * Tell the policy that a connection has become unresponsive so that it can start ANR.
+ * Check whether the connection of interest is a monitor or a window, and add the corresponding
+ * command entry to the command queue.
+ */
+void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& connection,
+                                                          std::string reason) {
+    const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();
+    if (connection.monitor) {
+        ALOGW("Monitor %s is unresponsive: %s", connection.inputChannel->getName().c_str(),
+              reason.c_str());
+        std::optional<int32_t> pid = findMonitorPidByTokenLocked(connectionToken);
+        if (!pid.has_value()) {
+            ALOGE("Could not find unresponsive monitor for connection %s",
+                  connection.inputChannel->getName().c_str());
+            return;
+        }
+        sendMonitorUnresponsiveCommandLocked(pid.value(), std::move(reason));
+        return;
+    }
+    // If not a monitor, must be a window
+    ALOGW("Window %s is unresponsive: %s", connection.inputChannel->getName().c_str(),
+          reason.c_str());
+    sendWindowUnresponsiveCommandLocked(connectionToken, std::move(reason));
+}
+
+/**
+ * Tell the policy that a connection has become responsive so that it can stop ANR.
+ */
+void InputDispatcher::processConnectionResponsiveLocked(const Connection& connection) {
+    const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();
+    if (connection.monitor) {
+        std::optional<int32_t> pid = findMonitorPidByTokenLocked(connectionToken);
+        if (!pid.has_value()) {
+            ALOGE("Could not find responsive monitor for connection %s",
+                  connection.inputChannel->getName().c_str());
+            return;
+        }
+        sendMonitorResponsiveCommandLocked(pid.value());
+        return;
+    }
+    // If not a monitor, must be a window
+    sendWindowResponsiveCommandLocked(connectionToken);
+}
+
 bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
                                                        DispatchEntry* dispatchEntry,
                                                        KeyEntry& keyEntry, bool handled) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index df0be99..c7299e9 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -233,6 +233,8 @@
     // Finds the display ID of the gesture monitor identified by the provided token.
     std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
             REQUIRES(mLock);
+    // Find a monitor pid by the provided token.
+    std::optional<int32_t> findMonitorPidByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
 
     // Input channels that will receive a copy of all input events sent to the provided display.
     std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock);
@@ -433,6 +435,34 @@
     void processNoFocusedWindowAnrLocked() REQUIRES(mLock);
 
     /**
+     * Tell policy about a window or a monitor that just became unresponsive. Starts ANR.
+     */
+    void processConnectionUnresponsiveLocked(const Connection& connection, std::string reason)
+            REQUIRES(mLock);
+    /**
+     * Tell policy about a window or a monitor that just became responsive.
+     */
+    void processConnectionResponsiveLocked(const Connection& connection) REQUIRES(mLock);
+
+    /**
+     * Post `doNotifyMonitorUnresponsiveLockedInterruptible` command.
+     */
+    void sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) REQUIRES(mLock);
+    /**
+     * Post `doNotifyWindowUnresponsiveLockedInterruptible` command.
+     */
+    void sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken, std::string reason)
+            REQUIRES(mLock);
+    /**
+     * Post `doNotifyMonitorResponsiveLockedInterruptible` command.
+     */
+    void sendMonitorResponsiveCommandLocked(int32_t pid) REQUIRES(mLock);
+    /**
+     * Post `doNotifyWindowResponsiveLockedInterruptible` command.
+     */
+    void sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) REQUIRES(mLock);
+
+    /**
      * This map will store the pending focus requests that cannot be currently processed. This can
      * happen if the window requested to be focused is not currently visible. Such a window might
      * become visible later, and these requests would be processed at that time.
@@ -577,7 +607,7 @@
                               int32_t displayId, std::string_view reason) REQUIRES(mLock);
     void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus)
             REQUIRES(mLock);
-    void onAnrLocked(const Connection& connection) REQUIRES(mLock);
+    void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
     void onAnrLocked(std::shared_ptr<InputApplicationHandle> application) REQUIRES(mLock);
     void onUntrustedTouchLocked(const std::string& obscuringPackage) REQUIRES(mLock);
     void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
@@ -592,11 +622,13 @@
             REQUIRES(mLock);
     void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+    // ANR-related callbacks - start
     void doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyConnectionUnresponsiveLockedInterruptible(CommandEntry* commandEntry)
-            REQUIRES(mLock);
-    void doNotifyConnectionResponsiveLockedInterruptible(CommandEntry* commandEntry)
-            REQUIRES(mLock);
+    void doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+    void doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+    void doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+    void doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+    // ANR-related callbacks - end
     void doNotifySensorLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index d9ec020..b976129 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -50,20 +50,31 @@
     virtual void notifyNoFocusedWindowAnr(
             const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) = 0;
 
-    /* Notifies the system that a connection just became unresponsive. This indicates that ANR
-     * should be raised for this connection. The connection is identified via token.
+    /* Notifies the system that a window just became unresponsive. This indicates that ANR
+     * should be raised for this window. The window is identified via token.
      * The string reason contains information about the input event that we haven't received
      * a response for.
      */
-    virtual void notifyConnectionUnresponsive(const sp<IBinder>& token,
-                                              const std::string& reason) = 0;
+    virtual void notifyWindowUnresponsive(const sp<IBinder>& token, const std::string& reason) = 0;
+    /* Notifies the system that a monitor just became unresponsive. This indicates that ANR
+     * should be raised for this monitor. The monitor is identified via its pid.
+     * The string reason contains information about the input event that we haven't received
+     * a response for.
+     */
+    virtual void notifyMonitorUnresponsive(int32_t pid, const std::string& reason) = 0;
 
-    /* Notifies the system that a connection just became responsive. This is only called after the
-     * connection was first marked "unresponsive". This indicates that ANR dialog (if any) should
-     * no longer should be shown to the user. The connection is eligible to cause a new ANR in the
+    /* Notifies the system that a window just became responsive. This is only called after the
+     * window was first marked "unresponsive". This indicates that ANR dialog (if any) should
+     * no longer should be shown to the user. The window is eligible to cause a new ANR in the
      * future.
      */
-    virtual void notifyConnectionResponsive(const sp<IBinder>& token) = 0;
+    virtual void notifyWindowResponsive(const sp<IBinder>& token) = 0;
+    /* Notifies the system that a monitor just became responsive. This is only called after the
+     * monitor was first marked "unresponsive". This indicates that ANR dialog (if any) should
+     * no longer should be shown to the user. The monitor is eligible to cause a new ANR in the
+     * future.
+     */
+    virtual void notifyMonitorResponsive(int32_t pid) = 0;
 
     /* Notifies the system that an input channel is unrecoverably broken. */
     virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index ac72ac4..3e6910d 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -54,10 +54,11 @@
     if (!hasEventHubDevices()) {
         return false;
     }
-    // devices are either all enabled or all disabled, so we only need to check the first
-    auto& devicePair = mDevices.begin()->second;
-    auto& contextPtr = devicePair.first;
-    return contextPtr->isDeviceEnabled();
+    // An input device composed of sub devices can be individually enabled or disabled.
+    // If any of the sub device is enabled then the input device is considered as enabled.
+    bool enabled = false;
+    for_each_subdevice([&enabled](auto& context) { enabled |= context.isDeviceEnabled(); });
+    return enabled;
 }
 
 void InputDevice::setEnabled(bool enabled, nsecs_t when) {
@@ -504,8 +505,7 @@
 }
 
 void InputDevice::notifyReset(nsecs_t when) {
-    NotifyDeviceResetArgs args(mContext->getNextId(), when, mId);
-    mContext->getListener()->notifyDeviceReset(&args);
+    mContext->notifyDeviceReset(when, mId);
 }
 
 std::optional<int32_t> InputDevice::getAssociatedDisplayId() {
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index be21ace..7c448e4 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -222,7 +222,9 @@
     }
 
     // Sensor input device is noisy, to save power disable it by default.
-    if (device->getClasses().test(InputDeviceClass::SENSOR)) {
+    // Input device is classified as SENSOR when any sub device is a SENSOR device, check Eventhub
+    // device class to disable SENSOR sub device only.
+    if (mEventHub->getDeviceClasses(eventHubId).test(InputDeviceClass::SENSOR)) {
         mEventHub->disableDevice(eventHubId);
     }
 }
@@ -337,8 +339,7 @@
     updateGlobalMetaStateLocked();
 
     // Enqueue configuration changed.
-    NotifyConfigurationChangedArgs args(mContext.getNextId(), when);
-    mQueuedListener->notifyConfigurationChanged(&args);
+    mContext.notifyConfigurationChanged(when);
 }
 
 void InputReader::refreshConfigurationLocked(uint32_t changes) {
@@ -365,9 +366,7 @@
     }
 
     if (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE) {
-        const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
-                                                   mConfig.pointerCapture);
-        mQueuedListener->notifyPointerCaptureChanged(&args);
+        mContext.notifyPointerCaptureChanged(now, mConfig.pointerCapture);
     }
 }
 
@@ -866,16 +865,64 @@
     return mReader->mPolicy.get();
 }
 
-InputListenerInterface* InputReader::ContextImpl::getListener() {
-    return mReader->mQueuedListener.get();
-}
-
 EventHubInterface* InputReader::ContextImpl::getEventHub() {
     return mReader->mEventHub.get();
 }
 
-int32_t InputReader::ContextImpl::getNextId() {
-    return mIdGenerator.nextId();
+void InputReader::ContextImpl::notifyConfigurationChanged(nsecs_t when) {
+    NotifyConfigurationChangedArgs args(mIdGenerator.nextId(), when);
+    mReader->mQueuedListener->notifyConfigurationChanged(&args);
+}
+
+void InputReader::ContextImpl::notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source,
+                                         int32_t displayId, uint32_t policyFlags, int32_t action,
+                                         int32_t flags, int32_t keyCode, int32_t scanCode,
+                                         int32_t metaState, nsecs_t downTime) {
+    NotifyKeyArgs args(mIdGenerator.nextId(), eventTime, deviceId, source, displayId, policyFlags,
+                       action, flags, keyCode, scanCode, metaState, downTime);
+    mReader->mQueuedListener->notifyKey(&args);
+}
+void InputReader::ContextImpl::notifyMotion(
+        nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
+        uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags,
+        int32_t metaState, int32_t buttonState, MotionClassification classification,
+        int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties,
+        const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
+        float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+        const std::vector<TouchVideoFrame>& videoFrames) {
+    NotifyMotionArgs args(mIdGenerator.nextId(), eventTime, deviceId, source, displayId,
+                          policyFlags, action, actionButton, flags, metaState, buttonState,
+                          classification, edgeFlags, pointerCount, pointerProperties, pointerCoords,
+                          xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime,
+                          videoFrames);
+    mReader->mQueuedListener->notifyMotion(&args);
+}
+
+void InputReader::ContextImpl::notifySensor(nsecs_t when, int32_t deviceId,
+                                            InputDeviceSensorType sensorType,
+                                            InputDeviceSensorAccuracy accuracy,
+                                            bool accuracyChanged, nsecs_t timestamp,
+                                            std::vector<float> values) {
+    NotifySensorArgs args(mIdGenerator.nextId(), when, deviceId, AINPUT_SOURCE_SENSOR, sensorType,
+                          accuracy, accuracyChanged, timestamp, std::move(values));
+    mReader->mQueuedListener->notifySensor(&args);
+}
+
+void InputReader::ContextImpl::notifySwitch(nsecs_t eventTime, uint32_t switchValues,
+                                            uint32_t switchMask) {
+    NotifySwitchArgs args(mIdGenerator.nextId(), eventTime, 0 /*policyFlags*/, switchValues,
+                          switchMask);
+    mReader->mQueuedListener->notifySwitch(&args);
+}
+
+void InputReader::ContextImpl::notifyDeviceReset(nsecs_t when, int32_t deviceId) {
+    NotifyDeviceResetArgs args(mIdGenerator.nextId(), when, deviceId);
+    mReader->mQueuedListener->notifyDeviceReset(&args);
+}
+
+void InputReader::ContextImpl::notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) {
+    const NotifyPointerCaptureChangedArgs args(mIdGenerator.nextId(), when, hasCapture);
+    mReader->mQueuedListener->notifyPointerCaptureChanged(&args);
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 48d4596..7be932a 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -127,11 +127,30 @@
         void dispatchExternalStylusState(const StylusState& outState)
                 NO_THREAD_SAFETY_ANALYSIS override;
         InputReaderPolicyInterface* getPolicy() NO_THREAD_SAFETY_ANALYSIS override;
-        InputListenerInterface* getListener() NO_THREAD_SAFETY_ANALYSIS override;
         EventHubInterface* getEventHub() NO_THREAD_SAFETY_ANALYSIS override;
-        int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override;
         void updateLedMetaState(int32_t metaState) NO_THREAD_SAFETY_ANALYSIS override;
         int32_t getLedMetaState() NO_THREAD_SAFETY_ANALYSIS override;
+
+        // Send events to InputListener interface
+        void notifyConfigurationChanged(nsecs_t when) override;
+        void notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
+                       uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
+                       int32_t scanCode, int32_t metaState, nsecs_t downTime) override;
+        void notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
+                          uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags,
+                          int32_t metaState, int32_t buttonState,
+                          MotionClassification classification, int32_t edgeFlags,
+                          uint32_t pointerCount, const PointerProperties* pointerProperties,
+                          const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
+                          float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+                          const std::vector<TouchVideoFrame>& videoFrames) override;
+        void notifySwitch(nsecs_t eventTime, uint32_t switchValues, uint32_t switchMask) override;
+        void notifySensor(nsecs_t when, int32_t deviceId, InputDeviceSensorType sensorType,
+                          InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
+                          nsecs_t timestamp, std::vector<float> values) override;
+        void notifyDeviceReset(nsecs_t when, int32_t deviceId) override;
+        void notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) override;
+
     } mContext;
 
     friend class ContextImpl;
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index dc807f7..edab312 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -55,13 +55,33 @@
     virtual void dispatchExternalStylusState(const StylusState& outState) = 0;
 
     virtual InputReaderPolicyInterface* getPolicy() = 0;
-    virtual InputListenerInterface* getListener() = 0;
     virtual EventHubInterface* getEventHub() = 0;
 
-    virtual int32_t getNextId() = 0;
-
     virtual void updateLedMetaState(int32_t metaState) = 0;
     virtual int32_t getLedMetaState() = 0;
+
+    // Send events to InputListener interface
+
+    virtual void notifyConfigurationChanged(nsecs_t when) = 0;
+    virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
+                           uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
+                           int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0;
+    virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t source,
+                              int32_t displayId, uint32_t policyFlags, int32_t action,
+                              int32_t actionButton, int32_t flags, int32_t metaState,
+                              int32_t buttonState, MotionClassification classification,
+                              int32_t edgeFlags, uint32_t pointerCount,
+                              const PointerProperties* pointerProperties,
+                              const PointerCoords* pointerCoords, float xPrecision,
+                              float yPrecision, float xCursorPosition, float yCursorPosition,
+                              nsecs_t downTime,
+                              const std::vector<TouchVideoFrame>& videoFrames) = 0;
+    virtual void notifySwitch(nsecs_t eventTime, uint32_t switchValues, uint32_t switchMask) = 0;
+    virtual void notifySensor(nsecs_t when, int32_t deviceId, InputDeviceSensorType sensorType,
+                              InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
+                              nsecs_t timestamp, std::vector<float> values) = 0;
+    virtual void notifyDeviceReset(nsecs_t when, int32_t deviceId) = 0;
+    virtual void notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 254b64b..7f7b33c 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -175,8 +175,7 @@
         }
         bumpGeneration();
         if (changes) {
-            NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
-            getListener()->notifyDeviceReset(&args);
+            getContext()->notifyDeviceReset(when, getDeviceId());
         }
     }
 
@@ -383,40 +382,35 @@
             while (!released.isEmpty()) {
                 int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
                 buttonState &= ~actionButton;
-                NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, getDeviceId(),
-                                             mSource, displayId, policyFlags,
-                                             AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
-                                             metaState, buttonState, MotionClassification::NONE,
-                                             AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                             &pointerCoords, mXPrecision, mYPrecision,
-                                             xCursorPosition, yCursorPosition, downTime,
-                                             /* videoFrames */ {});
-                getListener()->notifyMotion(&releaseArgs);
+                getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+                                           AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
+                                           metaState, buttonState, MotionClassification::NONE,
+                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                           &pointerCoords, mXPrecision, mYPrecision,
+                                           xCursorPosition, yCursorPosition, downTime,
+                                           /* videoFrames */ {});
             }
         }
 
-        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
-                              policyFlags, motionEventAction, 0, 0, metaState, currentButtonState,
-                              MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                              &pointerProperties, &pointerCoords, mXPrecision, mYPrecision,
-                              xCursorPosition, yCursorPosition, downTime,
-                              /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
+        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+                                   motionEventAction, 0, 0, metaState, currentButtonState,
+                                   MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                   &pointerProperties, &pointerCoords, mXPrecision, mYPrecision,
+                                   xCursorPosition, yCursorPosition, downTime,
+                                   /* videoFrames */ {});
 
         if (buttonsPressed) {
             BitSet32 pressed(buttonsPressed);
             while (!pressed.isEmpty()) {
                 int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
                 buttonState |= actionButton;
-                NotifyMotionArgs pressArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
-                                           displayId, policyFlags,
+                getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
                                            AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
                                            metaState, buttonState, MotionClassification::NONE,
                                            AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                            &pointerCoords, mXPrecision, mYPrecision,
                                            xCursorPosition, yCursorPosition, downTime,
                                            /* videoFrames */ {});
-                getListener()->notifyMotion(&pressArgs);
             }
         }
 
@@ -424,13 +418,12 @@
 
         // Send hover move after UP to tell the application that the mouse is hovering now.
         if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
-            NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
-                                       displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
-                                       0, metaState, currentButtonState, MotionClassification::NONE,
+            getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+                                       AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+                                       currentButtonState, MotionClassification::NONE,
                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                        &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
                                        yCursorPosition, downTime, /* videoFrames */ {});
-            getListener()->notifyMotion(&hoverArgs);
         }
 
         // Send scroll events.
@@ -438,13 +431,12 @@
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-            NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
-                                        displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
-                                        metaState, currentButtonState, MotionClassification::NONE,
-                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                        &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
-                                        yCursorPosition, downTime, /* videoFrames */ {});
-            getListener()->notifyMotion(&scrollArgs);
+            getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+                                       AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+                                       currentButtonState, MotionClassification::NONE,
+                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                       &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
+                                       yCursorPosition, downTime, /* videoFrames */ {});
         }
     }
 
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index bd64d8d..6ca6ec9 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -19,7 +19,6 @@
 
 #include "EventHub.h"
 #include "InputDevice.h"
-#include "InputListener.h"
 #include "InputReaderContext.h"
 #include "StylusState.h"
 #include "VibrationElement.h"
@@ -48,7 +47,6 @@
     inline const std::string getDeviceName() { return mDeviceContext.getName(); }
     inline InputReaderContext* getContext() { return mDeviceContext.getContext(); }
     inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); }
-    inline InputListenerInterface* getListener() { return getContext()->getListener(); }
 
     virtual uint32_t getSources() = 0;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index abd8aa9..ac4669c 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -337,13 +337,12 @@
     // TODO: Use the input device configuration to control this behavior more finely.
     uint32_t policyFlags = 0;
 
-    NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_JOYSTICK,
-                          ADISPLAY_ID_NONE, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
-                          buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                          &pointerProperties, &pointerCoords, 0, 0,
-                          AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                          AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
-    getListener()->notifyMotion(&args);
+    getContext()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE,
+                               policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
+                               MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                               &pointerProperties, &pointerCoords, 0, 0,
+                               AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                               AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
 }
 
 void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis,
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 8b9f235..03d7405 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -350,10 +350,9 @@
         policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
     }
 
-    NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, getDisplayId(),
-                       policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
-                       AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
-    getListener()->notifyKey(&args);
+    getContext()->notifyKey(when, getDeviceId(), mSource, getDisplayId(), policyFlags,
+                            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
+                            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
 }
 
 ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 594ff42..3f8a364 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -121,13 +121,12 @@
         int32_t metaState = getContext()->getGlobalMetaState();
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
 
-        NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
-                                    displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
-                                    metaState, /* buttonState */ 0, MotionClassification::NONE,
-                                    AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                    &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                    AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
-        getListener()->notifyMotion(&scrollArgs);
+        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+                                   AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+                                   /* buttonState */ 0, MotionClassification::NONE,
+                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                   &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                   AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
     }
 
     mRotaryEncoderScrollAccumulator.finishSync();
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index 7ac2dec..68c1e40 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -405,13 +405,10 @@
             // Convert to Android unit
             convertFromLinuxToAndroid(values, sensorType);
             // Notify dispatcher for sensor event
-            NotifySensorArgs args(getContext()->getNextId(), when, getDeviceId(),
-                                  AINPUT_SOURCE_SENSOR, sensorType, sensor.sensorInfo.accuracy,
-                                  sensor.accuracy !=
-                                          sensor.sensorInfo.accuracy /* accuracyChanged */,
-                                  timestamp /* hwTimestamp */, values);
-
-            getListener()->notifySensor(&args);
+            getContext()->notifySensor(when, getDeviceId(), sensorType, sensor.sensorInfo.accuracy,
+                                       sensor.accuracy !=
+                                               sensor.sensorInfo.accuracy /* accuracyChanged */,
+                                       timestamp /* hwTimestamp */, std::move(values));
             sensor.lastSampleTimeNs = timestamp;
             sensor.accuracy = sensor.sensorInfo.accuracy;
         }
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index 4f73681..07de244 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -56,10 +56,7 @@
 void SwitchInputMapper::sync(nsecs_t when) {
     if (mUpdatedSwitchMask) {
         uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
-        NotifySwitchArgs args(getContext()->getNextId(), when, 0 /*policyFlags*/,
-                              updatedSwitchValues, mUpdatedSwitchMask);
-        getListener()->notifySwitch(&args);
-
+        getContext()->notifySwitch(when, updatedSwitchValues, mUpdatedSwitchMask);
         mUpdatedSwitchMask = 0;
     }
 }
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index a86443d..ff6341f 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -66,9 +66,8 @@
          (currentButtonState & buttonState)) ||
         (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
          !(currentButtonState & buttonState))) {
-        NotifyKeyArgs args(context->getNextId(), when, deviceId, source, displayId, policyFlags,
-                           action, 0, keyCode, 0, context->getGlobalMetaState(), when);
-        context->getListener()->notifyKey(&args);
+        context->notifyKey(when, deviceId, source, displayId, policyFlags, action, 0 /*flags*/,
+                           keyCode, 0 /*scanCode*/, context->getGlobalMetaState(), when);
     }
 }
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index ce12c27..fb30b88 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -397,8 +397,7 @@
     if (changes && resetNeeded) {
         // Send reset, unless this is the first time the device has been configured,
         // in which case the reader will call reset itself after all mappers are ready.
-        NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
-        getListener()->notifyDeviceReset(&args);
+        getContext()->notifyDeviceReset(when, getDeviceId());
     }
 }
 
@@ -1829,10 +1828,9 @@
     int32_t metaState = getContext()->getGlobalMetaState();
     policyFlags |= POLICY_FLAG_VIRTUAL;
 
-    NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
-                       mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode,
-                       scanCode, metaState, downTime);
-    getListener()->notifyKey(&args);
+    getContext()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, mViewport.displayId,
+                            policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode,
+                            metaState, downTime);
 }
 
 void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) {
@@ -2508,12 +2506,11 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
 
         const int32_t displayId = mPointerController->getDisplayId();
-        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
-                              policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                              buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
-                              1, &pointerProperties, &pointerCoords, 0, 0, x, y,
-                              mPointerGesture.downTime, /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
+        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+                                   AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, buttonState,
+                                   MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                   &pointerProperties, &pointerCoords, 0, 0, x, y,
+                                   mPointerGesture.downTime, /* videoFrames */ {});
     }
 
     // Update state.
@@ -3428,28 +3425,28 @@
         mPointerSimple.down = false;
 
         // Send up.
-        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
-                              policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
-                              mLastRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
-                              &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
-                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
-                              /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
+        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+                                   AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
+                                   mLastRawState.buttonState, MotionClassification::NONE,
+                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+                                   &mPointerSimple.lastCoords, mOrientedXPrecision,
+                                   mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                                   mPointerSimple.downTime,
+                                   /* videoFrames */ {});
     }
 
     if (mPointerSimple.hovering && !hovering) {
         mPointerSimple.hovering = false;
 
         // Send hover exit.
-        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
-                              policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
-                              mLastRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
-                              &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
-                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
-                              /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
+        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+                                   AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
+                                   mLastRawState.buttonState, MotionClassification::NONE,
+                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+                                   &mPointerSimple.lastCoords, mOrientedXPrecision,
+                                   mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                                   mPointerSimple.downTime,
+                                   /* videoFrames */ {});
     }
 
     if (down) {
@@ -3458,25 +3455,24 @@
             mPointerSimple.downTime = when;
 
             // Send down.
-            NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource,
-                                  displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0,
-                                  metaState, mCurrentRawState.buttonState,
-                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
-                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
-            getListener()->notifyMotion(&args);
+            getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+                                       AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState,
+                                       mCurrentRawState.buttonState, MotionClassification::NONE,
+                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                       &mPointerSimple.currentProperties,
+                                       &mPointerSimple.currentCoords, mOrientedXPrecision,
+                                       mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                                       mPointerSimple.downTime, /* videoFrames */ {});
         }
 
         // Send move.
-        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
-                              policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
-                              mCurrentRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
-                              &mPointerSimple.currentCoords, mOrientedXPrecision,
-                              mOrientedYPrecision, xCursorPosition, yCursorPosition,
-                              mPointerSimple.downTime, /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
+        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+                                   AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
+                                   mCurrentRawState.buttonState, MotionClassification::NONE,
+                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                   &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+                                   mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                   yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
     }
 
     if (hovering) {
@@ -3484,25 +3480,24 @@
             mPointerSimple.hovering = true;
 
             // Send hover enter.
-            NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource,
-                                  displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
-                                  metaState, mCurrentRawState.buttonState,
-                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
-                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
-            getListener()->notifyMotion(&args);
+            getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+                                       AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState,
+                                       mCurrentRawState.buttonState, MotionClassification::NONE,
+                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                       &mPointerSimple.currentProperties,
+                                       &mPointerSimple.currentCoords, mOrientedXPrecision,
+                                       mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                                       mPointerSimple.downTime, /* videoFrames */ {});
         }
 
         // Send hover move.
-        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
-                              policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                              mCurrentRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
-                              &mPointerSimple.currentCoords, mOrientedXPrecision,
-                              mOrientedYPrecision, xCursorPosition, yCursorPosition,
-                              mPointerSimple.downTime, /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
+        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+                                   AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+                                   mCurrentRawState.buttonState, MotionClassification::NONE,
+                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                   &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+                                   mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                   yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
     }
 
     if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
@@ -3517,14 +3512,14 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
-                              policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
-                              mCurrentRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
-                              &pointerCoords, mOrientedXPrecision, mOrientedYPrecision,
-                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
-                              /* videoFrames */ {});
-        getListener()->notifyMotion(&args);
+        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+                                   AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+                                   mCurrentRawState.buttonState, MotionClassification::NONE,
+                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                   &mPointerSimple.currentProperties, &pointerCoords,
+                                   mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                   yCursorPosition, mPointerSimple.downTime,
+                                   /* videoFrames */ {});
     }
 
     // Save state.
@@ -3595,12 +3590,11 @@
     std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
     std::for_each(frames.begin(), frames.end(),
                   [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
-    NotifyMotionArgs args(getContext()->getNextId(), when, deviceId, source, displayId, policyFlags,
-                          action, actionButton, flags, metaState, buttonState,
-                          MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
-                          pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
-                          downTime, std::move(frames));
-    getListener()->notifyMotion(&args);
+    getContext()->notifyMotion(when, deviceId, source, displayId, policyFlags, action, actionButton,
+                               flags, metaState, buttonState, MotionClassification::NONE, edgeFlags,
+                               pointerCount, pointerProperties, pointerCoords, xPrecision,
+                               yPrecision, xCursorPosition, yCursorPosition, downTime,
+                               std::move(frames));
 }
 
 bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties,
@@ -3644,6 +3638,9 @@
     const float xScaled = float(x - mRawPointerAxes.x.minValue) * mXScale;
     const float yScaled = float(y - mRawPointerAxes.y.minValue) * mYScale;
 
+    const float xScaledMax = float(mRawPointerAxes.x.maxValue - x) * mXScale;
+    const float yScaledMax = float(mRawPointerAxes.y.maxValue - y) * mYScale;
+
     // Rotate to surface coordinate.
     // 0 - no swap and reverse.
     // 90 - swap x/y and reverse y.
@@ -3655,16 +3652,16 @@
             y = yScaled + mYTranslate;
             break;
         case DISPLAY_ORIENTATION_90:
-            y = mSurfaceRight - xScaled;
+            y = xScaledMax - (mRawSurfaceWidth - mSurfaceRight);
             x = yScaled + mYTranslate;
             break;
         case DISPLAY_ORIENTATION_180:
-            x = mSurfaceRight - xScaled;
-            y = mSurfaceBottom - yScaled;
+            x = xScaledMax - (mRawSurfaceWidth - mSurfaceRight);
+            y = yScaledMax - (mRawSurfaceHeight - mSurfaceBottom);
             break;
         case DISPLAY_ORIENTATION_270:
             y = xScaled + mXTranslate;
-            x = mSurfaceBottom - yScaled;
+            x = yScaledMax - (mRawSurfaceHeight - mSurfaceBottom);
             break;
         default:
             assert(false);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 6dbc88f..c7e05eb 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -140,27 +140,49 @@
         ASSERT_EQ(expectedApplication, application);
     }
 
-    void assertNotifyConnectionUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
-                                                     const sp<IBinder>& expectedConnectionToken) {
-        sp<IBinder> connectionToken = getUnresponsiveConnectionToken(timeout);
+    void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
+                                                 const sp<IBinder>& expectedConnectionToken) {
+        sp<IBinder> connectionToken = getUnresponsiveWindowToken(timeout);
         ASSERT_EQ(expectedConnectionToken, connectionToken);
     }
 
-    void assertNotifyConnectionResponsiveWasCalled(const sp<IBinder>& expectedConnectionToken) {
-        sp<IBinder> connectionToken = getResponsiveConnectionToken();
+    void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedConnectionToken) {
+        sp<IBinder> connectionToken = getResponsiveWindowToken();
         ASSERT_EQ(expectedConnectionToken, connectionToken);
     }
 
-    sp<IBinder> getUnresponsiveConnectionToken(std::chrono::nanoseconds timeout) {
-        std::unique_lock lock(mLock);
-        android::base::ScopedLockAssertion assumeLocked(mLock);
-        return getAnrTokenLockedInterruptible(timeout, mAnrConnectionTokens, lock);
+    void assertNotifyMonitorUnresponsiveWasCalled(std::chrono::nanoseconds timeout) {
+        int32_t pid = getUnresponsiveMonitorPid(timeout);
+        ASSERT_EQ(MONITOR_PID, pid);
     }
 
-    sp<IBinder> getResponsiveConnectionToken() {
+    void assertNotifyMonitorResponsiveWasCalled() {
+        int32_t pid = getResponsiveMonitorPid();
+        ASSERT_EQ(MONITOR_PID, pid);
+    }
+
+    sp<IBinder> getUnresponsiveWindowToken(std::chrono::nanoseconds timeout) {
         std::unique_lock lock(mLock);
         android::base::ScopedLockAssertion assumeLocked(mLock);
-        return getAnrTokenLockedInterruptible(0s, mResponsiveConnectionTokens, lock);
+        return getAnrTokenLockedInterruptible(timeout, mAnrWindowTokens, lock);
+    }
+
+    sp<IBinder> getResponsiveWindowToken() {
+        std::unique_lock lock(mLock);
+        android::base::ScopedLockAssertion assumeLocked(mLock);
+        return getAnrTokenLockedInterruptible(0s, mResponsiveWindowTokens, lock);
+    }
+
+    int32_t getUnresponsiveMonitorPid(std::chrono::nanoseconds timeout) {
+        std::unique_lock lock(mLock);
+        android::base::ScopedLockAssertion assumeLocked(mLock);
+        return getAnrTokenLockedInterruptible(timeout, mAnrMonitorPids, lock);
+    }
+
+    int32_t getResponsiveMonitorPid() {
+        std::unique_lock lock(mLock);
+        android::base::ScopedLockAssertion assumeLocked(mLock);
+        return getAnrTokenLockedInterruptible(0s, mResponsiveMonitorPids, lock);
     }
 
     // All three ANR-related callbacks behave the same way, so we use this generic function to wait
@@ -182,7 +204,7 @@
         const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
         if (storage.empty()) {
             ADD_FAILURE() << "Did not receive the ANR callback";
-            return nullptr;
+            return {};
         }
         // Ensure that the ANR didn't get raised too early. We can't be too strict here because
         // the dispatcher started counting before this function was called
@@ -201,10 +223,14 @@
     void assertNotifyAnrWasNotCalled() {
         std::scoped_lock lock(mLock);
         ASSERT_TRUE(mAnrApplications.empty());
-        ASSERT_TRUE(mAnrConnectionTokens.empty());
-        ASSERT_TRUE(mResponsiveConnectionTokens.empty())
+        ASSERT_TRUE(mAnrWindowTokens.empty());
+        ASSERT_TRUE(mAnrMonitorPids.empty());
+        ASSERT_TRUE(mResponsiveWindowTokens.empty())
                 << "ANR was not called, but please also consume the 'connection is responsive' "
                    "signal";
+        ASSERT_TRUE(mResponsiveMonitorPids.empty())
+                << "Monitor ANR was not called, but please also consume the 'monitor is responsive'"
+                   " signal";
     }
 
     void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
@@ -251,8 +277,10 @@
 
     // ANR handling
     std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
-    std::queue<sp<IBinder>> mAnrConnectionTokens GUARDED_BY(mLock);
-    std::queue<sp<IBinder>> mResponsiveConnectionTokens GUARDED_BY(mLock);
+    std::queue<sp<IBinder>> mAnrWindowTokens GUARDED_BY(mLock);
+    std::queue<sp<IBinder>> mResponsiveWindowTokens GUARDED_BY(mLock);
+    std::queue<int32_t> mAnrMonitorPids GUARDED_BY(mLock);
+    std::queue<int32_t> mResponsiveMonitorPids GUARDED_BY(mLock);
     std::condition_variable mNotifyAnr;
 
     void notifyConfigurationChanged(nsecs_t when) override {
@@ -260,16 +288,27 @@
         mConfigurationChangedTime = when;
     }
 
-    void notifyConnectionUnresponsive(const sp<IBinder>& connectionToken,
-                                      const std::string&) override {
+    void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, const std::string&) override {
         std::scoped_lock lock(mLock);
-        mAnrConnectionTokens.push(connectionToken);
+        mAnrWindowTokens.push(connectionToken);
         mNotifyAnr.notify_all();
     }
 
-    void notifyConnectionResponsive(const sp<IBinder>& connectionToken) override {
+    void notifyMonitorUnresponsive(int32_t pid, const std::string&) override {
         std::scoped_lock lock(mLock);
-        mResponsiveConnectionTokens.push(connectionToken);
+        mAnrMonitorPids.push(pid);
+        mNotifyAnr.notify_all();
+    }
+
+    void notifyWindowResponsive(const sp<IBinder>& connectionToken) override {
+        std::scoped_lock lock(mLock);
+        mResponsiveWindowTokens.push(connectionToken);
+        mNotifyAnr.notify_all();
+    }
+
+    void notifyMonitorResponsive(int32_t pid) override {
+        std::scoped_lock lock(mLock);
+        mResponsiveMonitorPids.push(pid);
         mNotifyAnr.notify_all();
     }
 
@@ -1962,11 +2001,10 @@
     std::optional<uint32_t> consumeSeq = monitor.receiveEvent();
     ASSERT_TRUE(consumeSeq);
 
-    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(DISPATCHING_TIMEOUT,
-                                                             monitor.getToken());
+    mFakePolicy->assertNotifyMonitorUnresponsiveWasCalled(DISPATCHING_TIMEOUT);
     monitor.finishEvent(*consumeSeq);
     ASSERT_TRUE(mDispatcher->waitForIdle());
-    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(monitor.getToken());
+    mFakePolicy->assertNotifyMonitorResponsiveWasCalled();
 }
 
 TEST_F(InputDispatcherTest, TestMoveEvent) {
@@ -3194,13 +3232,13 @@
     std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
     ASSERT_TRUE(sequenceNum);
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
 
     mWindow->finishEvent(*sequenceNum);
     mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
                           ADISPLAY_ID_DEFAULT, 0 /*flags*/);
     ASSERT_TRUE(mDispatcher->waitForIdle());
-    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
 }
 
 // Send a key to the app and have the app not respond right away.
@@ -3210,7 +3248,7 @@
     std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
     ASSERT_TRUE(sequenceNum);
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
     ASSERT_TRUE(mDispatcher->waitForIdle());
 }
 
@@ -3315,7 +3353,7 @@
     // We have now sent down and up. Let's consume first event and then ANR on the second.
     mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
 }
 
 // If an app is not responding to a key event, gesture monitors should continue to receive
@@ -3332,7 +3370,7 @@
 
     // Stuck on the ACTION_UP
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
 
     // New tap will go to the gesture monitor, but not to the window
     tapOnWindow();
@@ -3341,7 +3379,7 @@
 
     mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion
     mDispatcher->waitForIdle();
-    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
     mWindow->assertNoEvents();
     monitor.assertNoEvents();
 }
@@ -3360,7 +3398,7 @@
     mWindow->consumeMotionDown();
     // Stuck on the ACTION_UP
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
 
     // New tap will go to the gesture monitor, but not to the window
     tapOnWindow();
@@ -3369,7 +3407,7 @@
 
     mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion
     mDispatcher->waitForIdle();
-    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
     mWindow->assertNoEvents();
     monitor.assertNoEvents();
 }
@@ -3386,19 +3424,19 @@
     mWindow->consumeMotionDown();
     // Block on ACTION_UP
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
     mWindow->consumeMotionUp(); // Now the connection should be healthy again
     mDispatcher->waitForIdle();
-    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
     mWindow->assertNoEvents();
 
     tapOnWindow();
     mWindow->consumeMotionDown();
-    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken());
     mWindow->consumeMotionUp();
 
     mDispatcher->waitForIdle();
-    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
     mFakePolicy->assertNotifyAnrWasNotCalled();
     mWindow->assertNoEvents();
 }
@@ -3411,7 +3449,7 @@
                                WINDOW_LOCATION));
 
     const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(windowTimeout, mWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(windowTimeout, mWindow->getToken());
     std::this_thread::sleep_for(windowTimeout);
     // 'notifyConnectionUnresponsive' should only be called once per connection
     mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -3421,7 +3459,7 @@
                           ADISPLAY_ID_DEFAULT, 0 /*flags*/);
     mWindow->assertNoEvents();
     mDispatcher->waitForIdle();
-    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken());
     mFakePolicy->assertNotifyAnrWasNotCalled();
 }
 
@@ -3589,7 +3627,7 @@
 
     const std::chrono::duration timeout =
             mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
     // Because we injected two DOWN events in a row, CANCEL is enqueued for the first event
     // sequence to make it consistent
     mFocusedWindow->consumeMotionCancel();
@@ -3600,7 +3638,7 @@
     mFocusedWindow->assertNoEvents();
     mUnfocusedWindow->assertNoEvents();
     ASSERT_TRUE(mDispatcher->waitForIdle());
-    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mFocusedWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken());
     mFakePolicy->assertNotifyAnrWasNotCalled();
 }
 
@@ -3614,8 +3652,8 @@
 
     tapOnFocusedWindow();
     // we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window
-    sp<IBinder> anrConnectionToken1 = mFakePolicy->getUnresponsiveConnectionToken(10ms);
-    sp<IBinder> anrConnectionToken2 = mFakePolicy->getUnresponsiveConnectionToken(0ms);
+    sp<IBinder> anrConnectionToken1 = mFakePolicy->getUnresponsiveWindowToken(10ms);
+    sp<IBinder> anrConnectionToken2 = mFakePolicy->getUnresponsiveWindowToken(0ms);
 
     // We don't know which window will ANR first. But both of them should happen eventually.
     ASSERT_TRUE(mFocusedWindow->getToken() == anrConnectionToken1 ||
@@ -3630,8 +3668,8 @@
     mFocusedWindow->consumeMotionUp();
     mUnfocusedWindow->consumeMotionOutside();
 
-    sp<IBinder> responsiveToken1 = mFakePolicy->getResponsiveConnectionToken();
-    sp<IBinder> responsiveToken2 = mFakePolicy->getResponsiveConnectionToken();
+    sp<IBinder> responsiveToken1 = mFakePolicy->getResponsiveWindowToken();
+    sp<IBinder> responsiveToken2 = mFakePolicy->getResponsiveWindowToken();
 
     // Both applications should be marked as responsive, in any order
     ASSERT_TRUE(mFocusedWindow->getToken() == responsiveToken1 ||
@@ -3655,7 +3693,7 @@
     ASSERT_TRUE(upEventSequenceNum);
     const std::chrono::duration timeout =
             mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
 
     // Tap once again
     // We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success
@@ -3676,7 +3714,7 @@
     // The second tap did not go to the focused window
     mFocusedWindow->assertNoEvents();
     // Since all events are finished, connection should be deemed healthy again
-    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mFocusedWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken());
     mFakePolicy->assertNotifyAnrWasNotCalled();
 }
 
@@ -3788,7 +3826,7 @@
 
     const std::chrono::duration timeout =
             mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
 
     mUnfocusedWindow->consumeMotionDown();
     mFocusedWindow->consumeMotionDown();
@@ -3807,7 +3845,7 @@
         ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionEvent.getAction());
     }
     ASSERT_TRUE(mDispatcher->waitForIdle());
-    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mFocusedWindow->getToken());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken());
 
     mUnfocusedWindow->assertNoEvents();
     mFocusedWindow->assertNoEvents();
@@ -4172,6 +4210,7 @@
     notifyPointerCaptureChanged(true);
 
     // Ensure that Pointer Capture is disabled.
+    mFakePolicy->waitForSetPointerCapture(false);
     mWindow->consumeCaptureEvent(false);
     mWindow->assertNoEvents();
 }
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 23f3026..91d5864 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1481,6 +1481,32 @@
     ASSERT_EQ(1U, mReader->getInputDevices().size());
 }
 
+TEST_F(InputReaderTest, GetMergedInputDevicesEnabled) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
+    // Add two subdevices to device
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
+    // Must add at least one mapper or the device will be ignored!
+    device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
+    device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+
+    // Push same device instance for next device to be added, so they'll have same identifier.
+    mReader->pushNextDevice(device);
+    mReader->pushNextDevice(device);
+    // Sensor device is initially disabled
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1",
+                                      InputDeviceClass::KEYBOARD | InputDeviceClass::SENSOR,
+                                      nullptr));
+    // Device is disabled because the only sub device is a sensor device and disabled initially.
+    ASSERT_FALSE(mFakeEventHub->isDeviceEnabled(eventHubIds[0]));
+    ASSERT_FALSE(device->isEnabled());
+    ASSERT_NO_FATAL_FAILURE(
+            addDevice(eventHubIds[1], "fake2", InputDeviceClass::KEYBOARD, nullptr));
+    // The merged device is enabled if any sub device is enabled
+    ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[1]));
+    ASSERT_TRUE(device->isEnabled());
+}
+
 TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) {
     constexpr int32_t deviceId = END_RESERVED_ID + 1000;
     constexpr Flags<InputDeviceClass> deviceClass(InputDeviceClass::KEYBOARD);
@@ -2721,6 +2747,7 @@
     ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::ACCELEROMETER,
                                     std::chrono::microseconds(10000),
                                     std::chrono::microseconds(0)));
+    ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID));
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, 20000);
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, -20000);
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Z, 40000);
@@ -2750,6 +2777,7 @@
     ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::GYROSCOPE,
                                     std::chrono::microseconds(10000),
                                     std::chrono::microseconds(0)));
+    ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID));
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RX, 20000);
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RY, -20000);
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RZ, 40000);
@@ -8017,6 +8045,34 @@
     processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
 }
 
+TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_Corner) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    const int32_t x = 0;
+    const int32_t y = 0;
+
+    const int32_t xExpected = x;
+    const int32_t yExpected = y;
+    processPositionAndVerify(mapper, x - 1, y, x, y, xExpected, yExpected);
+
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_90);
+    // expect x/y = swap x/y then reverse y.
+    const int32_t xExpected90 = y;
+    const int32_t yExpected90 = DISPLAY_WIDTH - 1;
+    processPositionAndVerify(mapper, x - 1, y, x, y, xExpected90, yExpected90);
+
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_270);
+    // expect x/y = swap x/y then reverse x.
+    const int32_t xExpected270 = DISPLAY_HEIGHT - 1;
+    const int32_t yExpected270 = x;
+    processPositionAndVerify(mapper, x - 1, y, x, y, xExpected270, yExpected270);
+}
+
 TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) {
     // we need a pointer controller for mouse mode of touchpad (start pointer at 0,0)
     std::shared_ptr<FakePointerController> fakePointerController =
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
index 4a711ca..2f32827 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -114,14 +114,15 @@
 
 HalResult 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 (boost > Boost::DISPLAY_UPDATE_IMMINENT ||
-        mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::OFF) {
+    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;
     }
 
-    if (mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) {
+    if (mBoostSupportedArray[idx] == HalSupport::UNKNOWN) {
         bool isSupported = false;
         auto isSupportedRet = mHandle->isBoostSupported(boost, &isSupported);
         if (!isSupportedRet.isOk()) {
@@ -130,8 +131,7 @@
             return HalResult::FAILED;
         }
 
-        mBoostSupportedArray[static_cast<int32_t>(boost)] =
-                isSupported ? HalSupport::ON : HalSupport::OFF;
+        mBoostSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
         if (!isSupported) {
             ALOGV("Skipped setBoost %s because Power HAL doesn't support it",
                   toString(boost).c_str());
@@ -145,14 +145,15 @@
 
 HalResult 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 (mode > Mode::DISPLAY_INACTIVE ||
-        mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::OFF) {
+    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;
     }
 
-    if (mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) {
+    if (mModeSupportedArray[idx] == HalSupport::UNKNOWN) {
         bool isSupported = false;
         auto isSupportedRet = mHandle->isModeSupported(mode, &isSupported);
         if (!isSupportedRet.isOk()) {
@@ -161,8 +162,7 @@
             return HalResult::FAILED;
         }
 
-        mModeSupportedArray[static_cast<int32_t>(mode)] =
-                isSupported ? HalSupport::ON : HalSupport::OFF;
+        mModeSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
         if (!isSupported) {
             ALOGV("Skipped setMode %s because Power HAL doesn't support it",
                   toString(mode).c_str());
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 6561707..424a8b3 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -389,33 +389,58 @@
     mBufferInfo.mFrameLatencyNeeded = true;
 }
 
+bool BufferLayer::shouldPresentNow(nsecs_t expectedPresentTime) const {
+    // If this is not a valid vsync for the layer's uid, return and try again later
+    const bool isVsyncValidForUid =
+            mFlinger->mScheduler->isVsyncValid(expectedPresentTime, mOwnerUid);
+    if (!isVsyncValidForUid) {
+        ATRACE_NAME("!isVsyncValidForUid");
+        return false;
+    }
+
+    // AutoRefresh layers and sideband streams should always be presented
+    if (getSidebandStreamChanged() || getAutoRefresh()) {
+        return true;
+    }
+
+    // If the next planned present time is not current, return and try again later
+    if (frameIsEarly(expectedPresentTime)) {
+        ATRACE_NAME("frameIsEarly()");
+        return false;
+    }
+
+    // If this layer doesn't have a frame is shouldn't be presented
+    if (!hasFrameUpdate()) {
+        return false;
+    }
+
+    // Defer to the derived class to decide whether the next buffer is due for
+    // presentation.
+    return isBufferDue(expectedPresentTime);
+}
+
 bool BufferLayer::frameIsEarly(nsecs_t expectedPresentTime) const {
     // TODO(b/169901895): kEarlyLatchVsyncThreshold should be based on the
     // vsync period. We can do this change as soon as ag/13100772 is merged.
     constexpr static std::chrono::nanoseconds kEarlyLatchVsyncThreshold = 5ms;
 
     const auto presentTime = nextPredictedPresentTime();
-    if (std::abs(presentTime - expectedPresentTime) >= kEarlyLatchMaxThreshold.count()) {
+    if (!presentTime.has_value()) {
         return false;
     }
 
-    return presentTime >= expectedPresentTime &&
-            presentTime - expectedPresentTime >= kEarlyLatchVsyncThreshold.count();
+    if (std::abs(*presentTime - expectedPresentTime) >= kEarlyLatchMaxThreshold.count()) {
+        return false;
+    }
+
+    return *presentTime >= expectedPresentTime &&
+            *presentTime - expectedPresentTime >= kEarlyLatchVsyncThreshold.count();
 }
 
 bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
                               nsecs_t expectedPresentTime) {
     ATRACE_CALL();
 
-    // If this is not a valid vsync for the layer's uid, return and try again later
-    const bool isVsyncValidForUid =
-            mFlinger->mScheduler->isVsyncValid(expectedPresentTime, mOwnerUid);
-    if (!isVsyncValidForUid) {
-        ATRACE_NAME("!isVsyncValidForUid");
-        mFlinger->setTransactionFlags(eTraversalNeeded);
-        return false;
-    }
-
     bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
 
     if (refreshRequired) {
@@ -435,12 +460,6 @@
         return false;
     }
 
-    if (frameIsEarly(expectedPresentTime)) {
-        ATRACE_NAME("frameIsEarly()");
-        mFlinger->signalLayerUpdate();
-        return false;
-    }
-
     // If the head buffer's acquire fence hasn't signaled yet, return and
     // try again later
     if (!fenceHasSignaled()) {
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 5cd9a7c..2118f4a 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -175,12 +175,24 @@
     void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
 
     // Transform hint provided to the producer. This must be accessed holding
-    /// the mStateLock.
+    // the mStateLock.
     ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
 
     bool getAutoRefresh() const { return mAutoRefresh; }
     bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
 
+    // Returns true if the next buffer should be presented at the expected present time
+    bool shouldPresentNow(nsecs_t expectedPresentTime) const final;
+
+    // Returns true if the next buffer should be presented at the expected present time,
+    // overridden by BufferStateLayer and BufferQueueLayer for implementation
+    // specific logic
+    virtual bool isBufferDue(nsecs_t /*expectedPresentTime*/) const = 0;
+
+    // Returns true if the next frame is considered too early to present
+    // at the given expectedPresentTime
+    bool frameIsEarly(nsecs_t expectedPresentTime) const;
+
     std::atomic<bool> mAutoRefresh{false};
     std::atomic<bool> mSidebandStreamChanged{false};
 
@@ -225,13 +237,8 @@
 
     FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
 
-    // Returns true if the next frame is considered too early to present
-    // at the given expectedPresentTime
-    bool frameIsEarly(nsecs_t expectedPresentTime) const;
-
-    // Returns the predicted present time of the next frame if available or
-    // 0 otherwise.
-    virtual nsecs_t nextPredictedPresentTime() const = 0;
+    // Returns the predicted present time of the next frame if available
+    virtual std::optional<nsecs_t> nextPredictedPresentTime() const = 0;
 
     // The amount of time SF can delay a frame if it is considered early based
     // on the VsyncModulator::VsyncConfig::appWorkDuration
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 04cec4f..32e6b10 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -100,15 +100,7 @@
     return mQueuedFrames;
 }
 
-bool BufferQueueLayer::shouldPresentNow(nsecs_t expectedPresentTime) const {
-    if (getSidebandStreamChanged() || getAutoRefresh()) {
-        return true;
-    }
-
-    if (!hasFrameUpdate()) {
-        return false;
-    }
-
+bool BufferQueueLayer::isBufferDue(nsecs_t expectedPresentTime) const {
     Mutex::Autolock lock(mQueueItemLock);
 
     const int64_t addedTime = mQueueItems[0].item.mTimestamp;
@@ -223,16 +215,16 @@
     return mQueuedFrames > 0;
 }
 
-nsecs_t BufferQueueLayer::nextPredictedPresentTime() const {
+std::optional<nsecs_t> BufferQueueLayer::nextPredictedPresentTime() const {
     Mutex::Autolock lock(mQueueItemLock);
     if (mQueueItems.empty()) {
-        return 0;
+        return std::nullopt;
     }
 
     const auto& bufferData = mQueueItems[0];
 
     if (!bufferData.item.mIsAutoTimestamp || !bufferData.surfaceFrame) {
-        return 0;
+        return std::nullopt;
     }
 
     return bufferData.surfaceFrame->getPredictions().presentTime;
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 2347d8c..0e8fdbe 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -53,7 +53,8 @@
 
     int32_t getQueuedFrameCount() const override;
 
-    bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
+    // Returns true if the next buffer should be presented at the expected present time
+    bool isBufferDue(nsecs_t expectedPresentTime) const override;
 
     // Implements BufferLayer.
     bool fenceHasSignaled() const override;
@@ -116,7 +117,7 @@
     // Temporary - Used only for LEGACY camera mode.
     uint32_t getProducerStickyTransform() const;
 
-    nsecs_t nextPredictedPresentTime() const override;
+    std::optional<nsecs_t> nextPredictedPresentTime() const override;
 
     sp<BufferLayerConsumer> mConsumer;
     sp<IGraphicBufferProducer> mProducer;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index bca1c69..0cc15c2 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -212,14 +212,6 @@
     }
 }
 
-bool BufferStateLayer::shouldPresentNow(nsecs_t /*expectedPresentTime*/) const {
-    if (getSidebandStreamChanged() || getAutoRefresh()) {
-        return true;
-    }
-
-    return hasFrameUpdate();
-}
-
 bool BufferStateLayer::willPresentCurrentTransaction() const {
     // Returns true if the most recent Transaction applied to CurrentState will be presented.
     return (getSidebandStreamChanged() || getAutoRefresh() ||
@@ -343,7 +335,8 @@
 
 bool BufferStateLayer::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) {
+                                 const client_cache_t& clientCacheId, uint64_t frameNumber,
+                                 std::optional<nsecs_t> /* dequeueTime */) {
     ATRACE_CALL();
 
     if (mCurrentState.buffer) {
@@ -602,9 +595,9 @@
     return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
 }
 
-nsecs_t BufferStateLayer::nextPredictedPresentTime() const {
+std::optional<nsecs_t> BufferStateLayer::nextPredictedPresentTime() const {
     if (!getDrawingState().isAutoTimestamp || !mSurfaceFrame) {
-        return 0;
+        return std::nullopt;
     }
 
     return mSurfaceFrame->getPredictions().presentTime;
@@ -644,7 +637,10 @@
     if (s.buffer == nullptr) {
         return BAD_VALUE;
     }
-    decrementPendingBufferCount();
+
+    if (s.buffer != mBufferInfo.mBuffer) {
+        decrementPendingBufferCount();
+    }
 
     mPreviousBufferId = getCurrentBufferId();
     mBufferInfo.mBuffer = s.buffer;
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 6414e6b..f638caa 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -45,7 +45,7 @@
     void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
                                    const CompositorTiming& compositorTiming) override;
 
-    bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
+    bool isBufferDue(nsecs_t /*expectedPresentTime*/) const override { return true; }
 
     uint32_t doTransactionResize(uint32_t flags, Layer::State* /*stateToCommit*/) override {
         return flags;
@@ -71,7 +71,8 @@
     bool setFrame(const Rect& frame) override;
     bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime,
                    nsecs_t desiredPresentTime, bool isAutoTimestamp,
-                   const client_cache_t& clientCacheId, uint64_t frameNumber) override;
+                   const client_cache_t& clientCacheId, uint64_t frameNumber,
+                   std::optional<nsecs_t> dequeueTime) override;
     bool setAcquireFence(const sp<Fence>& fence) override;
     bool setDataspace(ui::Dataspace dataspace) override;
     bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
@@ -152,7 +153,7 @@
 
     bool bufferNeedsFiltering() const override;
 
-    nsecs_t nextPredictedPresentTime() const override;
+    std::optional<nsecs_t> nextPredictedPresentTime() const override;
 
     static const std::array<float, 16> IDENTITY_MATRIX;
 
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 43792dc..4f99495 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -167,15 +167,10 @@
 
     // Update framebuffer space
     const Rect newBounds(size);
-    ScaleVector scale;
-    scale = getScale(state.framebufferSpace.bounds, newBounds);
     state.framebufferSpace.bounds = newBounds;
-    state.framebufferSpace.content.scaleSelf(scale.x, scale.y);
 
     // Update display space
-    scale = getScale(state.displaySpace.bounds, newBounds);
     state.displaySpace.bounds = newBounds;
-    state.displaySpace.content.scaleSelf(scale.x, scale.y);
     state.transform = state.layerStackSpace.getTransform(state.displaySpace);
 
     // Update oriented display space
@@ -185,9 +180,7 @@
         std::swap(orientedSize.width, orientedSize.height);
     }
     const Rect newOrientedBounds(orientedSize);
-    scale = getScale(state.orientedDisplaySpace.bounds, newOrientedBounds);
     state.orientedDisplaySpace.bounds = newOrientedBounds;
-    state.orientedDisplaySpace.content.scaleSelf(scale.x, scale.y);
 
     dirtyEntireOutput();
 }
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 1452192..376bac8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -338,15 +338,12 @@
     EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.bounds);
 
     EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
-    EXPECT_EQ(Rect(0, 0, 900, 450), state.orientedDisplaySpace.content);
     EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.bounds);
 
     EXPECT_EQ(displayRect, state.displaySpace.bounds);
-    EXPECT_EQ(Rect(0, 0, 450, 900), state.displaySpace.content);
     EXPECT_EQ(ui::ROTATION_90, state.displaySpace.orientation);
 
     EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
-    EXPECT_EQ(Rect(0, 0, 450, 900), state.framebufferSpace.content);
     EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.orientation);
 
     EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index a8fef04..c994434 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -239,11 +239,16 @@
     return minTime;
 }
 
+int64_t TraceCookieCounter::getCookieForTracing() {
+    return ++mTraceCookie;
+}
+
 SurfaceFrame::SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
                            std::string debugName, PredictionState predictionState,
                            frametimeline::TimelineItem&& predictions,
                            std::shared_ptr<TimeStats> timeStats,
-                           JankClassificationThresholds thresholds)
+                           JankClassificationThresholds thresholds,
+                           TraceCookieCounter* traceCookieCounter)
       : mToken(token),
         mOwnerPid(ownerPid),
         mOwnerUid(ownerUid),
@@ -254,7 +259,8 @@
         mPredictions(predictions),
         mActuals({0, 0, 0}),
         mTimeStats(timeStats),
-        mJankClassificationThresholds(thresholds) {}
+        mJankClassificationThresholds(thresholds),
+        mTraceCookieCounter(*traceCookieCounter) {}
 
 void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) {
     std::lock_guard<std::mutex> lock(mMutex);
@@ -443,7 +449,7 @@
 
 void SurfaceFrame::trace(int64_t displayFrameToken) {
     using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource;
-    FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+    {
         std::lock_guard<std::mutex> lock(mMutex);
         if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) {
             ALOGD("Cannot trace SurfaceFrame - %s with invalid token", mLayerName.c_str());
@@ -453,36 +459,83 @@
                   mLayerName.c_str());
             return;
         }
+    }
+
+    int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+    // Expected timeline start
+    FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+        std::lock_guard<std::mutex> lock(mMutex);
         auto packet = ctx.NewTracePacket();
         packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
-        packet->set_timestamp(static_cast<uint64_t>(systemTime()));
+        packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime));
 
         auto* event = packet->set_frame_timeline_event();
-        auto* surfaceFrameEvent = event->set_surface_frame();
+        auto* expectedSurfaceFrameStartEvent = event->set_expected_surface_frame_start();
 
-        surfaceFrameEvent->set_token(mToken);
-        surfaceFrameEvent->set_display_frame_token(displayFrameToken);
+        expectedSurfaceFrameStartEvent->set_cookie(expectedTimelineCookie);
+
+        expectedSurfaceFrameStartEvent->set_token(mToken);
+        expectedSurfaceFrameStartEvent->set_display_frame_token(displayFrameToken);
+
+        expectedSurfaceFrameStartEvent->set_pid(mOwnerPid);
+        expectedSurfaceFrameStartEvent->set_layer_name(mDebugName);
+    });
+    // Expected timeline end
+    FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        auto packet = ctx.NewTracePacket();
+        packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+        packet->set_timestamp(static_cast<uint64_t>(mPredictions.endTime));
+
+        auto* event = packet->set_frame_timeline_event();
+        auto* expectedSurfaceFrameEndEvent = event->set_frame_end();
+
+        expectedSurfaceFrameEndEvent->set_cookie(expectedTimelineCookie);
+    });
+
+    int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+    // Actual timeline start
+    FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        auto packet = ctx.NewTracePacket();
+        packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+        // Actual start time is not yet available, so use expected start instead
+        packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime));
+
+        auto* event = packet->set_frame_timeline_event();
+        auto* actualSurfaceFrameStartEvent = event->set_actual_surface_frame_start();
+
+        actualSurfaceFrameStartEvent->set_cookie(actualTimelineCookie);
+
+        actualSurfaceFrameStartEvent->set_token(mToken);
+        actualSurfaceFrameStartEvent->set_display_frame_token(displayFrameToken);
+
+        actualSurfaceFrameStartEvent->set_pid(mOwnerPid);
+        actualSurfaceFrameStartEvent->set_layer_name(mDebugName);
 
         if (mPresentState == PresentState::Dropped) {
-            surfaceFrameEvent->set_present_type(FrameTimelineEvent::PRESENT_DROPPED);
+            actualSurfaceFrameStartEvent->set_present_type(FrameTimelineEvent::PRESENT_DROPPED);
         } else if (mPresentState == PresentState::Unknown) {
-            surfaceFrameEvent->set_present_type(FrameTimelineEvent::PRESENT_UNSPECIFIED);
+            actualSurfaceFrameStartEvent->set_present_type(FrameTimelineEvent::PRESENT_UNSPECIFIED);
         } else {
-            surfaceFrameEvent->set_present_type(toProto(mFramePresentMetadata));
+            actualSurfaceFrameStartEvent->set_present_type(toProto(mFramePresentMetadata));
         }
-        surfaceFrameEvent->set_on_time_finish(mFrameReadyMetadata ==
-                                              FrameReadyMetadata::OnTimeFinish);
-        surfaceFrameEvent->set_gpu_composition(mGpuComposition);
-        surfaceFrameEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
+        actualSurfaceFrameStartEvent->set_on_time_finish(mFrameReadyMetadata ==
+                                                         FrameReadyMetadata::OnTimeFinish);
+        actualSurfaceFrameStartEvent->set_gpu_composition(mGpuComposition);
+        actualSurfaceFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
+    });
+    // Actual timeline end
+    FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        auto packet = ctx.NewTracePacket();
+        packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+        packet->set_timestamp(static_cast<uint64_t>(mActuals.endTime));
 
-        surfaceFrameEvent->set_expected_start_ns(mPredictions.startTime);
-        surfaceFrameEvent->set_expected_end_ns(mPredictions.endTime);
+        auto* event = packet->set_frame_timeline_event();
+        auto* actualSurfaceFrameEndEvent = event->set_frame_end();
 
-        surfaceFrameEvent->set_actual_start_ns(mActuals.startTime);
-        surfaceFrameEvent->set_actual_end_ns(mActuals.endTime);
-
-        surfaceFrameEvent->set_layer_name(mDebugName);
-        surfaceFrameEvent->set_pid(mOwnerPid);
+        actualSurfaceFrameEndEvent->set_cookie(actualTimelineCookie);
     });
 }
 
@@ -520,11 +573,13 @@
 
 FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
                              JankClassificationThresholds thresholds)
-      : mCurrentDisplayFrame(std::make_shared<DisplayFrame>(timeStats, thresholds)),
-        mMaxDisplayFrames(kDefaultMaxDisplayFrames),
+      : mMaxDisplayFrames(kDefaultMaxDisplayFrames),
         mTimeStats(std::move(timeStats)),
         mSurfaceFlingerPid(surfaceFlingerPid),
-        mJankClassificationThresholds(thresholds) {}
+        mJankClassificationThresholds(thresholds) {
+    mCurrentDisplayFrame =
+            std::make_shared<DisplayFrame>(mTimeStats, thresholds, &mTraceCookieCounter);
+}
 
 void FrameTimeline::onBootFinished() {
     perfetto::TracingInitArgs args;
@@ -547,27 +602,29 @@
         return std::make_shared<SurfaceFrame>(ISurfaceComposer::INVALID_VSYNC_ID, ownerPid,
                                               ownerUid, std::move(layerName), std::move(debugName),
                                               PredictionState::None, TimelineItem(), mTimeStats,
-                                              mJankClassificationThresholds);
+                                              mJankClassificationThresholds, &mTraceCookieCounter);
     }
     std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token);
     if (predictions) {
         return std::make_shared<SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
                                               std::move(debugName), PredictionState::Valid,
                                               std::move(*predictions), mTimeStats,
-                                              mJankClassificationThresholds);
+                                              mJankClassificationThresholds, &mTraceCookieCounter);
     }
     return std::make_shared<SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
                                           std::move(debugName), PredictionState::Expired,
-                                          TimelineItem(), mTimeStats,
-                                          mJankClassificationThresholds);
+                                          TimelineItem(), mTimeStats, mJankClassificationThresholds,
+                                          &mTraceCookieCounter);
 }
 
 FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats,
-                                          JankClassificationThresholds thresholds)
+                                          JankClassificationThresholds thresholds,
+                                          TraceCookieCounter* traceCookieCounter)
       : mSurfaceFlingerPredictions(TimelineItem()),
         mSurfaceFlingerActuals(TimelineItem()),
         mTimeStats(timeStats),
-        mJankClassificationThresholds(thresholds) {
+        mJankClassificationThresholds(thresholds),
+        mTraceCookieCounter(*traceCookieCounter) {
     mSurfaceFrames.reserve(kNumSurfaceFramesInitial);
 }
 
@@ -725,32 +782,69 @@
 }
 
 void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const {
+    if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) {
+        ALOGD("Cannot trace DisplayFrame with invalid token");
+        return;
+    }
+
+    int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+    // Expected timeline start
     FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
-        if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) {
-            ALOGD("Cannot trace DisplayFrame with invalid token");
-            return;
-        }
         auto packet = ctx.NewTracePacket();
         packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
-        packet->set_timestamp(static_cast<uint64_t>(systemTime()));
+        packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerPredictions.startTime));
 
         auto* event = packet->set_frame_timeline_event();
-        auto* displayFrameEvent = event->set_display_frame();
+        auto* expectedDisplayFrameStartEvent = event->set_expected_display_frame_start();
 
-        displayFrameEvent->set_token(mToken);
-        displayFrameEvent->set_present_type(toProto(mFramePresentMetadata));
-        displayFrameEvent->set_on_time_finish(mFrameReadyMetadata ==
-                                              FrameReadyMetadata::OnTimeFinish);
-        displayFrameEvent->set_gpu_composition(mGpuComposition);
-        displayFrameEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
+        expectedDisplayFrameStartEvent->set_cookie(expectedTimelineCookie);
 
-        displayFrameEvent->set_expected_start_ns(mSurfaceFlingerPredictions.startTime);
-        displayFrameEvent->set_expected_end_ns(mSurfaceFlingerPredictions.endTime);
+        expectedDisplayFrameStartEvent->set_token(mToken);
+        expectedDisplayFrameStartEvent->set_pid(surfaceFlingerPid);
+    });
+    // Expected timeline end
+    FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+        auto packet = ctx.NewTracePacket();
+        packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+        packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerPredictions.endTime));
 
-        displayFrameEvent->set_actual_start_ns(mSurfaceFlingerActuals.startTime);
-        displayFrameEvent->set_actual_end_ns(mSurfaceFlingerActuals.endTime);
+        auto* event = packet->set_frame_timeline_event();
+        auto* expectedDisplayFrameEndEvent = event->set_frame_end();
 
-        displayFrameEvent->set_pid(surfaceFlingerPid);
+        expectedDisplayFrameEndEvent->set_cookie(expectedTimelineCookie);
+    });
+
+    int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+    // Expected timeline start
+    FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+        auto packet = ctx.NewTracePacket();
+        packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+        packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerActuals.startTime));
+
+        auto* event = packet->set_frame_timeline_event();
+        auto* actualDisplayFrameStartEvent = event->set_actual_display_frame_start();
+
+        actualDisplayFrameStartEvent->set_cookie(actualTimelineCookie);
+
+        actualDisplayFrameStartEvent->set_token(mToken);
+        actualDisplayFrameStartEvent->set_pid(surfaceFlingerPid);
+
+        actualDisplayFrameStartEvent->set_present_type(toProto(mFramePresentMetadata));
+        actualDisplayFrameStartEvent->set_on_time_finish(mFrameReadyMetadata ==
+                                                         FrameReadyMetadata::OnTimeFinish);
+        actualDisplayFrameStartEvent->set_gpu_composition(mGpuComposition);
+        actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
+    });
+    // Expected timeline end
+    FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+        auto packet = ctx.NewTracePacket();
+        packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+        packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerActuals.endTime));
+
+        auto* event = packet->set_frame_timeline_event();
+        auto* actualDisplayFrameEndEvent = event->set_frame_end();
+
+        actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie);
     });
 
     for (auto& surfaceFrame : mSurfaceFrames) {
@@ -786,8 +880,8 @@
     }
     mDisplayFrames.push_back(mCurrentDisplayFrame);
     mCurrentDisplayFrame.reset();
-    mCurrentDisplayFrame =
-            std::make_shared<DisplayFrame>(mTimeStats, mJankClassificationThresholds);
+    mCurrentDisplayFrame = std::make_shared<DisplayFrame>(mTimeStats, mJankClassificationThresholds,
+                                                          &mTraceCookieCounter);
 }
 
 nsecs_t FrameTimeline::DisplayFrame::getBaseTime() const {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index f5239aa..ed38cc6 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -127,6 +127,23 @@
     None,    // Predictions are either not present or didn't come from TokenManager
 };
 
+/*
+ * Trace cookie is used to send start and end timestamps of <Surface/Display>Frames separately
+ * without needing to resend all the other information. We send all info to perfetto, along with a
+ * new cookie, in the start of a frame. For the corresponding end, we just send the same cookie.
+ * This helps in reducing the amount of data emitted by the producer.
+ */
+class TraceCookieCounter {
+public:
+    int64_t getCookieForTracing();
+
+private:
+    // Friend class for testing
+    friend class android::frametimeline::FrameTimelineTest;
+
+    std::atomic<int64_t> mTraceCookie = 0;
+};
+
 class SurfaceFrame {
 public:
     enum class PresentState {
@@ -139,7 +156,8 @@
     // TokenManager), Thresholds and TimeStats pointer.
     SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
                  std::string debugName, PredictionState predictionState, TimelineItem&& predictions,
-                 std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds);
+                 std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
+                 TraceCookieCounter* traceCookieCounter);
     ~SurfaceFrame() = default;
 
     // Returns std::nullopt if the frame hasn't been classified yet.
@@ -205,6 +223,9 @@
     // for BufferStuffing where the current buffer is expected to be ready but the previous buffer
     // was latched instead.
     nsecs_t mLastLatchTime GUARDED_BY(mMutex) = 0;
+    // TraceCookieCounter is used to obtain the cookie for sendig trace packets to perfetto. Using a
+    // reference here because the counter is owned by FrameTimeline, which outlives SurfaceFrame.
+    TraceCookieCounter& mTraceCookieCounter;
 };
 
 /*
@@ -291,7 +312,8 @@
      */
     class DisplayFrame {
     public:
-        DisplayFrame(std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds);
+        DisplayFrame(std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
+                     TraceCookieCounter* traceCookieCounter);
         virtual ~DisplayFrame() = default;
         // Dumpsys interface - dumps only if the DisplayFrame itself is janky or is at least one
         // SurfaceFrame is janky.
@@ -360,6 +382,10 @@
         // The refresh rate (vsync period) in nanoseconds as seen by SF during this DisplayFrame's
         // timeline
         nsecs_t mVsyncPeriod = 0;
+        // TraceCookieCounter is used to obtain the cookie for sendig trace packets to perfetto.
+        // Using a reference here because the counter is owned by FrameTimeline, which outlives
+        // DisplayFrame.
+        TraceCookieCounter& mTraceCookieCounter;
     };
 
     FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
@@ -402,6 +428,7 @@
             mPendingPresentFences GUARDED_BY(mMutex);
     std::shared_ptr<DisplayFrame> mCurrentDisplayFrame GUARDED_BY(mMutex);
     TokenManager mTokenManager;
+    TraceCookieCounter mTraceCookieCounter;
     mutable std::mutex mMutex;
     uint32_t mMaxDisplayFrames;
     std::shared_ptr<TimeStats> mTimeStats;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 4731f50..a545d18 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -688,6 +688,7 @@
     shadowLayer.source.buffer.fence = nullptr;
     shadowLayer.frameNumber = 0;
     shadowLayer.bufferId = 0;
+    shadowLayer.name = getName();
 
     if (shadowLayer.shadow.ambientColor.a <= 0.f && shadowLayer.shadow.spotColor.a <= 0.f) {
         return {};
@@ -723,6 +724,7 @@
 
     // If layer is blacked out, force alpha to 1 so that we draw a black color layer.
     layerSettings.alpha = blackout ? 1.0f : 0.0f;
+    layerSettings.name = getName();
 }
 
 std::vector<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCompositionList(
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index d6023b6..f78b5f3 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -446,7 +446,7 @@
     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 */) {
+                           uint64_t /* frameNumber */, std::optional<nsecs_t> /* dequeueTime */) {
         return false;
     };
     virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 6a511a8..9230e72 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -233,11 +233,12 @@
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
 
-void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
-    mCurrentFps = refreshRate.getFps().getIntValue();
+void RefreshRateOverlay::changeRefreshRate(const Fps& fps) {
+    mCurrentFps = fps.getIntValue();
     auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame];
     mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
-                      mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */));
+                      mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
+                      std::nullopt /* dequeueTime */);
 
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
@@ -249,15 +250,17 @@
     mFrame = (mFrame + 1) % buffers.size();
     auto buffer = buffers[mFrame];
     mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
-                      mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */));
+                      mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
+                      std::nullopt /* dequeueTime */);
 
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
 
 void RefreshRateOverlay::reset() {
     mBufferCache.clear();
-    mLowFps = mFlinger.mRefreshRateConfigs->getMinRefreshRate().getFps().getIntValue();
-    mHighFps = mFlinger.mRefreshRateConfigs->getMaxRefreshRate().getFps().getIntValue();
+    const auto range = mFlinger.mRefreshRateConfigs->getSupportedRefreshRateRange();
+    mLowFps = range.min.getIntValue();
+    mHighFps = range.max.getIntValue();
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 4ca1337..c16cfa0 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -23,7 +23,7 @@
 #include <ui/Size.h>
 #include <utils/StrongPointer.h>
 
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Fps.h"
 
 namespace android {
 
@@ -34,14 +34,12 @@
 class Layer;
 class SurfaceFlinger;
 
-using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
-
 class RefreshRateOverlay {
 public:
     RefreshRateOverlay(SurfaceFlinger&, bool showSpinner);
 
     void setViewport(ui::Size);
-    void changeRefreshRate(const RefreshRate&);
+    void changeRefreshRate(const Fps&);
     void onInvalidate();
     void reset();
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 35b382e..975754b 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -26,6 +26,7 @@
 #include <utils/Trace.h>
 #include <chrono>
 #include <cmath>
+#include "../SurfaceFlingerProperties.h"
 
 #undef LOG_TAG
 #define LOG_TAG "RefreshRateConfigs"
@@ -38,6 +39,26 @@
                               toString(layer.seamlessness).c_str(),
                               to_string(layer.desiredRefreshRate).c_str());
 }
+
+std::vector<Fps> constructKnownFrameRates(
+        const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+    std::vector<Fps> knownFrameRates = {Fps(24.0f), Fps(30.0f), Fps(45.0f), Fps(60.0f), Fps(72.0f)};
+    knownFrameRates.reserve(knownFrameRates.size() + configs.size());
+
+    // Add all supported refresh rates to the set
+    for (const auto& config : configs) {
+        const auto refreshRate = Fps::fromPeriodNsecs(config->getVsyncPeriod());
+        knownFrameRates.emplace_back(refreshRate);
+    }
+
+    // Sort and remove duplicates
+    std::sort(knownFrameRates.begin(), knownFrameRates.end(), Fps::comparesLess);
+    knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
+                                      Fps::EqualsWithMargin()),
+                          knownFrameRates.end());
+    return knownFrameRates;
+}
+
 } // namespace
 
 using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
@@ -153,9 +174,9 @@
     float score;
 };
 
-const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
-        const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
-        GlobalSignals* outSignalsConsidered) const {
+RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
+                                                   const GlobalSignals& globalSignals,
+                                                   GlobalSignals* outSignalsConsidered) const {
     ATRACE_CALL();
     ALOGV("getBestRefreshRate %zu layers", layers.size());
 
@@ -468,9 +489,20 @@
     return bestRefreshRate;
 }
 
-const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const {
+std::optional<Fps> RefreshRateConfigs::onKernelTimerChanged(
+        std::optional<HwcConfigIndexType> desiredActiveConfigId, bool timerExpired) const {
     std::lock_guard lock(mLock);
-    return getMinRefreshRateByPolicyLocked();
+
+    const auto& current = desiredActiveConfigId ? *mRefreshRates.at(*desiredActiveConfigId)
+                                                : *mCurrentRefreshRate;
+    const auto& min = *mMinSupportedRefreshRate;
+
+    if (current != min) {
+        const auto& refreshRate = timerExpired ? min : current;
+        return refreshRate.getFps();
+    }
+
+    return {};
 }
 
 const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
@@ -486,7 +518,7 @@
     return *mPrimaryRefreshRates.front();
 }
 
-const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
+RefreshRate RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
     std::lock_guard lock(mLock);
     return getMaxRefreshRateByPolicyLocked();
 }
@@ -505,12 +537,12 @@
     return *mPrimaryRefreshRates.back();
 }
 
-const RefreshRate& RefreshRateConfigs::getCurrentRefreshRate() const {
+RefreshRate RefreshRateConfigs::getCurrentRefreshRate() const {
     std::lock_guard lock(mLock);
     return *mCurrentRefreshRate;
 }
 
-const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicy() const {
+RefreshRate RefreshRateConfigs::getCurrentRefreshRateByPolicy() const {
     std::lock_guard lock(mLock);
     return getCurrentRefreshRateByPolicyLocked();
 }
@@ -532,16 +564,23 @@
         const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
         HwcConfigIndexType currentConfigId)
       : mKnownFrameRates(constructKnownFrameRates(configs)) {
+    updateDisplayConfigs(configs, currentConfigId);
+}
+
+void RefreshRateConfigs::updateDisplayConfigs(
+        const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+        HwcConfigIndexType currentConfigId) {
+    std::lock_guard lock(mLock);
     LOG_ALWAYS_FATAL_IF(configs.empty());
     LOG_ALWAYS_FATAL_IF(currentConfigId.value() < 0);
     LOG_ALWAYS_FATAL_IF(currentConfigId.value() >= configs.size());
 
+    mRefreshRates.clear();
     for (auto configId = HwcConfigIndexType(0); configId.value() < configs.size(); configId++) {
         const auto& config = configs.at(static_cast<size_t>(configId.value()));
+        const auto fps = Fps::fromPeriodNsecs(config->getVsyncPeriod());
         mRefreshRates.emplace(configId,
-                              std::make_unique<RefreshRate>(configId, config,
-                                                            Fps::fromPeriodNsecs(
-                                                                    config->getVsyncPeriod()),
+                              std::make_unique<RefreshRate>(configId, config, fps,
                                                             RefreshRate::ConstructorTag(0)));
         if (configId == currentConfigId) {
             mCurrentRefreshRate = mRefreshRates.at(configId).get();
@@ -549,24 +588,27 @@
     }
 
     std::vector<const RefreshRate*> sortedConfigs;
-    getSortedRefreshRateList([](const RefreshRate&) { return true; }, &sortedConfigs);
+    getSortedRefreshRateListLocked([](const RefreshRate&) { return true; }, &sortedConfigs);
     mDisplayManagerPolicy.defaultConfig = currentConfigId;
     mMinSupportedRefreshRate = sortedConfigs.front();
     mMaxSupportedRefreshRate = sortedConfigs.back();
 
     mSupportsFrameRateOverride = false;
-    for (const auto& config1 : sortedConfigs) {
-        for (const auto& config2 : sortedConfigs) {
-            if (getFrameRateDivider(config1->getFps(), config2->getFps()) >= 2) {
-                mSupportsFrameRateOverride = true;
-                break;
+    if (android::sysprop::enable_frame_rate_override(true)) {
+        for (const auto& config1 : sortedConfigs) {
+            for (const auto& config2 : sortedConfigs) {
+                if (getFrameRateDivider(config1->getFps(), config2->getFps()) >= 2) {
+                    mSupportsFrameRateOverride = true;
+                    break;
+                }
             }
         }
     }
+
     constructAvailableRefreshRates();
 }
 
-bool RefreshRateConfigs::isPolicyValid(const Policy& policy) {
+bool RefreshRateConfigs::isPolicyValidLocked(const Policy& policy) const {
     // defaultConfig must be a valid config, and within the given refresh rate range.
     auto iter = mRefreshRates.find(policy.defaultConfig);
     if (iter == mRefreshRates.end()) {
@@ -584,7 +626,7 @@
 
 status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
     std::lock_guard lock(mLock);
-    if (!isPolicyValid(policy)) {
+    if (!isPolicyValidLocked(policy)) {
         ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
         return BAD_VALUE;
     }
@@ -599,7 +641,7 @@
 
 status_t RefreshRateConfigs::setOverridePolicy(const std::optional<Policy>& policy) {
     std::lock_guard lock(mLock);
-    if (policy && !isPolicyValid(*policy)) {
+    if (policy && !isPolicyValidLocked(*policy)) {
         return BAD_VALUE;
     }
     Policy previousPolicy = *getCurrentPolicyLocked();
@@ -635,14 +677,14 @@
     return false;
 }
 
-void RefreshRateConfigs::getSortedRefreshRateList(
+void RefreshRateConfigs::getSortedRefreshRateListLocked(
         const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
         std::vector<const RefreshRate*>* outRefreshRates) {
     outRefreshRates->clear();
     outRefreshRates->reserve(mRefreshRates.size());
     for (const auto& [type, refreshRate] : mRefreshRates) {
         if (shouldAddRefreshRate(*refreshRate)) {
-            ALOGV("getSortedRefreshRateList: config %d added to list policy",
+            ALOGV("getSortedRefreshRateListLocked: config %d added to list policy",
                   refreshRate->configId.value());
             outRefreshRates->push_back(refreshRate.get());
         }
@@ -668,8 +710,9 @@
     ALOGV("constructAvailableRefreshRates: %s ", policy->toString().c_str());
 
     auto filterRefreshRates = [&](Fps min, Fps max, const char* listName,
-                                  std::vector<const RefreshRate*>* outRefreshRates) {
-        getSortedRefreshRateList(
+                                  std::vector<const RefreshRate*>*
+                                          outRefreshRates) REQUIRES(mLock) {
+        getSortedRefreshRateListLocked(
                 [&](const RefreshRate& refreshRate) REQUIRES(mLock) {
                     const auto& hwcConfig = refreshRate.hwcConfig;
 
@@ -702,25 +745,6 @@
                        &mAppRequestRefreshRates);
 }
 
-std::vector<Fps> RefreshRateConfigs::constructKnownFrameRates(
-        const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
-    std::vector<Fps> knownFrameRates = {Fps(24.0f), Fps(30.0f), Fps(45.0f), Fps(60.0f), Fps(72.0f)};
-    knownFrameRates.reserve(knownFrameRates.size() + configs.size());
-
-    // Add all supported refresh rates to the set
-    for (const auto& config : configs) {
-        const auto refreshRate = Fps::fromPeriodNsecs(config->getVsyncPeriod());
-        knownFrameRates.emplace_back(refreshRate);
-    }
-
-    // Sort and remove duplicates
-    std::sort(knownFrameRates.begin(), knownFrameRates.end(), Fps::comparesLess);
-    knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
-                                      Fps::EqualsWithMargin()),
-                          knownFrameRates.end());
-    return knownFrameRates;
-}
-
 Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const {
     if (frameRate.lessThanOrEqualWithMargin(*mKnownFrameRates.begin())) {
         return *mKnownFrameRates.begin();
@@ -740,7 +764,7 @@
 
 RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const {
     std::lock_guard lock(mLock);
-    const auto& deviceMin = getMinRefreshRate();
+    const auto& deviceMin = *mMinSupportedRefreshRate;
     const auto& minByPolicy = getMinRefreshRateByPolicyLocked();
     const auto& maxByPolicy = getMaxRefreshRateByPolicyLocked();
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index e4bbf7f..4b99145 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -68,8 +68,6 @@
                     std::shared_ptr<const HWC2::Display::Config> config, Fps fps, ConstructorTag)
               : configId(configId), hwcConfig(config), fps(std::move(fps)) {}
 
-        RefreshRate(const RefreshRate&) = delete;
-
         HwcConfigIndexType getConfigId() const { return configId; }
         nsecs_t getVsyncPeriod() const { return hwcConfig->getVsyncPeriod(); }
         int32_t getConfigGroup() const { return hwcConfig->getConfigGroup(); }
@@ -111,27 +109,26 @@
     using AllRefreshRatesMapType =
             std::unordered_map<HwcConfigIndexType, std::unique_ptr<const RefreshRate>>;
 
+    struct FpsRange {
+        Fps min{0.0f};
+        Fps max{std::numeric_limits<float>::max()};
+
+        bool operator==(const FpsRange& other) const {
+            return min.equalsWithMargin(other.min) && max.equalsWithMargin(other.max);
+        }
+
+        bool operator!=(const FpsRange& other) const { return !(*this == other); }
+
+        std::string toString() const {
+            return base::StringPrintf("[%s %s]", to_string(min).c_str(), to_string(max).c_str());
+        }
+    };
+
     struct Policy {
     private:
         static constexpr int kAllowGroupSwitchingDefault = false;
 
     public:
-        struct Range {
-            Fps min{0.0f};
-            Fps max{std::numeric_limits<float>::max()};
-
-            bool operator==(const Range& other) const {
-                return min.equalsWithMargin(other.min) && max.equalsWithMargin(other.max);
-            }
-
-            bool operator!=(const Range& other) const { return !(*this == other); }
-
-            std::string toString() const {
-                return base::StringPrintf("[%s %s]", to_string(min).c_str(),
-                                          to_string(max).c_str());
-            }
-        };
-
         // The default config, used to ensure we only initiate display config switches within the
         // same config group as defaultConfigId's group.
         HwcConfigIndexType defaultConfig;
@@ -140,29 +137,29 @@
         // The primary refresh rate range represents display manager's general guidance on the
         // display configs we'll consider when switching refresh rates. Unless we get an explicit
         // signal from an app, we should stay within this range.
-        Range primaryRange;
+        FpsRange primaryRange;
         // The app request refresh rate range allows us to consider more display configs when
         // switching refresh rates. Although we should generally stay within the primary range,
         // specific considerations, such as layer frame rate settings specified via the
         // setFrameRate() api, may cause us to go outside the primary range. We never go outside the
         // app request range. The app request range will be greater than or equal to the primary
         // refresh rate range, never smaller.
-        Range appRequestRange;
+        FpsRange appRequestRange;
 
         Policy() = default;
 
-        Policy(HwcConfigIndexType defaultConfig, const Range& range)
+        Policy(HwcConfigIndexType defaultConfig, const FpsRange& range)
               : Policy(defaultConfig, kAllowGroupSwitchingDefault, range, range) {}
 
-        Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching, const Range& range)
+        Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching, const FpsRange& range)
               : Policy(defaultConfig, allowGroupSwitching, range, range) {}
 
-        Policy(HwcConfigIndexType defaultConfig, const Range& primaryRange,
-               const Range& appRequestRange)
+        Policy(HwcConfigIndexType defaultConfig, const FpsRange& primaryRange,
+               const FpsRange& appRequestRange)
               : Policy(defaultConfig, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {}
 
         Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching,
-               const Range& primaryRange, const Range& appRequestRange)
+               const FpsRange& primaryRange, const FpsRange& appRequestRange)
               : defaultConfig(defaultConfig),
                 allowGroupSwitching(allowGroupSwitching),
                 primaryRange(primaryRange),
@@ -258,35 +255,35 @@
     //   globalSignals - global state of touch and idle
     //   outSignalsConsidered - An output param that tells the caller whether the refresh rate was
     //                          chosen based on touch boost and/or idle timer.
-    const RefreshRate& getBestRefreshRate(const std::vector<LayerRequirement>& layers,
-                                          const GlobalSignals& globalSignals,
-                                          GlobalSignals* outSignalsConsidered = nullptr) const
+    RefreshRate getBestRefreshRate(const std::vector<LayerRequirement>& layers,
+                                   const GlobalSignals& globalSignals,
+                                   GlobalSignals* outSignalsConsidered = nullptr) const
             EXCLUDES(mLock);
 
-    // Returns the lowest refresh rate supported by the device. This won't change at runtime.
-    const RefreshRate& getMinRefreshRate() const { return *mMinSupportedRefreshRate; }
+    FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
+        std::lock_guard lock(mLock);
+        return {mMinSupportedRefreshRate->getFps(), mMaxSupportedRefreshRate->getFps()};
+    }
 
-    // Returns the lowest refresh rate according to the current policy. May change at runtime. Only
-    // uses the primary range, not the app request range.
-    const RefreshRate& getMinRefreshRateByPolicy() const EXCLUDES(mLock);
-
-    // Returns the highest refresh rate supported by the device. This won't change at runtime.
-    const RefreshRate& getMaxRefreshRate() const { return *mMaxSupportedRefreshRate; }
+    std::optional<Fps> onKernelTimerChanged(std::optional<HwcConfigIndexType> desiredActiveConfigId,
+                                            bool timerExpired) const EXCLUDES(mLock);
 
     // Returns the highest refresh rate according to the current policy. May change at runtime. Only
     // uses the primary range, not the app request range.
-    const RefreshRate& getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
+    RefreshRate getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
 
     // Returns the current refresh rate
-    const RefreshRate& getCurrentRefreshRate() const EXCLUDES(mLock);
+    RefreshRate getCurrentRefreshRate() const EXCLUDES(mLock);
 
     // Returns the current refresh rate, if allowed. Otherwise the default that is allowed by
     // the policy.
-    const RefreshRate& getCurrentRefreshRateByPolicy() const;
+    RefreshRate getCurrentRefreshRateByPolicy() const;
 
-    // Returns the refresh rate that corresponds to a HwcConfigIndexType. This won't change at
+    // Returns the refresh rate that corresponds to a HwcConfigIndexType. This may change at
     // runtime.
-    const RefreshRate& getRefreshRateFromConfigId(HwcConfigIndexType configId) const {
+    // TODO(b/159590486) An invalid config id may be given here if the dipslay configs have changed.
+    RefreshRate getRefreshRateFromConfigId(HwcConfigIndexType configId) const EXCLUDES(mLock) {
+        std::lock_guard lock(mLock);
         return *mRefreshRates.at(configId);
     };
 
@@ -302,10 +299,17 @@
     RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
                        HwcConfigIndexType currentConfigId);
 
+    void updateDisplayConfigs(
+            const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+            HwcConfigIndexType currentConfig) EXCLUDES(mLock);
+
     // Returns whether switching configs (refresh rate or resolution) is possible.
     // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the configs only
     // differ in resolution.
-    bool canSwitch() const { return mRefreshRates.size() > 1; }
+    bool canSwitch() const EXCLUDES(mLock) {
+        std::lock_guard lock(mLock);
+        return mRefreshRates.size() > 1;
+    }
 
     // Class to enumerate options around toggling the kernel timer on and off. We have an option
     // for no change to avoid extra calls to kernel.
@@ -334,12 +338,10 @@
     friend class RefreshRateConfigsTest;
 
     void constructAvailableRefreshRates() REQUIRES(mLock);
-    static std::vector<Fps> constructKnownFrameRates(
-            const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs);
 
-    void getSortedRefreshRateList(
+    void getSortedRefreshRateListLocked(
             const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
-            std::vector<const RefreshRate*>* outRefreshRates);
+            std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock);
 
     // Returns the refresh rate with the highest score in the collection specified from begin
     // to end. If there are more than one with the same highest refresh rate, the first one is
@@ -364,7 +366,7 @@
     const RefreshRate& getCurrentRefreshRateByPolicyLocked() const REQUIRES(mLock);
 
     const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
-    bool isPolicyValid(const Policy& policy);
+    bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
 
     // Return the display refresh rate divider to match the layer
     // frame rate, or 0 if the display refresh rate is not a multiple of the
@@ -376,9 +378,9 @@
     float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
                                     bool isSeamlessSwitch) const REQUIRES(mLock);
 
-    // The list of refresh rates, indexed by display config ID. This must not change after this
+    // The list of refresh rates, indexed by display config ID. This may change after this
     // object is initialized.
-    AllRefreshRatesMapType mRefreshRates;
+    AllRefreshRatesMapType mRefreshRates GUARDED_BY(mLock);
 
     // The list of refresh rates in the primary range of the current policy, ordered by vsyncPeriod
     // (the first element is the lowest refresh rate).
@@ -398,9 +400,9 @@
     std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
 
     // The min and max refresh rates supported by the device.
-    // This will not change at runtime.
-    const RefreshRate* mMinSupportedRefreshRate;
-    const RefreshRate* mMaxSupportedRefreshRate;
+    // This may change at runtime.
+    const RefreshRate* mMinSupportedRefreshRate GUARDED_BY(mLock);
+    const RefreshRate* mMaxSupportedRefreshRate GUARDED_BY(mLock);
 
     mutable std::mutex mLock;
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 18c899b..49e3903 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -612,7 +612,7 @@
         mFeatures.contentRequirements = summary;
 
         newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
-        auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+        auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
         frameRateOverridesChanged =
                 updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
 
@@ -629,7 +629,7 @@
         }
     }
     if (frameRateChanged) {
-        auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+        auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
         mSchedulerCallback.changeRefreshRate(newRefreshRate,
                                              consideredSignals.idle ? ConfigEvent::None
                                                                     : ConfigEvent::Changed);
diff --git a/services/surfaceflinger/StartPropertySetThread.cpp b/services/surfaceflinger/StartPropertySetThread.cpp
index db82772..f42cd53 100644
--- a/services/surfaceflinger/StartPropertySetThread.cpp
+++ b/services/surfaceflinger/StartPropertySetThread.cpp
@@ -31,6 +31,7 @@
     property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0");
     // Clear BootAnimation exit flag
     property_set("service.bootanim.exit", "0");
+    property_set("service.bootanim.progress", "0");
     // Start BootAnimation if not started
     property_set("ctl.start", "bootanim");
     // Exit immediately
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 6967f69..2e00ca8 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -995,7 +995,7 @@
 
 void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) {
     ATRACE_CALL();
-    auto& refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(info.configId);
+    auto refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(info.configId);
     ALOGV("setDesiredActiveConfig(%s)", refreshRate.getName().c_str());
 
     std::lock_guard<std::mutex> lock(mActiveConfigLock);
@@ -1030,7 +1030,7 @@
     }
 
     if (mRefreshRateOverlay) {
-        mRefreshRateOverlay->changeRefreshRate(refreshRate);
+        mRefreshRateOverlay->changeRefreshRate(refreshRate.getFps());
     }
 }
 
@@ -1076,14 +1076,14 @@
         return;
     }
 
-    auto& oldRefreshRate =
+    auto oldRefreshRate =
             mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig());
 
     std::lock_guard<std::mutex> lock(mActiveConfigLock);
     mRefreshRateConfigs->setCurrentConfigId(mUpcomingActiveConfig.configId);
     display->setActiveConfig(mUpcomingActiveConfig.configId);
 
-    auto& refreshRate =
+    auto refreshRate =
             mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId);
     mRefreshRateStats->setRefreshRate(refreshRate.getFps());
 
@@ -1126,7 +1126,7 @@
         return;
     }
 
-    auto& refreshRate =
+    auto refreshRate =
             mRefreshRateConfigs->getRefreshRateFromConfigId(desiredActiveConfig->configId);
     ALOGV("performSetActiveConfig changing active config to %d(%s)",
           refreshRate.getConfigId().value(), refreshRate.getName().c_str());
@@ -1914,19 +1914,17 @@
 
 bool SurfaceFlinger::handleMessageTransaction() {
     ATRACE_CALL();
+
+    if (getTransactionFlags(eTransactionFlushNeeded)) {
+        flushPendingTransactionQueues();
+        flushTransactionQueue();
+    }
     uint32_t transactionFlags = peekTransactionFlags();
-
-    bool flushedATransaction = flushTransactionQueues();
-
     bool runHandleTransaction =
-            (transactionFlags && (transactionFlags != eTransactionFlushNeeded)) ||
-            flushedATransaction ||
-            mForceTraversal;
+            ((transactionFlags & (~eTransactionFlushNeeded)) != 0) || mForceTraversal;
 
     if (runHandleTransaction) {
         handleTransaction(eTransactionMask);
-    } else {
-        getTransactionFlags(eTransactionFlushNeeded);
     }
 
     if (transactionFlushNeeded()) {
@@ -1994,11 +1992,6 @@
 
     mScheduler->onDisplayRefreshed(presentTime);
 
-    // Set presentation information before calling postComposition, such that jank information from
-    // this' frame classification is already available when sending jank info to clients.
-    mFrameTimeline->setSfPresent(systemTime(),
-                                 std::make_shared<FenceTime>(mPreviousPresentFences[0]));
-
     postFrame();
     postComposition();
 
@@ -2120,11 +2113,6 @@
     ATRACE_CALL();
     ALOGV("postComposition");
 
-    nsecs_t dequeueReadyTime = systemTime();
-    for (const auto& layer : mLayersWithQueuedFrames) {
-        layer->releasePendingBuffer(dequeueReadyTime);
-    }
-
     const auto* display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()).get();
 
     getBE().mGlCompositionDoneTimeline.updateSignalTimes();
@@ -2146,6 +2134,17 @@
     auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFences[0]);
     getBE().mDisplayTimeline.push(presentFenceTime);
 
+    // Set presentation information before calling Layer::releasePendingBuffer, such that jank
+    // information from previous' frame classification is already available when sending jank info
+    // to clients, so they get jank classification as early as possible.
+    mFrameTimeline->setSfPresent(systemTime(),
+                                 std::make_shared<FenceTime>(mPreviousPresentFences[0]));
+
+    nsecs_t dequeueReadyTime = systemTime();
+    for (const auto& layer : mLayersWithQueuedFrames) {
+        layer->releasePendingBuffer(dequeueReadyTime);
+    }
+
     const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(systemTime());
 
     // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
@@ -2621,6 +2620,9 @@
             if (currentState.physical->hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
                 const auto displayId = currentState.physical->id;
                 const auto configs = getHwComposer().getConfigs(displayId);
+                const auto currentConfig =
+                        HwcConfigIndexType(getHwComposer().getActiveConfigIndex(displayId));
+                mRefreshRateConfigs->updateDisplayConfigs(configs, currentConfig);
                 mVsyncConfiguration->reset();
                 updatePhaseConfiguration(mRefreshRateConfigs->getCurrentRefreshRate());
                 if (mRefreshRateOverlay) {
@@ -2827,7 +2829,6 @@
         });
     }
 
-    commitInputWindowCommands();
     commitTransaction();
 }
 
@@ -2868,11 +2869,6 @@
                                                                      : nullptr);
 }
 
-void SurfaceFlinger::commitInputWindowCommands() {
-    mInputWindowCommands.merge(mPendingInputWindowCommands);
-    mPendingInputWindowCommands.clear();
-}
-
 void SurfaceFlinger::updateCursorAsync() {
     compositionengine::CompositionRefreshArgs refreshArgs;
     for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
@@ -3236,55 +3232,55 @@
     mForceTraversal = true;
 }
 
-bool SurfaceFlinger::flushTransactionQueues() {
+void SurfaceFlinger::flushPendingTransactionQueues() {
     // to prevent onHandleDestroyed from being called while the lock is held,
     // we must keep a copy of the transactions (specifically the composer
     // states) around outside the scope of the lock
     std::vector<const TransactionState> transactions;
-    bool flushedATransaction = false;
     {
-        Mutex::Autolock _l(mStateLock);
+        Mutex::Autolock _l(mQueueLock);
 
-        auto it = mTransactionQueues.begin();
-        while (it != mTransactionQueues.end()) {
+        auto it = mPendingTransactionQueues.begin();
+        while (it != mPendingTransactionQueues.end()) {
             auto& [applyToken, transactionQueue] = *it;
 
             while (!transactionQueue.empty()) {
                 const auto& transaction = transactionQueue.front();
                 if (!transactionIsReadyToBeApplied(transaction.desiredPresentTime,
                                                    transaction.states)) {
-                    setTransactionFlags(eTransactionFlushNeeded);
                     break;
                 }
                 transactions.push_back(transaction);
-                applyTransactionState(transaction.frameTimelineVsyncId, transaction.states,
-                                      transaction.displays, transaction.flags,
-                                      mPendingInputWindowCommands, transaction.desiredPresentTime,
-                                      transaction.isAutoTimestamp, transaction.buffer,
-                                      transaction.postTime, transaction.privileged,
-                                      transaction.hasListenerCallbacks,
-                                      transaction.listenerCallbacks, transaction.originPid,
-                                      transaction.originUid, transaction.id, /*isMainThread*/ true);
                 transactionQueue.pop();
-                flushedATransaction = true;
             }
 
             if (transactionQueue.empty()) {
-                it = mTransactionQueues.erase(it);
-                mTransactionCV.broadcast();
+                it = mPendingTransactionQueues.erase(it);
+                mTransactionQueueCV.broadcast();
             } else {
                 it = std::next(it, 1);
             }
         }
     }
-    return flushedATransaction;
+
+    {
+        Mutex::Autolock _l(mStateLock);
+        for (const auto& transaction : transactions) {
+            applyTransactionState(transaction.frameTimelineVsyncId, transaction.states,
+                                  transaction.displays, transaction.flags, mInputWindowCommands,
+                                  transaction.desiredPresentTime, transaction.isAutoTimestamp,
+                                  transaction.buffer, transaction.postTime, transaction.privileged,
+                                  transaction.hasListenerCallbacks, transaction.listenerCallbacks,
+                                  transaction.originPid, transaction.originUid, transaction.id);
+        }
+    }
 }
 
 bool SurfaceFlinger::transactionFlushNeeded() {
-    return !mTransactionQueues.empty();
+    Mutex::Autolock _l(mQueueLock);
+    return !mPendingTransactionQueues.empty();
 }
 
-
 bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime,
                                                    const Vector<ComposerState>& states,
                                                    bool updateTransactionCounters) {
@@ -3306,6 +3302,8 @@
         if (s.acquireFence && s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
           ready = false;
         }
+
+        Mutex::Autolock _l(mStateLock);
         sp<Layer> layer = nullptr;
         if (s.surface) {
             layer = fromHandleLocked(s.surface).promote();
@@ -3318,9 +3316,8 @@
             ready = false;
         }
         if (updateTransactionCounters) {
-              // See BufferStateLayer::mPendingBufferTransactions
-              if (layer) layer->incrementPendingBufferCount();
-
+            // See BufferStateLayer::mPendingBufferTransactions
+            if (layer) layer->incrementPendingBufferCount();
         }
     }
     return ready;
@@ -3337,88 +3334,153 @@
     const int64_t postTime = systemTime();
 
     bool privileged = callingThreadHasUnscopedSurfaceFlingerAccess();
+    {
+        Mutex::Autolock _l(mQueueLock);
 
-    Mutex::Autolock _l(mStateLock);
-
-    // If its TransactionQueue already has a pending TransactionState or if it is pending
-    auto itr = mTransactionQueues.find(applyToken);
-    // if this is an animation frame, wait until prior animation frame has
-    // been applied by SF
-    if (flags & eAnimation) {
-        while (itr != mTransactionQueues.end()) {
-            status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
-            if (CC_UNLIKELY(err != NO_ERROR)) {
-                ALOGW_IF(err == TIMED_OUT,
-                         "setTransactionState timed out "
-                         "waiting for animation frame to apply");
-                break;
+        // If its TransactionQueue already has a pending TransactionState or if it is pending
+        auto itr = mPendingTransactionQueues.find(applyToken);
+        // if this is an animation frame, wait until prior animation frame has
+        // been applied by SF
+        if (flags & eAnimation) {
+            while (itr != mPendingTransactionQueues.end() ||
+                   (!mTransactionQueue.empty() && mAnimTransactionPending)) {
+                status_t err = mTransactionQueueCV.waitRelative(mQueueLock, s2ns(5));
+                if (CC_UNLIKELY(err != NO_ERROR)) {
+                    ALOGW_IF(err == TIMED_OUT,
+                             "setTransactionState timed out "
+                             "waiting for animation frame to apply");
+                    break;
+                }
+                itr = mPendingTransactionQueues.find(applyToken);
             }
-            itr = mTransactionQueues.find(applyToken);
         }
+
+        const bool pendingTransactions = itr != mPendingTransactionQueues.end();
+        // Expected present time is computed and cached on invalidate, so it may be stale.
+        if (!pendingTransactions) {
+            const auto now = systemTime();
+            const bool nextVsyncPending = now < mExpectedPresentTime.load();
+            const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(now);
+            mExpectedPresentTime = calculateExpectedPresentTime(stats);
+            // The transaction might arrive just before the next vsync but after
+            // invalidate was called. In that case we need to get the next vsync
+            // afterwards.
+            if (nextVsyncPending) {
+                mExpectedPresentTime += stats.vsyncPeriod;
+            }
+        }
+
+        // TODO(b/159125966): Remove eEarlyWakeup completly as no client should use this flag
+        if (flags & eEarlyWakeup) {
+            ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
+        }
+
+        if (!privileged && (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
+            ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
+            flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
+        }
+
+        IPCThreadState* ipc = IPCThreadState::self();
+        const int originPid = ipc->getCallingPid();
+        const int originUid = ipc->getCallingUid();
+
+        // Call transactionIsReadyToBeApplied first in case we need to incrementPendingBufferCount
+        // if the transaction contains a buffer.
+        if (!transactionIsReadyToBeApplied(isAutoTimestamp ? 0 : desiredPresentTime, states,
+                                           true) ||
+            pendingTransactions) {
+            mPendingTransactionQueues[applyToken].emplace(frameTimelineVsyncId, states, displays,
+                                                          flags, inputWindowCommands,
+                                                          desiredPresentTime, isAutoTimestamp,
+                                                          uncacheBuffer, postTime, privileged,
+                                                          hasListenerCallbacks, listenerCallbacks,
+                                                          originPid, originUid, transactionId);
+
+            setTransactionFlags(eTransactionFlushNeeded);
+            return NO_ERROR;
+        }
+
+        mTransactionQueue.emplace_back(frameTimelineVsyncId, states, displays, flags,
+                                       inputWindowCommands, desiredPresentTime, isAutoTimestamp,
+                                       uncacheBuffer, postTime, privileged, hasListenerCallbacks,
+                                       listenerCallbacks, originPid, originUid, transactionId);
     }
 
-    const bool pendingTransactions = itr != mTransactionQueues.end();
-    // Expected present time is computed and cached on invalidate, so it may be stale.
-    if (!pendingTransactions) {
-        const auto now = systemTime();
-        const bool nextVsyncPending = now < mExpectedPresentTime.load();
-        const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(now);
-        mExpectedPresentTime = calculateExpectedPresentTime(stats);
-        // The transaction might arrive just before the next vsync but after
-        // invalidate was called. In that case we need to get the next vsync
-        // afterwards.
-        if (nextVsyncPending) {
-            mExpectedPresentTime += stats.vsyncPeriod;
-        }
-    }
+    const auto schedule = [](uint32_t flags) {
+        if (flags & eEarlyWakeup) return TransactionSchedule::Early;
+        if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
+        if (flags & eExplicitEarlyWakeupStart) return TransactionSchedule::EarlyStart;
+        return TransactionSchedule::Late;
+    }(flags);
+    setTransactionFlags(eTransactionFlushNeeded, schedule);
 
-    IPCThreadState* ipc = IPCThreadState::self();
-    const int originPid = ipc->getCallingPid();
-    const int originUid = ipc->getCallingUid();
-
-    if (pendingTransactions ||
-        !transactionIsReadyToBeApplied(isAutoTimestamp ? 0 : desiredPresentTime, states, true)) {
-        mTransactionQueues[applyToken].emplace(frameTimelineVsyncId, states, displays, flags,
-                                               desiredPresentTime, isAutoTimestamp, uncacheBuffer,
-                                               postTime, privileged, hasListenerCallbacks,
-                                               listenerCallbacks, originPid, originUid,
-                                               transactionId);
-        setTransactionFlags(eTransactionFlushNeeded);
+    // if this is a synchronous transaction, wait for it to take effect
+    // before returning.
+    const bool synchronous = flags & eSynchronous;
+    const bool syncInput = inputWindowCommands.syncInputWindows;
+    if (!synchronous && !syncInput) {
         return NO_ERROR;
     }
 
-    applyTransactionState(frameTimelineVsyncId, states, displays, flags, inputWindowCommands,
-                          desiredPresentTime, isAutoTimestamp, uncacheBuffer, postTime, privileged,
-                          hasListenerCallbacks, listenerCallbacks, originPid, originUid,
-                          transactionId, /*isMainThread*/ false);
-    return NO_ERROR;
-}
+    // Handle synchronous cases.
+    {
+        Mutex::Autolock _l(mStateLock);
+        if (synchronous) {
+            mTransactionPending = true;
+        }
+        if (syncInput) {
+            mPendingSyncInputWindows = true;
+        }
 
-void SurfaceFlinger::applyTransactionState(
-        int64_t frameTimelineVsyncId, const Vector<ComposerState>& states,
-        const Vector<DisplayState>& displays, uint32_t flags,
-        const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
-        bool isAutoTimestamp, const client_cache_t& uncacheBuffer, const int64_t postTime,
-        bool privileged, bool hasListenerCallbacks,
-        const std::vector<ListenerCallbacks>& listenerCallbacks, int originPid, int originUid,
-        uint64_t transactionId, bool isMainThread) {
-    uint32_t transactionFlags = 0;
-
-    if (flags & eAnimation) {
-        // For window updates that are part of an animation we must wait for
-        // previous animation "frames" to be handled.
-        while (!isMainThread && mAnimTransactionPending) {
+        // applyTransactionState can be called by either the main SF thread or by
+        // another process through setTransactionState.  While a given process may wish
+        // to wait on synchronous transactions, the main SF thread should never
+        // be blocked.  Therefore, we only wait if isMainThread is false.
+        while (mTransactionPending || mPendingSyncInputWindows) {
             status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
             if (CC_UNLIKELY(err != NO_ERROR)) {
                 // just in case something goes wrong in SF, return to the
-                // caller after a few seconds.
-                ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out "
-                        "waiting for previous animation frame");
-                mAnimTransactionPending = false;
+                // called after a few seconds.
+                ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out!");
+                mTransactionPending = false;
+                mPendingSyncInputWindows = false;
                 break;
             }
         }
     }
+    return NO_ERROR;
+}
+
+void SurfaceFlinger::flushTransactionQueue() {
+    std::vector<TransactionState> transactionQueue;
+    {
+        Mutex::Autolock _l(mQueueLock);
+        if (!mTransactionQueue.empty()) {
+            transactionQueue.swap(mTransactionQueue);
+        }
+        mTransactionQueueCV.broadcast();
+    }
+
+    Mutex::Autolock _l(mStateLock);
+    for (const auto& t : transactionQueue) {
+        applyTransactionState(t.frameTimelineVsyncId, t.states, t.displays, t.flags,
+                              t.inputWindowCommands, t.desiredPresentTime, t.isAutoTimestamp,
+                              t.buffer, t.postTime, t.privileged, t.hasListenerCallbacks,
+                              t.listenerCallbacks, t.originPid, t.originUid, t.id);
+    }
+}
+
+void SurfaceFlinger::applyTransactionState(int64_t frameTimelineVsyncId,
+                                           const Vector<ComposerState>& states,
+                                           const Vector<DisplayState>& displays, uint32_t flags,
+                                           const InputWindowCommands& inputWindowCommands,
+                                           const int64_t desiredPresentTime, bool isAutoTimestamp,
+                                           const client_cache_t& uncacheBuffer,
+                                           const int64_t postTime, bool privileged,
+                                           bool hasListenerCallbacks,
+                                           const std::vector<ListenerCallbacks>& listenerCallbacks,
+                                           int originPid, int originUid, uint64_t transactionId) {
+    uint32_t transactionFlags = 0;
 
     for (const DisplayState& display : displays) {
         transactionFlags |= setDisplayStateLocked(display);
@@ -3477,80 +3539,25 @@
         transactionFlags = eTransactionNeeded;
     }
 
-    // If we are on the main thread, we are about to preform a traversal. Clear the traversal bit
-    // so we don't have to wake up again next frame to preform an uneeded traversal.
-    if (isMainThread && (transactionFlags & eTraversalNeeded)) {
-        transactionFlags = transactionFlags & (~eTraversalNeeded);
-        mForceTraversal = true;
-    }
-
-    const auto schedule = [](uint32_t flags) {
-        if (flags & eEarlyWakeup) return TransactionSchedule::Early;
-        if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
-        if (flags & eExplicitEarlyWakeupStart) return TransactionSchedule::EarlyStart;
-        return TransactionSchedule::Late;
-    }(flags);
-
     if (transactionFlags) {
         if (mInterceptor->isEnabled()) {
             mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags,
                                           originPid, originUid, transactionId);
         }
 
-        // TODO(b/159125966): Remove eEarlyWakeup completly as no client should use this flag
-        if (flags & eEarlyWakeup) {
-            ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
+        // We are on the main thread, we are about to preform a traversal. Clear the traversal bit
+        // so we don't have to wake up again next frame to preform an unnecessary traversal.
+        if (transactionFlags & eTraversalNeeded) {
+            transactionFlags = transactionFlags & (~eTraversalNeeded);
+            mForceTraversal = true;
         }
-
-        if (!privileged && (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
-            ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
-            flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
+        if (transactionFlags) {
+            setTransactionFlags(transactionFlags);
         }
 
-        // this triggers the transaction
-        setTransactionFlags(transactionFlags, schedule);
-
         if (flags & eAnimation) {
             mAnimTransactionPending = true;
         }
-
-        // if this is a synchronous transaction, wait for it to take effect
-        // before returning.
-        const bool synchronous = flags & eSynchronous;
-        const bool syncInput = inputWindowCommands.syncInputWindows;
-        if (!synchronous && !syncInput) {
-            return;
-        }
-
-        if (synchronous) {
-            mTransactionPending = true;
-        }
-        if (syncInput) {
-            mPendingSyncInputWindows = true;
-        }
-
-
-        // applyTransactionState can be called by either the main SF thread or by
-        // another process through setTransactionState.  While a given process may wish
-        // to wait on synchronous transactions, the main SF thread should never
-        // be blocked.  Therefore, we only wait if isMainThread is false.
-        while (!isMainThread && (mTransactionPending || mPendingSyncInputWindows)) {
-            status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
-            if (CC_UNLIKELY(err != NO_ERROR)) {
-                // just in case something goes wrong in SF, return to the
-                // called after a few seconds.
-                ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out!");
-                mTransactionPending = false;
-                mPendingSyncInputWindows = false;
-                break;
-            }
-        }
-    } else {
-        // Update VsyncModulator state machine even if transaction is not needed.
-        if (schedule == TransactionSchedule::EarlyStart ||
-            schedule == TransactionSchedule::EarlyEnd) {
-            modulateVsync(&VsyncModulator::setTransactionSchedule, schedule);
-        }
     }
 }
 
@@ -3840,7 +3847,9 @@
             ALOGE("Attempt to update InputWindowInfo without permission ACCESS_SURFACE_FLINGER");
         }
     }
+    std::optional<nsecs_t> dequeueBufferTimestamp;
     if (what & layer_state_t::eMetadataChanged) {
+        dequeueBufferTimestamp = s.metadata.getInt64(METADATA_DEQUEUE_TIME);
         if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eColorSpaceAgnosticChanged) {
@@ -3928,7 +3937,7 @@
                 : layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1;
 
         if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp,
-                             s.cachedBuffer, frameNumber)) {
+                             s.cachedBuffer, frameNumber, dequeueBufferTimestamp)) {
             flags |= eTraversalNeeded;
         }
     }
@@ -3940,7 +3949,7 @@
 }
 
 uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& inputWindowCommands) {
-    bool hasChanges = mPendingInputWindowCommands.merge(inputWindowCommands);
+    bool hasChanges = mInputWindowCommands.merge(inputWindowCommands);
     return hasChanges ? eTraversalNeeded : 0;
 }
 
@@ -4205,9 +4214,11 @@
     d.width = 0;
     d.height = 0;
     displays.add(d);
-    setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, state, displays, 0, nullptr,
-                        mPendingInputWindowCommands, systemTime(), true, {}, false, {},
-                        0 /* Undefined transactionId */);
+
+    // This called on the main thread, apply it directly.
+    applyTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, state, displays, 0,
+                          mInputWindowCommands, systemTime(), true, {}, systemTime(), true, false,
+                          {}, getpid(), getuid(), 0 /* Undefined transactionId */);
 
     setPowerModeInternal(display, hal::PowerMode::ON);
     const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
@@ -4507,12 +4518,9 @@
 
 void SurfaceFlinger::dumpFrameEventsLocked(std::string& result) {
     result.append("Layer frame timestamps:\n");
-
-    const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
-    const size_t count = currentLayers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        currentLayers[i]->dumpFrameEvents(result);
-    }
+    // Traverse all layers to dump frame-events for each layer
+    mCurrentState.traverseInZOrder(
+        [&] (Layer* layer) { layer->dumpFrameEvents(result); });
 }
 
 void SurfaceFlinger::dumpBufferingStats(std::string& result) const {
@@ -5387,7 +5395,7 @@
 
 void SurfaceFlinger::repaintEverything() {
     mRepaintEverything = true;
-    signalTransaction();
+    setTransactionFlags(eTransactionNeeded);
 }
 
 void SurfaceFlinger::repaintEverythingForHWC() {
@@ -5406,16 +5414,16 @@
     // mRefreshRateConfigs->getCurrentRefreshRate()
     static_cast<void>(schedule([=] {
         const auto desiredActiveConfig = getDesiredActiveConfig();
-        const auto& current = desiredActiveConfig
-                ? mRefreshRateConfigs->getRefreshRateFromConfigId(desiredActiveConfig->configId)
-                : mRefreshRateConfigs->getCurrentRefreshRate();
-        const auto& min = mRefreshRateConfigs->getMinRefreshRate();
+        const std::optional<HwcConfigIndexType> desiredConfigId = desiredActiveConfig
+                ? std::make_optional(desiredActiveConfig->configId)
+                : std::nullopt;
 
-        if (current != min) {
-            const bool timerExpired = mKernelIdleTimerEnabled && expired;
-
+        const bool timerExpired = mKernelIdleTimerEnabled && expired;
+        const auto newRefreshRate =
+                mRefreshRateConfigs->onKernelTimerChanged(desiredConfigId, timerExpired);
+        if (newRefreshRate) {
             if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) {
-                mRefreshRateOverlay->changeRefreshRate(timerExpired ? min : current);
+                mRefreshRateOverlay->changeRefreshRate(*newRefreshRate);
             }
             mEventQueue->invalidate();
         }
@@ -6065,7 +6073,7 @@
     toggleKernelIdleTimer();
 
     auto configId = mScheduler->getPreferredConfigId();
-    auto& preferredRefreshRate = configId
+    auto preferredRefreshRate = configId
             ? mRefreshRateConfigs->getRefreshRateFromConfigId(*configId)
             // NOTE: Choose the default config ID, if Scheduler doesn't have one in mind.
             : mRefreshRateConfigs->getRefreshRateFromConfigId(currentPolicy.defaultConfig);
@@ -6371,7 +6379,8 @@
                 mRefreshRateOverlay->setViewport(display->getSize());
             }
 
-            mRefreshRateOverlay->changeRefreshRate(mRefreshRateConfigs->getCurrentRefreshRate());
+            mRefreshRateOverlay->changeRefreshRate(
+                    mRefreshRateConfigs->getCurrentRefreshRate().getFps());
         }
     }));
 }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 9f0f2ea..bae15dc 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -99,6 +99,8 @@
 class TimeStats;
 class FrameTracer;
 
+using gui::ScreenCaptureResults;
+
 namespace frametimeline {
 class FrameTimeline;
 }
@@ -434,15 +436,16 @@
     struct TransactionState {
         TransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& composerStates,
                          const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
-                         int64_t desiredPresentTime, bool isAutoTimestamp,
-                         const client_cache_t& uncacheBuffer, int64_t postTime, bool privileged,
-                         bool hasListenerCallbacks,
+                         const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+                         bool isAutoTimestamp, const client_cache_t& uncacheBuffer,
+                         int64_t postTime, bool privileged, bool hasListenerCallbacks,
                          std::vector<ListenerCallbacks> listenerCallbacks, int originPid,
                          int originUid, uint64_t transactionId)
               : frameTimelineVsyncId(frameTimelineVsyncId),
                 states(composerStates),
                 displays(displayStates),
                 flags(transactionFlags),
+                inputWindowCommands(inputWindowCommands),
                 desiredPresentTime(desiredPresentTime),
                 isAutoTimestamp(isAutoTimestamp),
                 buffer(uncacheBuffer),
@@ -458,6 +461,7 @@
         Vector<ComposerState> states;
         Vector<DisplayState> displays;
         uint32_t flags;
+        InputWindowCommands inputWindowCommands;
         const int64_t desiredPresentTime;
         const bool isAutoTimestamp;
         client_cache_t buffer;
@@ -723,6 +727,7 @@
     /*
      * Transactions
      */
+    void flushTransactionQueue();
     void applyTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
                                const Vector<DisplayState>& displays, uint32_t flags,
                                const InputWindowCommands& inputWindowCommands,
@@ -730,10 +735,12 @@
                                const client_cache_t& uncacheBuffer, const int64_t postTime,
                                bool privileged, bool hasListenerCallbacks,
                                const std::vector<ListenerCallbacks>& listenerCallbacks,
-                               int originPid, int originUid, uint64_t transactionId,
-                               bool isMainThread = false) REQUIRES(mStateLock);
+                               int originPid, int originUid, uint64_t transactionId)
+            REQUIRES(mStateLock);
     // Returns true if at least one transaction was flushed
     bool flushTransactionQueues();
+    // flush pending transaction that was presented after desiredPresentTime.
+    void flushPendingTransactionQueues();
     // Returns true if there is at least one transaction that needs to be flushed
     bool transactionFlushNeeded();
     uint32_t getTransactionFlags(uint32_t flags);
@@ -751,7 +758,7 @@
     void commitOffscreenLayers();
     bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
                                        const Vector<ComposerState>& states,
-                                       bool updateTransactionCounters = false) REQUIRES(mStateLock);
+                                       bool updateTransactionCounters = false);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
             REQUIRES(mStateLock);
@@ -1168,8 +1175,11 @@
     uint32_t mTexturePoolSize = 0;
     std::vector<uint32_t> mTexturePool;
 
-    std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> mTransactionQueues;
-
+    mutable Mutex mQueueLock;
+    Condition mTransactionQueueCV;
+    std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
+            mPendingTransactionQueues GUARDED_BY(mQueueLock);
+    std::vector<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock);
     /*
      * Feature prototyping
      */
@@ -1246,7 +1256,6 @@
     const float mEmulatedDisplayDensity;
 
     sp<os::IInputFlinger> mInputFlinger;
-    InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock);
     // Should only be accessed by the main thread.
     InputWindowCommands mInputWindowCommands;
 
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 97725ec..c043866 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -376,5 +376,9 @@
             defaultValue);
 }
 
+bool enable_frame_rate_override(bool defaultValue) {
+    return SurfaceFlingerProperties::enable_frame_rate_override().value_or(defaultValue);
+}
+
 } // namespace sysprop
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 37a6b40..816cb09 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -98,6 +98,8 @@
 
 bool update_device_product_info_on_hotplug_reconnect(bool defaultValue);
 
+bool enable_frame_rate_override(bool defaultValue);
+
 } // namespace sysprop
 } // namespace android
 #endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 421484f..4d25a7a 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -445,3 +445,12 @@
     access: Readonly
     prop_name: "ro.surface_flinger.update_device_product_info_on_hotplug_reconnect"
 }
+
+# Enables the frame rate override feature
+prop {
+    api_name: "enable_frame_rate_override"
+    type: Boolean
+    scope: Public
+    access: Readonly
+    prop_name: "ro.surface_flinger.enable_frame_rate_override"
+}
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index da66ece..0e0be09 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -41,6 +41,10 @@
     prop_name: "ro.surface_flinger.display_update_imminent_timeout_ms"
   }
   prop {
+    api_name: "enable_frame_rate_override"
+    prop_name: "ro.surface_flinger.enable_frame_rate_override"
+  }
+  prop {
     api_name: "enable_protected_contents"
     prop_name: "ro.surface_flinger.protected_contents"
   }
diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp
index e66df4a..ecfed13 100644
--- a/services/surfaceflinger/tests/LayerState_test.cpp
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -22,6 +22,8 @@
 #include <gui/LayerState.h>
 
 namespace android {
+using gui::ScreenCaptureResults;
+
 namespace test {
 
 TEST(LayerStateTest, ParcellingDisplayCaptureArgs) {
@@ -86,11 +88,11 @@
     results.result = BAD_VALUE;
 
     Parcel p;
-    results.write(p);
+    results.writeToParcel(&p);
     p.setDataPosition(0);
 
     ScreenCaptureResults results2;
-    results2.read(p);
+    results2.readFromParcel(&p);
 
     // GraphicBuffer object is reallocated so compare the data in the graphic buffer
     // rather than the object itself
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index a361b1e..3ef63d5 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -68,6 +68,9 @@
                                        Rect(displayState.layerStackSpaceRect), Rect(resolution));
                 t.apply();
                 SurfaceComposerClient::Transaction().apply(true);
+                // wait for 3 vsyncs to ensure the buffer is latched.
+                usleep(static_cast<int32_t>(1e6 / displayConfig.refreshRate) * 3);
+
                 BufferItem item;
                 itemConsumer->acquireBuffer(&item, 0, true);
                 auto sc = std::make_unique<ScreenCapture>(item.mGraphicBuffer);
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 169698b..e2584e2 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -33,8 +33,13 @@
 using testing::AtLeast;
 using testing::Contains;
 using FrameTimelineEvent = perfetto::protos::FrameTimelineEvent;
-using ProtoDisplayFrame = perfetto::protos::FrameTimelineEvent_DisplayFrame;
-using ProtoSurfaceFrame = perfetto::protos::FrameTimelineEvent_SurfaceFrame;
+using ProtoExpectedDisplayFrameStart =
+        perfetto::protos::FrameTimelineEvent_ExpectedDisplayFrameStart;
+using ProtoExpectedSurfaceFrameStart =
+        perfetto::protos::FrameTimelineEvent_ExpectedSurfaceFrameStart;
+using ProtoActualDisplayFrameStart = perfetto::protos::FrameTimelineEvent_ActualDisplayFrameStart;
+using ProtoActualSurfaceFrameStart = perfetto::protos::FrameTimelineEvent_ActualSurfaceFrameStart;
+using ProtoFrameEnd = perfetto::protos::FrameTimelineEvent_FrameEnd;
 using ProtoPresentType = perfetto::protos::FrameTimelineEvent_PresentType;
 using ProtoJankType = perfetto::protos::FrameTimelineEvent_JankType;
 
@@ -67,10 +72,11 @@
 
     void SetUp() override {
         mTimeStats = std::make_shared<mock::TimeStats>();
-        mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, mSurfaceFlingerPid,
+        mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, kSurfaceFlingerPid,
                                                                kTestThresholds);
         mFrameTimeline->registerDataSource();
         mTokenManager = &mFrameTimeline->mTokenManager;
+        mTraceCookieCounter = &mFrameTimeline->mTraceCookieCounter;
         maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
         maxTokenRetentionTime = mTokenManager->kMaxRetentionTime;
     }
@@ -130,22 +136,31 @@
                 a.presentTime == b.presentTime;
     }
 
-    const std::map<int64_t, TokenManagerPrediction>& getPredictions() {
+    const std::map<int64_t, TokenManagerPrediction>& getPredictions() const {
         return mTokenManager->mPredictions;
     }
 
-    uint32_t getNumberOfDisplayFrames() {
+    uint32_t getNumberOfDisplayFrames() const {
         std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex);
         return static_cast<uint32_t>(mFrameTimeline->mDisplayFrames.size());
     }
 
+    int64_t snoopCurrentTraceCookie() const { return mTraceCookieCounter->mTraceCookie; }
+
+    void flushTrace() {
+        using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource;
+        FrameTimelineDataSource::Trace(
+                [&](FrameTimelineDataSource::TraceContext ctx) { ctx.Flush(); });
+    }
+
     std::shared_ptr<mock::TimeStats> mTimeStats;
     std::unique_ptr<impl::FrameTimeline> mFrameTimeline;
     impl::TokenManager* mTokenManager;
+    TraceCookieCounter* mTraceCookieCounter;
     FenceToFenceTimeMap fenceFactory;
     uint32_t* maxDisplayFrames;
     nsecs_t maxTokenRetentionTime;
-    pid_t mSurfaceFlingerPid = 666;
+    static constexpr pid_t kSurfaceFlingerPid = 666;
     static constexpr nsecs_t kPresentThreshold =
             std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count();
     static constexpr nsecs_t kDeadlineThreshold =
@@ -549,7 +564,7 @@
 TEST_F(FrameTimelineTest, tracing_sanityTest) {
     auto tracingSession = getTracingSessionForTest();
     // Global increment
-    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
     // Layer specific increment
     EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -574,23 +589,18 @@
     mFrameTimeline->setSfPresent(55, presentFence2);
     presentFence2->signalForTest(55);
 
-    // The SurfaceFrame packet from the first frame is emitted, but not flushed yet. Emitting a new
-    // packet will flush it. To emit a new packet, we'll need to call flushPendingPresentFences()
-    // again, which is done by setSfPresent().
-    addEmptyDisplayFrame();
+    flushTrace();
     tracingSession->StopBlocking();
 
     auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
-    // Display Frame 1 has two packets - DisplayFrame and a SurfaceFrame.
-    // Display Frame 2 has one packet - DisplayFrame. However, this packet has been emitted but not
-    // flushed through traced, so this is not counted.
-    EXPECT_EQ(packets.size(), 2);
+    // Display Frame 1 has 8 packets - 4 from DisplayFrame and 4 from SurfaceFrame.
+    EXPECT_EQ(packets.size(), 8);
 }
 
 TEST_F(FrameTimelineTest, traceDisplayFrame_invalidTokenDoesNotEmitTracePacket) {
     auto tracingSession = getTracingSessionForTest();
     // Global increment
-    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
@@ -608,20 +618,17 @@
     mFrameTimeline->setSfPresent(55, presentFence2);
     presentFence2->signalForTest(60);
 
-    addEmptyDisplayFrame();
+    flushTrace();
     tracingSession->StopBlocking();
 
     auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
-    // Display Frame 1 has no packets.
-    // Display Frame 2 has one packet - DisplayFrame. However, this packet has
-    // been emitted but not flushed through traced, so this is not counted.
     EXPECT_EQ(packets.size(), 0);
 }
 
 TEST_F(FrameTimelineTest, traceSurfaceFrame_invalidTokenDoesNotEmitTracePacket) {
     auto tracingSession = getTracingSessionForTest();
     // Global increment
-    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
@@ -644,21 +651,38 @@
     mFrameTimeline->setSfPresent(55, presentFence2);
     presentFence2->signalForTest(60);
 
-    addEmptyDisplayFrame();
+    flushTrace();
     tracingSession->StopBlocking();
 
     auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
-    // Display Frame 1 has one packet - DisplayFrame (SurfaceFrame shouldn't be traced since it has
-    // an invalid token).
-    // Display Frame 2 has one packet - DisplayFrame. However, this packet has
-    // been emitted but not flushed through traced, so this is not counted.
-    EXPECT_EQ(packets.size(), 1);
+    // Display Frame 1 has 4 packets (SurfaceFrame shouldn't be traced since it has an invalid
+    // token).
+    EXPECT_EQ(packets.size(), 4);
 }
 
-void validateDisplayFrameEvent(const ProtoDisplayFrame& received, const ProtoDisplayFrame& source) {
+void validateTraceEvent(const ProtoExpectedDisplayFrameStart& received,
+                        const ProtoExpectedDisplayFrameStart& source) {
+    ASSERT_TRUE(received.has_cookie());
+    EXPECT_EQ(received.cookie(), source.cookie());
+
     ASSERT_TRUE(received.has_token());
     EXPECT_EQ(received.token(), source.token());
 
+    ASSERT_TRUE(received.has_pid());
+    EXPECT_EQ(received.pid(), source.pid());
+}
+
+void validateTraceEvent(const ProtoActualDisplayFrameStart& received,
+                        const ProtoActualDisplayFrameStart& source) {
+    ASSERT_TRUE(received.has_cookie());
+    EXPECT_EQ(received.cookie(), source.cookie());
+
+    ASSERT_TRUE(received.has_token());
+    EXPECT_EQ(received.token(), source.token());
+
+    ASSERT_TRUE(received.has_pid());
+    EXPECT_EQ(received.pid(), source.pid());
+
     ASSERT_TRUE(received.has_present_type());
     EXPECT_EQ(received.present_type(), source.present_type());
     ASSERT_TRUE(received.has_on_time_finish());
@@ -667,25 +691,43 @@
     EXPECT_EQ(received.gpu_composition(), source.gpu_composition());
     ASSERT_TRUE(received.has_jank_type());
     EXPECT_EQ(received.jank_type(), source.jank_type());
-
-    ASSERT_TRUE(received.has_expected_start_ns());
-    EXPECT_EQ(received.expected_start_ns(), source.expected_start_ns());
-    ASSERT_TRUE(received.has_expected_end_ns());
-    EXPECT_EQ(received.expected_end_ns(), source.expected_end_ns());
-
-    ASSERT_TRUE(received.has_actual_start_ns());
-    EXPECT_EQ(received.actual_start_ns(), source.actual_start_ns());
-    ASSERT_TRUE(received.has_actual_end_ns());
-    EXPECT_EQ(received.actual_end_ns(), source.actual_end_ns());
 }
 
-void validateSurfaceFrameEvent(const ProtoSurfaceFrame& received, const ProtoSurfaceFrame& source) {
+void validateTraceEvent(const ProtoExpectedSurfaceFrameStart& received,
+                        const ProtoExpectedSurfaceFrameStart& source) {
+    ASSERT_TRUE(received.has_cookie());
+    EXPECT_EQ(received.cookie(), source.cookie());
+
     ASSERT_TRUE(received.has_token());
     EXPECT_EQ(received.token(), source.token());
 
     ASSERT_TRUE(received.has_display_frame_token());
     EXPECT_EQ(received.display_frame_token(), source.display_frame_token());
 
+    ASSERT_TRUE(received.has_pid());
+    EXPECT_EQ(received.pid(), source.pid());
+
+    ASSERT_TRUE(received.has_layer_name());
+    EXPECT_EQ(received.layer_name(), source.layer_name());
+}
+
+void validateTraceEvent(const ProtoActualSurfaceFrameStart& received,
+                        const ProtoActualSurfaceFrameStart& source) {
+    ASSERT_TRUE(received.has_cookie());
+    EXPECT_EQ(received.cookie(), source.cookie());
+
+    ASSERT_TRUE(received.has_token());
+    EXPECT_EQ(received.token(), source.token());
+
+    ASSERT_TRUE(received.has_display_frame_token());
+    EXPECT_EQ(received.display_frame_token(), source.display_frame_token());
+
+    ASSERT_TRUE(received.has_pid());
+    EXPECT_EQ(received.pid(), source.pid());
+
+    ASSERT_TRUE(received.has_layer_name());
+    EXPECT_EQ(received.layer_name(), source.layer_name());
+
     ASSERT_TRUE(received.has_present_type());
     EXPECT_EQ(received.present_type(), source.present_type());
     ASSERT_TRUE(received.has_on_time_finish());
@@ -694,27 +736,17 @@
     EXPECT_EQ(received.gpu_composition(), source.gpu_composition());
     ASSERT_TRUE(received.has_jank_type());
     EXPECT_EQ(received.jank_type(), source.jank_type());
+}
 
-    ASSERT_TRUE(received.has_expected_start_ns());
-    EXPECT_EQ(received.expected_start_ns(), source.expected_start_ns());
-    ASSERT_TRUE(received.has_expected_end_ns());
-    EXPECT_EQ(received.expected_end_ns(), source.expected_end_ns());
-
-    ASSERT_TRUE(received.has_actual_start_ns());
-    EXPECT_EQ(received.actual_start_ns(), source.actual_start_ns());
-    ASSERT_TRUE(received.has_actual_end_ns());
-    EXPECT_EQ(received.actual_end_ns(), source.actual_end_ns());
-
-    ASSERT_TRUE(received.has_layer_name());
-    EXPECT_EQ(received.layer_name(), source.layer_name());
-    ASSERT_TRUE(received.has_pid());
-    EXPECT_EQ(received.pid(), source.pid());
+void validateTraceEvent(const ProtoFrameEnd& received, const ProtoFrameEnd& source) {
+    ASSERT_TRUE(received.has_cookie());
+    EXPECT_EQ(received.cookie(), source.cookie());
 }
 
 TEST_F(FrameTimelineTest, traceDisplayFrame_emitsValidTracePacket) {
     auto tracingSession = getTracingSessionForTest();
     // Global increment
-    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
@@ -727,16 +759,27 @@
     mFrameTimeline->setSfPresent(26, presentFence1);
     presentFence1->signalForTest(31);
 
-    ProtoDisplayFrame protoDisplayFrame;
-    protoDisplayFrame.set_token(displayFrameToken1);
-    protoDisplayFrame.set_present_type(ProtoPresentType(FrameTimelineEvent::PRESENT_ON_TIME));
-    protoDisplayFrame.set_on_time_finish(true);
-    protoDisplayFrame.set_gpu_composition(false);
-    protoDisplayFrame.set_jank_type(ProtoJankType(FrameTimelineEvent::JANK_NONE));
-    protoDisplayFrame.set_expected_start_ns(10);
-    protoDisplayFrame.set_expected_end_ns(25);
-    protoDisplayFrame.set_actual_start_ns(20);
-    protoDisplayFrame.set_actual_end_ns(26);
+    int64_t traceCookie = snoopCurrentTraceCookie();
+    ProtoExpectedDisplayFrameStart protoExpectedDisplayFrameStart;
+    protoExpectedDisplayFrameStart.set_cookie(traceCookie + 1);
+    protoExpectedDisplayFrameStart.set_token(displayFrameToken1);
+    protoExpectedDisplayFrameStart.set_pid(kSurfaceFlingerPid);
+
+    ProtoFrameEnd protoExpectedDisplayFrameEnd;
+    protoExpectedDisplayFrameEnd.set_cookie(traceCookie + 1);
+
+    ProtoActualDisplayFrameStart protoActualDisplayFrameStart;
+    protoActualDisplayFrameStart.set_cookie(traceCookie + 2);
+    protoActualDisplayFrameStart.set_token(displayFrameToken1);
+    protoActualDisplayFrameStart.set_pid(kSurfaceFlingerPid);
+    protoActualDisplayFrameStart.set_present_type(
+            ProtoPresentType(FrameTimelineEvent::PRESENT_ON_TIME));
+    protoActualDisplayFrameStart.set_on_time_finish(true);
+    protoActualDisplayFrameStart.set_gpu_composition(false);
+    protoActualDisplayFrameStart.set_jank_type(ProtoJankType(FrameTimelineEvent::JANK_NONE));
+
+    ProtoFrameEnd protoActualDisplayFrameEnd;
+    protoActualDisplayFrameEnd.set_cookie(traceCookie + 2);
 
     // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
     // next frame
@@ -744,30 +787,61 @@
     mFrameTimeline->setSfPresent(55, presentFence2);
     presentFence2->signalForTest(55);
 
-    addEmptyDisplayFrame();
+    flushTrace();
     tracingSession->StopBlocking();
 
     auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
-    // Display Frame 1 has one packet - DisplayFrame.
-    // Display Frame 2 has one packet - DisplayFrame. However, this packet has been emitted but not
-    // flushed through traced, so this is not counted.
-    EXPECT_EQ(packets.size(), 1);
+    EXPECT_EQ(packets.size(), 4);
 
-    const auto& packet = packets[0];
-    ASSERT_TRUE(packet.has_timestamp());
-    ASSERT_TRUE(packet.has_frame_timeline_event());
+    // Packet - 0 : ExpectedDisplayFrameStart
+    const auto& packet0 = packets[0];
+    ASSERT_TRUE(packet0.has_timestamp());
+    EXPECT_EQ(packet0.timestamp(), 10);
+    ASSERT_TRUE(packet0.has_frame_timeline_event());
 
-    const auto& event = packet.frame_timeline_event();
-    ASSERT_TRUE(event.has_display_frame());
-    ASSERT_FALSE(event.has_surface_frame());
-    const auto& displayFrameEvent = event.display_frame();
-    validateDisplayFrameEvent(displayFrameEvent, protoDisplayFrame);
+    const auto& event0 = packet0.frame_timeline_event();
+    ASSERT_TRUE(event0.has_expected_display_frame_start());
+    const auto& expectedDisplayFrameStart = event0.expected_display_frame_start();
+    validateTraceEvent(expectedDisplayFrameStart, protoExpectedDisplayFrameStart);
+
+    // Packet - 1 : FrameEnd (ExpectedDisplayFrame)
+    const auto& packet1 = packets[1];
+    ASSERT_TRUE(packet1.has_timestamp());
+    EXPECT_EQ(packet1.timestamp(), 25);
+    ASSERT_TRUE(packet1.has_frame_timeline_event());
+
+    const auto& event1 = packet1.frame_timeline_event();
+    ASSERT_TRUE(event1.has_frame_end());
+    const auto& expectedDisplayFrameEnd = event1.frame_end();
+    validateTraceEvent(expectedDisplayFrameEnd, protoExpectedDisplayFrameEnd);
+
+    // Packet - 2 : ActualDisplayFrameStart
+    const auto& packet2 = packets[2];
+    ASSERT_TRUE(packet2.has_timestamp());
+    EXPECT_EQ(packet2.timestamp(), 20);
+    ASSERT_TRUE(packet2.has_frame_timeline_event());
+
+    const auto& event2 = packet2.frame_timeline_event();
+    ASSERT_TRUE(event2.has_actual_display_frame_start());
+    const auto& actualDisplayFrameStart = event2.actual_display_frame_start();
+    validateTraceEvent(actualDisplayFrameStart, protoActualDisplayFrameStart);
+
+    // Packet - 3 : FrameEnd (ActualDisplayFrame)
+    const auto& packet3 = packets[3];
+    ASSERT_TRUE(packet3.has_timestamp());
+    EXPECT_EQ(packet3.timestamp(), 26);
+    ASSERT_TRUE(packet3.has_frame_timeline_event());
+
+    const auto& event3 = packet3.frame_timeline_event();
+    ASSERT_TRUE(event3.has_frame_end());
+    const auto& actualDisplayFrameEnd = event3.frame_end();
+    validateTraceEvent(actualDisplayFrameEnd, protoActualDisplayFrameEnd);
 }
 
 TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) {
     auto tracingSession = getTracingSessionForTest();
     // Global increment
-    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
     // Layer specific increment
     EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -785,19 +859,33 @@
     surfaceFrame1->setActualQueueTime(15);
     surfaceFrame1->setAcquireFenceTime(20);
 
-    ProtoSurfaceFrame protoSurfaceFrame;
-    protoSurfaceFrame.set_token(surfaceFrameToken);
-    protoSurfaceFrame.set_display_frame_token(displayFrameToken1);
-    protoSurfaceFrame.set_present_type(ProtoPresentType(FrameTimelineEvent::PRESENT_ON_TIME));
-    protoSurfaceFrame.set_on_time_finish(true);
-    protoSurfaceFrame.set_gpu_composition(false);
-    protoSurfaceFrame.set_jank_type(ProtoJankType(FrameTimelineEvent::JANK_NONE));
-    protoSurfaceFrame.set_expected_start_ns(10);
-    protoSurfaceFrame.set_expected_end_ns(25);
-    protoSurfaceFrame.set_actual_start_ns(0);
-    protoSurfaceFrame.set_actual_end_ns(20);
-    protoSurfaceFrame.set_layer_name(sLayerNameOne);
-    protoSurfaceFrame.set_pid(sPidOne);
+    // First 2 cookies will be used by the DisplayFrame
+    int64_t traceCookie = snoopCurrentTraceCookie() + 2;
+
+    ProtoExpectedSurfaceFrameStart protoExpectedSurfaceFrameStart;
+    protoExpectedSurfaceFrameStart.set_cookie(traceCookie + 1);
+    protoExpectedSurfaceFrameStart.set_token(surfaceFrameToken);
+    protoExpectedSurfaceFrameStart.set_display_frame_token(displayFrameToken1);
+    protoExpectedSurfaceFrameStart.set_pid(sPidOne);
+    protoExpectedSurfaceFrameStart.set_layer_name(sLayerNameOne);
+
+    ProtoFrameEnd protoExpectedSurfaceFrameEnd;
+    protoExpectedSurfaceFrameEnd.set_cookie(traceCookie + 1);
+
+    ProtoActualSurfaceFrameStart protoActualSurfaceFrameStart;
+    protoActualSurfaceFrameStart.set_cookie(traceCookie + 2);
+    protoActualSurfaceFrameStart.set_token(surfaceFrameToken);
+    protoActualSurfaceFrameStart.set_display_frame_token(displayFrameToken1);
+    protoActualSurfaceFrameStart.set_pid(sPidOne);
+    protoActualSurfaceFrameStart.set_layer_name(sLayerNameOne);
+    protoActualSurfaceFrameStart.set_present_type(
+            ProtoPresentType(FrameTimelineEvent::PRESENT_ON_TIME));
+    protoActualSurfaceFrameStart.set_on_time_finish(true);
+    protoActualSurfaceFrameStart.set_gpu_composition(false);
+    protoActualSurfaceFrameStart.set_jank_type(ProtoJankType(FrameTimelineEvent::JANK_NONE));
+
+    ProtoFrameEnd protoActualSurfaceFrameEnd;
+    protoActualSurfaceFrameEnd.set_cookie(traceCookie + 2);
 
     // Set up the display frame
     mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, 11);
@@ -812,24 +900,55 @@
     mFrameTimeline->setSfPresent(55, presentFence2);
     presentFence2->signalForTest(55);
 
-    addEmptyDisplayFrame();
+    flushTrace();
     tracingSession->StopBlocking();
 
     auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
-    // Display Frame 1 has one packet - DisplayFrame and a SurfaceFrame.
-    // Display Frame 2 has one packet - DisplayFrame. However, this packet has been emitted but not
-    // flushed through traced, so this is not counted.
-    EXPECT_EQ(packets.size(), 2);
+    EXPECT_EQ(packets.size(), 8);
 
-    const auto& packet = packets[1];
-    ASSERT_TRUE(packet.has_timestamp());
-    ASSERT_TRUE(packet.has_frame_timeline_event());
+    // Packet - 4 : ExpectedSurfaceFrameStart
+    const auto& packet4 = packets[4];
+    ASSERT_TRUE(packet4.has_timestamp());
+    EXPECT_EQ(packet4.timestamp(), 10);
+    ASSERT_TRUE(packet4.has_frame_timeline_event());
 
-    const auto& event = packet.frame_timeline_event();
-    ASSERT_TRUE(!event.has_display_frame());
-    ASSERT_TRUE(event.has_surface_frame());
-    const auto& surfaceFrameEvent = event.surface_frame();
-    validateSurfaceFrameEvent(surfaceFrameEvent, protoSurfaceFrame);
+    const auto& event4 = packet4.frame_timeline_event();
+    ASSERT_TRUE(event4.has_expected_surface_frame_start());
+    const auto& expectedSurfaceFrameStart = event4.expected_surface_frame_start();
+    validateTraceEvent(expectedSurfaceFrameStart, protoExpectedSurfaceFrameStart);
+
+    // Packet - 5 : FrameEnd (ExpectedSurfaceFrame)
+    const auto& packet5 = packets[5];
+    ASSERT_TRUE(packet5.has_timestamp());
+    EXPECT_EQ(packet5.timestamp(), 25);
+    ASSERT_TRUE(packet5.has_frame_timeline_event());
+
+    const auto& event5 = packet5.frame_timeline_event();
+    ASSERT_TRUE(event5.has_frame_end());
+    const auto& expectedSurfaceFrameEnd = event5.frame_end();
+    validateTraceEvent(expectedSurfaceFrameEnd, protoExpectedSurfaceFrameEnd);
+
+    // Packet - 6 : ActualSurfaceFrameStart
+    const auto& packet6 = packets[6];
+    ASSERT_TRUE(packet6.has_timestamp());
+    EXPECT_EQ(packet6.timestamp(), 10);
+    ASSERT_TRUE(packet6.has_frame_timeline_event());
+
+    const auto& event6 = packet6.frame_timeline_event();
+    ASSERT_TRUE(event6.has_actual_surface_frame_start());
+    const auto& actualSurfaceFrameStart = event6.actual_surface_frame_start();
+    validateTraceEvent(actualSurfaceFrameStart, protoActualSurfaceFrameStart);
+
+    // Packet - 7 : FrameEnd (ActualSurfaceFrame)
+    const auto& packet7 = packets[7];
+    ASSERT_TRUE(packet7.has_timestamp());
+    EXPECT_EQ(packet7.timestamp(), 20);
+    ASSERT_TRUE(packet7.has_frame_timeline_event());
+
+    const auto& event7 = packet7.frame_timeline_event();
+    ASSERT_TRUE(event7.has_frame_end());
+    const auto& actualSurfaceFrameEnd = event7.frame_end();
+    validateTraceEvent(actualSurfaceFrameEnd, protoActualSurfaceFrameEnd);
 }
 
 // Tests for Jank classification
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 45f0ee6..0813968 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -56,6 +56,21 @@
         return refreshRateConfigs.mKnownFrameRates;
     }
 
+    RefreshRate getMinRefreshRateByPolicy(const RefreshRateConfigs& refreshRateConfigs) {
+        std::lock_guard lock(refreshRateConfigs.mLock);
+        return refreshRateConfigs.getMinRefreshRateByPolicyLocked();
+    }
+
+    RefreshRate getMinSupportedRefreshRate(const RefreshRateConfigs& refreshRateConfigs) {
+        std::lock_guard lock(refreshRateConfigs.mLock);
+        return *refreshRateConfigs.mMinSupportedRefreshRate;
+    }
+
+    RefreshRate getMaxSupportedRefreshRate(const RefreshRateConfigs& refreshRateConfigs) {
+        std::lock_guard lock(refreshRateConfigs.mLock);
+        return *refreshRateConfigs.mMaxSupportedRefreshRate;
+    }
+
     // Test config IDs
     static inline const HwcConfigIndexType HWC_CONFIG_ID_60 = HwcConfigIndexType(0);
     static inline const HwcConfigIndexType HWC_CONFIG_ID_90 = HwcConfigIndexType(1);
@@ -209,13 +224,13 @@
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    const auto& minRate = refreshRateConfigs->getMinRefreshRate();
-    const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate();
+    const auto& minRate = getMinSupportedRefreshRate(*refreshRateConfigs);
+    const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs);
 
     ASSERT_EQ(mExpected60Config, minRate);
     ASSERT_EQ(mExpected90Config, performanceRate);
 
-    const auto& minRateByPolicy = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto& minRateByPolicy = getMinRefreshRateByPolicy(*refreshRateConfigs);
     const auto& performanceRateByPolicy = refreshRateConfigs->getMaxRefreshRateByPolicy();
     ASSERT_EQ(minRateByPolicy, minRate);
     ASSERT_EQ(performanceRateByPolicy, performanceRate);
@@ -226,9 +241,9 @@
             std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    const auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
-    const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate();
-    const auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto& minRate = getMinRefreshRateByPolicy(*refreshRateConfigs);
+    const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs);
+    const auto& minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs);
     const auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
 
     ASSERT_EQ(mExpected60Config, minRate);
@@ -239,7 +254,7 @@
               0);
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
 
-    const auto& minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs);
     const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
 
     ASSERT_EQ(mExpected90DifferentGroupConfig, performanceRate);
@@ -252,9 +267,9 @@
             std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentResolutions,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    const auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
-    const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate();
-    const auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto& minRate = getMinRefreshRateByPolicy(*refreshRateConfigs);
+    const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs);
+    const auto& minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs);
     const auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
 
     ASSERT_EQ(mExpected60Config, minRate);
@@ -265,7 +280,7 @@
               0);
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
 
-    const auto& minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs);
     const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
 
     ASSERT_EQ(mExpected90DifferentResolutionConfig, performanceRate);
@@ -278,8 +293,8 @@
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
-    auto& performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy();
+    auto minRate = getMinRefreshRateByPolicy(*refreshRateConfigs);
+    auto performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy();
 
     ASSERT_EQ(mExpected60Config, minRate);
     ASSERT_EQ(mExpected90Config, performanceRate);
@@ -287,8 +302,8 @@
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
               0);
 
-    auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
-    auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+    auto minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs);
+    auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
     ASSERT_EQ(mExpected60Config, minRate60);
     ASSERT_EQ(mExpected60Config, performanceRate60);
 }
@@ -298,20 +313,20 @@
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
     {
-        auto& current = refreshRateConfigs->getCurrentRefreshRate();
+        auto current = refreshRateConfigs->getCurrentRefreshRate();
         EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_60);
     }
 
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
     {
-        auto& current = refreshRateConfigs->getCurrentRefreshRate();
+        auto current = refreshRateConfigs->getCurrentRefreshRate();
         EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_90);
     }
 
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(90), Fps(90)}}),
               0);
     {
-        auto& current = refreshRateConfigs->getCurrentRefreshRate();
+        auto current = refreshRateConfigs->getCurrentRefreshRate();
         EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_90);
     }
 }
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
index 65efc85..8552e15 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
@@ -604,11 +604,11 @@
     EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect());
 }
 
-TEST_F(HandleTransactionLockedTest, processesDisplayFrameChanges) {
+TEST_F(HandleTransactionLockedTest, processesDisplayRectChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
-    const Rect oldFrame(0, 0, 0, 0);
-    const Rect newFrame(0, 0, 123, 456);
+    const Rect oldDisplayRect(0, 0);
+    const Rect newDisplayRect(123, 456);
 
     // --------------------------------------------------------------------
     // Preconditions
@@ -618,8 +618,8 @@
     display.inject();
 
     // There is a change to the layerStackSpaceRect state
-    display.mutableDrawingDisplayState().orientedDisplaySpaceRect = oldFrame;
-    display.mutableCurrentDisplayState().orientedDisplaySpaceRect = newFrame;
+    display.mutableDrawingDisplayState().orientedDisplaySpaceRect = oldDisplayRect;
+    display.mutableCurrentDisplayState().orientedDisplaySpaceRect = newDisplayRect;
 
     // --------------------------------------------------------------------
     // Invocation
@@ -629,7 +629,7 @@
     // --------------------------------------------------------------------
     // Postconditions
 
-    EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect());
+    EXPECT_EQ(newDisplayRect, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect());
 }
 
 TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) {
@@ -722,5 +722,61 @@
     mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
 }
 
+TEST_F(HandleTransactionLockedTest, processesDisplaySizeDisplayRectAndLayerStackRectChanges) {
+    using Case = NonHwcVirtualDisplayCase;
+
+    constexpr uint32_t kOldWidth = 567;
+    constexpr uint32_t kOldHeight = 456;
+    const auto kOldSize = Rect(kOldWidth, kOldHeight);
+
+    constexpr uint32_t kNewWidth = 234;
+    constexpr uint32_t kNewHeight = 123;
+    const auto kNewSize = Rect(kNewWidth, kNewHeight);
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is set up
+    auto nativeWindow = new mock::NativeWindow();
+    auto displaySurface = new compositionengine::mock::DisplaySurface();
+    sp<GraphicBuffer> buf = new GraphicBuffer();
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.setNativeWindow(nativeWindow);
+    display.setDisplaySurface(displaySurface);
+    // Setup injection expectations
+    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+            .WillOnce(DoAll(SetArgPointee<1>(kOldWidth), Return(0)));
+    EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+            .WillOnce(DoAll(SetArgPointee<1>(kOldHeight), Return(0)));
+    display.inject();
+
+    // There is a change to the layerStackSpaceRect state
+    display.mutableDrawingDisplayState().width = kOldWidth;
+    display.mutableDrawingDisplayState().height = kOldHeight;
+    display.mutableDrawingDisplayState().layerStackSpaceRect = kOldSize;
+    display.mutableDrawingDisplayState().orientedDisplaySpaceRect = kOldSize;
+
+    display.mutableCurrentDisplayState().width = kNewWidth;
+    display.mutableCurrentDisplayState().height = kNewHeight;
+    display.mutableCurrentDisplayState().layerStackSpaceRect = kNewSize;
+    display.mutableCurrentDisplayState().orientedDisplaySpaceRect = kNewSize;
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    EXPECT_CALL(*displaySurface, resizeBuffers(kNewWidth, kNewHeight)).Times(1);
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+    EXPECT_EQ(display.mutableDisplayDevice()->getBounds(), kNewSize);
+    EXPECT_EQ(display.mutableDisplayDevice()->getWidth(), kNewWidth);
+    EXPECT_EQ(display.mutableDisplayDevice()->getHeight(), kNewHeight);
+    EXPECT_EQ(display.mutableDisplayDevice()->getOrientedDisplaySpaceRect(), kNewSize);
+    EXPECT_EQ(display.mutableDisplayDevice()->getLayerStackSpaceRect(), kNewSize);
+}
+
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index b57d473..739a9b2 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -23,6 +23,7 @@
 #include <compositionengine/impl/Display.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/mock/DisplaySurface.h>
+#include <gui/ScreenCaptureResults.h>
 
 #include "BufferQueueLayer.h"
 #include "BufferStateLayer.h"
@@ -365,7 +366,7 @@
         return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
     }
 
-    auto& getTransactionQueue() { return mFlinger->mTransactionQueues; }
+    auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
 
     auto setTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& states,
                              const Vector<DisplayState>& displays, uint32_t flags,
@@ -381,7 +382,7 @@
                                              listenerCallbacks, transactionId);
     }
 
-    auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(); };
+    auto flushPendingTransactionQueues() { return mFlinger->flushPendingTransactionQueues(); };
 
     auto onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
         return mFlinger->onTransact(code, data, reply, flags);
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 06275c6..a4a4408 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -127,7 +127,7 @@
     }
 
     void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
-        ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
+        ASSERT_EQ(0, mFlinger.getPendingTransactionQueue().size());
         // called in SurfaceFlinger::signalTransaction
         EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
         EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillOnce(Return(systemTime()));
@@ -154,12 +154,12 @@
         } else {
             EXPECT_LE(returnedTime, applicationTime + s2ns(5));
         }
-        auto transactionQueue = mFlinger.getTransactionQueue();
+        auto transactionQueue = mFlinger.getPendingTransactionQueue();
         EXPECT_EQ(0, transactionQueue.size());
     }
 
     void PlaceOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
-        ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
+        ASSERT_EQ(0, mFlinger.getPendingTransactionQueue().size());
         // called in SurfaceFlinger::signalTransaction
         EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
 
@@ -183,12 +183,12 @@
         nsecs_t returnedTime = systemTime();
         EXPECT_LE(returnedTime, applicationSentTime + s2ns(5));
         // This transaction should have been placed on the transaction queue
-        auto transactionQueue = mFlinger.getTransactionQueue();
+        auto transactionQueue = mFlinger.getPendingTransactionQueue();
         EXPECT_EQ(1, transactionQueue.size());
     }
 
     void BlockedByPriorTransaction(uint32_t flags, bool syncInputWindows) {
-        ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
+        ASSERT_EQ(0, mFlinger.getPendingTransactionQueue().size());
         // called in SurfaceFlinger::signalTransaction
         nsecs_t time = systemTime();
         EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
@@ -239,7 +239,7 @@
         }
 
         // check that there is one binder on the pending queue.
-        auto transactionQueue = mFlinger.getTransactionQueue();
+        auto transactionQueue = mFlinger.getPendingTransactionQueue();
         EXPECT_EQ(1, transactionQueue.size());
 
         auto& [applyToken, transactionStates] = *(transactionQueue.begin());
@@ -258,7 +258,7 @@
 };
 
 TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
-    ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
+    ASSERT_EQ(0, mFlinger.getPendingTransactionQueue().size());
     // called in SurfaceFlinger::signalTransaction
     EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
 
@@ -275,7 +275,7 @@
                                  transactionA.isAutoTimestamp, transactionA.uncacheBuffer,
                                  mHasListenerCallbacks, mCallbacks, transactionA.id);
 
-    auto& transactionQueue = mFlinger.getTransactionQueue();
+    auto& transactionQueue = mFlinger.getPendingTransactionQueue();
     ASSERT_EQ(1, transactionQueue.size());
 
     auto& [applyToken, transactionStates] = *(transactionQueue.begin());
@@ -294,9 +294,9 @@
                                  empty.desiredPresentTime, empty.isAutoTimestamp,
                                  empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks, empty.id);
 
-    // flush transaction queue should flush as desiredPresentTime has
+    // flush pending transaction queue should flush as desiredPresentTime has
     // passed
-    mFlinger.flushTransactionQueues();
+    mFlinger.flushPendingTransactionQueues();
 
     EXPECT_EQ(0, transactionQueue.size());
 }