Merge "Delete non-colormanagement code" into main
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index 4842312..615701c 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -207,9 +207,7 @@
 int PropertiesHelper::dry_run_ = -1;
 int PropertiesHelper::unroot_ = -1;
 int PropertiesHelper::parallel_run_ = -1;
-#if !defined(__ANDROID_VNDK__)
 int PropertiesHelper::strict_run_ = -1;
-#endif
 
 bool PropertiesHelper::IsUserBuild() {
     if (build_type_.empty()) {
@@ -240,7 +238,6 @@
     return parallel_run_ == 1;
 }
 
-#if !defined(__ANDROID_VNDK__)
 bool PropertiesHelper::IsStrictRun() {
     if (strict_run_ == -1) {
         // Defaults to using stricter timeouts.
@@ -248,7 +245,6 @@
     }
     return strict_run_ == 1;
 }
-#endif
 
 int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
index 6049e3e..9e955e3 100644
--- a/cmds/dumpstate/DumpstateUtil.h
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -198,18 +198,14 @@
      * will default to true. This results in shortened timeouts for flaky
      * sections.
      */
-#if !defined(__ANDROID_VNDK__)
     static bool IsStrictRun();
-#endif
 
   private:
     static std::string build_type_;
     static int dry_run_;
     static int unroot_;
     static int parallel_run_;
-#if !defined(__ANDROID_VNDK__)
     static int strict_run_;
-#endif
 };
 
 /*
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index 5cf402c..df02588 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -18,6 +18,7 @@
 #define DEXOPT_H_
 
 #include "installd_constants.h"
+#include "unique_file.h"
 
 #include <sys/types.h>
 
@@ -156,6 +157,10 @@
 // artifacts.
 int get_odex_visibility(const char* apk_path, const char* instruction_set, const char* oat_dir);
 
+UniqueFile maybe_open_reference_profile(const std::string& pkgname, const std::string& dex_path,
+                                        const char* profile_name, bool profile_guided,
+                                        bool is_public, int uid, bool is_secondary_dex);
+
 }  // namespace installd
 }  // namespace android
 
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 407cb24..27ae8f6 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -14,20 +14,21 @@
  ** limitations under the License.
  */
 
-#include <algorithm>
 #include <inttypes.h>
-#include <limits>
-#include <random>
-#include <regex>
 #include <selinux/android.h>
 #include <selinux/avc.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/capability.h>
+#include <sys/mman.h>
 #include <sys/prctl.h>
 #include <sys/stat.h>
-#include <sys/mman.h>
 #include <sys/wait.h>
+#include <algorithm>
+#include <iterator>
+#include <limits>
+#include <random>
+#include <regex>
 
 #include <android-base/logging.h>
 #include <android-base/macros.h>
@@ -47,6 +48,7 @@
 #include "otapreopt_parameters.h"
 #include "otapreopt_utils.h"
 #include "system_properties.h"
+#include "unique_file.h"
 #include "utils.h"
 
 #ifndef LOG_TAG
@@ -87,6 +89,9 @@
 static_assert(DEXOPT_MASK           == (0x3dfe | DEXOPT_IDLE_BACKGROUND_JOB),
               "DEXOPT_MASK unexpected.");
 
+constexpr const char* kAotCompilerFilters[]{
+        "space-profile", "space", "speed-profile", "speed", "everything-profile", "everything",
+};
 
 template<typename T>
 static constexpr bool IsPowerOfTwo(T x) {
@@ -415,6 +420,32 @@
         return (strcmp(arg, "!") == 0) ? nullptr : arg;
     }
 
+    bool IsAotCompilation() const {
+        if (std::find(std::begin(kAotCompilerFilters), std::end(kAotCompilerFilters),
+                      parameters_.compiler_filter) == std::end(kAotCompilerFilters)) {
+            return false;
+        }
+
+        int dexopt_flags = parameters_.dexopt_flags;
+        bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0;
+        bool is_secondary_dex = (dexopt_flags & DEXOPT_SECONDARY_DEX) != 0;
+        bool is_public = (dexopt_flags & DEXOPT_PUBLIC) != 0;
+
+        if (profile_guided) {
+            UniqueFile reference_profile =
+                    maybe_open_reference_profile(parameters_.pkgName, parameters_.apk_path,
+                                                 parameters_.profile_name, profile_guided,
+                                                 is_public, parameters_.uid, is_secondary_dex);
+            struct stat sbuf;
+            if (reference_profile.fd() == -1 ||
+                (fstat(reference_profile.fd(), &sbuf) != -1 && sbuf.st_size == 0)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
     bool ShouldSkipPreopt() const {
         // There's one thing we have to be careful about: we may/will be asked to compile an app
         // living in the system image. This may be a valid request - if the app wasn't compiled,
@@ -439,9 +470,12 @@
         //       (This is ugly as it's the only thing where we need to understand the contents
         //        of parameters_, but it beats postponing the decision or using the call-
         //        backs to do weird things.)
+
+        // In addition, no need to preopt for "verify". The existing vdex files in the OTA package
+        // and the /data partition will still be usable after the OTA update is applied.
         const char* apk_path = parameters_.apk_path;
         CHECK(apk_path != nullptr);
-        if (StartsWith(apk_path, android_root_)) {
+        if (StartsWith(apk_path, android_root_) || !IsAotCompilation()) {
             const char* last_slash = strrchr(apk_path, '/');
             if (last_slash != nullptr) {
                 std::string path(apk_path, last_slash - apk_path + 1);
@@ -526,61 +560,6 @@
         return Dexopt();
     }
 
-    ////////////////////////////////////
-    // Helpers, mostly taken from ART //
-    ////////////////////////////////////
-
-    // Choose a random relocation offset. Taken from art/runtime/gc/image_space.cc.
-    static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) {
-        constexpr size_t kPageSize = PAGE_SIZE;
-        static_assert(IsPowerOfTwo(kPageSize), "page size must be power of two");
-        CHECK_EQ(min_delta % kPageSize, 0u);
-        CHECK_EQ(max_delta % kPageSize, 0u);
-        CHECK_LT(min_delta, max_delta);
-
-        std::default_random_engine generator;
-        generator.seed(GetSeed());
-        std::uniform_int_distribution<int32_t> distribution(min_delta, max_delta);
-        int32_t r = distribution(generator);
-        if (r % 2 == 0) {
-            r = RoundUp(r, kPageSize);
-        } else {
-            r = RoundDown(r, kPageSize);
-        }
-        CHECK_LE(min_delta, r);
-        CHECK_GE(max_delta, r);
-        CHECK_EQ(r % kPageSize, 0u);
-        return r;
-    }
-
-    static uint64_t GetSeed() {
-#ifdef __BIONIC__
-        // Bionic exposes arc4random, use it.
-        uint64_t random_data;
-        arc4random_buf(&random_data, sizeof(random_data));
-        return random_data;
-#else
-#error "This is only supposed to run with bionic. Otherwise, implement..."
-#endif
-    }
-
-    void AddCompilerOptionFromSystemProperty(const char* system_property,
-            const char* prefix,
-            bool runtime,
-            std::vector<std::string>& out) const {
-        const std::string* value = system_properties_.GetProperty(system_property);
-        if (value != nullptr) {
-            if (runtime) {
-                out.push_back("--runtime-arg");
-            }
-            if (prefix != nullptr) {
-                out.push_back(StringPrintf("%s%s", prefix, value->c_str()));
-            } else {
-                out.push_back(*value);
-            }
-        }
-    }
-
     static constexpr const char* kBootClassPathPropertyName = "BOOTCLASSPATH";
     static constexpr const char* kAndroidRootPathPropertyName = "ANDROID_ROOT";
     static constexpr const char* kAndroidDataPathPropertyName = "ANDROID_DATA";
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index c86993c..c40caf5 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -19,9 +19,12 @@
 #include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
+#include <algorithm>
 #include <array>
 #include <fstream>
+#include <iostream>
 #include <sstream>
 
 #include <android-base/file.h>
@@ -29,6 +32,7 @@
 #include <android-base/macros.h>
 #include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <libdm/dm.h>
 #include <selinux/android.h>
@@ -37,7 +41,7 @@
 #include "otapreopt_utils.h"
 
 #ifndef LOG_TAG
-#define LOG_TAG "otapreopt"
+#define LOG_TAG "otapreopt_chroot"
 #endif
 
 using android::base::StringPrintf;
@@ -49,20 +53,22 @@
 // so just try the possibilities one by one.
 static constexpr std::array kTryMountFsTypes = {"ext4", "erofs"};
 
-static void CloseDescriptor(int fd) {
-    if (fd >= 0) {
-        int result = close(fd);
-        UNUSED(result);  // Ignore result. Printing to logcat will open a new descriptor
-                         // that we do *not* want.
-    }
-}
-
 static void CloseDescriptor(const char* descriptor_string) {
     int fd = -1;
     std::istringstream stream(descriptor_string);
     stream >> fd;
     if (!stream.fail()) {
-        CloseDescriptor(fd);
+        if (fd >= 0) {
+            if (close(fd) < 0) {
+                PLOG(ERROR) << "Failed to close " << fd;
+            }
+        }
+    }
+}
+
+static void SetCloseOnExec(int fd) {
+    if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
+        PLOG(ERROR) << "Failed to set FD_CLOEXEC on " << fd;
     }
 }
 
@@ -129,24 +135,39 @@
 }
 
 // Entry for otapreopt_chroot. Expected parameters are:
-//   [cmd] [status-fd] [target-slot] "dexopt" [dexopt-params]
-// The file descriptor denoted by status-fd will be closed. The rest of the parameters will
-// be passed on to otapreopt in the chroot.
+//
+//   [cmd] [status-fd] [target-slot-suffix]
+//
+// The file descriptor denoted by status-fd will be closed. Dexopt commands on
+// the form
+//
+//   "dexopt" [dexopt-params]
+//
+// are then read from stdin until EOF and passed on to /system/bin/otapreopt one
+// by one. After each call a line with the current command count is written to
+// stdout and flushed.
 static int otapreopt_chroot(const int argc, char **arg) {
     // Validate arguments
-    // We need the command, status channel and target slot, at a minimum.
-    if(argc < 3) {
-        PLOG(ERROR) << "Not enough arguments.";
+    if (argc == 2 && std::string_view(arg[1]) == "--version") {
+        // Accept a single --version flag, to allow the script to tell this binary
+        // from the earlier one.
+        std::cout << "2" << std::endl;
+        return 0;
+    }
+    if (argc != 3) {
+        LOG(ERROR) << "Wrong number of arguments: " << argc;
         exit(208);
     }
-    // Close all file descriptors. They are coming from the caller, we do not want to pass them
-    // on across our fork/exec into a different domain.
-    // 1) Default descriptors.
-    CloseDescriptor(STDIN_FILENO);
-    CloseDescriptor(STDOUT_FILENO);
-    CloseDescriptor(STDERR_FILENO);
-    // 2) The status channel.
-    CloseDescriptor(arg[1]);
+    const char* status_fd = arg[1];
+    const char* slot_suffix = arg[2];
+
+    // Set O_CLOEXEC on standard fds. They are coming from the caller, we do not
+    // want to pass them on across our fork/exec into a different domain.
+    SetCloseOnExec(STDIN_FILENO);
+    SetCloseOnExec(STDOUT_FILENO);
+    SetCloseOnExec(STDERR_FILENO);
+    // Close the status channel.
+    CloseDescriptor(status_fd);
 
     // We need to run the otapreopt tool from the postinstall partition. As such, set up a
     // mount namespace and change root.
@@ -185,20 +206,20 @@
     //  2) We're in a mount namespace here, so when we die, this will be cleaned up.
     //  3) Ignore errors. Printing anything at this stage will open a file descriptor
     //     for logging.
-    if (!ValidateTargetSlotSuffix(arg[2])) {
-        LOG(ERROR) << "Target slot suffix not legal: " << arg[2];
+    if (!ValidateTargetSlotSuffix(slot_suffix)) {
+        LOG(ERROR) << "Target slot suffix not legal: " << slot_suffix;
         exit(207);
     }
-    TryExtraMount("vendor", arg[2], "/postinstall/vendor");
+    TryExtraMount("vendor", slot_suffix, "/postinstall/vendor");
 
     // Try to mount the product partition. update_engine doesn't do this for us, but we
     // want it for product APKs. Same notes as vendor above.
-    TryExtraMount("product", arg[2], "/postinstall/product");
+    TryExtraMount("product", slot_suffix, "/postinstall/product");
 
     // Try to mount the system_ext partition. update_engine doesn't do this for
     // us, but we want it for system_ext APKs. Same notes as vendor and product
     // above.
-    TryExtraMount("system_ext", arg[2], "/postinstall/system_ext");
+    TryExtraMount("system_ext", slot_suffix, "/postinstall/system_ext");
 
     constexpr const char* kPostInstallLinkerconfig = "/postinstall/linkerconfig";
     // Try to mount /postinstall/linkerconfig. we will set it up after performing the chroot
@@ -329,30 +350,37 @@
         exit(218);
     }
 
-    // Now go on and run otapreopt.
+    // Now go on and read dexopt lines from stdin and pass them on to otapreopt.
 
-    // Incoming:  cmd + status-fd + target-slot + cmd...      | Incoming | = argc
-    // Outgoing:  cmd             + target-slot + cmd...      | Outgoing | = argc - 1
-    std::vector<std::string> cmd;
-    cmd.reserve(argc);
-    cmd.push_back("/system/bin/otapreopt");
+    int count = 1;
+    for (std::array<char, 1000> linebuf;
+         std::cin.clear(), std::cin.getline(&linebuf[0], linebuf.size()); ++count) {
+        // Subtract one from gcount() since getline() counts the newline.
+        std::string line(&linebuf[0], std::cin.gcount() - 1);
 
-    // The first parameter is the status file descriptor, skip.
-    for (size_t i = 2; i < static_cast<size_t>(argc); ++i) {
-        cmd.push_back(arg[i]);
+        if (std::cin.fail()) {
+            LOG(ERROR) << "Command exceeds max length " << linebuf.size() << " - skipped: " << line;
+            continue;
+        }
+
+        std::vector<std::string> tokenized_line = android::base::Tokenize(line, " ");
+        std::vector<std::string> cmd{"/system/bin/otapreopt", slot_suffix};
+        std::move(tokenized_line.begin(), tokenized_line.end(), std::back_inserter(cmd));
+
+        LOG(INFO) << "Command " << count << ": " << android::base::Join(cmd, " ");
+
+        // Fork and execute otapreopt in its own process.
+        std::string error_msg;
+        bool exec_result = Exec(cmd, &error_msg);
+        if (!exec_result) {
+            LOG(ERROR) << "Running otapreopt failed: " << error_msg;
+        }
+
+        // Print the count to stdout and flush to indicate progress.
+        std::cout << count << std::endl;
     }
 
-    // Fork and execute otapreopt in its own process.
-    std::string error_msg;
-    bool exec_result = Exec(cmd, &error_msg);
-    if (!exec_result) {
-        LOG(ERROR) << "Running otapreopt failed: " << error_msg;
-    }
-
-    if (!exec_result) {
-        exit(213);
-    }
-
+    LOG(INFO) << "No more dexopt commands";
     return 0;
 }
 
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
index e483d54..28bd793 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -16,7 +16,9 @@
 # limitations under the License.
 #
 
-# This script will run as a postinstall step to drive otapreopt.
+# This script runs as a postinstall step to drive otapreopt. It comes with the
+# OTA package, but runs /system/bin/otapreopt_chroot in the (old) active system
+# image. See system/extras/postinst/postinst.sh for some docs.
 
 TARGET_SLOT="$1"
 STATUS_FD="$2"
@@ -31,12 +33,11 @@
 
 BOOT_COMPLETE=$(getprop $BOOT_PROPERTY_NAME)
 if [ "$BOOT_COMPLETE" != "1" ] ; then
-  echo "Error: boot-complete not detected."
+  echo "$0: Error: boot-complete not detected."
   # We must return 0 to not block sideload.
   exit 0
 fi
 
-
 # Compute target slot suffix.
 # TODO: Once bootctl is not restricted, we should query from there. Or get this from
 #       update_engine as a parameter.
@@ -45,44 +46,63 @@
 elif [ "$TARGET_SLOT" = "1" ] ; then
   TARGET_SLOT_SUFFIX="_b"
 else
-  echo "Unknown target slot $TARGET_SLOT"
+  echo "$0: Unknown target slot $TARGET_SLOT"
   exit 1
 fi
 
+if [ "$(/system/bin/otapreopt_chroot --version)" != 2 ]; then
+  # We require an updated chroot wrapper that reads dexopt commands from stdin.
+  # Even if we kept compat with the old binary, the OTA preopt wouldn't work due
+  # to missing sepolicy rules, so there's no use spending time trying to dexopt
+  # (b/291974157).
+  echo "$0: Current system image is too old to work with OTA preopt - skipping."
+  exit 0
+fi
 
 PREPARE=$(cmd otadexopt prepare)
 # Note: Ignore preparation failures. Step and done will fail and exit this.
 #       This is necessary to support suspends - the OTA service will keep
 #       the state around for us.
 
-PROGRESS=$(cmd otadexopt progress)
-print -u${STATUS_FD} "global_progress $PROGRESS"
-
-i=0
-while ((i<MAXIMUM_PACKAGES)) ; do
+# Create an array with all dexopt commands in advance, to know how many there are.
+otadexopt_cmds=()
+while (( ${#otadexopt_cmds[@]} < MAXIMUM_PACKAGES )) ; do
   DONE=$(cmd otadexopt done)
   if [ "$DONE" = "OTA complete." ] ; then
     break
   fi
-
-  DEXOPT_PARAMS=$(cmd otadexopt next)
-
-  /system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX $DEXOPT_PARAMS >&- 2>&-
-
-  PROGRESS=$(cmd otadexopt progress)
-  print -u${STATUS_FD} "global_progress $PROGRESS"
-
-  i=$((i+1))
+  otadexopt_cmds+=("$(cmd otadexopt next)")
 done
 
 DONE=$(cmd otadexopt done)
+cmd otadexopt cleanup
+
+echo "$0: Using streaming otapreopt_chroot on ${#otadexopt_cmds[@]} packages"
+
+function print_otadexopt_cmds {
+  for cmd in "${otadexopt_cmds[@]}" ; do
+    print "$cmd"
+  done
+}
+
+function report_progress {
+  while read count ; do
+    # mksh can't do floating point arithmetic, so emulate a fixed point calculation.
+    (( permilles = 1000 * count / ${#otadexopt_cmds[@]} ))
+    printf 'global_progress %d.%03d\n' $((permilles / 1000)) $((permilles % 1000)) >&${STATUS_FD}
+  done
+}
+
+print_otadexopt_cmds | \
+  /system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX | \
+  report_progress
+
 if [ "$DONE" = "OTA incomplete." ] ; then
-  echo "Incomplete."
+  echo "$0: Incomplete."
 else
-  echo "Complete or error."
+  echo "$0: Complete or error."
 fi
 
 print -u${STATUS_FD} "global_progress 1.0"
-cmd otadexopt cleanup
 
 exit 0
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index 4221a3a..7648265 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -208,36 +208,13 @@
     }
 
     // Compute compiler filter.
-    {
-        std::string dex2oat_compiler_filter_arg;
-        {
-            // If we are booting without the real /data, don't spend time compiling.
-            std::string vold_decrypt = GetProperty("vold.decrypt", "");
-            bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" ||
-                    vold_decrypt == "1";
-
-            bool have_dex2oat_relocation_skip_flag = false;
-            if (skip_compilation) {
-                dex2oat_compiler_filter_arg = "--compiler-filter=extract";
-                have_dex2oat_relocation_skip_flag = true;
-            } else if (compiler_filter != nullptr) {
-                dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s",
-                                                           compiler_filter);
-            }
-            if (have_dex2oat_relocation_skip_flag) {
-                AddRuntimeArg("-Xnorelocate");
-            }
-        }
-
-        if (dex2oat_compiler_filter_arg.empty()) {
-            dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter",
-                                                           "--compiler-filter=%s");
-        }
-        AddArg(dex2oat_compiler_filter_arg);
-
-        if (compilation_reason != nullptr) {
-            AddArg(std::string("--compilation-reason=") + compilation_reason);
-        }
+    if (compiler_filter != nullptr) {
+        AddArg(StringPrintf("--compiler-filter=%s", compiler_filter));
+    } else {
+        AddArg(MapPropertyToArg("dalvik.vm.dex2oat-filter", "--compiler-filter=%s"));
+    }
+    if (compilation_reason != nullptr) {
+        AddArg(std::string("--compilation-reason=") + compilation_reason);
     }
 
     AddArg(MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size",
diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp
index 304ba7b..56f84a5 100644
--- a/cmds/installd/run_dex2oat_test.cpp
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -441,24 +441,6 @@
     VerifyExpectedFlags();
 }
 
-TEST_F(RunDex2OatTest, SkipRelocationInMinFramework) {
-    setSystemProperty("vold.decrypt", "trigger_restart_min_framework");
-    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
-
-    SetExpectedFlagUsed("--compiler-filter", "=extract");
-    SetExpectedFlagUsed("-Xnorelocate", "");
-    VerifyExpectedFlags();
-}
-
-TEST_F(RunDex2OatTest, SkipRelocationIfDecryptedWithFullDiskEncryption) {
-    setSystemProperty("vold.decrypt", "1");
-    CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
-
-    SetExpectedFlagUsed("--compiler-filter", "=extract");
-    SetExpectedFlagUsed("-Xnorelocate", "");
-    VerifyExpectedFlags();
-}
-
 TEST_F(RunDex2OatTest, DalvikVmDex2oatFilter) {
     setSystemProperty("dalvik.vm.dex2oat-filter", "speed");
     auto args = RunDex2OatArgs::MakeDefaultTestArgs();
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 3ce586b..c962c15 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -263,8 +263,8 @@
 }
 
 prebuilt_etc {
-    name: "android.hardware.threadnetwork.prebuilt.xml",
-    src: "android.hardware.threadnetwork.xml",
+    name: "android.hardware.thread_network.prebuilt.xml",
+    src: "android.hardware.thread_network.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
 }
 
diff --git a/data/etc/android.hardware.threadnetwork.xml b/data/etc/android.hardware.thread_network.xml
similarity index 83%
rename from data/etc/android.hardware.threadnetwork.xml
rename to data/etc/android.hardware.thread_network.xml
index 9cbdc90..b116ed6 100644
--- a/data/etc/android.hardware.threadnetwork.xml
+++ b/data/etc/android.hardware.thread_network.xml
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<!-- Adds the feature indicating support for the ThreadNetwork API -->
+<!-- Adds the feature indicating support for the Thread networking protocol -->
 <permissions>
-    <feature name="android.hardware.threadnetwork" />
+    <feature name="android.hardware.thread_network" />
 </permissions>
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index cedd361..ba8b02d 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -14,6 +14,23 @@
  * limitations under the License.
  */
 
+ /**
+ * @defgroup APerformanceHint Performance Hint Manager
+ *
+ * APerformanceHint allows apps to create performance hint sessions for groups
+ * of threads, and provide hints to the system about the workload of those threads,
+ * to help the system more accurately allocate power for them. It is the NDK
+ * counterpart to the Java PerformanceHintManager SDK API.
+ *
+ * @{
+ */
+
+/**
+ * @file performance_hint.h
+ * @brief API for creating and managing a hint session.
+ */
+
+
 #ifndef ANDROID_NATIVE_PERFORMANCE_HINT_H
 #define ANDROID_NATIVE_PERFORMANCE_HINT_H
 
@@ -48,7 +65,7 @@
  * An opaque type representing a handle to a performance hint manager.
  * It must be released after use.
  *
- * <p>To use:<ul>
+ * To use:<ul>
  *    <li>Obtain the performance hint manager instance by calling
  *        {@link APerformanceHint_getManager} function.</li>
  *    <li>Create an {@link APerformanceHintSession} with
@@ -61,50 +78,43 @@
 /**
  * An opaque type representing a handle to a performance hint session.
  * A session can only be acquired from a {@link APerformanceHintManager}
- * with {@link APerformanceHint_getPreferredUpdateRateNanos}. It must be
+ * with {@link APerformanceHint_createSession}. It must be
  * freed with {@link APerformanceHint_closeSession} after use.
  *
  * A Session represents a group of threads with an inter-related workload such that hints for
  * their performance should be considered as a unit. The threads in a given session should be
- * long-life and not created or destroyed dynamically.
+ * long-lived and not created or destroyed dynamically.
  *
- * <p>Each session is expected to have a periodic workload with a target duration for each
- * cycle. The cycle duration is likely greater than the target work duration to allow other
- * parts of the pipeline to run within the available budget. For example, a renderer thread may
- * work at 60hz in order to produce frames at the display's frame but have a target work
- * duration of only 6ms.</p>
+ * The work duration API can be used with periodic workloads to dynamically adjust thread
+ * performance and keep the work on schedule while optimizing the available power budget.
+ * When using the work duration API, the starting target duration should be specified
+ * while creating the session, and can later be adjusted with
+ * {@link APerformanceHint_updateTargetWorkDuration}. While using the work duration
+ * API, the client is expected to call {@link APerformanceHint_reportActualWorkDuration} each
+ * cycle to report the actual time taken to complete to the system.
  *
- * <p>After each cycle of work, the client is expected to use
- * {@link APerformanceHint_reportActualWorkDuration} to report the actual time taken to
- * complete.</p>
- *
- * <p>To use:<ul>
- *    <li>Update a sessions target duration for each cycle of work
- *        with  {@link APerformanceHint_updateTargetWorkDuration}.</li>
- *    <li>Report the actual duration for the last cycle of work with
- *        {@link APerformanceHint_reportActualWorkDuration}.</li>
- *    <li>Release the session instance with
- *        {@link APerformanceHint_closeSession}.</li></ul></p>
+ * All timings should be from `std::chrono::steady_clock` or `clock_gettime(CLOCK_MONOTONIC, ...)`
  */
 typedef struct APerformanceHintSession APerformanceHintSession;
 
 /**
   * Acquire an instance of the performance hint manager.
   *
-  * @return manager instance on success, nullptr on failure.
+  * @return APerformanceHintManager instance on success, nullptr on failure.
   */
 APerformanceHintManager* APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID_API_T__);
 
 /**
  * Creates a session for the given set of threads and sets their initial target work
  * duration.
+ *
  * @param manager The performance hint manager instance.
  * @param threadIds The list of threads to be associated with this session. They must be part of
- *     this app's thread group.
- * @param size the size of threadIds.
- * @param initialTargetWorkDurationNanos The desired duration in nanoseconds for the new session.
- *     This must be positive.
- * @return manager instance on success, nullptr on failure.
+ *     this process' thread group.
+ * @param size The size of the list of threadIds.
+ * @param initialTargetWorkDurationNanos The target duration in nanoseconds for the new session.
+ *     This must be positive if using the work duration API, or 0 otherwise.
+ * @return APerformanceHintManager instance on success, nullptr on failure.
  */
 APerformanceHintSession* APerformanceHint_createSession(
         APerformanceHintManager* manager,
@@ -124,8 +134,8 @@
  * Updates this session's target duration for each cycle of work.
  *
  * @param session The performance hint session instance to update.
- * @param targetDurationNanos the new desired duration in nanoseconds. This must be positive.
- * @return 0 on success
+ * @param targetDurationNanos The new desired duration in nanoseconds. This must be positive.
+ * @return 0 on success.
  *         EINVAL if targetDurationNanos is not positive.
  *         EPIPE if communication with the system service has failed.
  */
@@ -136,14 +146,13 @@
 /**
  * Reports the actual duration for the last cycle of work.
  *
- * <p>The system will attempt to adjust the core placement of the threads within the thread
- * group and/or the frequency of the core on which they are run to bring the actual duration
- * close to the target duration.</p>
+ * The system will attempt to adjust the scheduling and performance of the
+ * threads within the thread group to bring the actual duration close to the target duration.
  *
  * @param session The performance hint session instance to update.
- * @param actualDurationNanos how long the thread group took to complete its last task in
- *     nanoseconds. This must be positive.
- * @return 0 on success
+ * @param actualDurationNanos The duration of time the thread group took to complete its last
+ *     task in nanoseconds. This must be positive.
+ * @return 0 on success.
  *         EINVAL if actualDurationNanos is not positive.
  *         EPIPE if communication with the system service has failed.
  */
@@ -164,12 +173,13 @@
  * Set a list of threads to the performance hint session. This operation will replace
  * the current list of threads with the given list of threads.
  *
- * @param session The performance hint session instance for the threads.
+ * @param session The performance hint session instance to update.
  * @param threadIds The list of threads to be associated with this session. They must be part of
  *     this app's thread group.
- * @param size the size of the list of threadIds.
+ * @param size The size of the list of threadIds.
  * @return 0 on success.
- *         EINVAL if the list of thread ids is empty or if  any of the thread ids is not part of the thread group.
+ *         EINVAL if the list of thread ids is empty or if any of the thread ids are not part of
+               the thread group.
  *         EPIPE if communication with the system service has failed.
  *         EPERM if any thread id doesn't belong to the application.
  */
@@ -178,6 +188,21 @@
         const pid_t* threadIds,
         size_t size) __INTRODUCED_IN(__ANDROID_API_U__);
 
+/**
+ * This tells the session that these threads can be
+ * safely scheduled to prefer power efficiency over performance.
+ *
+ * @param session The performance hint session instance to update.
+ * @param enabled The flag which sets whether this session will use power-efficient scheduling.
+ * @return 0 on success.
+ *         EPIPE if communication with the system service has failed.
+ */
+int APerformanceHint_setPreferPowerEfficiency(
+        APerformanceHintSession* session,
+        bool enabled) __INTRODUCED_IN(__ANDROID_API_V__);
+
 __END_DECLS
 
 #endif // ANDROID_NATIVE_PERFORMANCE_HINT_H
+
+/** @} */
\ No newline at end of file
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index b58feac..2e99495 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <android/os/IInputConstants.h>
 #include <input/Input.h>
 #include <input/RingBuffer.h>
 #include <utils/BitSet.h>
@@ -35,19 +36,20 @@
     static const size_t MAX_DEGREE = 4;
 
     enum class Strategy : int32_t {
-        DEFAULT = -1,
-        MIN = 0,
-        IMPULSE = 0,
-        LSQ1 = 1,
-        LSQ2 = 2,
-        LSQ3 = 3,
-        WLSQ2_DELTA = 4,
-        WLSQ2_CENTRAL = 5,
-        WLSQ2_RECENT = 6,
-        INT1 = 7,
-        INT2 = 8,
-        LEGACY = 9,
+        DEFAULT = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_DEFAULT,
+        IMPULSE = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_IMPULSE,
+        LSQ1 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ1,
+        LSQ2 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ2,
+        LSQ3 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ3,
+        WLSQ2_DELTA = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA,
+        WLSQ2_CENTRAL = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL,
+        WLSQ2_RECENT = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT,
+        INT1 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_INT1,
+        INT2 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_INT2,
+        LEGACY = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LEGACY,
+        MIN = IMPULSE,
         MAX = LEGACY,
+        ftl_last = LEGACY,
     };
 
     /*
@@ -81,8 +83,6 @@
     // TODO(b/32830165): support axis-specific strategies.
     VelocityTracker(const Strategy strategy = Strategy::DEFAULT);
 
-    ~VelocityTracker();
-
     /** Return true if the axis is supported for velocity tracking, false otherwise. */
     static bool isAxisSupported(int32_t axis);
 
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 3f1fc33..6c2b313 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -190,6 +190,9 @@
         "-performance-move-const-arg", // b/273486801
         "portability*",
     ],
+    lto: {
+        thin: true,
+    },
 }
 
 cc_library_headers {
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index 3da06ba..fc273e0 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -73,8 +73,8 @@
         ALOGV("MemoryHeapBase: Attempting to force MemFD");
         fd = memfd_create_region(name ? name : "MemoryHeapBase", size);
         if (fd < 0 || (mapfd(fd, true, size) != NO_ERROR)) return;
-        const int SEAL_FLAGS = ((mFlags & READ_ONLY) ? F_SEAL_FUTURE_WRITE : 0) |
-                ((mFlags & MEMFD_ALLOW_SEALING_FLAG) ? 0 : F_SEAL_SEAL);
+        const int SEAL_FLAGS = ((mFlags & READ_ONLY) ? F_SEAL_FUTURE_WRITE : 0) | F_SEAL_GROW |
+                F_SEAL_SHRINK | ((mFlags & MEMFD_ALLOW_SEALING_FLAG) ? 0 : F_SEAL_SEAL);
         if (SEAL_FLAGS && (fcntl(fd, F_ADD_SEALS, SEAL_FLAGS) == -1)) {
             ALOGE("MemoryHeapBase: MemFD %s sealing with flags %x failed with error  %s", name,
                   SEAL_FLAGS, strerror(errno));
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index d36ebac..672d6cf 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -97,34 +97,12 @@
     crate_name: "binder_ndk_bindgen",
     wrapper_src: "sys/BinderBindings.hpp",
     source_stem: "bindings",
-    bindgen_flags: [
+    bindgen_flag_files: [
         // Unfortunately the only way to specify the rust_non_exhaustive enum
         // style for a type is to make it the default
-        "--default-enum-style",
-        "rust_non_exhaustive",
         // and then specify constified enums for the enums we don't want
         // rustified
-        "--constified-enum",
-        "android::c_interface::consts::.*",
-
-        "--allowlist-type",
-        "android::c_interface::.*",
-        "--allowlist-type",
-        "AStatus",
-        "--allowlist-type",
-        "AIBinder_Class",
-        "--allowlist-type",
-        "AIBinder",
-        "--allowlist-type",
-        "AIBinder_Weak",
-        "--allowlist-type",
-        "AIBinder_DeathRecipient",
-        "--allowlist-type",
-        "AParcel",
-        "--allowlist-type",
-        "binder_status_t",
-        "--allowlist-function",
-        ".*",
+        "libbinder_ndk_bindgen_flags.txt",
     ],
     shared_libs: [
         "libbinder_ndk",
diff --git a/libs/binder/rust/libbinder_ndk_bindgen_flags.txt b/libs/binder/rust/libbinder_ndk_bindgen_flags.txt
new file mode 100644
index 0000000..551c59f
--- /dev/null
+++ b/libs/binder/rust/libbinder_ndk_bindgen_flags.txt
@@ -0,0 +1,11 @@
+--default-enum-style=rust_non_exhaustive
+--constified-enum=android::c_interface::consts::.*
+--allowlist-type=android::c_interface::.*
+--allowlist-type=AStatus
+--allowlist-type=AIBinder_Class
+--allowlist-type=AIBinder
+--allowlist-type=AIBinder_Weak
+--allowlist-type=AIBinder_DeathRecipient
+--allowlist-type=AParcel
+--allowlist-type=binder_status_t
+--allowlist-function=.*
diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs
index 8d9ce0e..eb04cc3 100644
--- a/libs/binder/rust/src/error.rs
+++ b/libs/binder/rust/src/error.rs
@@ -370,6 +370,94 @@
     }
 }
 
+/// A conversion from `std::result::Result<T, E>` to `binder::Result<T>`. If this type is `Ok(T)`,
+/// it's returned as is. If this type is `Err(E)`, `E` is converted into `Status` which can be
+/// either a general binder exception, or a service-specific exception.
+///
+/// # Examples
+///
+/// ```
+/// // std::io::Error is formatted as the exception's message
+/// fn file_exists(name: &str) -> binder::Result<bool> {
+///     std::fs::metadata(name)
+///         .or_service_specific_exception(NOT_FOUND)?
+/// }
+///
+/// // A custom function is used to create the exception's message
+/// fn file_exists(name: &str) -> binder::Result<bool> {
+///     std::fs::metadata(name)
+///         .or_service_specific_exception_with(NOT_FOUND,
+///             |e| format!("file {} not found: {:?}", name, e))?
+/// }
+///
+/// // anyhow::Error is formatted as the exception's message
+/// use anyhow::{Context, Result};
+/// fn file_exists(name: &str) -> binder::Result<bool> {
+///     std::fs::metadata(name)
+///         .context("file {} not found")
+///         .or_service_specific_exception(NOT_FOUND)?
+/// }
+///
+/// // General binder exceptions can be created similarly
+/// fn file_exists(name: &str) -> binder::Result<bool> {
+///     std::fs::metadata(name)
+///         .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)?
+/// }
+/// ```
+pub trait IntoBinderResult<T, E> {
+    /// Converts the embedded error into a general binder exception of code `exception`. The
+    /// message of the exception is set by formatting the error for debugging.
+    fn or_binder_exception(self, exception: ExceptionCode) -> result::Result<T, Status>;
+
+    /// Converts the embedded error into a general binder exception of code `exception`. The
+    /// message of the exception is set by lazily evaluating the `op` function.
+    fn or_binder_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>(
+        self,
+        exception: ExceptionCode,
+        op: O,
+    ) -> result::Result<T, Status>;
+
+    /// Converts the embedded error into a service-specific binder exception. `error_code` is used
+    /// to distinguish different service-specific binder exceptions. The message of the exception
+    /// is set by formatting the error for debugging.
+    fn or_service_specific_exception(self, error_code: i32) -> result::Result<T, Status>;
+
+    /// Converts the embedded error into a service-specific binder exception. `error_code` is used
+    /// to distinguish different service-specific binder exceptions. The message of the exception
+    /// is set by lazily evaluating the `op` function.
+    fn or_service_specific_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>(
+        self,
+        error_code: i32,
+        op: O,
+    ) -> result::Result<T, Status>;
+}
+
+impl<T, E: std::fmt::Debug> IntoBinderResult<T, E> for result::Result<T, E> {
+    fn or_binder_exception(self, exception: ExceptionCode) -> result::Result<T, Status> {
+        self.or_binder_exception_with(exception, |e| format!("{:?}", e))
+    }
+
+    fn or_binder_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>(
+        self,
+        exception: ExceptionCode,
+        op: O,
+    ) -> result::Result<T, Status> {
+        self.map_err(|e| Status::new_exception_str(exception, Some(op(e))))
+    }
+
+    fn or_service_specific_exception(self, error_code: i32) -> result::Result<T, Status> {
+        self.or_service_specific_exception_with(error_code, |e| format!("{:?}", e))
+    }
+
+    fn or_service_specific_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>(
+        self,
+        error_code: i32,
+        op: O,
+    ) -> result::Result<T, Status> {
+        self.map_err(|e| Status::new_service_specific_error_str(error_code, Some(op(e))))
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -406,4 +494,66 @@
         assert_eq!(status.service_specific_error(), 0);
         assert_eq!(status.get_description(), "Status(-5, EX_ILLEGAL_STATE): ''".to_string());
     }
+
+    #[test]
+    fn convert_to_service_specific_exception() {
+        let res: std::result::Result<(), Status> =
+            Err("message").or_service_specific_exception(-42);
+
+        assert!(res.is_err());
+        let status = res.unwrap_err();
+        assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC);
+        assert_eq!(status.service_specific_error(), -42);
+        assert_eq!(
+            status.get_description(),
+            "Status(-8, EX_SERVICE_SPECIFIC): '-42: \"message\"'".to_string()
+        );
+    }
+
+    #[test]
+    fn convert_to_service_specific_exception_with() {
+        let res: std::result::Result<(), Status> = Err("message")
+            .or_service_specific_exception_with(-42, |e| format!("outer message: {:?}", e));
+
+        assert!(res.is_err());
+        let status = res.unwrap_err();
+        assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC);
+        assert_eq!(status.service_specific_error(), -42);
+        assert_eq!(
+            status.get_description(),
+            "Status(-8, EX_SERVICE_SPECIFIC): '-42: outer message: \"message\"'".to_string()
+        );
+    }
+
+    #[test]
+    fn convert_to_binder_exception() {
+        let res: std::result::Result<(), Status> =
+            Err("message").or_binder_exception(ExceptionCode::ILLEGAL_STATE);
+
+        assert!(res.is_err());
+        let status = res.unwrap_err();
+        assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE);
+        assert_eq!(status.service_specific_error(), 0);
+        assert_eq!(
+            status.get_description(),
+            "Status(-5, EX_ILLEGAL_STATE): '\"message\"'".to_string()
+        );
+    }
+
+    #[test]
+    fn convert_to_binder_exception_with() {
+        let res: std::result::Result<(), Status> = Err("message")
+            .or_binder_exception_with(ExceptionCode::ILLEGAL_STATE, |e| {
+                format!("outer message: {:?}", e)
+            });
+
+        assert!(res.is_err());
+        let status = res.unwrap_err();
+        assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE);
+        assert_eq!(status.service_specific_error(), 0);
+        assert_eq!(
+            status.get_description(),
+            "Status(-5, EX_ILLEGAL_STATE): 'outer message: \"message\"'".to_string()
+        );
+    }
 }
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 0c8b48f..8841fe6 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -106,7 +106,7 @@
 
 pub use crate::binder_async::{BinderAsyncPool, BoxFuture};
 pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak};
-pub use error::{ExceptionCode, Status, StatusCode};
+pub use error::{ExceptionCode, IntoBinderResult, Status, StatusCode};
 pub use native::{
     add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service,
     LazyServiceGuard,
diff --git a/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp
index 278dd2b..140270f 100644
--- a/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp
+++ b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp
@@ -37,7 +37,8 @@
     ASSERT_NE(mHeap.get(), nullptr);
     int fd = mHeap->getHeapID();
     EXPECT_NE(fd, -1);
-    EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL);
+    EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL);
+    EXPECT_EQ(ftruncate(fd, 4096), -1);
 }
 
 TEST(MemoryHeapBase, MemfdUnsealed) {
@@ -48,7 +49,8 @@
     ASSERT_NE(mHeap.get(), nullptr);
     int fd = mHeap->getHeapID();
     EXPECT_NE(fd, -1);
-    EXPECT_EQ(fcntl(fd, F_GET_SEALS), 0);
+    EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_GROW | F_SEAL_SHRINK);
+    EXPECT_EQ(ftruncate(fd, 4096), -1);
 }
 
 TEST(MemoryHeapBase, MemfdSealedProtected) {
@@ -59,7 +61,9 @@
     ASSERT_NE(mHeap.get(), nullptr);
     int fd = mHeap->getHeapID();
     EXPECT_NE(fd, -1);
-    EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL | F_SEAL_FUTURE_WRITE);
+    EXPECT_EQ(fcntl(fd, F_GET_SEALS),
+              F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL | F_SEAL_FUTURE_WRITE);
+    EXPECT_EQ(ftruncate(fd, 4096), -1);
 }
 
 TEST(MemoryHeapBase, MemfdUnsealedProtected) {
@@ -71,7 +75,8 @@
     ASSERT_NE(mHeap.get(), nullptr);
     int fd = mHeap->getHeapID();
     EXPECT_NE(fd, -1);
-    EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_FUTURE_WRITE);
+    EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_FUTURE_WRITE);
+    EXPECT_EQ(ftruncate(fd, 4096), -1);
 }
 
 #else
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 0d1503e..383795e 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -129,3 +129,18 @@
     ],
     export_include_dirs: ["include_random_parcel_seeds"],
 }
+
+cc_binary_host {
+    name: "binder2corpus",
+    static_libs: [
+        "libbinder_random_parcel_seeds",
+    ],
+    srcs: [
+        "binder2corpus/binder2corpus.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libutils",
+    ],
+}
diff --git a/libs/binder/tests/parcel_fuzzer/binder2corpus/README.md b/libs/binder/tests/parcel_fuzzer/binder2corpus/README.md
new file mode 100644
index 0000000..59bf9f3
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/binder2corpus/README.md
@@ -0,0 +1,31 @@
+# binder2corpus
+
+This tool converts recordings generated by record_binder tool to fuzzer seeds for fuzzService.
+
+# Steps to add corpus:
+
+## Start recording the service binder
+ex. record_binder start manager
+
+## Run test on device or keep device idle
+ex. atest servicemanager_test
+
+## Stop the recording
+record_binder stop manager
+
+## Pull the recording on host
+Recordings are present on device at /data/local/recordings/<service_name>. Use adb pull.
+Use inspect command of record_binder to check if there are some transactions captured.
+ex. record_binder inspect manager
+
+## run corpus generator tool
+binder2corpus <recording_path> <dir_to_write_corpus>
+
+## Build fuzzer and sync data directory
+ex. m servicemanager_fuzzer && adb sync data
+
+## Push corpus on device
+ex. adb push servicemanager_fuzzer_corpus/ /data/fuzz/x86_64/servicemanager_fuzzer/
+
+## Run fuzzer with corpus directory as argument
+ex. adb shell /data/fuzz/x86_64/servicemanager_fuzzer/servicemanager_fuzzer /data/fuzz/x86_64/servicemanager_fuzzer/servicemanager_fuzzer_corpus
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp b/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp
new file mode 100644
index 0000000..c0fdaea
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <binder/RecordedTransaction.h>
+
+#include <fuzzseeds/random_parcel_seeds.h>
+
+#include <sys/prctl.h>
+
+using android::generateSeedsFromRecording;
+using android::status_t;
+using android::base::unique_fd;
+using android::binder::debug::RecordedTransaction;
+
+status_t generateCorpus(const char* recordingPath, const char* corpusDir) {
+    unique_fd fd(open(recordingPath, O_RDONLY));
+    if (!fd.ok()) {
+        std::cerr << "Failed to open recording file at path " << recordingPath
+                  << " with error: " << strerror(errno) << '\n';
+        return android::BAD_VALUE;
+    }
+
+    if (auto res = mkdir(corpusDir, 0766); res != 0) {
+        std::cerr
+                << "Failed to create corpus directory at path. Delete directory if already exists: "
+                << corpusDir << std::endl;
+        return android::BAD_VALUE;
+    }
+
+    int transactionNumber = 0;
+    while (auto transaction = RecordedTransaction::fromFile(fd)) {
+        ++transactionNumber;
+        std::string filePath = std::string(corpusDir) + std::string("transaction_") +
+                std::to_string(transactionNumber);
+        constexpr int openFlags = O_WRONLY | O_CREAT | O_BINARY | O_CLOEXEC;
+        android::base::unique_fd corpusFd(open(filePath.c_str(), openFlags, 0666));
+        if (!corpusFd.ok()) {
+            std::cerr << "Failed to open fd. Path " << filePath
+                      << " with error: " << strerror(errno) << std::endl;
+            return android::UNKNOWN_ERROR;
+        }
+        generateSeedsFromRecording(corpusFd, transaction.value());
+    }
+
+    if (transactionNumber == 0) {
+        std::cerr << "No valid transaction has been found in recording file:  " << recordingPath
+                  << std::endl;
+        return android::BAD_VALUE;
+    }
+
+    return android::NO_ERROR;
+}
+
+void printHelp(const char* toolName) {
+    std::cout << "Usage: \n\n"
+              << toolName
+              << " <recording_path> <destination_directory> \n\n*Use "
+                 "record_binder tool for recording binder transactions."
+              << std::endl;
+}
+
+int main(int argc, char** argv) {
+    if (argc != 3) {
+        printHelp(argv[0]);
+        return 1;
+    }
+    const char* sourcePath = argv[1];
+    const char* corpusDir = argv[2];
+    if (android::NO_ERROR != generateCorpus(sourcePath, corpusDir)) {
+        std::cerr << "Failed to generate fuzzer corpus." << std::endl;
+        return 1;
+    }
+    return 0;
+}
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 47d2a0a..93ac116 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -60,8 +60,15 @@
 
     while (provider.remaining_bytes() > 0) {
         // Most of the AIDL services will have small set of transaction codes.
-        uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>()
-                                               : provider.ConsumeIntegralInRange<uint32_t>(0, 100);
+        // TODO(b/295942369) : Add remaining transact codes from IBinder.h
+        uint32_t code = provider.ConsumeBool()
+                ? provider.ConsumeIntegral<uint32_t>()
+                : provider.PickValueInArray<int64_t>(
+                          {provider.ConsumeIntegralInRange<uint32_t>(0, 100),
+                           IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION,
+                           IBinder::SHELL_COMMAND_TRANSACTION, IBinder::INTERFACE_TRANSACTION,
+                           IBinder::SYSPROPS_TRANSACTION, IBinder::EXTENSION_TRANSACTION,
+                           IBinder::TWEET_TRANSACTION, IBinder::LIKE_TRANSACTION});
         uint32_t flags = provider.ConsumeIntegral<uint32_t>();
         Parcel data;
         // for increased fuzz coverage
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
index 96092b1..690c39a 100644
--- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
@@ -36,8 +36,8 @@
         triage_assignee: "waghpawan@google.com",
 
         // This fuzzer should be used only test fuzzService locally
-        fuzz_on_haiku_host: true,
-        fuzz_on_haiku_device: true,
+        fuzz_on_haiku_host: false,
+        fuzz_on_haiku_device: false,
     },
 }
 
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
index 46205d7..ba1a6a1 100644
--- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
@@ -33,6 +33,8 @@
     ON_KNOWN_UID,
     ON_SYSTEM_AID,
     ON_ROOT_AID,
+    ON_DUMP_TRANSACT,
+    ON_SHELL_CMD_TRANSACT,
 };
 
 // This service is to verify that fuzzService is functioning properly
@@ -92,6 +94,16 @@
         return Status::ok();
     }
 
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override {
+        if (mCrash == CrashType::ON_DUMP_TRANSACT && code == DUMP_TRANSACTION) {
+            LOG_ALWAYS_FATAL("Expected crash, DUMP.");
+        } else if (mCrash == CrashType::ON_SHELL_CMD_TRANSACT &&
+                   code == SHELL_COMMAND_TRANSACTION) {
+            LOG_ALWAYS_FATAL("Expected crash, SHELL_CMD.");
+        }
+        return BnTestService::onTransact(code, data, reply, flags);
+    }
+
 private:
     CrashType mCrash;
 };
@@ -121,6 +133,10 @@
         gCrashType = CrashType::ON_ROOT_AID;
     } else if (arg == "BINDER") {
         gCrashType = CrashType::ON_BINDER;
+    } else if (arg == "DUMP") {
+        gCrashType = CrashType::ON_DUMP_TRANSACT;
+    } else if (arg == "SHELL_CMD") {
+        gCrashType = CrashType::ON_SHELL_CMD_TRANSACT;
     } else {
         printf("INVALID ARG\n");
         exit(0); // success because this is a crash test
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
index 25906d8..c447bff 100755
--- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
@@ -27,7 +27,7 @@
     exit 1
 fi
 
-for CRASH_TYPE in PLAIN KNOWN_UID AID_SYSTEM AID_ROOT BINDER; do
+for CRASH_TYPE in PLAIN KNOWN_UID AID_SYSTEM AID_ROOT BINDER DUMP SHELL_CMD; do
     echo "INFO: Running fuzzer : test_service_fuzzer_should_crash $CRASH_TYPE"
 
     ./test_service_fuzzer_should_crash "$CRASH_TYPE" -max_total_time=30 &>"$FUZZER_OUT"
diff --git a/libs/bufferstreams/rust/Android.bp b/libs/bufferstreams/rust/Android.bp
index 95a85b5..ff95148 100644
--- a/libs/bufferstreams/rust/Android.bp
+++ b/libs/bufferstreams/rust/Android.bp
@@ -17,7 +17,8 @@
     crate_name: "bufferstreams",
     srcs: ["src/lib.rs"],
     edition: "2021",
-    vendor_available: true,
-    host_supported: true,
+    rlibs: [
+        "libnativewindow_rs",
+    ],
     min_sdk_version: "30",
 }
diff --git a/libs/bufferstreams/rust/src/lib.rs b/libs/bufferstreams/rust/src/lib.rs
index 51f1c73..1d321c8 100644
--- a/libs/bufferstreams/rust/src/lib.rs
+++ b/libs/bufferstreams/rust/src/lib.rs
@@ -14,9 +14,143 @@
 
 //! libbufferstreams: Reactive Streams for Graphics Buffers
 
+use nativewindow::*;
+use std::sync::{Arc, Weak};
+use std::time::Instant;
+
 /// This function will print Hello World.
 #[no_mangle]
 pub extern "C" fn hello() -> bool {
     println!("Hello world.");
     true
 }
+
+/// BufferPublishers provide buffers to BufferSusbscribers. Depending on the
+/// particular object in question, these could be allocated locally or provided
+/// over IPC.
+///
+/// BufferPublishers are required to adhere to the following, based on the
+/// reactive streams specification:
+/// *   The total number of on_next´s signalled by a Publisher to a Subscriber
+/// MUST be less than or equal to the total number of elements requested by that
+/// Subscriber´s Subscription at all times.
+/// *   A Publisher MAY signal fewer on_next than requested and terminate the
+/// Subscription by calling on_complete or on_error.
+/// *   on_subscribe, on_next, on_error and on_complete signaled to a Subscriber
+/// MUST be signaled serially.
+/// *   If a Publisher fails it MUST signal an on_error.
+/// *   If a Publisher terminates successfully (finite stream) it MUST signal an
+/// on_complete.
+/// *   If a Publisher signals either on_error or on_complete on a Subscriber,
+/// that Subscriber’s Subscription MUST be considered cancelled.
+/// *   Once a terminal state has been signaled (on_error, on_complete) it is
+/// REQUIRED that no further signals occur.
+/// *   If a Subscription is cancelled its Subscriber MUST eventually stop being
+///  signaled.
+/// *  A Publisher MAY support multiple Subscribers and decides whether each
+/// Subscription is unicast or multicast.
+pub trait BufferPublisher {
+    /// This function will create the subscription between the publisher and
+    /// the subscriber.
+    fn subscribe(&self, subscriber: Weak<dyn BufferSubscriber>);
+}
+
+/// BufferSubscribers can subscribe to BufferPublishers. They can request Frames
+/// via the BufferSubscription they get from the publisher, then receive Frames
+/// via on_next.
+///
+/// BufferSubcribers are required to adhere to the following, based on the
+/// reactive streams specification:
+/// *   The total number of on_next´s signalled by a Publisher to a Subscriber
+/// MUST be less than or equal to the total number of elements requested by that
+/// Subscriber´s Subscription at all times.
+/// *   A Publisher MAY signal fewer on_next than requested and terminate the
+/// Subscription by calling on_complete or on_error.
+/// *   on_subscribe, on_next, on_error and on_complete signaled to a Subscriber
+/// MUST be signaled serially.
+/// *   If a Publisher fails it MUST signal an on_error.
+/// *   If a Publisher terminates successfully (finite stream) it MUST signal an
+/// on_complete.
+/// *   If a Publisher signals either on_error or on_complete on a Subscriber,
+/// that Subscriber’s Subscription MUST be considered cancelled.
+/// *   Once a terminal state has been signaled (on_error, on_complete) it is
+/// REQUIRED that no further signals occur.
+/// *   If a Subscription is cancelled its Subscriber MUST eventually stop being
+/// signaled.
+/// *   Publisher.subscribe MAY be called as many times as wanted but MUST be
+/// with a different Subscriber each time.
+/// *   A Publisher MAY support multiple Subscribers and decides whether each
+/// Subscription is unicast or multicast.
+pub trait BufferSubscriber {
+    /// This function will be called at the beginning of the subscription.
+    fn on_subscribe(&self, subscription: Arc<dyn BufferSubscription>);
+    /// This function will be called for buffer that comes in.
+    fn on_next(&self, frame: Frame);
+    /// This function will be called in case of an error.
+    fn on_error(&self, error: BufferError);
+    /// This function will be called on finite streams when done.
+    fn on_complete(&self);
+}
+
+/// BufferSubscriptions serve as the bridge between BufferPublishers and
+/// BufferSubscribers. BufferSubscribers receive a BufferSubscription when they
+/// subscribe to a BufferPublisher via on_subscribe.
+/// This object is to be used by the BufferSubscriber to cancel its subscription
+/// or request more buffers.
+///
+/// BufferSubcriptions are required to adhere to the following, based on the
+/// reactive streams specification:
+/// *   Subscription.request and Subscription.cancel MUST only be called inside
+/// of its Subscriber context.
+/// *   The Subscription MUST allow the Subscriber to call Subscription.request
+/// synchronously from within on_next or on_subscribe.
+/// *   Subscription.request MUST place an upper bound on possible synchronous
+/// recursion between Publisher and Subscriber.
+/// *   Subscription.request SHOULD respect the responsivity of its caller by
+/// returning in a timely manner.
+/// *   Subscription.cancel MUST respect the responsivity of its caller by
+/// returning in a timely manner, MUST be idempotent and MUST be thread-safe.
+/// *   After the Subscription is cancelled, additional
+/// Subscription.request(n: u64) MUST be NOPs.
+/// *   After the Subscription is cancelled, additional Subscription.cancel()
+/// MUST be NOPs.
+/// *   While the Subscription is not cancelled, Subscription.request(n: u64)
+/// MUST register the given number of additional elements to be produced to the
+/// respective subscriber.
+/// *   While the Subscription is not cancelled, Subscription.request(n: u64)
+/// MUST signal on_error if the argument is <= 0. The cause message SHOULD
+/// explain that non-positive request signals are illegal.
+/// *  While the Subscription is not cancelled, Subscription.request(n: u64)
+/// MAY synchronously call on_next on this (or other) subscriber(s).
+/// *  While the Subscription is not cancelled, Subscription.request(n: u64)
+/// MAY synchronously call on_complete or on_error on this (or other)
+/// subscriber(s).
+/// *  While the Subscription is not cancelled, Subscription.cancel() MUST
+/// request the Publisher to eventually stop signaling its Subscriber. The
+/// operation is NOT REQUIRED to affect the Subscription immediately.
+/// *  While the Subscription is not cancelled, Subscription.cancel() MUST
+/// request the Publisher to eventually drop any references to the corresponding
+/// subscriber.
+/// *  While the Subscription is not cancelled, calling Subscription.cancel MAY
+/// cause the Publisher, if stateful, to transition into the shut-down state if
+/// no other Subscription exists at this point.
+/// *  Calling Subscription.cancel MUST return normally.
+/// *  Calling Subscription.request MUST return normally.
+pub trait BufferSubscription {
+    /// request
+    fn request(&self, n: u64);
+    /// cancel
+    fn cancel(&self);
+}
+/// Type used to describe errors produced by subscriptions.
+type BufferError = Box<dyn std::error::Error + Send + Sync + 'static>;
+
+/// Struct used to contain the buffer.
+pub struct Frame {
+    /// A handle to the C buffer interface.
+    pub buffer: AHardwareBuffer,
+    /// The time at which the buffer was dispatched.
+    pub present_time: Instant,
+    /// A fence used for reading/writing safely.
+    pub fence: i32,
+}
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index d7e7eb8..298838d 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -62,6 +62,7 @@
     name: "guiconstants_aidl",
     srcs: [
         "android/gui/DropInputMode.aidl",
+        "android/gui/StalledTransactionInfo.aidl",
         "android/**/TouchOcclusionMode.aidl",
     ],
 }
@@ -140,6 +141,7 @@
         "android/gui/IWindowInfosListener.aidl",
         "android/gui/IWindowInfosPublisher.aidl",
         "android/gui/IWindowInfosReportedListener.aidl",
+        "android/gui/StalledTransactionInfo.aidl",
         "android/gui/WindowInfo.aidl",
         "android/gui/WindowInfosUpdate.aidl",
     ],
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 5c324b2..207fa4f 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -303,13 +303,8 @@
                 // frame numbers that were in a sync. We remove the frame from mSyncedFrameNumbers
                 // set and then check if it's empty. If there are no more pending syncs, we can
                 // proceed with flushing the shadow queue.
-                // We also want to check if mSyncTransaction is null because it's possible another
-                // sync request came in while waiting, but it hasn't started processing yet. In that
-                // case, we don't actually want to flush the frames in between since they will get
-                // processed and merged with the sync transaction and released earlier than if they
-                // were sent to SF
                 mSyncedFrameNumbers.erase(currFrameNumber);
-                if (mSyncedFrameNumbers.empty() && mSyncTransaction == nullptr) {
+                if (mSyncedFrameNumbers.empty()) {
                     flushShadowQueue();
                 }
             } else {
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS
index 826a418..070f6bf 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -1,3 +1,5 @@
+# Bug component: 1075131
+
 chrisforbes@google.com
 jreck@google.com
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 3c508c7..7840120 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -59,7 +59,7 @@
 #include <private/gui/ComposerServiceAIDL.h>
 
 // This server size should always be smaller than the server cache size
-#define BUFFER_CACHE_MAX_SIZE 64
+#define BUFFER_CACHE_MAX_SIZE 4096
 
 namespace android {
 
@@ -450,7 +450,9 @@
             callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
                              surfaceControlStats);
         }
+    }
 
+    for (const auto& transactionStats : listenerStats.transactionStats) {
         for (const auto& surfaceStats : transactionStats.surfaceStats) {
             // The callbackMap contains the SurfaceControl object, which we need to look up the
             // layerId. Since we don't know which callback contains the SurfaceControl, iterate
@@ -1306,6 +1308,13 @@
     return status.isOk() ? display : nullptr;
 }
 
+std::optional<gui::StalledTransactionInfo> SurfaceComposerClient::getStalledTransactionInfo(
+        pid_t pid) {
+    std::optional<gui::StalledTransactionInfo> result;
+    ComposerServiceAIDL::getComposerService()->getStalledTransactionInfo(pid, &result);
+    return result;
+}
+
 void SurfaceComposerClient::Transaction::setAnimationTransaction() {
     mAnimation = true;
 }
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 81a8825..e1726b7 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -46,6 +46,7 @@
 import android.gui.OverlayProperties;
 import android.gui.PullAtomData;
 import android.gui.ARect;
+import android.gui.StalledTransactionInfo;
 import android.gui.StaticDisplayInfo;
 import android.gui.WindowInfosListenerInfo;
 
@@ -505,4 +506,10 @@
     void removeWindowInfosListener(IWindowInfosListener windowInfosListener);
 
     OverlayProperties getOverlaySupport();
+
+    /**
+     * Returns an instance of StalledTransaction if a transaction from the passed pid has not been
+     * applied in SurfaceFlinger due to an unsignaled fence. Otherwise, null is returned.
+     */
+    @nullable StalledTransactionInfo getStalledTransactionInfo(int pid);
 }
diff --git a/libs/gui/android/gui/StalledTransactionInfo.aidl b/libs/gui/android/gui/StalledTransactionInfo.aidl
new file mode 100644
index 0000000..e6aa9bd
--- /dev/null
+++ b/libs/gui/android/gui/StalledTransactionInfo.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+/** @hide */
+parcelable StalledTransactionInfo {
+    String layerName;
+    long bufferId;
+    long frameNumber;
+}
\ No newline at end of file
diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp
index 073cc08..cd738ac 100644
--- a/libs/gui/fuzzer/Android.bp
+++ b/libs/gui/fuzzer/Android.bp
@@ -24,6 +24,7 @@
 
 cc_defaults {
     name: "libgui_fuzzer_defaults",
+    defaults: ["android.hardware.power-ndk_shared"],
     static_libs: [
         "android.hidl.token@1.0-utils",
         "libbinder_random_parcel",
@@ -46,7 +47,6 @@
         "android.hardware.configstore-utils",
         "android.hardware.graphics.bufferqueue@1.0",
         "android.hardware.graphics.bufferqueue@2.0",
-        "android.hardware.power-V4-ndk",
         "android.hidl.token@1.0",
         "libSurfaceFlingerProp",
         "libgui",
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index c70c2d0..a381687 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -157,6 +157,8 @@
     MOCK_METHOD(binder::Status, removeWindowInfosListener, (const sp<gui::IWindowInfosListener>&),
                 (override));
     MOCK_METHOD(binder::Status, getOverlaySupport, (gui::OverlayProperties*), (override));
+    MOCK_METHOD(binder::Status, getStalledTransactionInfo,
+                (int32_t, std::optional<gui::StalledTransactionInfo>*), (override));
 };
 
 class FakeBnSurfaceComposerClient : public gui::BnSurfaceComposerClient {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 3cf57b1..dbcbd3b 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -371,6 +371,10 @@
     //! Get token for a physical display given its stable ID
     static sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId);
 
+    // Returns StalledTransactionInfo if a transaction from the provided pid has not been applied
+    // due to an unsignaled fence.
+    static std::optional<gui::StalledTransactionInfo> getStalledTransactionInfo(pid_t pid);
+
     struct SCHash {
         std::size_t operator()(const sp<SurfaceControl>& sc) const {
             return std::hash<SurfaceControl *>{}(sc.get());
diff --git a/libs/gui/tests/OWNERS b/libs/gui/tests/OWNERS
index 156efdb..48cd30d 100644
--- a/libs/gui/tests/OWNERS
+++ b/libs/gui/tests/OWNERS
@@ -1,3 +1,6 @@
 # Android > Android OS & Apps > Framework (Java + Native) > Window Manager > Surfaces
 # Bug component: 316245 = per-file BLASTBufferQueue_test.cpp, DisplayInfo_test.cpp, EndToEndNativeInputTest.cpp, WindowInfos_test.cpp
 # Buganizer template url: https://b.corp.google.com/issues/new?component=316245&template=1018194 = per-file BLASTBufferQueue_test.cpp, DisplayInfo_test.cpp, EndToEndNativeInputTest.cpp, WindowInfos_test.cpp
+
+# Android > Android OS & Apps > graphics > Core Graphics Stack (CoGS)
+# Bug component: 1075130
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 1e3147e..30b7a9f 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -1012,6 +1012,11 @@
         return binder::Status::ok();
     }
 
+    binder::Status getStalledTransactionInfo(
+            int32_t /*pid*/, std::optional<gui::StalledTransactionInfo>* /*result*/) override {
+        return binder::Status::ok();
+    }
+
 protected:
     IBinder* onAsBinder() override { return nullptr; }
 
diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp
index 341eb6f..6c602e0 100644
--- a/libs/input/InputVerifier.cpp
+++ b/libs/input/InputVerifier.cpp
@@ -31,7 +31,7 @@
 // --- InputVerifier ---
 
 InputVerifier::InputVerifier(const std::string& name)
-      : mVerifier(android::input::verifier::create(name)){};
+      : mVerifier(android::input::verifier::create(rust::String::lossy(name))){};
 
 Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t action,
                                             uint32_t pointerCount,
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 8704eee..116b778 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "VelocityTracker"
 
 #include <android-base/logging.h>
+#include <ftl/enum.h>
 #include <inttypes.h>
 #include <limits.h>
 #include <math.h>
@@ -148,27 +149,19 @@
 VelocityTracker::VelocityTracker(const Strategy strategy)
       : mLastEventTime(0), mCurrentPointerIdBits(0), mOverrideStrategy(strategy) {}
 
-VelocityTracker::~VelocityTracker() {
-}
-
 bool VelocityTracker::isAxisSupported(int32_t axis) {
     return DEFAULT_STRATEGY_BY_AXIS.find(axis) != DEFAULT_STRATEGY_BY_AXIS.end();
 }
 
 void VelocityTracker::configureStrategy(int32_t axis) {
     const bool isDifferentialAxis = DIFFERENTIAL_AXES.find(axis) != DIFFERENTIAL_AXES.end();
-
-    std::unique_ptr<VelocityTrackerStrategy> createdStrategy;
-    if (mOverrideStrategy != VelocityTracker::Strategy::DEFAULT) {
-        createdStrategy = createStrategy(mOverrideStrategy, /*deltaValues=*/isDifferentialAxis);
+    if (isDifferentialAxis || mOverrideStrategy == VelocityTracker::Strategy::DEFAULT) {
+        // Do not allow overrides of strategies for differential axes, for now.
+        mConfiguredStrategies[axis] = createStrategy(DEFAULT_STRATEGY_BY_AXIS.at(axis),
+                                                     /*deltaValues=*/isDifferentialAxis);
     } else {
-        createdStrategy = createStrategy(DEFAULT_STRATEGY_BY_AXIS.at(axis),
-                                         /*deltaValues=*/isDifferentialAxis);
+        mConfiguredStrategies[axis] = createStrategy(mOverrideStrategy, /*deltaValues=*/false);
     }
-
-    LOG_ALWAYS_FATAL_IF(createdStrategy == nullptr,
-                        "Could not create velocity tracker strategy for axis '%" PRId32 "'!", axis);
-    mConfiguredStrategies[axis] = std::move(createdStrategy);
 }
 
 std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy(
@@ -216,6 +209,9 @@
         default:
             break;
     }
+    LOG(FATAL) << "Invalid strategy: " << ftl::enum_string(strategy)
+               << ", deltaValues = " << deltaValues;
+
     return nullptr;
 }
 
@@ -272,12 +268,10 @@
     mConfiguredStrategies[axis]->addMovement(eventTime, pointerId, position);
 
     if (DEBUG_VELOCITY) {
-        ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", pointerId=%" PRId32
-              ", activePointerId=%s",
-              eventTime, pointerId, toString(mActivePointerId).c_str());
-
-        ALOGD("  %d: axis=%d, position=%0.3f, velocity=%s", pointerId, axis, position,
-              toString(getVelocity(axis, pointerId)).c_str());
+        LOG(INFO) << "VelocityTracker: addMovement axis=" << MotionEvent::getLabel(axis)
+                  << ", eventTime=" << eventTime << ", pointerId=" << pointerId
+                  << ", activePointerId=" << toString(mActivePointerId) << ", position=" << position
+                  << ", velocity=" << toString(getVelocity(axis, pointerId));
     }
 }
 
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index dab843b..8f6f95b 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -57,4 +57,88 @@
 
     /* The default pointer acceleration value. */
     const int DEFAULT_POINTER_ACCELERATION = 3;
+
+    /**
+     * Use the default Velocity Tracker Strategy. Different axes may use different default
+     * strategies.
+     */
+    const int VELOCITY_TRACKER_STRATEGY_DEFAULT = -1;
+
+    /**
+     * Velocity Tracker Strategy: Impulse.
+     * Physical model of pushing an object.  Quality: VERY GOOD.
+     * Works with duplicate coordinates, unclean finger liftoff.
+     */
+    const int VELOCITY_TRACKER_STRATEGY_IMPULSE = 0;
+
+    /**
+     * Velocity Tracker Strategy: LSQ1.
+     * 1st order least squares.  Quality: POOR.
+     * Frequently underfits the touch data especially when the finger accelerates
+     * or changes direction.  Often underestimates velocity.  The direction
+     * is overly influenced by historical touch points.
+     */
+    const int VELOCITY_TRACKER_STRATEGY_LSQ1 = 1;
+
+    /**
+     * Velocity Tracker Strategy: LSQ2.
+     * 2nd order least squares.  Quality: VERY GOOD.
+     * Pretty much ideal, but can be confused by certain kinds of touch data,
+     * particularly if the panel has a tendency to generate delayed,
+     * duplicate or jittery touch coordinates when the finger is released.
+     */
+    const int VELOCITY_TRACKER_STRATEGY_LSQ2 = 2;
+
+    /**
+     * Velocity Tracker Strategy: LSQ3.
+     * 3rd order least squares.  Quality: UNUSABLE.
+     * Frequently overfits the touch data yielding wildly divergent estimates
+     * of the velocity when the finger is released.
+     */
+    const int VELOCITY_TRACKER_STRATEGY_LSQ3 = 3;
+
+    /**
+     * Velocity Tracker Strategy: WLSQ2_DELTA.
+     * 2nd order weighted least squares, delta weighting.  Quality: EXPERIMENTAL
+     */
+    const int VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA = 4;
+
+    /**
+     * Velocity Tracker Strategy: WLSQ2_CENTRAL.
+     * 2nd order weighted least squares, central weighting.  Quality: EXPERIMENTALe
+     */
+    const int VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL = 5;
+
+    /**
+     * Velocity Tracker Strategy: WLSQ2_RECENT.
+     * 2nd order weighted least squares, recent weighting.  Quality: EXPERIMENTAL
+     */
+    const int VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT = 6;
+
+    /**
+     * Velocity Tracker Strategy: INT1.
+     * 1st order integrating filter.  Quality: GOOD.
+     * Not as good as 'lsq2' because it cannot estimate acceleration but it is
+     * more tolerant of errors.  Like 'lsq1', this strategy tends to underestimate
+     * the velocity of a fling but this strategy tends to respond to changes in
+     * direction more quickly and accurately.
+     */
+    const int VELOCITY_TRACKER_STRATEGY_INT1 = 7;
+
+    /**
+     * Velocity Tracker Strategy: INT2.
+     * 2nd order integrating filter.  Quality: EXPERIMENTAL.
+     * For comparison purposes only.  Unlike 'int1' this strategy can compensate
+     * for acceleration but it typically overestimates the effect.
+     */
+    const int VELOCITY_TRACKER_STRATEGY_INT2 = 8;
+
+    /**
+     * Velocity Tracker Strategy: Legacy.
+     * Legacy velocity tracker algorithm.  Quality: POOR.
+     * For comparison purposes only.  This algorithm is strongly influenced by
+     * old data points, consistently underestimates velocity and takes a very long
+     * time to adjust to changes in direction.
+     */
+    const int VELOCITY_TRACKER_STRATEGY_LEGACY = 9;
 }
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index cadac88..86b996b 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -18,6 +18,7 @@
         "InputDevice_test.cpp",
         "InputEvent_test.cpp",
         "InputPublisherAndConsumer_test.cpp",
+        "InputVerifier_test.cpp",
         "MotionPredictor_test.cpp",
         "RingBuffer_test.cpp",
         "TfLiteMotionPredictor_test.cpp",
diff --git a/libs/input/tests/InputVerifier_test.cpp b/libs/input/tests/InputVerifier_test.cpp
new file mode 100644
index 0000000..e24fa6e
--- /dev/null
+++ b/libs/input/tests/InputVerifier_test.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <input/InputVerifier.h>
+#include <string>
+
+namespace android {
+
+TEST(InputVerifierTest, CreationWithInvalidUtfStringDoesNotCrash) {
+    constexpr char bytes[] = {static_cast<char>(0xC0), static_cast<char>(0x80)};
+    const std::string name(bytes, sizeof(bytes));
+    InputVerifier verifier(name);
+}
+
+} // namespace android
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index ffebff1..1c8ec90 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -278,6 +278,11 @@
         const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions,
         std::optional<float> targetVelocity) {
     checkVelocity(computeVelocity(strategy, motions, AMOTION_EVENT_AXIS_SCROLL), targetVelocity);
+    // The strategy LSQ2 is not compatible with AXIS_SCROLL. In those situations, we should fall
+    // back to a strategy that supports differential axes.
+    checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, motions,
+                                  AMOTION_EVENT_AXIS_SCROLL),
+                  targetVelocity);
 }
 
 static void computeAndCheckQuadraticVelocity(const std::vector<PlanarMotionEventEntry>& motions,
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index a5bcc62..a2ec57c 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -61,6 +61,7 @@
     /// program termination.
     ///
     /// Available since API level 26.
+    #[inline]
     pub fn new(
         width: u32,
         height: u32,
@@ -199,6 +200,7 @@
     #[test]
     #[should_panic]
     fn take_from_raw_panics_on_null() {
+        // SAFETY: Passing a null pointer is safe, it should just panic.
         unsafe { AHardwareBuffer::take_from_raw(ptr::null_mut()) };
     }
 
@@ -216,9 +218,13 @@
         };
         let mut raw_buffer_ptr = ptr::null_mut();
 
+        // SAFETY: The pointers are valid because they come from references, and
+        // `AHardwareBuffer_allocate` doesn't retain them after it returns.
         let status = unsafe { ffi::AHardwareBuffer_allocate(&buffer_desc, &mut raw_buffer_ptr) };
         assert_eq!(status, 0);
 
+        // SAFETY: The pointer must be valid because it was just allocated successfully, and we
+        // don't use it after calling this.
         let buffer = unsafe { AHardwareBuffer::take_from_raw(raw_buffer_ptr as *mut c_void) };
         assert_eq!(buffer.width(), 1024);
     }
diff --git a/libs/nativewindow/tests/benchmark/Android.bp b/libs/nativewindow/tests/benchmark/Android.bp
new file mode 100644
index 0000000..6f844cf
--- /dev/null
+++ b/libs/nativewindow/tests/benchmark/Android.bp
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_defaults {
+    name: "nativewindow_benchmark_defaults_cc",
+    shared_libs: ["libnativewindow"],
+    static_libs: [
+        "libbase",
+        "libgoogle-benchmark-main",
+    ],
+    test_suites: [
+        "device-tests",
+        "NativeWindowBenchmarks",
+    ],
+}
+
+cc_benchmark {
+    name: "nativewindow_buffer_benchmarks_cc",
+    srcs: ["buffer_benchmarks.cc"],
+    defaults: ["nativewindow_benchmark_defaults_cc"],
+}
+
+rust_defaults {
+    name: "nativewindow_benchmark_defaults_rs",
+    rustlibs: [
+        "libnativewindow_rs",
+        "libcriterion",
+    ],
+    test_suites: [
+        "device-tests",
+        "NativeWindowBenchmarks",
+    ],
+}
+
+rust_benchmark {
+    name: "nativewindow_buffer_benchmarks_rs",
+    srcs: ["buffer_benchmarks.rs"],
+    defaults: ["nativewindow_benchmark_defaults_rs"],
+}
diff --git a/libs/nativewindow/tests/benchmark/README.md b/libs/nativewindow/tests/benchmark/README.md
new file mode 100644
index 0000000..7eae538
--- /dev/null
+++ b/libs/nativewindow/tests/benchmark/README.md
@@ -0,0 +1,22 @@
+# libnativewindow Benchmarks
+
+This directory contains benchmarks for the C++ and Rust variants of
+libnativewindow.
+
+## Running
+
+It is currently a little tricky to get statistics from Rust benchmarks directly
+from tradefed. But we can hack it by using atest to build/push, then running
+the benchmarks by hand to get stats.
+
+```
+  $ atest nativewindow_buffer_benchmarks_rs nativewindow_buffer_benchmarks_cc -d
+  $ adb shell /data/local/tmp/nativewindow_buffer_benchmarks_cc/x86_64/nativewindow_buffer_benchmarks_cc
+  $ adb shell /data/local/tmp/nativewindow_buffer_benchmarks_rs/x86_64/nativewindow_buffer_benchmarks_rs --bench
+```
+
+## Results
+
+On a remote emulator, the results we see from the benchmarks from Rust and C++
+seem to be roughly equivalent! Allocating/deallocating a 720p buffer takes
+~2.3ms on each.
diff --git a/libs/nativewindow/tests/benchmark/buffer_benchmarks.cc b/libs/nativewindow/tests/benchmark/buffer_benchmarks.cc
new file mode 100644
index 0000000..9b31993
--- /dev/null
+++ b/libs/nativewindow/tests/benchmark/buffer_benchmarks.cc
@@ -0,0 +1,74 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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/macros.h>
+#include <android/hardware_buffer.h>
+#include <benchmark/benchmark.h>
+
+constexpr AHardwareBuffer_Desc k720pDesc = {.width = 1280,
+                                            .height = 720,
+                                            .layers = 1,
+                                            .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+                                            .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+                                            .stride = 0};
+
+static void BM_BufferAllocationDeallocation(benchmark::State& state) {
+    AHardwareBuffer* buffer = nullptr;
+    for (auto _ : state) {
+        int status = AHardwareBuffer_allocate(&k720pDesc, &buffer);
+        if (UNLIKELY(status != 0)) {
+            state.SkipWithError("Unable to allocate buffer.");
+        }
+        AHardwareBuffer_release(buffer);
+        buffer = nullptr;
+    }
+}
+BENCHMARK(BM_BufferAllocationDeallocation);
+
+static void BM_AHardwareBuffer_Id(benchmark::State& state) {
+    AHardwareBuffer* buffer = nullptr;
+    int status = AHardwareBuffer_allocate(&k720pDesc, &buffer);
+    if (UNLIKELY(status != 0)) {
+        state.SkipWithError("Unable to allocate buffer.");
+    }
+
+    for (auto _ : state) {
+        uint64_t id = 0;
+        int status = AHardwareBuffer_getId(buffer, &id);
+        if (UNLIKELY(status != 0)) {
+            state.SkipWithError("Unable to get ID.");
+        }
+    }
+
+    AHardwareBuffer_release(buffer);
+}
+BENCHMARK(BM_AHardwareBuffer_Id);
+
+static void BM_AHardwareBuffer_Desc(benchmark::State& state) {
+    AHardwareBuffer* buffer = nullptr;
+    int status = AHardwareBuffer_allocate(&k720pDesc, &buffer);
+    if (UNLIKELY(status != 0)) {
+        state.SkipWithError("Unable to allocate buffer.");
+    }
+
+    for (auto _ : state) {
+        AHardwareBuffer_Desc desc = {};
+        AHardwareBuffer_describe(buffer, &desc);
+    }
+
+    AHardwareBuffer_release(buffer);
+}
+BENCHMARK(BM_AHardwareBuffer_Desc);
+
+BENCHMARK_MAIN();
diff --git a/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs b/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs
new file mode 100644
index 0000000..fbd49c0
--- /dev/null
+++ b/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs
@@ -0,0 +1,60 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Benchmark for libnativewindow AHardwareBuffer bindings
+
+#![allow(dead_code)]
+#![allow(missing_docs)]
+
+use criterion::*;
+use nativewindow::*;
+
+#[inline]
+fn create_720p_buffer() -> AHardwareBuffer {
+    AHardwareBuffer::new(
+        1280,
+        720,
+        1,
+        AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+        AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+    )
+    .unwrap()
+}
+
+fn criterion_benchmark(c: &mut Criterion) {
+    c.bench_function("allocate_deallocate", |b| {
+        b.iter(|| {
+            let buffer = create_720p_buffer();
+            drop(buffer);
+        })
+    });
+
+    let buffer = create_720p_buffer();
+    c.bench_with_input(BenchmarkId::new("id", "buffer"), &buffer, |b, buffer| {
+        b.iter(|| {
+            buffer.id();
+        })
+    });
+
+    // This benchmark exercises getters that need to fetch data via an
+    // underlying call to AHardwareBuffer_describe.
+    c.bench_with_input(BenchmarkId::new("desc", "buffer"), &buffer, |b, buffer| {
+        b.iter(|| {
+            buffer.width();
+        })
+    });
+}
+
+criterion_group!(benches, criterion_benchmark);
+criterion_main!(benches);
diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS
index 5d23a5e..66e1aa1 100644
--- a/libs/renderengine/OWNERS
+++ b/libs/renderengine/OWNERS
@@ -1,3 +1,5 @@
+# Bug component: 1075131
+
 adyabr@google.com
 alecmouri@google.com
 djsollen@google.com
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index dad3c19..90dcae4 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -23,7 +23,7 @@
 #include <SkImage.h>
 #include <include/gpu/ganesh/SkImageGanesh.h>
 #include <include/gpu/ganesh/SkSurfaceGanesh.h>
-
+#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
 #include <android/hardware_buffer.h>
 #include "ColorSpaces.h"
 #include "log/log_main.h"
@@ -40,13 +40,44 @@
     AHardwareBuffer_Desc desc;
     AHardwareBuffer_describe(buffer, &desc);
     bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
-    GrBackendFormat backendFormat =
-            GrAHardwareBufferUtils::GetBackendFormat(context, buffer, desc.format, false);
-    mBackendTexture =
-            GrAHardwareBufferUtils::MakeBackendTexture(context, buffer, desc.width, desc.height,
-                                                       &mDeleteProc, &mUpdateProc, &mImageCtx,
-                                                       createProtectedImage, backendFormat,
-                                                       isOutputBuffer);
+    GrBackendFormat backendFormat;
+
+    GrBackendApi backend = context->backend();
+    if (backend == GrBackendApi::kOpenGL) {
+        backendFormat =
+                GrAHardwareBufferUtils::GetGLBackendFormat(context, desc.format, false);
+        mBackendTexture =
+                GrAHardwareBufferUtils::MakeGLBackendTexture(context,
+                                                             buffer,
+                                                             desc.width,
+                                                             desc.height,
+                                                             &mDeleteProc,
+                                                             &mUpdateProc,
+                                                             &mImageCtx,
+                                                             createProtectedImage,
+                                                             backendFormat,
+                                                             isOutputBuffer);
+    } else if (backend == GrBackendApi::kVulkan) {
+        backendFormat =
+                GrAHardwareBufferUtils::GetVulkanBackendFormat(context,
+                                                               buffer,
+                                                               desc.format,
+                                                               false);
+        mBackendTexture =
+                GrAHardwareBufferUtils::MakeVulkanBackendTexture(context,
+                                                                 buffer,
+                                                                 desc.width,
+                                                                 desc.height,
+                                                                 &mDeleteProc,
+                                                                 &mUpdateProc,
+                                                                 &mImageCtx,
+                                                                 createProtectedImage,
+                                                                 backendFormat,
+                                                                 isOutputBuffer);
+    } else {
+        LOG_ALWAYS_FATAL("Unexpected backend %d", backend);
+    }
+
     mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
     if (!mBackendTexture.isValid() || !desc.width || !desc.height) {
         LOG_ALWAYS_FATAL("Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d "
@@ -94,7 +125,7 @@
     switch (tex.backend()) {
         case GrBackendApi::kOpenGL: {
             GrGLTextureInfo textureInfo;
-            bool retrievedTextureInfo = tex.getGLTextureInfo(&textureInfo);
+            bool retrievedTextureInfo = GrBackendTextures::GetGLTextureInfo(tex, &textureInfo);
             LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
                              "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
                              "texType: %i\n\t\tGrGLTextureInfo: success: %i fTarget: %u fFormat: %u"
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 477beaf..1b19870 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1343,19 +1343,8 @@
         mFakePolicy = sp<FakeInputReaderPolicy>::make();
         mFakePointerController = std::make_shared<FakePointerController>();
         mFakePolicy->setPointerController(mFakePointerController);
-        mTestListener = std::make_unique<TestInputListener>(/*eventHappenedTimeout=*/2000ms,
-                                                            /*eventDidNotHappenTimeout=*/30ms);
 
-        mReader = std::make_unique<InputReader>(std::make_shared<EventHub>(), mFakePolicy,
-                                                *mTestListener);
-        ASSERT_EQ(mReader->start(), OK);
-
-        // Since this test is run on a real device, all the input devices connected
-        // to the test device will show up in mReader. We wait for those input devices to
-        // show up before beginning the tests.
-        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
-        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyInputDevicesChangedWasCalled());
-        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+        setupInputReader();
     }
 
     void TearDown() override {
@@ -1376,6 +1365,22 @@
                                       });
         return it != inputDevices.end() ? std::make_optional(*it) : std::nullopt;
     }
+
+    void setupInputReader() {
+        mTestListener = std::make_unique<TestInputListener>(/*eventHappenedTimeout=*/2000ms,
+                                                            /*eventDidNotHappenTimeout=*/30ms);
+
+        mReader = std::make_unique<InputReader>(std::make_shared<EventHub>(), mFakePolicy,
+                                                *mTestListener);
+        ASSERT_EQ(mReader->start(), OK);
+
+        // Since this test is run on a real device, all the input devices connected
+        // to the test device will show up in mReader. We wait for those input devices to
+        // show up before beginning the tests.
+        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyInputDevicesChangedWasCalled());
+        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+    }
 };
 
 TEST_F(InputReaderIntegrationTest, TestInvalidDevice) {
@@ -1509,7 +1514,7 @@
 
 // --- TouchIntegrationTest ---
 
-class TouchIntegrationTest : public InputReaderIntegrationTest {
+class BaseTouchIntegrationTest : public InputReaderIntegrationTest {
 protected:
     const std::string UNIQUE_ID = "local:0";
 
@@ -1554,7 +1559,55 @@
     InputDeviceInfo mDeviceInfo;
 };
 
-TEST_F(TouchIntegrationTest, MultiTouchDeviceSource) {
+enum class TouchIntegrationTestDisplays { DISPLAY_INTERNAL, DISPLAY_INPUT_PORT, DISPLAY_UNIQUE_ID };
+
+class TouchIntegrationTest : public BaseTouchIntegrationTest,
+                             public testing::WithParamInterface<TouchIntegrationTestDisplays> {
+protected:
+    static constexpr std::optional<uint8_t> DISPLAY_PORT = 0;
+    const std::string INPUT_PORT = "uinput_touch/input0";
+
+    void SetUp() override {
+#if !defined(__ANDROID__)
+        GTEST_SKIP();
+#endif
+        if (GetParam() == TouchIntegrationTestDisplays::DISPLAY_INTERNAL) {
+            BaseTouchIntegrationTest::SetUp();
+            return;
+        }
+
+        // setup policy with a input-port or UniqueId association to the display
+        bool isInputPortAssociation =
+                GetParam() == TouchIntegrationTestDisplays::DISPLAY_INPUT_PORT;
+
+        mFakePolicy = sp<FakeInputReaderPolicy>::make();
+        if (isInputPortAssociation) {
+            mFakePolicy->addInputPortAssociation(INPUT_PORT, DISPLAY_PORT.value());
+        } else {
+            mFakePolicy->addInputUniqueIdAssociation(INPUT_PORT, UNIQUE_ID);
+        }
+        mFakePointerController = std::make_shared<FakePointerController>();
+        mFakePolicy->setPointerController(mFakePointerController);
+
+        InputReaderIntegrationTest::setupInputReader();
+
+        mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT),
+                                                        INPUT_PORT);
+        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+
+        // Add a display linked to a physical port or UniqueId.
+        setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+                                     UNIQUE_ID, isInputPortAssociation ? DISPLAY_PORT : NO_PORT,
+                                     ViewportType::INTERNAL);
+        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+        const auto info = findDeviceByName(mDevice->getName());
+        ASSERT_TRUE(info);
+        mDeviceInfo = *info;
+    }
+};
+
+TEST_P(TouchIntegrationTest, MultiTouchDeviceSource) {
     // The UinputTouchScreen is an MT device that supports MT_TOOL_TYPE and also supports stylus
     // buttons. It should show up as a touchscreen, stylus, and keyboard (for reporting button
     // presses).
@@ -1562,7 +1615,7 @@
               mDeviceInfo.getSources());
 }
 
-TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
+TEST_P(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
     NotifyMotionArgs args;
     const Point centerPoint = mDevice->getCenterPoint();
 
@@ -1586,7 +1639,7 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
 }
 
-TEST_F(TouchIntegrationTest, InputEvent_ProcessMultiTouch) {
+TEST_P(TouchIntegrationTest, InputEvent_ProcessMultiTouch) {
     NotifyMotionArgs args;
     const Point centerPoint = mDevice->getCenterPoint();
 
@@ -1642,7 +1695,7 @@
  * palms, and wants to cancel Pointer 1, then it is safe to simply drop POINTER_1_UP event without
  * losing information about non-palm pointers.
  */
-TEST_F(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerUp) {
+TEST_P(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerUp) {
     NotifyMotionArgs args;
     const Point centerPoint = mDevice->getCenterPoint();
 
@@ -1685,7 +1738,7 @@
  * In this scenario, the movement of the second pointer just prior to liftoff is ignored, and never
  * gets sent to the listener.
  */
-TEST_F(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerMoveAndUp) {
+TEST_P(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerMoveAndUp) {
     NotifyMotionArgs args;
     const Point centerPoint = mDevice->getCenterPoint();
 
@@ -1725,7 +1778,7 @@
     assertReceivedMotion(AMOTION_EVENT_ACTION_MOVE, {centerPoint + Point(5, 5)});
 }
 
-TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) {
+TEST_P(TouchIntegrationTest, InputEvent_ProcessPalm) {
     NotifyMotionArgs args;
     const Point centerPoint = mDevice->getCenterPoint();
 
@@ -1776,7 +1829,7 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
 }
 
-TEST_F(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) {
+TEST_P(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) {
     const Point centerPoint = mDevice->getCenterPoint();
 
     // Send down with the pen tool selected. The policy should be notified of the stylus presence.
@@ -1828,19 +1881,69 @@
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId()));
 }
 
+TEST_P(TouchIntegrationTest, ExternalStylusConnectedDuringTouchGesture) {
+    const Point centerPoint = mDevice->getCenterPoint();
+
+    // Down
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendTrackingId(FIRST_TRACKING_ID);
+    mDevice->sendDown(centerPoint);
+    mDevice->sendSync();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+            WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+    // Move
+    mDevice->sendMove(centerPoint + Point(1, 1));
+    mDevice->sendSync();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+            WithMotionAction(AMOTION_EVENT_ACTION_MOVE)));
+
+    // Connecting an external stylus mid-gesture should not interrupt the ongoing gesture stream.
+    auto externalStylus = createUinputDevice<UinputExternalStylus>();
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+    const auto stylusInfo = findDeviceByName(externalStylus->getName());
+    ASSERT_TRUE(stylusInfo);
+
+    // Move
+    mDevice->sendMove(centerPoint + Point(2, 2));
+    mDevice->sendSync();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+            WithMotionAction(AMOTION_EVENT_ACTION_MOVE)));
+
+    // Disconnecting an external stylus mid-gesture should not interrupt the ongoing gesture stream.
+    externalStylus.reset();
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+
+    // Up
+    mDevice->sendUp();
+    mDevice->sendSync();
+    ASSERT_NO_FATAL_FAILURE(
+            mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
+
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+}
+
+INSTANTIATE_TEST_SUITE_P(TouchIntegrationTestDisplayVariants, TouchIntegrationTest,
+                         testing::Values(TouchIntegrationTestDisplays::DISPLAY_INTERNAL,
+                                         TouchIntegrationTestDisplays::DISPLAY_INPUT_PORT,
+                                         TouchIntegrationTestDisplays::DISPLAY_UNIQUE_ID));
+
 // --- StylusButtonIntegrationTest ---
 
 // Verify the behavior of button presses reported by various kinds of styluses, including buttons
 // reported by the touchscreen's device, by a fused external stylus, and by an un-fused external
 // stylus.
 template <typename UinputStylusDevice>
-class StylusButtonIntegrationTest : public TouchIntegrationTest {
+class StylusButtonIntegrationTest : public BaseTouchIntegrationTest {
 protected:
     void SetUp() override {
 #if !defined(__ANDROID__)
         GTEST_SKIP();
 #endif
-        TouchIntegrationTest::SetUp();
+        BaseTouchIntegrationTest::SetUp();
         mTouchscreen = mDevice.get();
         mTouchscreenInfo = mDeviceInfo;
 
@@ -1878,8 +1981,8 @@
     std::unique_ptr<UinputStylusDevice> mStylusDeviceLifecycleTracker{};
 
     // Hide the base class's device to expose it with a different name for readability.
-    using TouchIntegrationTest::mDevice;
-    using TouchIntegrationTest::mDeviceInfo;
+    using BaseTouchIntegrationTest::mDevice;
+    using BaseTouchIntegrationTest::mDeviceInfo;
 };
 
 using StylusButtonIntegrationTestTypes =
@@ -2131,7 +2234,7 @@
 // Verify the behavior of an external stylus. An external stylus can report pressure or button
 // data independently of the touchscreen, which is then sent as a MotionEvent as part of an
 // ongoing stylus gesture that is being emitted by the touchscreen.
-using ExternalStylusIntegrationTest = TouchIntegrationTest;
+using ExternalStylusIntegrationTest = BaseTouchIntegrationTest;
 
 TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureReported) {
     const Point centerPoint = mDevice->getCenterPoint();
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index fc917dd..41e250f 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -57,6 +57,18 @@
                                            "Expected notifyDeviceReset() to have been called."));
 }
 
+void TestInputListener::clearNotifyDeviceResetCalls() {
+    std::scoped_lock<std::mutex> lock(mLock);
+    std::get<std::vector<NotifyDeviceResetArgs>>(mQueues).clear();
+}
+
+void TestInputListener::assertNotifyDeviceResetWasCalled(
+        const ::testing::Matcher<NotifyDeviceResetArgs>& matcher) {
+    NotifyDeviceResetArgs outEventArgs;
+    ASSERT_NO_FATAL_FAILURE(assertNotifyDeviceResetWasCalled(&outEventArgs));
+    ASSERT_THAT(outEventArgs, matcher);
+}
+
 void TestInputListener::assertNotifyDeviceResetWasNotCalled() {
     ASSERT_NO_FATAL_FAILURE(
             assertNotCalled<NotifyDeviceResetArgs>("notifyDeviceReset() should not be called."));
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index deb6048..3c5e014 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -43,6 +43,10 @@
 
     void assertNotifyConfigurationChangedWasNotCalled();
 
+    void clearNotifyDeviceResetCalls();
+
+    void assertNotifyDeviceResetWasCalled(const ::testing::Matcher<NotifyDeviceResetArgs>& matcher);
+
     void assertNotifyDeviceResetWasCalled(NotifyDeviceResetArgs* outEventArgs = nullptr);
 
     void assertNotifyDeviceResetWasNotCalled();
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index 020ea86..183383f 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -139,6 +139,10 @@
         return mDeviceId == args.deviceId;
     }
 
+    bool MatchAndExplain(const NotifyDeviceResetArgs& args, std::ostream*) const {
+        return mDeviceId == args.deviceId;
+    }
+
     bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
         return mDeviceId == event.getDeviceId();
     }
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
index 97a2614..7ceaccf 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -159,10 +159,11 @@
 
 // --- UinputTouchScreen ---
 
-UinputTouchScreen::UinputTouchScreen(const Rect& size)
+UinputTouchScreen::UinputTouchScreen(const Rect& size, const std::string& physicalPort)
       : UinputKeyboard(DEVICE_NAME, PRODUCT_ID,
                        {BTN_TOUCH, BTN_TOOL_PEN, BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}),
-        mSize(size) {}
+        mSize(size),
+        mPhysicalPort(physicalPort) {}
 
 void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) {
     UinputKeyboard::configureDevice(fd, device);
@@ -177,6 +178,9 @@
     ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
     ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
     ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
+    if (!mPhysicalPort.empty()) {
+        ioctl(fd, UI_SET_PHYS, mPhysicalPort.c_str());
+    }
 
     device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN;
     device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX;
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
index 51e331d..5b07465 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -197,11 +197,12 @@
     const Point getCenterPoint();
 
 protected:
-    explicit UinputTouchScreen(const Rect& size);
+    explicit UinputTouchScreen(const Rect& size, const std::string& physicalPort = "");
 
 private:
     void configureDevice(int fd, uinput_user_dev* device) override;
     const Rect mSize;
+    const std::string mPhysicalPort;
 };
 
 } // namespace android
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index d7980f5..9313a89 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -56,6 +56,15 @@
     ],
     fuzz_config: {
         cc: ["android-framework-input@google.com"],
+        componentid: 155276,
+        hotlists: [
+            "4593311",
+        ],
+        description: "The fuzzer targets the APIs of libinputflinger library",
+        vector: "local_no_privileges_required",
+        service_privilege: "privileged",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped",
     },
 }
 
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 2523f3b..8b16890 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -9,7 +9,7 @@
 
 cc_library_shared {
     name: "libpowermanager",
-
+    defaults: ["android.hardware.power-ndk_export_shared"],
     srcs: [
         "BatterySaverPolicyConfig.cpp",
         "CoolingDevice.cpp",
@@ -41,7 +41,6 @@
         "android.hardware.power@1.1",
         "android.hardware.power@1.2",
         "android.hardware.power@1.3",
-        "android.hardware.power-V4-ndk",
     ],
 
     export_shared_lib_headers: [
@@ -49,7 +48,6 @@
         "android.hardware.power@1.1",
         "android.hardware.power@1.2",
         "android.hardware.power@1.3",
-        "android.hardware.power-V4-ndk",
     ],
 
     cflags: [
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
index 03fc38d..2b5ddb1 100644
--- a/services/powermanager/benchmarks/Android.bp
+++ b/services/powermanager/benchmarks/Android.bp
@@ -23,6 +23,7 @@
 
 cc_benchmark {
     name: "libpowermanager_benchmarks",
+    defaults: ["android.hardware.power-ndk_shared"],
     srcs: [
         "main.cpp",
         "PowerHalAidlBenchmarks.cpp",
@@ -41,7 +42,6 @@
         "android.hardware.power@1.1",
         "android.hardware.power@1.2",
         "android.hardware.power@1.3",
-        "android.hardware.power-V4-ndk",
     ],
     static_libs: [
         "libtestUtil",
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 08fcdc8..6fc96c0 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -23,6 +23,10 @@
 
 cc_test {
     name: "libpowermanager_test",
+    defaults: [
+        "android.hardware.power-ndk_shared",
+        "android.hardware.power-ndk_shared",
+    ],
     test_suites: ["device-tests"],
     srcs: [
         "IThermalManagerTest.cpp",
@@ -52,7 +56,6 @@
         "android.hardware.power@1.1",
         "android.hardware.power@1.2",
         "android.hardware.power@1.3",
-        "android.hardware.power-V4-ndk",
     ],
     static_libs: [
         "libgmock",
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 326645e..0101c17 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -26,6 +26,7 @@
     name: "libsurfaceflinger_defaults",
     defaults: [
         "android.hardware.graphics.composer3-ndk_shared",
+        "android.hardware.power-ndk_shared",
         "librenderengine_deps",
         "libtimestats_deps",
         "surfaceflinger_defaults",
@@ -48,7 +49,6 @@
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
         "android.hardware.graphics.composer@2.4",
-        "android.hardware.power-V4-ndk",
         "libbase",
         "libbinder",
         "libbinder_ndk",
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
index b56b252..fefc040 100644
--- a/services/surfaceflinger/ClientCache.h
+++ b/services/surfaceflinger/ClientCache.h
@@ -29,7 +29,9 @@
 #include <set>
 #include <unordered_map>
 
-#define BUFFER_CACHE_MAX_SIZE 64
+// 4096 is based on 64 buffers * 64 layers. Once this limit is reached, the least recently used
+// buffer is uncached before the new buffer is cached.
+#define BUFFER_CACHE_MAX_SIZE 4096
 
 namespace android {
 
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 3426495..06c5e4c 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -11,6 +11,7 @@
     name: "libcompositionengine_defaults",
     defaults: [
         "android.hardware.graphics.composer3-ndk_shared",
+        "android.hardware.power-ndk_shared",
         "librenderengine_deps",
         "libtimestats_deps",
         "surfaceflinger_defaults",
@@ -27,7 +28,6 @@
         "android.hardware.graphics.composer@2.4",
         "android.hardware.power@1.0",
         "android.hardware.power@1.3",
-        "android.hardware.power-V4-ndk",
         "libbase",
         "libcutils",
         "libgui",
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 3c7cbe6..70ccaf8 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -213,10 +213,7 @@
     ATRACE_INT(mRenderFrameRateFPSTrace.c_str(), renderFps.getIntValue());
 
     mRefreshRateSelector->setActiveMode(modeId, renderFps);
-
-    if (mRefreshRateOverlay) {
-        mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps);
-    }
+    updateRefreshRateOverlayRate(displayFps, renderFps);
 }
 
 status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info,
@@ -473,7 +470,7 @@
     mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features);
     mRefreshRateOverlay->setLayerStack(getLayerStack());
     mRefreshRateOverlay->setViewport(getSize());
-    updateRefreshRateOverlayRate(getActiveMode().modePtr->getFps(), getActiveMode().fps);
+    updateRefreshRateOverlayRate(getActiveMode().modePtr->getFps(), getActiveMode().fps, setByHwc);
 }
 
 void DisplayDevice::updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc) {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 5d41fdd..d42bce6 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -19,6 +19,7 @@
 #define LOG_TAG "SurfaceFlinger"
 
 #include "LayerSnapshot.h"
+#include "Layer.h"
 
 namespace android::surfaceflinger::frontend {
 
@@ -363,7 +364,7 @@
         geomBufferUsesDisplayInverseTransform = requested.transformToDisplayInverse;
     }
     if (forceUpdate || requested.what & layer_state_t::eDataspaceChanged) {
-        dataspace = requested.dataspace;
+        dataspace = Layer::translateDataspace(requested.dataspace);
     }
     if (forceUpdate || requested.what & layer_state_t::eExtendedRangeBrightnessChanged) {
         currentHdrSdrRatio = requested.currentHdrSdrRatio;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 162b546..159d0f0 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -854,8 +854,9 @@
     }
 
     if (forceUpdate || snapshot.clientChanges & layer_state_t::eCornerRadiusChanged ||
-        snapshot.changes.any(RequestedLayerState::Changes::Geometry)) {
-        updateRoundedCorner(snapshot, requested, parentSnapshot);
+        snapshot.changes.any(RequestedLayerState::Changes::Geometry |
+                             RequestedLayerState::Changes::BufferUsageFlags)) {
+        updateRoundedCorner(snapshot, requested, parentSnapshot, args);
     }
 
     if (forceUpdate || snapshot.clientChanges & layer_state_t::eShadowRadiusChanged ||
@@ -886,7 +887,12 @@
 
 void LayerSnapshotBuilder::updateRoundedCorner(LayerSnapshot& snapshot,
                                                const RequestedLayerState& requested,
-                                               const LayerSnapshot& parentSnapshot) {
+                                               const LayerSnapshot& parentSnapshot,
+                                               const Args& args) {
+    if (args.skipRoundCornersWhenProtected && requested.isProtected()) {
+        snapshot.roundedCorner = RoundedCornerState();
+        return;
+    }
     snapshot.roundedCorner = RoundedCornerState();
     RoundedCornerState parentRoundedCorner;
     if (parentSnapshot.roundedCorner.hasRoundedCorners()) {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index d361605..3d64b36 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -54,6 +54,7 @@
         std::unordered_set<uint32_t> excludeLayerIds;
         const std::unordered_map<std::string, bool>& supportedLayerGenericMetadata;
         const std::unordered_map<std::string, uint32_t>& genericLayerMetadataKeyMap;
+        bool skipRoundCornersWhenProtected = false;
     };
     LayerSnapshotBuilder();
 
@@ -103,7 +104,7 @@
                                     bool parentIsRelative, const Args& args);
     static void resetRelativeState(LayerSnapshot& snapshot);
     static void updateRoundedCorner(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
-                                    const LayerSnapshot& parentSnapshot);
+                                    const LayerSnapshot& parentSnapshot, const Args& args);
     void updateLayerBounds(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
                            const LayerSnapshot& parentSnapshot, uint32_t displayRotationFlags);
     static void updateShadows(LayerSnapshot& snapshot, const RequestedLayerState& requested,
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index a4777d1..d979c46 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -147,6 +147,8 @@
     const ui::Size oldBufferSize = hadBuffer
             ? ui::Size(externalTexture->getWidth(), externalTexture->getHeight())
             : ui::Size();
+    const uint64_t oldUsageFlags = hadBuffer ? externalTexture->getUsage() : 0;
+
     const bool hadSideStream = sidebandStream != nullptr;
     const layer_state_t& clientState = resolvedComposerState.state;
     const bool hadBlur = hasBlur();
@@ -177,6 +179,10 @@
                 changes |= RequestedLayerState::Changes::BufferSize;
                 changes |= RequestedLayerState::Changes::Geometry;
             }
+            const uint64_t usageFlags = hasBuffer ? externalTexture->getUsage() : 0;
+            if (oldUsageFlags != usageFlags) {
+                changes |= RequestedLayerState::Changes::BufferUsageFlags;
+            }
         }
 
         if (hasBuffer != hadBuffer) {
@@ -570,6 +576,10 @@
     return true;
 }
 
+bool RequestedLayerState::isProtected() const {
+    return externalTexture && externalTexture->getUsage() & GRALLOC_USAGE_PROTECTED;
+}
+
 void RequestedLayerState::clearChanges() {
     what = 0;
     changes.clear();
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 1c19d6d..0309302 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -56,6 +56,7 @@
         Animation = 1u << 17,
         BufferSize = 1u << 18,
         GameMode = 1u << 19,
+        BufferUsageFlags = 1u << 20,
     };
     static Rect reduce(const Rect& win, const Region& exclude);
     RequestedLayerState(const LayerCreationArgs&);
@@ -85,6 +86,7 @@
     bool willReleaseBufferOnLatch() const;
     bool backpressureEnabled() const;
     bool isSimpleBufferUpdate(const layer_state_t&) const;
+    bool isProtected() const;
 
     // Layer serial number.  This gives layers an explicit ordering, so we
     // have a stable sort order when their layer stack and Z-order are
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index 0d3c6eb..ca7c3c2 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -188,21 +188,36 @@
 }
 
 void TransactionHandler::onTransactionQueueStalled(uint64_t transactionId,
-                                                   sp<ITransactionCompletedListener>& listener,
-                                                   const std::string& reason) {
-    if (std::find(mStalledTransactions.begin(), mStalledTransactions.end(), transactionId) !=
-        mStalledTransactions.end()) {
-        return;
-    }
-
-    mStalledTransactions.push_back(transactionId);
-    listener->onTransactionQueueStalled(String8(reason.c_str()));
+                                                   StalledTransactionInfo stalledTransactionInfo) {
+    std::lock_guard lock{mStalledMutex};
+    mStalledTransactions.emplace(transactionId, std::move(stalledTransactionInfo));
 }
 
-void TransactionHandler::removeFromStalledTransactions(uint64_t id) {
-    auto it = std::find(mStalledTransactions.begin(), mStalledTransactions.end(), id);
-    if (it != mStalledTransactions.end()) {
-        mStalledTransactions.erase(it);
+void TransactionHandler::removeFromStalledTransactions(uint64_t transactionId) {
+    std::lock_guard lock{mStalledMutex};
+    mStalledTransactions.erase(transactionId);
+}
+
+std::optional<TransactionHandler::StalledTransactionInfo>
+TransactionHandler::getStalledTransactionInfo(pid_t pid) {
+    std::lock_guard lock{mStalledMutex};
+    for (auto [_, stalledTransactionInfo] : mStalledTransactions) {
+        if (pid == stalledTransactionInfo.pid) {
+            return stalledTransactionInfo;
+        }
+    }
+    return std::nullopt;
+}
+
+void TransactionHandler::onLayerDestroyed(uint32_t layerId) {
+    std::lock_guard lock{mStalledMutex};
+    for (auto it = mStalledTransactions.begin(); it != mStalledTransactions.end();) {
+        if (it->second.layerId == layerId) {
+            it = mStalledTransactions.erase(it);
+        } else {
+            it++;
+        }
     }
 }
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
index 04183bc..00f6bce 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.h
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -18,6 +18,7 @@
 
 #include <semaphore.h>
 #include <cstdint>
+#include <optional>
 #include <vector>
 
 #include <LocklessQueue.h>
@@ -63,9 +64,18 @@
     std::vector<TransactionState> flushTransactions();
     void addTransactionReadyFilter(TransactionFilter&&);
     void queueTransaction(TransactionState&&);
-    void onTransactionQueueStalled(uint64_t transactionId, sp<ITransactionCompletedListener>&,
-                                   const std::string& reason);
+
+    struct StalledTransactionInfo {
+        pid_t pid;
+        uint32_t layerId;
+        std::string layerName;
+        uint64_t bufferId;
+        uint64_t frameNumber;
+    };
+    void onTransactionQueueStalled(uint64_t transactionId, StalledTransactionInfo);
     void removeFromStalledTransactions(uint64_t transactionId);
+    std::optional<StalledTransactionInfo> getStalledTransactionInfo(pid_t pid);
+    void onLayerDestroyed(uint32_t layerId);
 
 private:
     // For unit tests
@@ -81,7 +91,10 @@
     LocklessQueue<TransactionState> mLocklessTransactionQueue;
     std::atomic<size_t> mPendingTransactionCount = 0;
     ftl::SmallVector<TransactionFilter, 2> mTransactionReadyFilters;
-    std::vector<uint64_t> mStalledTransactions;
+
+    std::mutex mStalledMutex;
+    std::unordered_map<uint64_t /* transactionId */, StalledTransactionInfo> mStalledTransactions
+            GUARDED_BY(mStalledMutex);
 };
 } // namespace surfaceflinger::frontend
 } // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 1737bdf..59a8825 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -835,12 +835,12 @@
         mFlinger->mUpdateInputInfo = true;
     }
 
-    commitTransaction(mDrawingState);
+    commitTransaction();
 
     return flags;
 }
 
-void Layer::commitTransaction(State&) {
+void Layer::commitTransaction() {
     // Set the present state for all bufferlessSurfaceFramesTX to Presented. The
     // bufferSurfaceFrameTX will be presented in latchBuffer.
     for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 25a6845..d1912e4 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -427,7 +427,7 @@
     bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const;
 
     // from graphics API
-    ui::Dataspace translateDataspace(ui::Dataspace dataspace);
+    static ui::Dataspace translateDataspace(ui::Dataspace dataspace);
     void updateCloneBufferInfo();
     uint64_t mPreviousFrameNumber = 0;
 
@@ -910,6 +910,7 @@
     void setTransformHint(std::optional<ui::Transform::RotationFlags> transformHint) {
         mTransformHint = transformHint;
     }
+    void commitTransaction();
     // Keeps track of the previously presented layer stacks. This is used to get
     // the release fences from the correct displays when we release the last buffer
     // from the layer.
@@ -930,7 +931,6 @@
     void preparePerFrameCompositionState();
     void preparePerFrameBufferCompositionState();
     void preparePerFrameEffectsCompositionState();
-    virtual void commitTransaction(State& stateToCommit);
     void gatherBufferInfo();
     void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&);
 
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index 4734097..3270e4c 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -1,3 +1,5 @@
+# Bug component: 1075131
+
 adyabr@google.com
 alecmouri@google.com
 chaviw@google.com
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f6f6ca6..06adfec 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2302,7 +2302,9 @@
                      .forceFullDamage = mForceFullDamage,
                      .supportedLayerGenericMetadata =
                              getHwComposer().getSupportedLayerGenericMetadata(),
-                     .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()};
+                     .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap(),
+                     .skipRoundCornersWhenProtected =
+                             !getRenderEngine().supportsProtectedContent()};
         mLayerSnapshotBuilder.update(args);
     }
 
@@ -2311,7 +2313,7 @@
         mUpdateInputInfo = true;
     }
     if (mLayerLifecycleManager.getGlobalChanges().any(Changes::VisibleRegion | Changes::Hierarchy |
-                                                      Changes::Visibility)) {
+                                                      Changes::Visibility | Changes::Geometry)) {
         mVisibleRegionsDirty = true;
     }
     if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Hierarchy | Changes::FrameRate)) {
@@ -2326,6 +2328,7 @@
     if (!mLegacyFrontEndEnabled) {
         ATRACE_NAME("DisplayCallbackAndStatsUpdates");
         applyTransactions(update.transactions, vsyncId);
+        traverseLegacyLayers([&](Layer* layer) { layer->commitTransaction(); });
         const nsecs_t latchTime = systemTime();
         bool unused = false;
 
@@ -2351,6 +2354,7 @@
             }
             it->second->latchBufferImpl(unused, latchTime, bgColorOnly);
             newDataLatched = true;
+
             mLayersWithQueuedFrames.emplace(it->second);
             mLayersIdsWithQueuedFrames.emplace(it->second->sequence);
         }
@@ -4446,9 +4450,13 @@
                     (flushState.queueProcessTime - transaction.postTime) >
                             std::chrono::nanoseconds(4s).count()) {
                     mTransactionHandler
-                            .onTransactionQueueStalled(transaction.id, listener,
-                                                       "Buffer processing hung up due to stuck "
-                                                       "fence. Indicates GPU hang");
+                            .onTransactionQueueStalled(transaction.id,
+                                                       {.pid = layer->getOwnerPid(),
+                                                        .layerId = static_cast<uint32_t>(
+                                                                layer->getSequence()),
+                                                        .layerName = layer->getDebugName(),
+                                                        .bufferId = s.bufferData->getId(),
+                                                        .frameNumber = s.bufferData->frameNumber});
                 }
                 ATRACE_FORMAT("fence unsignaled %s", layer->getDebugName());
                 return TraverseBuffersReturnValues::STOP_TRAVERSAL;
@@ -4541,9 +4549,12 @@
                     (flushState.queueProcessTime - transaction.postTime) >
                             std::chrono::nanoseconds(4s).count()) {
                     mTransactionHandler
-                            .onTransactionQueueStalled(transaction.id, listener,
-                                                       "Buffer processing hung up due to stuck "
-                                                       "fence. Indicates GPU hang");
+                            .onTransactionQueueStalled(transaction.id,
+                                                       {.pid = layer->ownerPid.val(),
+                                                        .layerId = layer->id,
+                                                        .layerName = layer->name,
+                                                        .bufferId = s.bufferData->getId(),
+                                                        .frameNumber = s.bufferData->frameNumber});
                 }
                 ATRACE_FORMAT("fence unsignaled %s", layer->name.c_str());
                 return TraverseBuffersReturnValues::STOP_TRAVERSAL;
@@ -5371,6 +5382,9 @@
     if (what & layer_state_t::eSidebandStreamChanged) {
         if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
     }
+    if (what & layer_state_t::eDataspaceChanged) {
+        if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded;
+    }
     if (what & layer_state_t::eBufferChanged) {
         std::optional<ui::Transform::RotationFlags> transformHint = std::nullopt;
         frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(layer->sequence);
@@ -5570,6 +5584,8 @@
         mDestroyedHandles.emplace_back(layerId);
     }
 
+    mTransactionHandler.onLayerDestroyed(layerId);
+
     Mutex::Autolock lock(mStateLock);
     markLayerPendingRemovalLocked(layer);
     layer->onHandleDestroyed();
@@ -5697,18 +5713,22 @@
         // Turn off the display
 
         if (displayId == mActiveDisplayId) {
-            if (setSchedFifo(false) != NO_ERROR) {
-                ALOGW("Failed to set SCHED_OTHER after powering off active display: %s",
-                      strerror(errno));
-            }
-            if (setSchedAttr(false) != NO_ERROR) {
-                ALOGW("Failed set uclamp.min after powering off active display: %s",
-                      strerror(errno));
-            }
+            if (const auto display = getActivatableDisplay()) {
+                onActiveDisplayChangedLocked(activeDisplay.get(), *display);
+            } else {
+                if (setSchedFifo(false) != NO_ERROR) {
+                    ALOGW("Failed to set SCHED_OTHER after powering off active display: %s",
+                          strerror(errno));
+                }
+                if (setSchedAttr(false) != NO_ERROR) {
+                    ALOGW("Failed set uclamp.min after powering off active display: %s",
+                          strerror(errno));
+                }
 
-            if (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
-                mScheduler->disableHardwareVsync(displayId, true);
-                mScheduler->enableSyntheticVsync();
+                if (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
+                    mScheduler->disableHardwareVsync(displayId, true);
+                    mScheduler->enableSyntheticVsync();
+                }
             }
         }
 
@@ -8190,19 +8210,22 @@
     bool setByHwc = getHwComposer().hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG);
     for (const auto& [id, display] : mPhysicalDisplays) {
         if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
-            if (setByHwc) {
-                const auto status =
-                        getHwComposer().setRefreshRateChangedCallbackDebugEnabled(id, enable);
-                if (status != NO_ERROR) {
-                    ALOGE("Error updating the refresh rate changed callback debug enabled");
-                    return;
-                }
-            }
-
             if (const auto device = getDisplayDeviceLocked(id)) {
-                device->enableRefreshRateOverlay(enable, setByHwc, mRefreshRateOverlaySpinner,
-                                                 mRefreshRateOverlayRenderRate,
-                                                 mRefreshRateOverlayShowInMiddle);
+                const auto enableOverlay = [&](const bool setByHwc) FTL_FAKE_GUARD(
+                                                   kMainThreadContext) {
+                    device->enableRefreshRateOverlay(enable, setByHwc, mRefreshRateOverlaySpinner,
+                                                     mRefreshRateOverlayRenderRate,
+                                                     mRefreshRateOverlayShowInMiddle);
+                };
+                enableOverlay(setByHwc);
+                if (setByHwc) {
+                    const auto status =
+                            getHwComposer().setRefreshRateChangedCallbackDebugEnabled(id, enable);
+                    if (status != NO_ERROR) {
+                        ALOGE("Error updating the refresh rate changed callback debug enabled");
+                        enableOverlay(/*setByHwc*/ false);
+                    }
+                }
             }
         }
     }
@@ -8322,6 +8345,20 @@
     getRenderEngine().onActiveDisplaySizeChanged(activeDisplay.getSize());
 }
 
+sp<DisplayDevice> SurfaceFlinger::getActivatableDisplay() const {
+    if (mPhysicalDisplays.size() == 1) return nullptr;
+
+    // TODO(b/255635821): Choose the pacesetter display, considering both internal and external
+    // displays. For now, pick the other internal display, assuming a dual-display foldable.
+    return findDisplay([this](const DisplayDevice& display) REQUIRES(mStateLock) {
+        const auto idOpt = PhysicalDisplayId::tryCast(display.getId());
+        return idOpt && *idOpt != mActiveDisplayId && display.isPoweredOn() &&
+                mPhysicalDisplays.get(*idOpt)
+                        .transform(&PhysicalDisplay::isInternal)
+                        .value_or(false);
+    });
+}
+
 void SurfaceFlinger::onActiveDisplayChangedLocked(const DisplayDevice* inactiveDisplayPtr,
                                                   const DisplayDevice& activeDisplay) {
     ATRACE_CALL();
@@ -8370,6 +8407,12 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getStalledTransactionInfo(
+        int pid, std::optional<TransactionHandler::StalledTransactionInfo>& result) {
+    result = mTransactionHandler.getStalledTransactionInfo(pid);
+    return NO_ERROR;
+}
+
 std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData(
         BufferData& bufferData, const char* layerName, uint64_t transactionId) {
     if (bufferData.buffer &&
@@ -8645,7 +8688,9 @@
                      .excludeLayerIds = std::move(excludeLayerIds),
                      .supportedLayerGenericMetadata =
                              getHwComposer().getSupportedLayerGenericMetadata(),
-                     .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()};
+                     .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap(),
+                     .skipRoundCornersWhenProtected =
+                             !getRenderEngine().supportsProtectedContent()};
         mLayerSnapshotBuilder.update(args);
 
         auto getLayerSnapshotsFn =
@@ -8680,7 +8725,9 @@
                      .excludeLayerIds = std::move(excludeLayerIds),
                      .supportedLayerGenericMetadata =
                              getHwComposer().getSupportedLayerGenericMetadata(),
-                     .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()};
+                     .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap(),
+                     .skipRoundCornersWhenProtected =
+                             !getRenderEngine().supportsProtectedContent()};
         mLayerSnapshotBuilder.update(args);
 
         auto getLayerSnapshotsFn =
@@ -9470,6 +9517,28 @@
     return binderStatusFromStatusT(status);
 }
 
+binder::Status SurfaceComposerAIDL::getStalledTransactionInfo(
+        int pid, std::optional<gui::StalledTransactionInfo>* outInfo) {
+    const int callingPid = IPCThreadState::self()->getCallingPid();
+    const int callingUid = IPCThreadState::self()->getCallingUid();
+    if (!checkPermission(sAccessSurfaceFlinger, callingPid, callingUid)) {
+        return binderStatusFromStatusT(PERMISSION_DENIED);
+    }
+
+    std::optional<TransactionHandler::StalledTransactionInfo> stalledTransactionInfo;
+    status_t status = mFlinger->getStalledTransactionInfo(pid, stalledTransactionInfo);
+    if (stalledTransactionInfo) {
+        gui::StalledTransactionInfo result;
+        result.layerName = String16{stalledTransactionInfo->layerName.c_str()},
+        result.bufferId = stalledTransactionInfo->bufferId,
+        result.frameNumber = stalledTransactionInfo->frameNumber,
+        outInfo->emplace(std::move(result));
+    } else {
+        outInfo->reset();
+    }
+    return binderStatusFromStatusT(status);
+}
+
 status_t SurfaceComposerAIDL::checkAccessPermission(bool usePermissionCache) {
     if (!mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
         IPCThreadState* ipc = IPCThreadState::self();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 869202f..5d2115c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -618,6 +618,9 @@
     status_t removeWindowInfosListener(
             const sp<gui::IWindowInfosListener>& windowInfosListener) const;
 
+    status_t getStalledTransactionInfo(
+            int pid, std::optional<TransactionHandler::StalledTransactionInfo>& result);
+
     // Implements IBinder::DeathRecipient.
     void binderDied(const wp<IBinder>& who) override;
 
@@ -937,7 +940,8 @@
     template <typename Predicate>
     sp<DisplayDevice> findDisplay(Predicate p) const REQUIRES(mStateLock) {
         const auto it = std::find_if(mDisplays.begin(), mDisplays.end(),
-                                     [&](const auto& pair) { return p(*pair.second); });
+                                     [&](const auto& pair)
+                                             REQUIRES(mStateLock) { return p(*pair.second); });
 
         return it == mDisplays.end() ? nullptr : it->second;
     }
@@ -1050,6 +1054,9 @@
     VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock);
     void releaseVirtualDisplay(VirtualDisplayId);
 
+    // Returns a display other than `mActiveDisplayId` that can be activated, if any.
+    sp<DisplayDevice> getActivatableDisplay() const REQUIRES(mStateLock, kMainThreadContext);
+
     void onActiveDisplayChangedLocked(const DisplayDevice* inactiveDisplayPtr,
                                       const DisplayDevice& activeDisplay)
             REQUIRES(mStateLock, kMainThreadContext);
@@ -1555,6 +1562,8 @@
                                           gui::WindowInfosListenerInfo* outInfo) override;
     binder::Status removeWindowInfosListener(
             const sp<gui::IWindowInfosListener>& windowInfosListener) override;
+    binder::Status getStalledTransactionInfo(int pid,
+                                             std::optional<gui::StalledTransactionInfo>* outInfo);
 
 private:
     static const constexpr bool kUsePermissionCache = true;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index fa5fa95..3dd33b9 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -148,6 +148,7 @@
     defaults: [
         "android.hardware.graphics.common-ndk_static",
         "android.hardware.graphics.composer3-ndk_static",
+        "android.hardware.power-ndk_static",
         "librenderengine_deps",
     ],
     static_libs: [
@@ -161,7 +162,6 @@
         "android.hardware.power@1.1",
         "android.hardware.power@1.2",
         "android.hardware.power@1.3",
-        "android.hardware.power-V4-ndk",
         "libaidlcommonsupport",
         "libcompositionengine_mocks",
         "libcompositionengine",
diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
index 1c9aee7..d30d5b8 100644
--- a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
@@ -99,8 +99,7 @@
 }
 
 void RefreshRateSelectionTest::commitTransaction(Layer* layer) {
-    auto c = layer->getDrawingState();
-    layer->commitTransaction(c);
+    layer->commitTransaction();
 }
 
 namespace {
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index e475b84..f64ba2a 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -17,6 +17,8 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <gui/fake/BufferData.h>
+
 #include "Client.h" // temporarily needed for LayerCreationArgs
 #include "FrontEnd/LayerCreationArgs.h"
 #include "FrontEnd/LayerHierarchy.h"
@@ -333,6 +335,43 @@
         mLifecycleManager.applyTransactions(transactions);
     }
 
+    void setRoundedCorners(uint32_t id, float radius) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eCornerRadiusChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.cornerRadius = radius;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setBuffer(uint32_t id, std::shared_ptr<renderengine::ExternalTexture> texture) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eBufferChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().externalTexture = texture;
+        transactions.back().states.front().state.bufferData =
+                std::make_shared<fake::BufferData>(texture->getId(), texture->getWidth(),
+                                                   texture->getHeight(), texture->getPixelFormat(),
+                                                   texture->getUsage());
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setDataspace(uint32_t id, ui::Dataspace dataspace) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eDataspaceChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.dataspace = dataspace;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
     LayerLifecycleManager mLifecycleManager;
 };
 
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index a581d5b..84c3775 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -17,11 +17,14 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <renderengine/mock/FakeExternalTexture.h>
+
 #include "FrontEnd/LayerHierarchy.h"
 #include "FrontEnd/LayerLifecycleManager.h"
 #include "FrontEnd/LayerSnapshotBuilder.h"
 #include "Layer.h"
 #include "LayerHierarchyTest.h"
+#include "ui/GraphicTypes.h"
 
 #define UPDATE_AND_VERIFY(BUILDER, ...)                                    \
     ({                                                                     \
@@ -68,12 +71,17 @@
         setColor(id);
     }
 
-    void updateAndVerify(LayerSnapshotBuilder& actualBuilder, bool hasDisplayChanges,
-                         const std::vector<uint32_t> expectedVisibleLayerIdsInZOrder) {
+    void update(LayerSnapshotBuilder& actualBuilder, LayerSnapshotBuilder::Args& args) {
         if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
             mHierarchyBuilder.update(mLifecycleManager.getLayers(),
                                      mLifecycleManager.getDestroyedLayers());
         }
+        args.root = mHierarchyBuilder.getHierarchy();
+        actualBuilder.update(args);
+    }
+
+    void updateAndVerify(LayerSnapshotBuilder& actualBuilder, bool hasDisplayChanges,
+                         const std::vector<uint32_t> expectedVisibleLayerIdsInZOrder) {
         LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
                                         .layerLifecycleManager = mLifecycleManager,
                                         .includeMetadata = false,
@@ -83,7 +91,7 @@
                                         .supportsBlur = true,
                                         .supportedLayerGenericMetadata = {},
                                         .genericLayerMetadataKeyMap = {}};
-        actualBuilder.update(args);
+        update(actualBuilder, args);
 
         // rebuild layer snapshots from scratch and verify that it matches the updated state.
         LayerSnapshotBuilder expectedBuilder(args);
@@ -596,4 +604,56 @@
               scheduler::LayerInfo::FrameRateCompatibility::Default);
 }
 
+TEST_F(LayerSnapshotTest, translateDataspace) {
+    setDataspace(1, ui::Dataspace::UNKNOWN);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->dataspace, ui::Dataspace::V0_SRGB);
+}
+
+TEST_F(LayerSnapshotTest, skipRoundCornersWhenProtected) {
+    setRoundedCorners(1, 42.f);
+    setRoundedCorners(2, 42.f);
+    setCrop(1, Rect{1000, 1000});
+    setCrop(2, Rect{1000, 1000});
+
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_TRUE(getSnapshot({.id = 1})->roundedCorner.hasRoundedCorners());
+    EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, 42.f);
+    EXPECT_TRUE(getSnapshot({.id = 2})->roundedCorner.hasRoundedCorners());
+
+    // add a buffer with the protected bit, check rounded corners are not set when
+    // skipRoundCornersWhenProtected == true
+    setBuffer(1,
+              std::make_shared<
+                      renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                               1ULL /* bufferId */,
+                                                               HAL_PIXEL_FORMAT_RGBA_8888,
+                                                               GRALLOC_USAGE_PROTECTED /*usage*/));
+
+    LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+                                    .layerLifecycleManager = mLifecycleManager,
+                                    .includeMetadata = false,
+                                    .displays = mFrontEndDisplayInfos,
+                                    .displayChanges = false,
+                                    .globalShadowSettings = globalShadowSettings,
+                                    .supportsBlur = true,
+                                    .supportedLayerGenericMetadata = {},
+                                    .genericLayerMetadataKeyMap = {},
+                                    .skipRoundCornersWhenProtected = true};
+    update(mSnapshotBuilder, args);
+    EXPECT_FALSE(getSnapshot({.id = 1})->roundedCorner.hasRoundedCorners());
+    // layer 2 doesn't have a buffer and should be unaffected
+    EXPECT_TRUE(getSnapshot({.id = 2})->roundedCorner.hasRoundedCorners());
+
+    // remove protected bit, check rounded corners are set
+    setBuffer(1,
+              std::make_shared<renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                                        2ULL /* bufferId */,
+                                                                        HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                        0 /*usage*/));
+    update(mSnapshotBuilder, args);
+    EXPECT_TRUE(getSnapshot({.id = 1})->roundedCorner.hasRoundedCorners());
+    EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, 42.f);
+}
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 44ab569..a1e4e25 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -85,8 +85,7 @@
 
 void SetFrameRateTest::commitTransaction() {
     for (auto layer : mLayers) {
-        auto c = layer->getDrawingState();
-        layer->commitTransaction(c);
+        layer->commitTransaction();
     }
 }
 
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
index bd2344c..ed8d909 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
@@ -55,13 +55,17 @@
     sp<DisplayDevice> mInnerDisplay, mOuterDisplay;
 };
 
-TEST_F(FoldableTest, foldUnfold) {
+TEST_F(FoldableTest, promotesPacesetterOnBoot) {
     // When the device boots, the inner display should be the pacesetter.
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
 
     // ...and should still be after powering on.
     mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
+}
+
+TEST_F(FoldableTest, promotesPacesetterOnFoldUnfold) {
+    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
 
     // The outer display should become the pacesetter after folding.
     mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
@@ -72,6 +76,10 @@
     mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::OFF);
     mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
+}
+
+TEST_F(FoldableTest, promotesPacesetterOnConcurrentPowerOn) {
+    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
 
     // The inner display should stay the pacesetter if both are powered on.
     // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates.
@@ -81,6 +89,28 @@
     // The outer display should become the pacesetter if designated.
     mFlinger.scheduler()->setPacesetterDisplay(kOuterDisplayId);
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
+
+    // The inner display should become the pacesetter if designated.
+    mFlinger.scheduler()->setPacesetterDisplay(kInnerDisplayId);
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
+}
+
+TEST_F(FoldableTest, promotesPacesetterOnConcurrentPowerOff) {
+    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+
+    // The outer display should become the pacesetter if the inner display powers off.
+    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
+
+    // The outer display should stay the pacesetter if both are powered on.
+    // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates.
+    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
+
+    // The inner display should become the pacesetter if the outer display powers off.
+    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::OFF);
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
 }
 
 TEST_F(FoldableTest, doesNotRequestHardwareVsyncIfPoweredOff) {
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index 764d19b..00b5bf0 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -61,10 +61,7 @@
         return sp<Layer>::make(args);
     }
 
-    void commitTransaction(Layer* layer) {
-        auto c = layer->getDrawingState();
-        layer->commitTransaction(c);
-    }
+    void commitTransaction(Layer* layer) { layer->commitTransaction(); }
 
     TestableSurfaceFlinger mFlinger;
     renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index e2c6491..caa265f 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -60,10 +60,7 @@
         return sp<Layer>::make(args);
     }
 
-    void commitTransaction(Layer* layer) {
-        auto c = layer->getDrawingState();
-        layer->commitTransaction(c);
-    }
+    void commitTransaction(Layer* layer) { layer->commitTransaction(); }
 
     TestableSurfaceFlinger mFlinger;
     renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
index 2b9520f..364618d 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
@@ -23,6 +23,7 @@
 
 using aidl::android::hardware::power::IPowerHintSession;
 using aidl::android::hardware::power::SessionHint;
+using aidl::android::hardware::power::SessionMode;
 using android::binder::Status;
 
 using namespace aidl::android::hardware::power;
@@ -45,6 +46,7 @@
                 (override));
     MOCK_METHOD(ndk::ScopedAStatus, sendHint, (SessionHint), (override));
     MOCK_METHOD(ndk::ScopedAStatus, setThreads, (const ::std::vector<int32_t>&), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, setMode, (SessionMode, bool), (override));
 };
 
 } // namespace android::Hwc2::mock
diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
index 4c0910a..106ab9e 100644
--- a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
+++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
@@ -38,6 +38,9 @@
 
 // -------------------------------------------------------------------------------------------------
 
+// Delay allowed for the scheduler to process callbacks during this test.
+static const auto TEST_TIMEOUT = 50ms;
+
 class VibratorCallbackSchedulerTest : public Test {
 public:
     void SetUp() override {
@@ -67,46 +70,51 @@
         return std::vector<int32_t>(mExpiredCallbacks);
     }
 
-    bool waitForCallbacks(uint32_t callbackCount, milliseconds timeout) {
-        time_point<steady_clock> expiration = steady_clock::now() + timeout;
+    int32_t waitForCallbacks(int32_t callbackCount, milliseconds timeout) {
+        time_point<steady_clock> expiration = steady_clock::now() + timeout + TEST_TIMEOUT;
+        int32_t expiredCallbackCount = 0;
         while (steady_clock::now() < expiration) {
             std::lock_guard<std::mutex> lock(mMutex);
-            if (callbackCount <= mExpiredCallbacks.size()) {
-                return true;
+            expiredCallbackCount = mExpiredCallbacks.size();
+            if (callbackCount <= expiredCallbackCount) {
+                return expiredCallbackCount;
             }
             mCondition.wait_until(mMutex, expiration);
         }
-        return false;
+        return expiredCallbackCount;
     }
 };
 
 // -------------------------------------------------------------------------------------------------
 
 TEST_F(VibratorCallbackSchedulerTest, TestScheduleRunsOnlyAfterDelay) {
-    mScheduler->schedule(createCallback(1), 15ms);
+    time_point<steady_clock> startTime = steady_clock::now();
+    mScheduler->schedule(createCallback(1), 50ms);
 
-    // Not triggered before delay.
-    ASSERT_FALSE(waitForCallbacks(1, 10ms));
-    ASSERT_TRUE(getExpiredCallbacks().empty());
+    ASSERT_EQ(1, waitForCallbacks(1, 50ms));
+    time_point<steady_clock> callbackTime = steady_clock::now();
 
-    ASSERT_TRUE(waitForCallbacks(1, 10ms));
+    // Callback happened at least 50ms after the beginning of the test.
+    ASSERT_TRUE(startTime + 50ms <= callbackTime);
     ASSERT_THAT(getExpiredCallbacks(), ElementsAre(1));
 }
 
 TEST_F(VibratorCallbackSchedulerTest, TestScheduleMultipleCallbacksRunsInDelayOrder) {
-    mScheduler->schedule(createCallback(1), 10ms);
-    mScheduler->schedule(createCallback(2), 5ms);
-    mScheduler->schedule(createCallback(3), 1ms);
+    // Schedule first callbacks long enough that all 3 will be scheduled together and run in order.
+    mScheduler->schedule(createCallback(1), 50ms);
+    mScheduler->schedule(createCallback(2), 40ms);
+    mScheduler->schedule(createCallback(3), 10ms);
 
-    ASSERT_TRUE(waitForCallbacks(3, 15ms));
+    ASSERT_EQ(3, waitForCallbacks(3, 50ms));
     ASSERT_THAT(getExpiredCallbacks(), ElementsAre(3, 2, 1));
 }
 
 TEST_F(VibratorCallbackSchedulerTest, TestDestructorDropsPendingCallbacksAndKillsThread) {
-    mScheduler->schedule(createCallback(1), 5ms);
+    // Schedule callback long enough that scheduler will be destroyed while it's still scheduled.
+    mScheduler->schedule(createCallback(1), 50ms);
     mScheduler.reset(nullptr);
 
-    // Should time out waiting for callback to run.
-    ASSERT_FALSE(waitForCallbacks(1, 10ms));
+    // Should timeout waiting for callback to run.
+    ASSERT_EQ(0, waitForCallbacks(1, 50ms));
     ASSERT_TRUE(getExpiredCallbacks().empty());
 }
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index 40cf9fb..e78f470 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -55,7 +55,12 @@
  * This version of the extension is largely designed to clean up the mix of
  * GrallocUsage and GrallocUsage2
  */
-#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 9
+/*
+ * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 10
+ *
+ * This version of the extension cleans up a bug introduced in version 9
+ */
+#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 10
 #define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
 
 #define VK_ANDROID_NATIVE_BUFFER_ENUM(type, id) \
@@ -69,6 +74,8 @@
     VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2)
 #define VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID \
     VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 3)
+#define VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID \
+    VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 4)
 
 /* clang-format off */
 typedef enum VkSwapchainImageUsageFlagBitsANDROID {
@@ -152,6 +159,23 @@
     VkImageUsageFlags                 imageUsage;
 } VkGrallocUsageInfoANDROID;
 
+/*
+ * struct VkGrallocUsageInfo2ANDROID
+ *
+ * sType: VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID
+ * pNext: NULL or a pointer to a structure extending this structure
+ * format: value specifying the format the image will be created with
+ * imageUsage: bitmask of VkImageUsageFlagBits describing intended usage
+ * swapchainImageUsage: is a bitmask of VkSwapchainImageUsageFlagsANDROID
+ */
+typedef struct {
+    VkStructureType                   sType;
+    const void*                       pNext;
+    VkFormat                          format;
+    VkImageUsageFlags                 imageUsage;
+    VkSwapchainImageUsageFlagsANDROID swapchainImageUsage;
+} VkGrallocUsageInfo2ANDROID;
+
 /* DEPRECATED in SPEC_VERSION 6 */
 typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(
     VkDevice                          device,
@@ -168,12 +192,18 @@
     uint64_t*                         grallocConsumerUsage,
     uint64_t*                         grallocProducerUsage);
 
-/* ADDED in SPEC_VERSION 9 */
+/* DEPRECATED in SPEC_VERSION 10 */
 typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage3ANDROID)(
     VkDevice                          device,
     const VkGrallocUsageInfoANDROID*  grallocUsageInfo,
     uint64_t*                         grallocUsage);
 
+/* ADDED in SPEC_VERSION 10 */
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage4ANDROID)(
+    VkDevice                          device,
+    const VkGrallocUsageInfo2ANDROID* grallocUsageInfo,
+    uint64_t*                         grallocUsage);
+
 typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)(
     VkDevice                          device,
     VkImage                           image,
@@ -208,13 +238,20 @@
     uint64_t*                         grallocProducerUsage
 );
 
-/* ADDED in SPEC_VERSION 9 */
+/* DEPRECATED in SPEC_VERSION 10 */
 VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage3ANDROID(
     VkDevice                          device,
     const VkGrallocUsageInfoANDROID*  grallocUsageInfo,
     uint64_t*                         grallocUsage
 );
 
+/* ADDED in SPEC_VERSION 10 */
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage4ANDROID(
+    VkDevice                          device,
+    const VkGrallocUsageInfo2ANDROID* grallocUsageInfo,
+    uint64_t*                         grallocUsage
+);
+
 VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID(
     VkDevice                          device,
     VkImage                           image,
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 273cdd5..bdba27e 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -763,6 +763,17 @@
             continue;
         }
 
+        // Ignore duplicate extensions (see: b/288929054)
+        bool duplicate_entry = false;
+        for (uint32_t j = 0; j < filter.name_count; j++) {
+            if (strcmp(name, filter.names[j]) == 0) {
+                duplicate_entry = true;
+                break;
+            }
+        }
+        if (duplicate_entry == true)
+            continue;
+
         filter.names[filter.name_count++] = name;
         if (ext_bit != ProcHook::EXTENSION_UNKNOWN) {
             if (ext_bit == ProcHook::ANDROID_native_buffer)
@@ -1422,13 +1433,15 @@
     if ((wrapper.GetHalExtensions()[ProcHook::ANDROID_native_buffer]) &&
         !data->driver.GetSwapchainGrallocUsageANDROID &&
         !data->driver.GetSwapchainGrallocUsage2ANDROID &&
-        !data->driver.GetSwapchainGrallocUsage3ANDROID) {
+        !data->driver.GetSwapchainGrallocUsage3ANDROID &&
+        !data->driver.GetSwapchainGrallocUsage4ANDROID) {
         ALOGE(
             "Driver's implementation of ANDROID_native_buffer is broken;"
             " must expose at least one of "
             "vkGetSwapchainGrallocUsageANDROID or "
             "vkGetSwapchainGrallocUsage2ANDROID or "
-            "vkGetSwapchainGrallocUsage3ANDROID");
+            "vkGetSwapchainGrallocUsage3ANDROID or "
+            "vkGetSwapchainGrallocUsage4ANDROID");
 
         data->driver.DestroyDevice(dev, pAllocator);
         FreeDeviceData(data, data_allocator);
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 798af5c..8f09008 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -512,6 +512,13 @@
         nullptr,
     },
     {
+        "vkGetSwapchainGrallocUsage4ANDROID",
+        ProcHook::DEVICE,
+        ProcHook::ANDROID_native_buffer,
+        nullptr,
+        nullptr,
+    },
+    {
         "vkGetSwapchainGrallocUsageANDROID",
         ProcHook::DEVICE,
         ProcHook::ANDROID_native_buffer,
@@ -692,6 +699,7 @@
     INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsageANDROID);
     INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID);
     INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage3ANDROID);
+    INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage4ANDROID);
     INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID);
     INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID);
     // clang-format on
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 31ba04b..4527214 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -128,6 +128,7 @@
     PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID;
     PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID;
     PFN_vkGetSwapchainGrallocUsage3ANDROID GetSwapchainGrallocUsage3ANDROID;
+    PFN_vkGetSwapchainGrallocUsage4ANDROID GetSwapchainGrallocUsage4ANDROID;
     PFN_vkAcquireImageANDROID AcquireImageANDROID;
     PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID;
     // clang-format on
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index a14fed2..d059f8f 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -23,6 +23,7 @@
 #include <dlfcn.h>
 #include <string.h>
 #include <sys/prctl.h>
+#include <unistd.h>
 
 #include <mutex>
 #include <string>
@@ -362,6 +363,7 @@
 void ForEachFileInZip(const std::string& zipname,
                       const std::string& dir_in_zip,
                       Functor functor) {
+    static const size_t kPageSize = getpagesize();
     int32_t err;
     ZipArchiveHandle zip = nullptr;
     if ((err = OpenArchive(zipname.c_str(), &zip)) != 0) {
@@ -389,7 +391,7 @@
         // the APK. Loading still may fail for other reasons, but this at least
         // lets us avoid failed-to-load log messages in the typical case of
         // compressed and/or unaligned libraries.
-        if (entry.method != kCompressStored || entry.offset % PAGE_SIZE != 0)
+        if (entry.method != kCompressStored || entry.offset % kPageSize != 0)
             continue;
         functor(filename);
     }
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 07b9569..bffbe9d 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1576,7 +1576,47 @@
     void* usage_info_pNext = nullptr;
     VkImageCompressionControlEXT image_compression = {};
     uint64_t native_usage = 0;
-    if (dispatch.GetSwapchainGrallocUsage3ANDROID) {
+    if (dispatch.GetSwapchainGrallocUsage4ANDROID) {
+        ATRACE_BEGIN("GetSwapchainGrallocUsage4ANDROID");
+        VkGrallocUsageInfo2ANDROID gralloc_usage_info = {};
+        gralloc_usage_info.sType =
+            VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID;
+        gralloc_usage_info.format = create_info->imageFormat;
+        gralloc_usage_info.imageUsage = create_info->imageUsage;
+        gralloc_usage_info.swapchainImageUsage = swapchain_image_usage;
+
+        // Look through the pNext chain for an image compression control struct
+        // if one is found AND the appropriate extensions are enabled,
+        // append it to be the gralloc usage pNext chain
+        const VkSwapchainCreateInfoKHR* create_infos = create_info;
+        while (create_infos->pNext) {
+            create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
+                create_infos->pNext);
+            switch (create_infos->sType) {
+                case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+                    const VkImageCompressionControlEXT* compression_infos =
+                        reinterpret_cast<const VkImageCompressionControlEXT*>(
+                            create_infos);
+                    image_compression = *compression_infos;
+                    image_compression.pNext = nullptr;
+                    usage_info_pNext = &image_compression;
+                } break;
+
+                default:
+                    // Ignore all other info structs
+                    break;
+            }
+        }
+        gralloc_usage_info.pNext = usage_info_pNext;
+
+        result = dispatch.GetSwapchainGrallocUsage4ANDROID(
+            device, &gralloc_usage_info, &native_usage);
+        ATRACE_END();
+        if (result != VK_SUCCESS) {
+            ALOGE("vkGetSwapchainGrallocUsage4ANDROID failed: %d", result);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+    } else if (dispatch.GetSwapchainGrallocUsage3ANDROID) {
         ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID");
         VkGrallocUsageInfoANDROID gralloc_usage_info = {};
         gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID;
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index f998b1a..2e87f17 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -959,6 +959,17 @@
     return VK_SUCCESS;
 }
 
+VkResult GetSwapchainGrallocUsage4ANDROID(
+    VkDevice,
+    const VkGrallocUsageInfo2ANDROID* grallocUsageInfo,
+    uint64_t* grallocUsage) {
+    // The null driver never reads or writes the gralloc buffer
+    ALOGV("TODO: vk%s - grallocUsageInfo->format:%i", __FUNCTION__,
+          grallocUsageInfo->format);
+    *grallocUsage = 0;
+    return VK_SUCCESS;
+}
+
 VkResult AcquireImageANDROID(VkDevice,
                              VkImage,
                              int fence,
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index 0cb7bd3..935535f 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -262,6 +262,7 @@
     {"vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSemaphoreCounterValue>(GetSemaphoreCounterValue))},
     {"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))},
     {"vkGetSwapchainGrallocUsage3ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage3ANDROID>(GetSwapchainGrallocUsage3ANDROID))},
+    {"vkGetSwapchainGrallocUsage4ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage4ANDROID>(GetSwapchainGrallocUsage4ANDROID))},
     {"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))},
     {"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))},
     {"vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMapMemory>(MapMemory))},
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 5c7fea0..fb3bd05 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -210,6 +210,7 @@
 VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
 VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
 VKAPI_ATTR VkResult GetSwapchainGrallocUsage3ANDROID(VkDevice device, const VkGrallocUsageInfoANDROID* grallocUsageInfo, uint64_t* grallocUsage);
+VKAPI_ATTR VkResult GetSwapchainGrallocUsage4ANDROID(VkDevice device, const VkGrallocUsageInfo2ANDROID* grallocUsageInfo, uint64_t* grallocUsage);
 VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
 VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
 VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index c25c6cb..866c1b7 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -70,6 +70,7 @@
     'vkGetSwapchainGrallocUsageANDROID',
     'vkGetSwapchainGrallocUsage2ANDROID',
     'vkGetSwapchainGrallocUsage3ANDROID',
+    'vkGetSwapchainGrallocUsage4ANDROID',
 ]
 
 # Dict for mapping dispatch table to a type.