Merge changes I8d2a0988,I5f8f7c4a into sc-dev

* changes:
  Fix bg blur not showing when layer alpha is 0
  Fix blur sampling outside bounds of layer
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 3a2c769..a999c9f 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -2414,7 +2414,9 @@
 
 static void Vibrate(int duration_ms) {
     // clang-format off
-    RunCommand("", {"cmd", "vibrator", "vibrate", "-f", std::to_string(duration_ms), "dumpstate"},
+    std::vector<std::string> args = {"cmd", "vibrator_manager", "synced", "-f", "-d", "dumpstate",
+                                     "oneshot", std::to_string(duration_ms)};
+    RunCommand("", args,
                CommandOptions::WithTimeout(10)
                    .Log("Vibrate: '%s'\n")
                    .Always()
@@ -2909,6 +2911,10 @@
         // own activity pushes out interesting data from the trace ring buffer.
         // The trace file is added to the zip by MaybeAddSystemTraceToZip().
         MaybeSnapshotSystemTrace();
+
+        // If a winscope trace is running, snapshot it now. It will be pulled into bugreport later
+        // from WMTRACE_DATA_DIR.
+        MaybeSnapshotWinTrace();
     }
     onUiIntensiveBugreportDumpsFinished(calling_uid);
     MaybeCheckUserConsent(calling_uid, calling_package);
@@ -3020,6 +3026,14 @@
     // file in the later stages.
 }
 
+void Dumpstate::MaybeSnapshotWinTrace() {
+    RunCommand(
+        // Empty name because it's not intended to be classified as a bugreport section.
+        // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport.
+        "", {"cmd", "window", "tracing", "save-for-bugreport"},
+        CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
+}
+
 void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) {
     if (calling_uid == AID_SHELL || !CalledByApi()) {
         return;
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index f83968b..83e6787 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -549,6 +549,7 @@
 
     void MaybeTakeEarlyScreenshot();
     void MaybeSnapshotSystemTrace();
+    void MaybeSnapshotWinTrace();
 
     void onUiIntensiveBugreportDumpsFinished(int32_t calling_uid);
 
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index ef052bd..ed31ad9 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -175,8 +175,10 @@
 private:
 
     bool ReadSystemProperties() {
+        // TODO This file does not have a stable format. It should be read by
+        // code shared by init and otapreopt. See b/181182967#comment80
         static constexpr const char* kPropertyFiles[] = {
-                "/default.prop", "/system/build.prop"
+                "/system/build.prop"
         };
 
         for (size_t i = 0; i < arraysize(kPropertyFiles); ++i) {
@@ -193,28 +195,38 @@
         //   export NAME VALUE
         // For simplicity, don't respect string quotation. The values we are interested in can be
         // encoded without them.
+        // init.environ.rc and etc/classpath have the same format for
+        // environment variable exports and can be matched by the same regex.
+        // TODO Just like with the system-properties above we really should have
+        // common code between init and otapreopt to deal with reading these
+        // things. See b/181182967
+        static constexpr const char* kEnvironmentVariableSources[] = {
+                "/init.environ.rc", "/etc/classpath"
+        };
+
         std::regex export_regex("\\s*export\\s+(\\S+)\\s+(\\S+)");
-        bool parse_result = ParseFile("/init.environ.rc", [&](const std::string& line) {
-            std::smatch export_match;
-            if (!std::regex_match(line, export_match, export_regex)) {
+        for (const char* env_vars_file : kEnvironmentVariableSources) {
+            bool parse_result = ParseFile(env_vars_file, [&](const std::string& line) {
+                std::smatch export_match;
+                if (!std::regex_match(line, export_match, export_regex)) {
+                    return true;
+                }
+
+                if (export_match.size() != 3) {
+                    return true;
+                }
+
+                std::string name = export_match[1].str();
+                std::string value = export_match[2].str();
+
+                system_properties_.SetProperty(name, value);
+
                 return true;
+            });
+            if (!parse_result) {
+                return false;
             }
-
-            if (export_match.size() != 3) {
-                return true;
-            }
-
-            std::string name = export_match[1].str();
-            std::string value = export_match[2].str();
-
-            system_properties_.SetProperty(name, value);
-
-            return true;
-        });
-        if (!parse_result) {
-            return false;
         }
-
         if (system_properties_.GetProperty(kAndroidDataPathPropertyName) == nullptr) {
             return false;
         }
@@ -337,9 +349,6 @@
             }
         }
 
-        // Clear cached artifacts.
-        ClearDirectory(isa_path);
-
         // Check whether we have a boot image.
         // TODO: check that the files are correct wrt/ jars.
         std::string preopted_boot_art_path =
@@ -383,37 +392,6 @@
         return false;
     }
 
-    static void ClearDirectory(const std::string& dir) {
-        DIR* c_dir = opendir(dir.c_str());
-        if (c_dir == nullptr) {
-            PLOG(WARNING) << "Unable to open " << dir << " to delete it's contents";
-            return;
-        }
-
-        for (struct dirent* de = readdir(c_dir); de != nullptr; de = readdir(c_dir)) {
-            const char* name = de->d_name;
-            if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
-                continue;
-            }
-            // We only want to delete regular files and symbolic links.
-            std::string file = StringPrintf("%s/%s", dir.c_str(), name);
-            if (de->d_type != DT_REG && de->d_type != DT_LNK) {
-                LOG(WARNING) << "Unexpected file "
-                             << file
-                             << " of type "
-                             << std::hex
-                             << de->d_type
-                             << " encountered.";
-            } else {
-                // Try to unlink the file.
-                if (unlink(file.c_str()) != 0) {
-                    PLOG(ERROR) << "Unable to unlink " << file;
-                }
-            }
-        }
-        CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory.";
-    }
-
     static const char* ParseNull(const char* arg) {
         return (strcmp(arg, "!") == 0) ? nullptr : arg;
     }
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 72c03bf..379cf92 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -20,14 +20,19 @@
 #include <sys/stat.h>
 #include <sys/wait.h>
 
+#include <fstream>
 #include <sstream>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
 #include <libdm/dm.h>
 #include <selinux/android.h>
 
+#include <apex_file_repository.h>
+#include <apex_constants.h>
 #include <apexd.h>
 
 #include "installd_constants.h"
@@ -64,15 +69,34 @@
     // system/apex/apexd/apexd.cpp.
     //
     // Only scan the APEX directory under /system, /system_ext and /vendor (within the chroot dir).
-    std::vector<const char*> apex_dirs{apex::kApexPackageSystemDir, apex::kApexPackageSystemExtDir,
+    std::vector<std::string> apex_dirs{apex::kApexPackageSystemDir, apex::kApexPackageSystemExtDir,
                                        apex::kApexPackageVendorDir};
+    // Initialize ApexFileRepository used internally in ScanPackagesDirAndActivate.
+    // This is a quick fix to fix apex activation in otapreopt_chroot.
+    apex::ApexFileRepository::GetInstance().AddPreInstalledApex(apex_dirs);
     for (const auto& dir : apex_dirs) {
         // Cast call to void to suppress warn_unused_result.
-        static_cast<void>(apex::ScanPackagesDirAndActivate(dir));
+        static_cast<void>(apex::ScanPackagesDirAndActivate(dir.c_str()));
     }
     return apex::GetActivePackages();
 }
 
+static void CreateApexInfoList(const std::vector<apex::ApexFile>& apex_files) {
+    // Setup the apex-info-list.xml file
+    const std::string apex_info_file = std::string(apex::kApexRoot) + "/" + apex::kApexInfoList;
+    std::fstream xml(apex_info_file.c_str(), std::ios::out | std::ios::trunc);
+    if (!xml.is_open()) {
+        PLOG(ERROR) << "Failed to open " << apex_info_file;
+        exit(216);
+    }
+
+    // we do not care about inactive apexs
+    std::vector<apex::ApexFile> inactive;
+    apex::CollectApexInfoList(xml, apex_files, inactive);
+    xml.flush();
+    xml.close();
+}
+
 static void DeactivateApexPackages(const std::vector<apex::ApexFile>& active_packages) {
     for (const apex::ApexFile& apex_file : active_packages) {
         const std::string& package_path = apex_file.GetPath();
@@ -181,6 +205,13 @@
     // want it for product APKs. Same notes as vendor above.
     TryExtraMount("product", arg[2], "/postinstall/product");
 
+    constexpr const char* kPostInstallLinkerconfig = "/postinstall/linkerconfig";
+    // Try to mount /postinstall/linkerconfig. we will set it up after performing the chroot
+    if (mount("tmpfs", kPostInstallLinkerconfig, "tmpfs", 0, nullptr) != 0) {
+        PLOG(ERROR) << "Failed to mount a tmpfs for " << kPostInstallLinkerconfig;
+        exit(215);
+    }
+
     // Setup APEX mount point and its security context.
     static constexpr const char* kPostinstallApexDir = "/postinstall/apex";
     // The following logic is similar to the one in system/core/rootdir/init.rc:
@@ -239,17 +270,37 @@
     // Try to mount APEX packages in "/apex" in the chroot dir. We need at least
     // the ART APEX, as it is required by otapreopt to run dex2oat.
     std::vector<apex::ApexFile> active_packages = ActivateApexPackages();
+    CreateApexInfoList(active_packages);
 
     // Check that an ART APEX has been activated; clean up and exit
     // early otherwise.
-    if (std::none_of(active_packages.begin(),
-                     active_packages.end(),
-                     [](const apex::ApexFile& package){
-                         return package.GetManifest().name() == "com.android.art";
-                     })) {
-        LOG(FATAL_WITHOUT_ABORT) << "No activated com.android.art APEX package.";
-        DeactivateApexPackages(active_packages);
-        exit(217);
+    static constexpr const std::string_view kRequiredApexs[] = {
+      "com.android.art",
+      "com.android.runtime",
+    };
+    for (std::string_view apex : kRequiredApexs) {
+        if (std::none_of(active_packages.begin(), active_packages.end(),
+                         [&](const apex::ApexFile& package) {
+                             return package.GetManifest().name() == apex;
+                         })) {
+            LOG(FATAL_WITHOUT_ABORT) << "No activated " << apex << " APEX package.";
+            DeactivateApexPackages(active_packages);
+            exit(217);
+        }
+    }
+
+    // Setup /linkerconfig. Doing it after the chroot means it doesn't need its own category
+    if (selinux_android_restorecon("/linkerconfig", 0) < 0) {
+        PLOG(ERROR) << "Failed to restorecon /linkerconfig";
+        exit(219);
+    }
+    std::vector<std::string> linkerconfig_cmd{"/apex/com.android.runtime/bin/linkerconfig",
+                                              "--target", "/linkerconfig"};
+    std::string linkerconfig_error_msg;
+    bool linkerconfig_exec_result = Exec(linkerconfig_cmd, &linkerconfig_error_msg);
+    if (!linkerconfig_exec_result) {
+        LOG(ERROR) << "Running linkerconfig failed: " << linkerconfig_error_msg;
+        exit(218);
     }
 
     // Now go on and run otapreopt.
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 3b46cb5..ba9ae20 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -33,6 +33,7 @@
 #include <unordered_map>
 
 #include <android-base/chrono_utils.h>
+#include <android-base/unique_fd.h>
 
 #include <binder/IBinder.h>
 #include <binder/Parcelable.h>
@@ -44,7 +45,6 @@
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
 
-#include <android-base/unique_fd.h>
 
 namespace android {
 class Parcel;
@@ -76,10 +76,15 @@
         uint32_t seq;
     } header;
 
-    // Body *must* be 8 byte aligned.
     // For keys and motions, rely on the fact that std::array takes up exactly as much space
     // as the underlying data. This is not guaranteed by C++, but it simplifies the conversions.
     static_assert(sizeof(std::array<uint8_t, 32>) == 32);
+
+    // For bool values, rely on the fact that they take up exactly one byte. This is not guaranteed
+    // by C++ and is implementation-dependent, but it simplifies the conversions.
+    static_assert(sizeof(bool) == 1);
+
+    // Body *must* be 8 byte aligned.
     union Body {
         struct Key {
             int32_t eventId;
@@ -154,8 +159,8 @@
         } motion;
 
         struct Finished {
-            uint32_t empty1;
-            uint32_t handled; // actually a bool, but we must maintain 8-byte alignment
+            bool handled;
+            uint8_t empty[7];
             nsecs_t consumeTime; // The time when the event was consumed by the receiving end
 
             inline size_t size() const { return sizeof(Finished); }
@@ -163,16 +168,18 @@
 
         struct Focus {
             int32_t eventId;
-            // The following two fields take up 4 bytes total
-            uint16_t hasFocus;    // actually a bool
-            uint16_t inTouchMode; // actually a bool, but we must maintain 8-byte alignment
+            // The following 3 fields take up 4 bytes total
+            bool hasFocus;
+            bool inTouchMode;
+            uint8_t empty[2];
 
             inline size_t size() const { return sizeof(Focus); }
         } focus;
 
         struct Capture {
             int32_t eventId;
-            uint32_t pointerCaptureEnabled; // actually a bool, but we maintain 8-byte alignment
+            bool pointerCaptureEnabled;
+            uint8_t empty[3];
 
             inline size_t size() const { return sizeof(Capture); }
         } capture;
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index e9d866b..6df04f0 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -216,6 +216,11 @@
         "performance*",
         "portability*",
     ],
+
+    pgo: {
+        sampling: true,
+        profile_file: "libbinder/libbinder.profdata",
+    },
 }
 
 // AIDL interface between libbinder and framework.jar
diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp
index 88c85bf..349658e 100644
--- a/libs/binder/BufferedTextOutput.cpp
+++ b/libs/binder/BufferedTextOutput.cpp
@@ -15,7 +15,6 @@
  */
 
 #include "BufferedTextOutput.h"
-#include <binder/Debug.h>
 
 #include <cutils/atomic.h>
 #include <utils/Log.h>
@@ -26,6 +25,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "Debug.h"
 #include "Static.h"
 
 // ---------------------------------------------------------------------------
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp
index 3a62059..e4ac4b4 100644
--- a/libs/binder/Debug.cpp
+++ b/libs/binder/Debug.cpp
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#include <binder/Debug.h>
+#include "Debug.h"
+
 #include <binder/ProcessState.h>
 
 #include <utils/misc.h>
diff --git a/libs/binder/include/binder/Debug.h b/libs/binder/Debug.h
similarity index 100%
rename from libs/binder/include/binder/Debug.h
rename to libs/binder/Debug.h
diff --git a/libs/binder/TextOutput.cpp b/libs/binder/TextOutput.cpp
index 684a7dc..a0ade50 100644
--- a/libs/binder/TextOutput.cpp
+++ b/libs/binder/TextOutput.cpp
@@ -16,7 +16,7 @@
 
 #include <binder/TextOutput.h>
 
-#include <binder/Debug.h>
+#include "Debug.h"
 
 #include <utils/String8.h>
 #include <utils/String16.h>
diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs
index 4492cf7..2598ebc 100644
--- a/libs/binder/rust/src/error.rs
+++ b/libs/binder/rust/src/error.rs
@@ -77,9 +77,7 @@
         e if e == ExceptionCode::ILLEGAL_ARGUMENT as i32 => ExceptionCode::ILLEGAL_ARGUMENT,
         e if e == ExceptionCode::NULL_POINTER as i32 => ExceptionCode::NULL_POINTER,
         e if e == ExceptionCode::ILLEGAL_STATE as i32 => ExceptionCode::ILLEGAL_STATE,
-        e if e == ExceptionCode::NETWORK_MAIN_THREAD as i32 => {
-            ExceptionCode::NETWORK_MAIN_THREAD
-        }
+        e if e == ExceptionCode::NETWORK_MAIN_THREAD as i32 => ExceptionCode::NETWORK_MAIN_THREAD,
         e if e == ExceptionCode::UNSUPPORTED_OPERATION as i32 => {
             ExceptionCode::UNSUPPORTED_OPERATION
         }
@@ -96,6 +94,16 @@
 /// Used in AIDL transactions to represent failed transactions.
 pub struct Status(*mut sys::AStatus);
 
+// Safety: The `AStatus` that the `Status` points to must have an entirely thread-safe API for the
+// duration of the `Status` object's lifetime. We ensure this by not allowing mutation of a `Status`
+// in Rust, and the NDK API says we're the owner of our `AStatus` objects so outside code should not
+// be mutating them underneath us.
+unsafe impl Sync for Status {}
+
+// Safety: `Status` always contains an owning pointer to a global, immutable, interned `AStatus`.
+// A thread-local `AStatus` would not be valid.
+unsafe impl Send for Status {}
+
 impl Status {
     /// Create a status object representing a successful transaction.
     pub fn ok() -> Self {
diff --git a/libs/binder/tests/binderTextOutputTest.cpp b/libs/binder/tests/binderTextOutputTest.cpp
index ce99f59..b37030e 100644
--- a/libs/binder/tests/binderTextOutputTest.cpp
+++ b/libs/binder/tests/binderTextOutputTest.cpp
@@ -26,7 +26,6 @@
 
 #include <binder/Parcel.h>
 #include <binder/TextOutput.h>
-#include <binder/Debug.h>
 
 static void CheckMessage(CapturedStderr& cap,
                          const char* expected,
diff --git a/libs/binder/tests/fuzzers/TextOutputFuzz.cpp b/libs/binder/tests/fuzzers/TextOutputFuzz.cpp
index c950020..5e3502a 100644
--- a/libs/binder/tests/fuzzers/TextOutputFuzz.cpp
+++ b/libs/binder/tests/fuzzers/TextOutputFuzz.cpp
@@ -16,7 +16,6 @@
 
 #include <fuzzer/FuzzedDataProvider.h>
 
-#include <binder/Debug.h>
 #include <binder/Parcel.h>
 #include <binder/TextOutput.h>
 #include "android-base/file.h"
diff --git a/libs/graphicsenv/OWNERS b/libs/graphicsenv/OWNERS
index c0bb75f..8c28464 100644
--- a/libs/graphicsenv/OWNERS
+++ b/libs/graphicsenv/OWNERS
@@ -1,6 +1,4 @@
 chrisforbes@google.com
 cnorthrop@google.com
-courtneygo@google.com
 lpy@google.com
 timvp@google.com
-zzyiwei@google.com
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 05e1935..989abd9 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -755,11 +755,10 @@
         return error;
     }
 
-    virtual status_t addFpsListener(const sp<IBinder>& layerHandle,
-                                    const sp<gui::IFpsListener>& listener) {
+    virtual status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) {
         Parcel data, reply;
         SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
-        SAFE_PARCEL(data.writeStrongBinder, layerHandle);
+        SAFE_PARCEL(data.writeInt32, taskId);
         SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
         const status_t error =
                 remote()->transact(BnSurfaceComposer::ADD_FPS_LISTENER, data, &reply);
@@ -1669,8 +1668,8 @@
         }
         case ADD_FPS_LISTENER: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            sp<IBinder> layerHandle;
-            status_t result = data.readNullableStrongBinder(&layerHandle);
+            int32_t taskId;
+            status_t result = data.readInt32(&taskId);
             if (result != NO_ERROR) {
                 ALOGE("addFpsListener: Failed to read layer handle");
                 return result;
@@ -1681,7 +1680,7 @@
                 ALOGE("addFpsListener: Failed to read listener");
                 return result;
             }
-            return addFpsListener(layerHandle, listener);
+            return addFpsListener(taskId, listener);
         }
         case REMOVE_FPS_LISTENER: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index f5cde59..5e8ab92 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1982,9 +1982,9 @@
     return ComposerService::getComposerService()->removeRegionSamplingListener(listener);
 }
 
-status_t SurfaceComposerClient::addFpsListener(const sp<IBinder>& layerHandle,
+status_t SurfaceComposerClient::addFpsListener(int32_t taskId,
                                                const sp<gui::IFpsListener>& listener) {
-    return ComposerService::getComposerService()->addFpsListener(layerHandle, listener);
+    return ComposerService::getComposerService()->addFpsListener(taskId, listener);
 }
 
 status_t SurfaceComposerClient::removeFpsListener(const sp<gui::IFpsListener>& listener) {
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 8b64bcf..88cfe4b 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -351,16 +351,15 @@
 
     /* Registers a listener that streams fps updates from SurfaceFlinger.
      *
-     * The listener will stream fps updates for the layer tree rooted at layerHandle. Usually, this
-     * should be tied to a task. Layers that are not descendants of that task are out of scope for
-     * FPS computations.
+     * The listener will stream fps updates for the layer tree rooted at the layer denoted by the
+     * task ID, i.e., the layer must have the task ID as part of its layer metadata with key
+     * METADATA_TASK_ID. If there is no such layer, then no fps is expected to be reported.
      *
      * Multiple listeners may be supported.
      *
-     * Requires the ACCESS_SURFACE_FLINGER permission.
+     * Requires the READ_FRAME_BUFFER permission.
      */
-    virtual status_t addFpsListener(const sp<IBinder>& layerHandle,
-                                    const sp<gui::IFpsListener>& listener) = 0;
+    virtual status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) = 0;
     /*
      * Removes a listener that was streaming fps updates from SurfaceFlinger.
      */
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index ab0347c..de88943 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -589,8 +589,7 @@
                                               const sp<IBinder>& stopLayerHandle,
                                               const sp<IRegionSamplingListener>& listener);
     static status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener);
-    static status_t addFpsListener(const sp<IBinder>& layerHandle,
-                                   const sp<gui::IFpsListener>& listener);
+    static status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener);
     static status_t removeFpsListener(const sp<gui::IFpsListener>& listener);
 
 private:
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 91b2aff..e8fb71d 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -824,8 +824,7 @@
             const sp<IRegionSamplingListener>& /*listener*/) override {
         return NO_ERROR;
     }
-    status_t addFpsListener(const sp<IBinder>& /*layerHandle*/,
-                            const sp<gui::IFpsListener>& /*listener*/) {
+    status_t addFpsListener(int32_t /*taskId*/, const sp<gui::IFpsListener>& /*listener*/) {
         return NO_ERROR;
     }
     status_t removeFpsListener(const sp<gui::IFpsListener>& /*listener*/) { return NO_ERROR; }
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 1e47a3c..ee2daec 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -577,8 +577,8 @@
     msg.header.type = InputMessage::Type::FOCUS;
     msg.header.seq = seq;
     msg.body.focus.eventId = eventId;
-    msg.body.focus.hasFocus = hasFocus ? 1 : 0;
-    msg.body.focus.inTouchMode = inTouchMode ? 1 : 0;
+    msg.body.focus.hasFocus = hasFocus;
+    msg.body.focus.inTouchMode = inTouchMode;
     return mChannel->sendMessage(&msg);
 }
 
@@ -595,7 +595,7 @@
     msg.header.type = InputMessage::Type::CAPTURE;
     msg.header.seq = seq;
     msg.body.capture.eventId = eventId;
-    msg.body.capture.pointerCaptureEnabled = pointerCaptureEnabled ? 1 : 0;
+    msg.body.capture.pointerCaptureEnabled = pointerCaptureEnabled;
     return mChannel->sendMessage(&msg);
 }
 
@@ -615,7 +615,7 @@
               mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str());
         return UNKNOWN_ERROR;
     }
-    callback(msg.header.seq, msg.body.finished.handled == 1, msg.body.finished.consumeTime);
+    callback(msg.header.seq, msg.body.finished.handled, msg.body.finished.consumeTime);
     return OK;
 }
 
@@ -1168,7 +1168,7 @@
     InputMessage msg;
     msg.header.type = InputMessage::Type::FINISHED;
     msg.header.seq = seq;
-    msg.body.finished.handled = handled ? 1 : 0;
+    msg.body.finished.handled = handled;
     msg.body.finished.consumeTime = getConsumeTime(seq);
     status_t result = mChannel->sendMessage(&msg);
     if (result == OK) {
@@ -1228,12 +1228,12 @@
 }
 
 void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) {
-    event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus == 1,
-                      msg->body.focus.inTouchMode == 1);
+    event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus,
+                      msg->body.focus.inTouchMode);
 }
 
 void InputConsumer::initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg) {
-    event->initialize(msg->body.capture.eventId, msg->body.capture.pointerCaptureEnabled == 1);
+    event->initialize(msg->body.capture.eventId, msg->body.capture.pointerCaptureEnabled);
 }
 
 void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index bce0ec8..4b90844 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -20,7 +20,10 @@
 /** @hide */
 interface IInputConstants
 {
-    const int DEFAULT_DISPATCHING_TIMEOUT_MILLIS = 5000; // 5 seconds
+    // This should be multiplied by the value of the system property ro.hw_timeout_multiplier before
+    // use. A pre-multiplied constant is available in Java in
+    // android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS.
+    const int UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS = 5000; // 5 seconds
 
     // Compatibility changes.
     /**
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index a886585..8f43608 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -49,6 +49,7 @@
   CHECK_OFFSET(InputMessage::Body::Key, downTime, 88);
 
   CHECK_OFFSET(InputMessage::Body::Motion, eventId, 0);
+  CHECK_OFFSET(InputMessage::Body::Motion, empty1, 4);
   CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8);
   CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16);
   CHECK_OFFSET(InputMessage::Body::Motion, source, 20);
@@ -60,6 +61,7 @@
   CHECK_OFFSET(InputMessage::Body::Motion, metaState, 72);
   CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 76);
   CHECK_OFFSET(InputMessage::Body::Motion, classification, 80);
+  CHECK_OFFSET(InputMessage::Body::Motion, empty2, 81);
   CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 84);
   CHECK_OFFSET(InputMessage::Body::Motion, downTime, 88);
   CHECK_OFFSET(InputMessage::Body::Motion, dsdx, 96);
@@ -73,16 +75,20 @@
   CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128);
   CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132);
   CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 136);
+  CHECK_OFFSET(InputMessage::Body::Motion, empty3, 140);
   CHECK_OFFSET(InputMessage::Body::Motion, pointers, 144);
 
   CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0);
   CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
-  CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6);
+  CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 5);
+  CHECK_OFFSET(InputMessage::Body::Focus, empty, 6);
 
   CHECK_OFFSET(InputMessage::Body::Capture, eventId, 0);
   CHECK_OFFSET(InputMessage::Body::Capture, pointerCaptureEnabled, 4);
+  CHECK_OFFSET(InputMessage::Body::Capture, empty, 5);
 
-  CHECK_OFFSET(InputMessage::Body::Finished, handled, 4);
+  CHECK_OFFSET(InputMessage::Body::Finished, handled, 0);
+  CHECK_OFFSET(InputMessage::Body::Finished, empty, 1);
   CHECK_OFFSET(InputMessage::Body::Finished, consumeTime, 8);
 }
 
diff --git a/opengl/OWNERS b/opengl/OWNERS
index b505712..a9bd4bb 100644
--- a/opengl/OWNERS
+++ b/opengl/OWNERS
@@ -1,7 +1,6 @@
 chrisforbes@google.com
 cnorthrop@google.com
-courtneygo@google.com
 ianelliott@google.com
 jessehall@google.com
 lpy@google.com
-zzyiwei@google.com
+timvp@google.com
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
index c29181d..d38f2ef 100644
--- a/opengl/libs/EGL/egl_angle_platform.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -22,6 +22,8 @@
 #pragma GCC diagnostic ignored "-Wunused-parameter"
 #include <EGL/Platform.h>
 #pragma GCC diagnostic pop
+
+#include <android-base/properties.h>
 #include <android/dlext.h>
 #include <dlfcn.h>
 #include <graphicsenv/GraphicsEnv.h>
@@ -33,7 +35,6 @@
 
 namespace angle {
 
-constexpr char kAngleEs2Lib[] = "libGLESv2_angle.so";
 constexpr int kAngleDlFlags = RTLD_LOCAL | RTLD_NOW;
 
 static GetDisplayPlatformFunc angleGetDisplayPlatform = nullptr;
@@ -107,18 +108,36 @@
     android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
     void* so = nullptr;
     if (ns) {
+        // Loading from an APK, so hard-code the suffix to "_angle".
+        constexpr char kAngleEs2Lib[] = "libGLESv2_angle.so";
         const android_dlextinfo dlextinfo = {
                 .flags = ANDROID_DLEXT_USE_NAMESPACE,
                 .library_namespace = ns,
         };
         so = android_dlopen_ext(kAngleEs2Lib, kAngleDlFlags, &dlextinfo);
+        if (so) {
+            ALOGD("dlopen_ext from APK (%s) success at %p", kAngleEs2Lib, so);
+        } else {
+            ALOGE("dlopen_ext(\"%s\") failed: %s", kAngleEs2Lib, dlerror());
+            return false;
+        }
     } else {
         // If we are here, ANGLE is loaded as built-in gl driver in the sphal.
-        so = android_load_sphal_library(kAngleEs2Lib, kAngleDlFlags);
-    }
-    if (!so) {
-        ALOGE("%s failed to dlopen %s!", __FUNCTION__, kAngleEs2Lib);
-        return false;
+        // Get the specified ANGLE library filename suffix.
+        std::string angleEs2LibSuffix = android::base::GetProperty("ro.hardware.egl", "");
+        if (angleEs2LibSuffix.empty()) {
+            ALOGE("%s failed to get valid ANGLE library filename suffix!", __FUNCTION__);
+            return false;
+        }
+
+        std::string angleEs2LibName = "libGLESv2_" + angleEs2LibSuffix + ".so";
+        so = android_load_sphal_library(angleEs2LibName.c_str(), kAngleDlFlags);
+        if (so) {
+            ALOGD("dlopen (%s) success at %p", angleEs2LibName.c_str(), so);
+        } else {
+            ALOGE("%s failed to dlopen %s!", __FUNCTION__, angleEs2LibName.c_str());
+            return false;
+        }
     }
 
     angleGetDisplayPlatform =
diff --git a/services/gpuservice/OWNERS b/services/gpuservice/OWNERS
index 5d02839..ac300d0 100644
--- a/services/gpuservice/OWNERS
+++ b/services/gpuservice/OWNERS
@@ -1,3 +1,2 @@
 chrisforbes@google.com
 lpy@google.com
-zzyiwei@google.com
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 465e8be..19f8694 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -48,6 +48,7 @@
 #define DEBUG_HOVER 0
 
 #include <android-base/chrono_utils.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android/os/IInputConstants.h>
 #include <binder/Binder.h>
@@ -78,6 +79,7 @@
 #define INDENT3 "      "
 #define INDENT4 "        "
 
+using android::base::HwTimeoutMultiplier;
 using android::base::StringPrintf;
 using android::os::BlockUntrustedTouchesMode;
 using android::os::IInputConstants;
@@ -89,8 +91,9 @@
 
 // Default input dispatching timeout if there is no focused application or paused window
 // from which to determine an appropriate dispatching timeout.
-constexpr std::chrono::duration DEFAULT_INPUT_DISPATCHING_TIMEOUT =
-        std::chrono::milliseconds(android::os::IInputConstants::DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
+const std::chrono::duration DEFAULT_INPUT_DISPATCHING_TIMEOUT = std::chrono::milliseconds(
+        android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
+        HwTimeoutMultiplier());
 
 // Amount of time to allow for all pending events to be processed when an app switch
 // key is on the way.  This is used to preempt input dispatch and drop input events
diff --git a/services/inputflinger/docs/anr.md b/services/inputflinger/docs/anr.md
index ce64fe9..5b931d6 100644
--- a/services/inputflinger/docs/anr.md
+++ b/services/inputflinger/docs/anr.md
@@ -22,7 +22,7 @@
 
 When a dispatch entry is sent to the app, its `deliveryTime` and `timeoutTime` fields are populated. The `deliveryTime` is the time that the event is delivered to the app. This is simply the current time inside `publishMotionEvent`. The `timeoutTime` is the time when this entry would be considered overdue. At that time, the ANR process would start for this connection.
 
-Most connections are associated with a window, and each window may have a custom timeout time. To calculate the timeout time of a specific event, simply add the `window.dispatchingTimeout` to the current time. In case where there is no associated window, such as gesture monitors, use the default dispatching timeout which is defined in `IInputConstants::DEFAULT_DISPATCHING_TIMEOUT_MILLIS`.
+Most connections are associated with a window, and each window may have a custom timeout time. To calculate the timeout time of a specific event, simply add the `window.dispatchingTimeout` to the current time. In case where there is no associated window, such as gesture monitors, use the default dispatching timeout which is defined in `android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS`.
 
 The `timeoutTime` field of the `DispatchEntry` is needed because the window associated with a specific connection may change its timeout time. Therefore, entries sent prior to the timeout change would need to follow the previous timeout value. If a window timeout changes, it only affects new events being dispatched, and does not alter the timeout times of events already sent to the app.
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 16cf010..1a7ddee 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -1412,28 +1412,29 @@
 }
 
 void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {
-    const RawState* last =
-            mRawStatesPending.empty() ? &mCurrentRawState : &mRawStatesPending.back();
-
     // Push a new state.
     mRawStatesPending.emplace_back();
 
-    RawState* next = &mRawStatesPending.back();
-    next->clear();
-    next->when = when;
-    next->readTime = readTime;
+    RawState& next = mRawStatesPending.back();
+    next.clear();
+    next.when = when;
+    next.readTime = readTime;
 
     // Sync button state.
-    next->buttonState =
+    next.buttonState =
             mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState();
 
     // Sync scroll
-    next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
-    next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel();
+    next.rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
+    next.rawHScroll = mCursorScrollAccumulator.getRelativeHWheel();
     mCursorScrollAccumulator.finishSync();
 
     // Sync touch
-    syncTouch(when, next);
+    syncTouch(when, &next);
+
+    // The last RawState is the actually second to last, since we just added a new state
+    const RawState& last =
+            mRawStatesPending.size() == 1 ? mCurrentRawState : mRawStatesPending.rbegin()[1];
 
     // Assign pointer ids.
     if (!mHavePointerIds) {
@@ -1443,10 +1444,10 @@
 #if DEBUG_RAW_EVENTS
     ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
           "hovering ids 0x%08x -> 0x%08x, canceled ids 0x%08x",
-          last->rawPointerData.pointerCount, next->rawPointerData.pointerCount,
-          last->rawPointerData.touchingIdBits.value, next->rawPointerData.touchingIdBits.value,
-          last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value,
-          next->rawPointerData.canceledIdBits.value);
+          last.rawPointerData.pointerCount, next.rawPointerData.pointerCount,
+          last.rawPointerData.touchingIdBits.value, next.rawPointerData.touchingIdBits.value,
+          last.rawPointerData.hoveringIdBits.value, next.rawPointerData.hoveringIdBits.value,
+          next.rawPointerData.canceledIdBits.value);
 #endif
 
     processRawTouches(false /*timeout*/);
@@ -3731,11 +3732,11 @@
     return nullptr;
 }
 
-void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) {
-    uint32_t currentPointerCount = current->rawPointerData.pointerCount;
-    uint32_t lastPointerCount = last->rawPointerData.pointerCount;
+void TouchInputMapper::assignPointerIds(const RawState& last, RawState& current) {
+    uint32_t currentPointerCount = current.rawPointerData.pointerCount;
+    uint32_t lastPointerCount = last.rawPointerData.pointerCount;
 
-    current->rawPointerData.clearIdBits();
+    current.rawPointerData.clearIdBits();
 
     if (currentPointerCount == 0) {
         // No pointers to assign.
@@ -3746,20 +3747,20 @@
         // All pointers are new.
         for (uint32_t i = 0; i < currentPointerCount; i++) {
             uint32_t id = i;
-            current->rawPointerData.pointers[i].id = id;
-            current->rawPointerData.idToIndex[id] = i;
-            current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(i));
+            current.rawPointerData.pointers[i].id = id;
+            current.rawPointerData.idToIndex[id] = i;
+            current.rawPointerData.markIdBit(id, current.rawPointerData.isHovering(i));
         }
         return;
     }
 
     if (currentPointerCount == 1 && lastPointerCount == 1 &&
-        current->rawPointerData.pointers[0].toolType == last->rawPointerData.pointers[0].toolType) {
+        current.rawPointerData.pointers[0].toolType == last.rawPointerData.pointers[0].toolType) {
         // Only one pointer and no change in count so it must have the same id as before.
-        uint32_t id = last->rawPointerData.pointers[0].id;
-        current->rawPointerData.pointers[0].id = id;
-        current->rawPointerData.idToIndex[id] = 0;
-        current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(0));
+        uint32_t id = last.rawPointerData.pointers[0].id;
+        current.rawPointerData.pointers[0].id = id;
+        current.rawPointerData.idToIndex[id] = 0;
+        current.rawPointerData.markIdBit(id, current.rawPointerData.isHovering(0));
         return;
     }
 
@@ -3777,9 +3778,9 @@
         for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
              lastPointerIndex++) {
             const RawPointerData::Pointer& currentPointer =
-                    current->rawPointerData.pointers[currentPointerIndex];
+                    current.rawPointerData.pointers[currentPointerIndex];
             const RawPointerData::Pointer& lastPointer =
-                    last->rawPointerData.pointers[lastPointerIndex];
+                    last.rawPointerData.pointers[lastPointerIndex];
             if (currentPointer.toolType == lastPointer.toolType) {
                 int64_t deltaX = currentPointer.x - lastPointer.x;
                 int64_t deltaY = currentPointer.y - lastPointer.y;
@@ -3883,12 +3884,12 @@
             matchedCurrentBits.markBit(currentPointerIndex);
             matchedLastBits.markBit(lastPointerIndex);
 
-            uint32_t id = last->rawPointerData.pointers[lastPointerIndex].id;
-            current->rawPointerData.pointers[currentPointerIndex].id = id;
-            current->rawPointerData.idToIndex[id] = currentPointerIndex;
-            current->rawPointerData.markIdBit(id,
-                                              current->rawPointerData.isHovering(
-                                                      currentPointerIndex));
+            uint32_t id = last.rawPointerData.pointers[lastPointerIndex].id;
+            current.rawPointerData.pointers[currentPointerIndex].id = id;
+            current.rawPointerData.idToIndex[id] = currentPointerIndex;
+            current.rawPointerData.markIdBit(id,
+                                             current.rawPointerData.isHovering(
+                                                     currentPointerIndex));
             usedIdBits.markBit(id);
 
 #if DEBUG_POINTER_ASSIGNMENT
@@ -3905,10 +3906,10 @@
         uint32_t currentPointerIndex = matchedCurrentBits.markFirstUnmarkedBit();
         uint32_t id = usedIdBits.markFirstUnmarkedBit();
 
-        current->rawPointerData.pointers[currentPointerIndex].id = id;
-        current->rawPointerData.idToIndex[id] = currentPointerIndex;
-        current->rawPointerData.markIdBit(id,
-                                          current->rawPointerData.isHovering(currentPointerIndex));
+        current.rawPointerData.pointers[currentPointerIndex].id = id;
+        current.rawPointerData.idToIndex[id] = currentPointerIndex;
+        current.rawPointerData.markIdBit(id,
+                                         current.rawPointerData.isHovering(currentPointerIndex));
 
 #if DEBUG_POINTER_ASSIGNMENT
         ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, id);
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index cb52e2d..9b84ed5 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -772,7 +772,7 @@
     bool isPointInsideSurface(int32_t x, int32_t y);
     const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y);
 
-    static void assignPointerIds(const RawState* last, RawState* current);
+    static void assignPointerIds(const RawState& last, RawState& current);
 
     const char* modeToString(DeviceMode deviceMode);
     void rotateAndScale(float& x, float& y);
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index a96dffd..eac3d95 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -424,12 +424,12 @@
     return isBufferDue(expectedPresentTime);
 }
 
-bool BufferLayer::frameIsEarly(nsecs_t expectedPresentTime) const {
+bool BufferLayer::frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const {
     // TODO(b/169901895): kEarlyLatchVsyncThreshold should be based on the
     // vsync period. We can do this change as soon as ag/13100772 is merged.
     constexpr static std::chrono::nanoseconds kEarlyLatchVsyncThreshold = 5ms;
 
-    const auto presentTime = nextPredictedPresentTime();
+    const auto presentTime = nextPredictedPresentTime(vsyncId);
     if (!presentTime.has_value()) {
         return false;
     }
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 2118f4a..d215298 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -191,7 +191,7 @@
 
     // Returns true if the next frame is considered too early to present
     // at the given expectedPresentTime
-    bool frameIsEarly(nsecs_t expectedPresentTime) const;
+    bool frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const;
 
     std::atomic<bool> mAutoRefresh{false};
     std::atomic<bool> mSidebandStreamChanged{false};
@@ -238,7 +238,7 @@
     FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
 
     // Returns the predicted present time of the next frame if available
-    virtual std::optional<nsecs_t> nextPredictedPresentTime() const = 0;
+    virtual std::optional<nsecs_t> nextPredictedPresentTime(int64_t vsyncId) const = 0;
 
     // The amount of time SF can delay a frame if it is considered early based
     // on the VsyncModulator::VsyncConfig::appWorkDuration
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 3615a02..63dd25f 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -215,7 +215,7 @@
     return mQueuedFrames > 0;
 }
 
-std::optional<nsecs_t> BufferQueueLayer::nextPredictedPresentTime() const {
+std::optional<nsecs_t> BufferQueueLayer::nextPredictedPresentTime(int64_t /*vsyncId*/) const {
     Mutex::Autolock lock(mQueueItemLock);
     if (mQueueItems.empty()) {
         return std::nullopt;
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 0ea02e1..3a34b95 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -117,7 +117,7 @@
     // Temporary - Used only for LEGACY camera mode.
     uint32_t getProducerStickyTransform() const;
 
-    std::optional<nsecs_t> nextPredictedPresentTime() const override;
+    std::optional<nsecs_t> nextPredictedPresentTime(int64_t vsyncId) const override;
 
     sp<BufferLayerConsumer> mConsumer;
     sp<IGraphicBufferProducer> mProducer;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 296085a..e017cdc 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -171,6 +171,14 @@
 
 void BufferStateLayer::onSurfaceFrameCreated(
         const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) {
+    while (mPendingJankClassifications.size() >= kPendingClassificationMaxSurfaceFrames) {
+        // Too many SurfaceFrames pending classification. The front of the deque is probably not
+        // tracked by FrameTimeline and will never be presented. This will only result in a memory
+        // leak.
+        ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak",
+              mName.c_str());
+        mPendingJankClassifications.pop_front();
+    }
     mPendingJankClassifications.emplace_back(surfaceFrame);
 }
 
@@ -605,13 +613,14 @@
     return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
 }
 
-std::optional<nsecs_t> BufferStateLayer::nextPredictedPresentTime() const {
-    const State& drawingState(getDrawingState());
-    if (!drawingState.isAutoTimestamp || !drawingState.bufferSurfaceFrameTX) {
+std::optional<nsecs_t> BufferStateLayer::nextPredictedPresentTime(int64_t vsyncId) const {
+    const auto prediction =
+            mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(vsyncId);
+    if (!prediction.has_value()) {
         return std::nullopt;
     }
 
-    return drawingState.bufferSurfaceFrameTX->getPredictions().presentTime;
+    return prediction->presentTime;
 }
 
 status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 6b422ea..003edd5 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -156,7 +156,7 @@
 
     bool bufferNeedsFiltering() const override;
 
-    std::optional<nsecs_t> nextPredictedPresentTime() const override;
+    std::optional<nsecs_t> nextPredictedPresentTime(int64_t vsyncId) const override;
 
     static const std::array<float, 16> IDENTITY_MATRIX;
 
@@ -174,6 +174,8 @@
     nsecs_t mCallbackHandleAcquireTime = -1;
 
     std::deque<std::shared_ptr<android::frametimeline::SurfaceFrame>> mPendingJankClassifications;
+    // An upper bound on the number of SurfaceFrames in the pending classifications deque.
+    static constexpr int kPendingClassificationMaxSurfaceFrames = 25;
 
     const std::string mBlastTransactionName{"BufferTX - " + mName};
     // This integer is incremented everytime a buffer arrives at the server for this layer,
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index d5bf569..325361b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -39,9 +39,6 @@
 using ::testing::StrictMock;
 
 struct CompositionEngineTest : public testing::Test {
-    android::mock::HWComposer* mHwc = new StrictMock<android::mock::HWComposer>();
-    renderengine::mock::RenderEngine* mRenderEngine =
-            new StrictMock<renderengine::mock::RenderEngine>();
     std::shared_ptr<TimeStats> mTimeStats;
 
     impl::CompositionEngine mEngine;
@@ -58,15 +55,18 @@
 }
 
 TEST_F(CompositionEngineTest, canSetHWComposer) {
-    mEngine.setHwComposer(std::unique_ptr<android::HWComposer>(mHwc));
+    android::mock::HWComposer* hwc = new StrictMock<android::mock::HWComposer>();
+    mEngine.setHwComposer(std::unique_ptr<android::HWComposer>(hwc));
 
-    EXPECT_EQ(mHwc, &mEngine.getHwComposer());
+    EXPECT_EQ(hwc, &mEngine.getHwComposer());
 }
 
 TEST_F(CompositionEngineTest, canSetRenderEngine) {
-    mEngine.setRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
+    renderengine::mock::RenderEngine* renderEngine =
+            new StrictMock<renderengine::mock::RenderEngine>();
+    mEngine.setRenderEngine(std::unique_ptr<renderengine::RenderEngine>(renderEngine));
 
-    EXPECT_EQ(mRenderEngine, &mEngine.getRenderEngine());
+    EXPECT_EQ(renderEngine, &mEngine.getRenderEngine());
 }
 
 TEST_F(CompositionEngineTest, canSetTimeStats) {
@@ -130,10 +130,10 @@
 struct CompositionEngineUpdateCursorAsyncTest : public CompositionEngineTest {
 public:
     struct Layer {
-        Layer() { EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE)); }
+        Layer() { EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE)); }
 
         StrictMock<mock::OutputLayer> outputLayer;
-        StrictMock<mock::LayerFE> layerFE;
+        sp<StrictMock<mock::LayerFE>> layerFE = sp<StrictMock<mock::LayerFE>>::make();
         LayerFECompositionState layerFEState;
     };
 
@@ -175,21 +175,21 @@
     {
         InSequence seq;
         EXPECT_CALL(mOutput2Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
-        EXPECT_CALL(mOutput2Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
+        EXPECT_CALL(*mOutput2Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
         EXPECT_CALL(mOutput2Layer1.outputLayer, writeCursorPositionToHWC());
     }
 
     {
         InSequence seq;
         EXPECT_CALL(mOutput3Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
-        EXPECT_CALL(mOutput3Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
+        EXPECT_CALL(*mOutput3Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
         EXPECT_CALL(mOutput3Layer1.outputLayer, writeCursorPositionToHWC());
     }
 
     {
         InSequence seq;
         EXPECT_CALL(mOutput3Layer2.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
-        EXPECT_CALL(mOutput3Layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
+        EXPECT_CALL(*mOutput3Layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
         EXPECT_CALL(mOutput3Layer2.outputLayer, writeCursorPositionToHWC());
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 3059beb..5f0b0ee 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -1697,12 +1697,12 @@
 
     struct Layer {
         Layer() {
-            EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
-            EXPECT_CALL(mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
+            EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*mLayerFE));
+            EXPECT_CALL(*mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
         }
 
         StrictMock<mock::OutputLayer> mOutputLayer;
-        StrictMock<mock::LayerFE> mLayerFE;
+        sp<StrictMock<mock::LayerFE>> mLayerFE = sp<StrictMock<mock::LayerFE>>::make();
         LayerFECompositionState mLayerFEState;
     };
 
@@ -2712,12 +2712,12 @@
 
     struct Layer {
         Layer() {
-            EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
+            EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE));
             EXPECT_CALL(outputLayer, getHwcLayer()).WillRepeatedly(Return(&hwc2Layer));
         }
 
         StrictMock<mock::OutputLayer> outputLayer;
-        StrictMock<mock::LayerFE> layerFE;
+        sp<StrictMock<mock::LayerFE>> layerFE = sp<StrictMock<mock::LayerFE>>::make();
         StrictMock<HWC2::mock::Layer> hwc2Layer;
     };
 
@@ -2793,11 +2793,11 @@
     // are passed. This happens to work with the current implementation, but
     // would not survive certain calls like Fence::merge() which would return a
     // new instance.
-    EXPECT_CALL(mLayer1.layerFE,
+    EXPECT_CALL(*mLayer1.layerFE,
                 onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer1Fence.get()))));
-    EXPECT_CALL(mLayer2.layerFE,
+    EXPECT_CALL(*mLayer2.layerFE,
                 onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer2Fence.get()))));
-    EXPECT_CALL(mLayer3.layerFE,
+    EXPECT_CALL(*mLayer3.layerFE,
                 onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer3Fence.get()))));
 
     mOutput.postFramebuffer();
@@ -2824,9 +2824,9 @@
     // Fence::merge is called, and since none of the fences are actually valid,
     // Fence::NO_FENCE is returned and passed to each onLayerDisplayed() call.
     // This is the best we can do without creating a real kernel fence object.
-    EXPECT_CALL(mLayer1.layerFE, onLayerDisplayed(Fence::NO_FENCE));
-    EXPECT_CALL(mLayer2.layerFE, onLayerDisplayed(Fence::NO_FENCE));
-    EXPECT_CALL(mLayer3.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+    EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+    EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+    EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed(Fence::NO_FENCE));
 
     mOutput.postFramebuffer();
 }
@@ -3330,12 +3330,12 @@
 struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeSurfacesTest {
     struct Layer {
         Layer() {
-            EXPECT_CALL(mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
-            EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
+            EXPECT_CALL(*mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
+            EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*mLayerFE));
         }
 
         StrictMock<mock::OutputLayer> mOutputLayer;
-        StrictMock<mock::LayerFE> mLayerFE;
+        sp<StrictMock<mock::LayerFE>> mLayerFE = sp<StrictMock<mock::LayerFE>>::make();
         LayerFECompositionState mLayerFEState;
     };
 
@@ -3546,12 +3546,12 @@
         Layer() {
             EXPECT_CALL(mOutputLayer, getState()).WillRepeatedly(ReturnRef(mOutputLayerState));
             EXPECT_CALL(mOutputLayer, editState()).WillRepeatedly(ReturnRef(mOutputLayerState));
-            EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
-            EXPECT_CALL(mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
+            EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*mLayerFE));
+            EXPECT_CALL(*mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
         }
 
         StrictMock<mock::OutputLayer> mOutputLayer;
-        StrictMock<mock::LayerFE> mLayerFE;
+        sp<StrictMock<mock::LayerFE>> mLayerFE = sp<StrictMock<mock::LayerFE>>::make();
         LayerFECompositionState mLayerFEState;
         impl::OutputLayerCompositionState mOutputLayerState;
         LayerFE::LayerSettings mLayerSettings;
@@ -3645,11 +3645,11 @@
     LayerFE::LayerSettings mShadowSettings;
     mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
 
-    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(_))
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(_))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(_))
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(_))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[1].mLayerSettings})));
-    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(_))
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
                     {mShadowSettings, mLayers[2].mLayerSettings})));
 
@@ -3683,7 +3683,7 @@
     mLayers[1].mLayerFEState.isOpaque = true;
     mLayers[2].mLayerFEState.isOpaque = true;
 
-    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(_))
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
 
     Region accumClearRegion(Rect(10, 11, 12, 13));
@@ -3709,7 +3709,7 @@
     mLayers[1].mLayerFEState.isOpaque = false;
     mLayers[2].mLayerFEState.isOpaque = false;
 
-    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(_))
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
 
     Region accumClearRegion(Rect(10, 11, 12, 13));
@@ -3773,9 +3773,9 @@
     mBlackoutSettings.alpha = 0.f;
     mBlackoutSettings.disableBlending = true;
 
-    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mBlackoutSettings})));
-    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
 
     auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
@@ -3835,11 +3835,11 @@
             false /* disabledBlurs */,
     };
 
-    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
 
     static_cast<void>(
@@ -3891,11 +3891,11 @@
             false /* disabledBlurs */,
     };
 
-    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
 
     static_cast<void>(
@@ -3948,11 +3948,11 @@
             false /* disabledBlurs */,
     };
 
-    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
 
     static_cast<void>(
@@ -4003,11 +4003,11 @@
             false /* disabledBlurs */,
     };
 
-    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
 
     static_cast<void>(
@@ -4056,11 +4056,11 @@
             false /* disabledBlurs */,
     };
 
-    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
 
     static_cast<void>(mOutput.generateClientCompositionRequests(true /* supportsProtectedContent */,
@@ -4188,7 +4188,7 @@
 
     EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
     EXPECT_CALL(leftLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
-    EXPECT_CALL(leftLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(leftLayerSettings))))
+    EXPECT_CALL(*leftLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(leftLayerSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({leftLayer.mLayerSettings})));
 
     compositionengine::LayerFE::ClientCompositionTargetSettings rightLayerSettings{
@@ -4206,7 +4206,7 @@
 
     EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
     EXPECT_CALL(rightLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
-    EXPECT_CALL(rightLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(rightLayerSettings))))
+    EXPECT_CALL(*rightLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(rightLayerSettings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({rightLayer.mLayerSettings})));
 
     constexpr bool supportsProtectedContent = true;
@@ -4246,7 +4246,7 @@
 
     EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
-    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mShadowSettings})));
 
     auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
@@ -4286,7 +4286,7 @@
 
     EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
-    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
             .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
                     {mShadowSettings, mLayers[2].mLayerSettings})));
 
diff --git a/services/surfaceflinger/FpsReporter.cpp b/services/surfaceflinger/FpsReporter.cpp
index c7dbf88..0bc2d3e 100644
--- a/services/surfaceflinger/FpsReporter.cpp
+++ b/services/surfaceflinger/FpsReporter.cpp
@@ -18,14 +18,16 @@
 #define LOG_TAG "FpsReporter"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include "FpsReporter.h"
+#include <algorithm>
 
+#include "FpsReporter.h"
 #include "Layer.h"
+#include "SurfaceFlinger.h"
 
 namespace android {
 
-FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline)
-      : mFrameTimeline(frameTimeline) {}
+FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger)
+      : mFrameTimeline(frameTimeline), mFlinger(flinger) {}
 
 void FpsReporter::dispatchLayerFps() const {
     std::vector<TrackedListener> localListeners;
@@ -41,16 +43,33 @@
                        });
     }
 
-    for (const auto& listener : localListeners) {
-        sp<Layer> promotedLayer = listener.layer.promote();
-        if (promotedLayer != nullptr) {
-            std::unordered_set<int32_t> layerIds;
+    std::unordered_set<int32_t> seenTasks;
+    std::vector<std::pair<TrackedListener, sp<Layer>>> listenersAndLayersToReport;
 
-            promotedLayer->traverse(LayerVector::StateSet::Drawing,
-                                    [&](Layer* layer) { layerIds.insert(layer->getSequence()); });
-
-            listener.listener->onFpsReported(mFrameTimeline.computeFps(layerIds));
+    mFlinger.mCurrentState.traverse([&](Layer* layer) {
+        auto& currentState = layer->getCurrentState();
+        if (currentState.metadata.has(METADATA_TASK_ID)) {
+            int32_t taskId = currentState.metadata.getInt32(METADATA_TASK_ID, 0);
+            if (seenTasks.count(taskId) == 0) {
+                // localListeners is expected to be tiny
+                for (TrackedListener& listener : localListeners) {
+                    if (listener.taskId == taskId) {
+                        seenTasks.insert(taskId);
+                        listenersAndLayersToReport.push_back({listener, sp<Layer>(layer)});
+                        break;
+                    }
+                }
+            }
         }
+    });
+
+    for (const auto& [listener, layer] : listenersAndLayersToReport) {
+        std::unordered_set<int32_t> layerIds;
+
+        layer->traverse(LayerVector::StateSet::Current,
+                        [&](Layer* layer) { layerIds.insert(layer->getSequence()); });
+
+        listener.listener->onFpsReported(mFrameTimeline.computeFps(layerIds));
     }
 }
 
@@ -59,11 +78,11 @@
     mListeners.erase(who);
 }
 
-void FpsReporter::addListener(const sp<gui::IFpsListener>& listener, const wp<Layer>& layer) {
+void FpsReporter::addListener(const sp<gui::IFpsListener>& listener, int32_t taskId) {
     sp<IBinder> asBinder = IInterface::asBinder(listener);
     asBinder->linkToDeath(this);
     std::lock_guard lock(mMutex);
-    mListeners.emplace(wp<IBinder>(asBinder), TrackedListener{listener, layer});
+    mListeners.emplace(wp<IBinder>(asBinder), TrackedListener{listener, taskId});
 }
 
 void FpsReporter::removeListener(const sp<gui::IFpsListener>& listener) {
diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h
index d64b3dc..1cec295 100644
--- a/services/surfaceflinger/FpsReporter.h
+++ b/services/surfaceflinger/FpsReporter.h
@@ -27,10 +27,11 @@
 namespace android {
 
 class Layer;
+class SurfaceFlinger;
 
 class FpsReporter : public IBinder::DeathRecipient {
 public:
-    FpsReporter(frametimeline::FrameTimeline& frameTimeline);
+    FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger);
 
     // Dispatches updated layer fps values for the registered listeners
     // This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock
@@ -41,7 +42,7 @@
     void binderDied(const wp<IBinder>&) override;
 
     // Registers an Fps listener that listens to fps updates for the provided layer
-    void addListener(const sp<gui::IFpsListener>& listener, const wp<Layer>& layer);
+    void addListener(const sp<gui::IFpsListener>& listener, int32_t taskId);
     // Deregisters an Fps listener
     void removeListener(const sp<gui::IFpsListener>& listener);
 
@@ -55,10 +56,11 @@
 
     struct TrackedListener {
         sp<gui::IFpsListener> listener;
-        wp<Layer> layer;
+        int32_t taskId;
     };
 
     frametimeline::FrameTimeline& mFrameTimeline;
+    SurfaceFlinger& mFlinger;
     std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex);
 };
 
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 75229e9..03e38f3 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -207,6 +207,17 @@
     }
 }
 
+FrameTimelineEvent::PredictionType toProto(PredictionState predictionState) {
+    switch (predictionState) {
+        case PredictionState::Valid:
+            return FrameTimelineEvent::PREDICTION_VALID;
+        case PredictionState::Expired:
+            return FrameTimelineEvent::PREDICTION_EXPIRED;
+        case PredictionState::None:
+            return FrameTimelineEvent::PREDICTION_UNKNOWN;
+    }
+}
+
 int32_t jankTypeBitmaskToProto(int32_t jankType) {
     if (jankType == JankType::None) {
         return FrameTimelineEvent::JANK_NONE;
@@ -628,6 +639,7 @@
                                                          FrameReadyMetadata::OnTimeFinish);
         actualSurfaceFrameStartEvent->set_gpu_composition(mGpuComposition);
         actualSurfaceFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
+        actualSurfaceFrameStartEvent->set_prediction_type(toProto(mPredictionState));
     });
 
     // Actual timeline end
@@ -968,13 +980,14 @@
                                                          FrameReadyMetadata::OnTimeFinish);
         actualDisplayFrameStartEvent->set_gpu_composition(mGpuComposition);
         actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
+        actualDisplayFrameStartEvent->set_prediction_type(toProto(mPredictionState));
     });
 
     // Actual timeline end
     FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
         auto packet = ctx.NewTracePacket();
         packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
-        packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerActuals.endTime));
+        packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerActuals.presentTime));
 
         auto* event = packet->set_frame_timeline_event();
         auto* actualDisplayFrameEndEvent = event->set_frame_end();
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 937868a..061ad0e 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1339,7 +1339,6 @@
 
 bool Layer::setMetadata(const LayerMetadata& data) {
     if (!mCurrentState.metadata.merge(data, true /* eraseEmpty */)) return false;
-    mCurrentState.sequence++;
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index ce97155..34a9f39 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -922,7 +922,9 @@
 
     pid_t getOwnerPid() { return mOwnerPid; }
 
-    virtual bool frameIsEarly(nsecs_t /*expectedPresentTime*/) const { return false; }
+    virtual bool frameIsEarly(nsecs_t /*expectedPresentTime*/, int64_t /*vsyncId*/) const {
+        return false;
+    }
 
     // This layer is not a clone, but it's the parent to the cloned hierarchy. The
     // variable mClonedChild represents the top layer that will be cloned so this
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index ad91183..593855e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1433,14 +1433,12 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::addFpsListener(const sp<IBinder>& layerHandle,
-                                        const sp<gui::IFpsListener>& listener) {
+status_t SurfaceFlinger::addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) {
     if (!listener) {
         return BAD_VALUE;
     }
 
-    const wp<Layer> layer = fromHandle(layerHandle);
-    mFpsReporter->addListener(listener, layer);
+    mFpsReporter->addListener(listener, taskId);
     return NO_ERROR;
 }
 
@@ -3006,7 +3004,7 @@
     mRegionSamplingThread =
             new RegionSamplingThread(*this, *mScheduler,
                                      RegionSamplingThread::EnvironmentTimingTunables());
-    mFpsReporter = new FpsReporter(*mFrameTimeline);
+    mFpsReporter = new FpsReporter(*mFrameTimeline, *this);
     // Dispatch a mode change request for the primary display on scheduler
     // initialization, so that the EventThreads always contain a reference to a
     // prior configuration.
@@ -3315,10 +3313,10 @@
 
                 while (!transactionQueue.empty()) {
                     const auto& transaction = transactionQueue.front();
-                    if (!transactionIsReadyToBeApplied(transaction.isAutoTimestamp,
+                    if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
+                                                       transaction.isAutoTimestamp,
                                                        transaction.desiredPresentTime,
-                                                       transaction.states,
-                                                       pendingBuffers)) {
+                                                       transaction.states, pendingBuffers)) {
                         setTransactionFlags(eTransactionFlushNeeded);
                         break;
                     }
@@ -3342,10 +3340,10 @@
                 const auto& transaction = mTransactionQueue.front();
                 bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) !=
                         mPendingTransactionQueues.end();
-                if (!transactionIsReadyToBeApplied(transaction.isAutoTimestamp,
+                if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
+                                                   transaction.isAutoTimestamp,
                                                    transaction.desiredPresentTime,
-                                                   transaction.states,
-                                                   pendingBuffers) ||
+                                                   transaction.states, pendingBuffers) ||
                     pendingTransactions) {
                     mPendingTransactionQueues[transaction.applyToken].push(transaction);
                 } else {
@@ -3375,7 +3373,8 @@
 }
 
 bool SurfaceFlinger::transactionIsReadyToBeApplied(
-        bool isAutoTimestamp, int64_t desiredPresentTime, const Vector<ComposerState>& states,
+        const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
+        const Vector<ComposerState>& states,
         std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers) {
     const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
     bool ready = true;
@@ -3388,24 +3387,26 @@
 
     for (const ComposerState& state : states) {
         const layer_state_t& s = state.state;
-        if (!(s.what & layer_state_t::eAcquireFenceChanged)) {
-            continue;
-        }
-        if (s.acquireFence && s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
+        const bool acquireFenceChanged = (s.what & layer_state_t::eAcquireFenceChanged);
+        if (acquireFenceChanged && s.acquireFence &&
+            s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
             ready = false;
         }
 
         sp<Layer> layer = nullptr;
         if (s.surface) {
             layer = fromHandleLocked(s.surface).promote();
-        } else {
+        } else if (acquireFenceChanged) {
             ALOGW("Transaction with buffer, but no Layer?");
             continue;
         }
         if (!layer) {
             continue;
         }
-        if (layer->frameIsEarly(expectedPresentTime)) {
+
+        const bool frameTimelineInfoChanged = (s.what & layer_state_t::eFrameTimelineInfoChanged);
+        const auto vsyncId = frameTimelineInfoChanged ? s.frameTimelineInfo.vsyncId : info.vsyncId;
+        if (isAutoTimestamp && layer->frameIsEarly(expectedPresentTime, vsyncId)) {
             ATRACE_NAME("frameIsEarly()");
             return false;
         }
@@ -3415,11 +3416,14 @@
             ready = false;
         }
 
-        // If backpressure is enabled and we already have a buffer to commit, keep the transaction
-        // in the queue.
-        const bool hasPendingBuffer = pendingBuffers.find(s.surface) != pendingBuffers.end();
-        if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
-            ready = false;
+        if (acquireFenceChanged) {
+            // If backpressure is enabled and we already have a buffer to commit, keep the
+            // transaction in the queue.
+            const bool hasPendingBuffer = pendingBuffers.find(s.surface) != pendingBuffers.end();
+            if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
+                ready = false;
+            }
+            pendingBuffers.insert(s.surface);
         }
         pendingBuffers.insert(s.surface);
     }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 6434ca2..40d63b2 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -363,6 +363,7 @@
     friend class BufferQueueLayer;
     friend class BufferStateLayer;
     friend class Client;
+    friend class FpsReporter;
     friend class Layer;
     friend class MonitoredProducer;
     friend class RefreshRateOverlay;
@@ -627,8 +628,7 @@
     status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
                                        const sp<IRegionSamplingListener>& listener) override;
     status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
-    status_t addFpsListener(const sp<IBinder>& layerHandle,
-                            const sp<gui::IFpsListener>& listener) override;
+    status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) override;
     status_t removeFpsListener(const sp<gui::IFpsListener>& listener) override;
     status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
                                         ui::DisplayModeId displayModeId, bool allowGroupSwitching,
@@ -800,7 +800,8 @@
     void commitTransaction() REQUIRES(mStateLock);
     void commitOffscreenLayers();
     bool transactionIsReadyToBeApplied(
-            bool isAutoTimestamp, int64_t desiredPresentTime, const Vector<ComposerState>& states,
+            const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
+            const Vector<ComposerState>& states,
             std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers)
             REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
diff --git a/services/surfaceflinger/TimeStats/OWNERS b/services/surfaceflinger/TimeStats/OWNERS
index 1441f91..ded3ebb 100644
--- a/services/surfaceflinger/TimeStats/OWNERS
+++ b/services/surfaceflinger/TimeStats/OWNERS
@@ -1,2 +1 @@
 alecmouri@google.com
-zzyiwei@google.com
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 6d28e62..aa1cce2 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -18,6 +18,10 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 
+#include <sys/epoll.h>
+
+#include <gui/DisplayEventReceiver.h>
+
 #include "LayerTransactionTest.h"
 #include "utils/CallbackUtils.h"
 
@@ -30,6 +34,24 @@
 
 class LayerCallbackTest : public LayerTransactionTest {
 public:
+    void SetUp() override {
+        LayerTransactionTest::SetUp();
+
+        EXPECT_EQ(NO_ERROR, mDisplayEventReceiver.initCheck());
+
+        mEpollFd = epoll_create1(EPOLL_CLOEXEC);
+        EXPECT_GT(mEpollFd, 1);
+
+        epoll_event event;
+        event.events = EPOLLIN;
+        EXPECT_EQ(0, epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mDisplayEventReceiver.getFd(), &event));
+    }
+
+    void TearDown() override {
+        close(mEpollFd);
+        LayerTransactionTest::TearDown();
+    }
+
     virtual sp<SurfaceControl> createBufferStateLayer() {
         return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
     }
@@ -82,6 +104,35 @@
             ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
         }
     }
+
+    DisplayEventReceiver mDisplayEventReceiver;
+    int mEpollFd;
+
+    struct Vsync {
+        int64_t vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID;
+        nsecs_t expectedPresentTime = std::numeric_limits<nsecs_t>::max();
+    };
+
+    Vsync waitForNextVsync() {
+        mDisplayEventReceiver.requestNextVsync();
+        epoll_event epollEvent;
+        Vsync vsync;
+        EXPECT_EQ(1, epoll_wait(mEpollFd, &epollEvent, 1, 1000))
+                << "Timeout waiting for vsync event";
+        DisplayEventReceiver::Event event;
+        while (mDisplayEventReceiver.getEvents(&event, 1) > 0) {
+            if (event.header.type != DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+                continue;
+            }
+
+            vsync = {event.vsync.vsyncId, event.vsync.expectedVSyncTimestamp};
+        }
+
+        EXPECT_GE(vsync.vsyncId, 1);
+        EXPECT_GT(event.vsync.expectedVSyncTimestamp, systemTime());
+
+        return vsync;
+    }
 };
 
 TEST_F(LayerCallbackTest, BufferColor) {
@@ -873,6 +924,29 @@
     expected.addExpectedPresentTime(systemTime());
     EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
 }
+
+TEST_F(LayerCallbackTest, ExpectedPresentTime) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    const Vsync vsync = waitForNextVsync();
+    transaction.setFrameTimelineInfo({vsync.vsyncId, 0});
+    transaction.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    expected.addExpectedPresentTimeForVsyncId(vsync.expectedPresentTime);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
index a9e5df3..a2291b2 100644
--- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -76,7 +76,7 @@
 
     void setupScheduler();
     void setupComposer(uint32_t virtualDisplayCount);
-    sp<BufferStateLayer> createBufferStateLayer();
+    sp<BufferStateLayer> createBufferStateLayer(LayerMetadata metadata);
 
     TestableSurfaceFlinger mFlinger;
     Hwc2::mock::Composer* mComposer = nullptr;
@@ -91,7 +91,7 @@
     sp<Layer> mUnrelated;
 
     sp<TestableFpsListener> mFpsListener;
-    sp<FpsReporter> mFpsReporter = new FpsReporter(mFrameTimeline);
+    sp<FpsReporter> mFpsReporter = new FpsReporter(mFrameTimeline, *(mFlinger.flinger()));
 };
 
 FpsReporterTest::FpsReporterTest() {
@@ -110,10 +110,10 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-sp<BufferStateLayer> FpsReporterTest::createBufferStateLayer() {
+sp<BufferStateLayer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) {
     sp<Client> client;
     LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
-                           LAYER_FLAGS, LayerMetadata());
+                           LAYER_FLAGS, metadata);
     return new BufferStateLayer(args);
 }
 
@@ -154,7 +154,10 @@
 
 TEST_F(FpsReporterTest, callsListeners) {
     mParent = createBufferStateLayer();
-    mTarget = createBufferStateLayer();
+    const constexpr int32_t kTaskId = 12;
+    LayerMetadata targetMetadata;
+    targetMetadata.setInt32(METADATA_TASK_ID, kTaskId);
+    mTarget = createBufferStateLayer(targetMetadata);
     mChild = createBufferStateLayer();
     mGrandChild = createBufferStateLayer();
     mUnrelated = createBufferStateLayer();
@@ -162,6 +165,10 @@
     mTarget->addChild(mChild);
     mChild->addChild(mGrandChild);
     mParent->commitChildList();
+    mFlinger.mutableCurrentState().layersSortedByZ.add(mParent);
+    mFlinger.mutableCurrentState().layersSortedByZ.add(mTarget);
+    mFlinger.mutableCurrentState().layersSortedByZ.add(mChild);
+    mFlinger.mutableCurrentState().layersSortedByZ.add(mGrandChild);
 
     float expectedFps = 44.0;
 
@@ -170,7 +177,7 @@
                                                 mGrandChild->getSequence())))
             .WillOnce(Return(expectedFps));
 
-    mFpsReporter->addListener(mFpsListener, mTarget);
+    mFpsReporter->addListener(mFpsListener, kTaskId);
     mFpsReporter->dispatchLayerFps();
     EXPECT_EQ(expectedFps, mFpsListener->lastReportedFps);
     mFpsReporter->removeListener(mFpsListener);
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 7e6141e..f2cb951 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -40,6 +40,7 @@
 using ProtoFrameEnd = perfetto::protos::FrameTimelineEvent_FrameEnd;
 using ProtoPresentType = perfetto::protos::FrameTimelineEvent_PresentType;
 using ProtoJankType = perfetto::protos::FrameTimelineEvent_JankType;
+using ProtoPredictionType = perfetto::protos::FrameTimelineEvent_PredictionType;
 
 namespace android::frametimeline {
 
@@ -717,7 +718,7 @@
 
 ProtoActualDisplayFrameStart createProtoActualDisplayFrameStart(
         int64_t cookie, int64_t token, pid_t pid, ProtoPresentType presentType, bool onTimeFinish,
-        bool gpuComposition, ProtoJankType jankType) {
+        bool gpuComposition, ProtoJankType jankType, ProtoPredictionType predictionType) {
     ProtoActualDisplayFrameStart proto;
     proto.set_cookie(cookie);
     proto.set_token(token);
@@ -726,6 +727,7 @@
     proto.set_on_time_finish(onTimeFinish);
     proto.set_gpu_composition(gpuComposition);
     proto.set_jank_type(jankType);
+    proto.set_prediction_type(predictionType);
     return proto;
 }
 
@@ -745,7 +747,7 @@
 ProtoActualSurfaceFrameStart createProtoActualSurfaceFrameStart(
         int64_t cookie, int64_t token, int64_t displayFrameToken, pid_t pid, std::string layerName,
         ProtoPresentType presentType, bool onTimeFinish, bool gpuComposition,
-        ProtoJankType jankType) {
+        ProtoJankType jankType, ProtoPredictionType predictionType) {
     ProtoActualSurfaceFrameStart proto;
     proto.set_cookie(cookie);
     proto.set_token(token);
@@ -756,6 +758,7 @@
     proto.set_on_time_finish(onTimeFinish);
     proto.set_gpu_composition(gpuComposition);
     proto.set_jank_type(jankType);
+    proto.set_prediction_type(predictionType);
     return proto;
 }
 
@@ -796,6 +799,8 @@
     EXPECT_EQ(received.gpu_composition(), source.gpu_composition());
     ASSERT_TRUE(received.has_jank_type());
     EXPECT_EQ(received.jank_type(), source.jank_type());
+    ASSERT_TRUE(received.has_prediction_type());
+    EXPECT_EQ(received.prediction_type(), source.prediction_type());
 }
 
 void validateTraceEvent(const ProtoExpectedSurfaceFrameStart& received,
@@ -841,6 +846,8 @@
     EXPECT_EQ(received.gpu_composition(), source.gpu_composition());
     ASSERT_TRUE(received.has_jank_type());
     EXPECT_EQ(received.jank_type(), source.jank_type());
+    ASSERT_TRUE(received.has_prediction_type());
+    EXPECT_EQ(received.prediction_type(), source.prediction_type());
 }
 
 void validateTraceEvent(const ProtoFrameEnd& received, const ProtoFrameEnd& source) {
@@ -869,7 +876,8 @@
             createProtoActualDisplayFrameStart(traceCookie + 2, displayFrameToken1,
                                                kSurfaceFlingerPid,
                                                FrameTimelineEvent::PRESENT_ON_TIME, true, false,
-                                               FrameTimelineEvent::JANK_NONE);
+                                               FrameTimelineEvent::JANK_NONE,
+                                               FrameTimelineEvent::PREDICTION_VALID);
     auto protoActualDisplayFrameEnd = createProtoFrameEnd(traceCookie + 2);
 
     addEmptyDisplayFrame();
@@ -915,7 +923,7 @@
     // Packet - 3 : FrameEnd (ActualDisplayFrame)
     const auto& packet3 = packets[3];
     ASSERT_TRUE(packet3.has_timestamp());
-    EXPECT_EQ(packet3.timestamp(), 26u);
+    EXPECT_EQ(packet3.timestamp(), 31u);
     ASSERT_TRUE(packet3.has_frame_timeline_event());
 
     const auto& event3 = packet3.frame_timeline_event();
@@ -944,7 +952,8 @@
             createProtoActualDisplayFrameStart(traceCookie + 1, displayFrameToken1,
                                                kSurfaceFlingerPid,
                                                FrameTimelineEvent::PRESENT_UNSPECIFIED, false,
-                                               false, FrameTimelineEvent::JANK_UNKNOWN);
+                                               false, FrameTimelineEvent::JANK_UNKNOWN,
+                                               FrameTimelineEvent::PREDICTION_EXPIRED);
     auto protoActualDisplayFrameEnd = createProtoFrameEnd(traceCookie + 1);
 
     addEmptyDisplayFrame();
@@ -969,7 +978,7 @@
     // Packet - 1 : FrameEnd (ActualDisplayFrame)
     const auto& packet1 = packets[1];
     ASSERT_TRUE(packet1.has_timestamp());
-    EXPECT_EQ(packet1.timestamp(), 26u);
+    EXPECT_EQ(packet1.timestamp(), 31u);
     ASSERT_TRUE(packet1.has_frame_timeline_event());
 
     const auto& event1 = packet1.frame_timeline_event();
@@ -1014,7 +1023,8 @@
             createProtoActualSurfaceFrameStart(traceCookie + 2, surfaceFrameToken,
                                                displayFrameToken1, sPidOne, sLayerNameOne,
                                                FrameTimelineEvent::PRESENT_DROPPED, false, false,
-                                               FrameTimelineEvent::JANK_NONE);
+                                               FrameTimelineEvent::JANK_NONE,
+                                               FrameTimelineEvent::PREDICTION_VALID);
     auto protoDroppedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 2);
 
     auto protoPresentedSurfaceFrameExpectedStart =
@@ -1025,7 +1035,8 @@
             createProtoActualSurfaceFrameStart(traceCookie + 4, surfaceFrameToken,
                                                displayFrameToken1, sPidOne, sLayerNameOne,
                                                FrameTimelineEvent::PRESENT_ON_TIME, true, false,
-                                               FrameTimelineEvent::JANK_NONE);
+                                               FrameTimelineEvent::JANK_NONE,
+                                               FrameTimelineEvent::PREDICTION_VALID);
     auto protoPresentedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 4);
 
     // Set up the display frame
@@ -1167,7 +1178,8 @@
             createProtoActualSurfaceFrameStart(traceCookie + 1, surfaceFrameToken,
                                                displayFrameToken, sPidOne, sLayerNameOne,
                                                FrameTimelineEvent::PRESENT_UNSPECIFIED, false,
-                                               false, FrameTimelineEvent::JANK_UNKNOWN);
+                                               false, FrameTimelineEvent::JANK_UNKNOWN,
+                                               FrameTimelineEvent::PREDICTION_EXPIRED);
     auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
 
     // Set up the display frame
diff --git a/services/surfaceflinger/tests/utils/CallbackUtils.h b/services/surfaceflinger/tests/utils/CallbackUtils.h
index 1318deb..459b35c 100644
--- a/services/surfaceflinger/tests/utils/CallbackUtils.h
+++ b/services/surfaceflinger/tests/utils/CallbackUtils.h
@@ -81,6 +81,10 @@
         mExpectedPresentTime = expectedPresentTime;
     }
 
+    void addExpectedPresentTimeForVsyncId(nsecs_t expectedPresentTime) {
+        mExpectedPresentTimeForVsyncId = expectedPresentTime;
+    }
+
     void verifyCallbackData(const CallbackData& callbackData) const {
         const auto& [latchTime, presentFence, surfaceControlStats] = callbackData;
         if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) {
@@ -93,6 +97,11 @@
                 // misses vsync and we have to wait another 33.3ms
                 ASSERT_LE(presentFence->getSignalTime(),
                           mExpectedPresentTime + nsecs_t(66.666666 * 1e6));
+            } else if (mExpectedPresentTimeForVsyncId >= 0) {
+                ASSERT_EQ(presentFence->wait(3000), NO_ERROR);
+                // We give 4ms for prediction error
+                ASSERT_GE(presentFence->getSignalTime(),
+                          mExpectedPresentTimeForVsyncId - 4'000'000);
             }
         } else {
             ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented";
@@ -151,6 +160,7 @@
     };
     ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
     nsecs_t mExpectedPresentTime = -1;
+    nsecs_t mExpectedPresentTimeForVsyncId = -1;
     std::unordered_map<sp<SurfaceControl>, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults;
 };
 
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index bcd9957..537e49b 100644
--- a/services/vibratorservice/VibratorHalController.cpp
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -217,10 +217,10 @@
     return apply(performEffectFn, "performEffect");
 }
 
-HalResult<void> HalController::performComposedEffect(
+HalResult<milliseconds> HalController::performComposedEffect(
         const std::vector<CompositeEffect>& primitiveEffects,
         const std::function<void()>& completionCallback) {
-    hal_fn<void> performComposedEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
+    hal_fn<milliseconds> performComposedEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
         return hal->performComposedEffect(primitiveEffects, completionCallback);
     };
     return apply(performComposedEffectFn, "performComposedEffect");
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 7fee82f..6faab38 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -224,12 +224,45 @@
     return ret;
 }
 
-HalResult<void> AidlHalWrapper::performComposedEffect(
+HalResult<milliseconds> AidlHalWrapper::performComposedEffect(
         const std::vector<CompositeEffect>& primitiveEffects,
         const std::function<void()>& completionCallback) {
     // This method should always support callbacks, so no need to double check.
     auto cb = new HalCallbackWrapper(completionCallback);
-    return HalResult<void>::fromStatus(getHal()->compose(primitiveEffects, cb));
+    milliseconds duration(0);
+    for (const auto& effect : primitiveEffects) {
+        auto durationResult = getPrimitiveDuration(effect.primitive);
+        if (durationResult.isOk()) {
+            duration += durationResult.value();
+        }
+        duration += milliseconds(effect.delayMs);
+    }
+    return HalResult<milliseconds>::fromStatus(getHal()->compose(primitiveEffects, cb), duration);
+}
+
+HalResult<milliseconds> AidlHalWrapper::getPrimitiveDuration(CompositePrimitive primitive) {
+    std::lock_guard<std::mutex> lock(mSupportedPrimitivesMutex);
+    if (mPrimitiveDurations.empty()) {
+        constexpr auto primitiveRange = enum_range<CompositePrimitive>();
+        constexpr auto primitiveCount = std::distance(primitiveRange.begin(), primitiveRange.end());
+        mPrimitiveDurations.resize(primitiveCount);
+    }
+    auto primitiveIdx = static_cast<size_t>(primitive);
+    if (primitiveIdx >= mPrimitiveDurations.size()) {
+        // Safety check, should not happen if enum_range is correct.
+        return HalResult<milliseconds>::unsupported();
+    }
+    auto& cache = mPrimitiveDurations[primitiveIdx];
+    if (cache.has_value()) {
+        return HalResult<milliseconds>::ok(*cache);
+    }
+    int32_t duration;
+    auto result = getHal()->getPrimitiveDuration(primitive, &duration);
+    if (result.isOk()) {
+        // Cache copy of returned value.
+        cache.emplace(duration);
+    }
+    return HalResult<milliseconds>::fromStatus(result, milliseconds(duration));
 }
 
 HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() {
@@ -333,10 +366,10 @@
 }
 
 template <typename I>
-HalResult<void> HidlHalWrapper<I>::performComposedEffect(const std::vector<CompositeEffect>&,
-                                                         const std::function<void()>&) {
+HalResult<std::chrono::milliseconds> HidlHalWrapper<I>::performComposedEffect(
+        const std::vector<CompositeEffect>&, const std::function<void()>&) {
     ALOGV("Skipped composed effect because Vibrator HAL AIDL is not available");
-    return HalResult<void>::unsupported();
+    return HalResult<std::chrono::milliseconds>::unsupported();
 }
 
 template <typename I>
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index c405545..16d571d 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -77,7 +77,7 @@
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
             const std::function<void()>& completionCallback) final override;
 
-    HalResult<void> performComposedEffect(
+    HalResult<std::chrono::milliseconds> performComposedEffect(
             const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
             const std::function<void()>& completionCallback) final override;
 
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 638b483..e22ad34 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -189,7 +189,7 @@
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
             const std::function<void()>& completionCallback) = 0;
 
-    virtual HalResult<void> performComposedEffect(
+    virtual HalResult<std::chrono::milliseconds> performComposedEffect(
             const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
             const std::function<void()>& completionCallback) = 0;
 
@@ -236,7 +236,7 @@
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
             const std::function<void()>& completionCallback) override final;
 
-    HalResult<void> performComposedEffect(
+    HalResult<std::chrono::milliseconds> performComposedEffect(
             const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
             const std::function<void()>& completionCallback) override final;
 
@@ -252,6 +252,12 @@
             GUARDED_BY(mSupportedEffectsMutex);
     std::optional<std::vector<hardware::vibrator::CompositePrimitive>> mSupportedPrimitives
             GUARDED_BY(mSupportedPrimitivesMutex);
+    std::vector<std::optional<std::chrono::milliseconds>> mPrimitiveDurations
+            GUARDED_BY(mSupportedPrimitivesMutex);
+
+    // Loads and caches from IVibrator.
+    HalResult<std::chrono::milliseconds> getPrimitiveDuration(
+            hardware::vibrator::CompositePrimitive primitive);
 
     // Loads directly from IVibrator handle, skipping caches.
     HalResult<Capabilities> getCapabilitiesInternal();
@@ -287,7 +293,7 @@
     HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
             override final;
 
-    HalResult<void> performComposedEffect(
+    HalResult<std::chrono::milliseconds> performComposedEffect(
             const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
             const std::function<void()>& completionCallback) override final;
 
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index 2d9d0d6..c4b39ed 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -71,7 +71,7 @@
                 (Effect effect, EffectStrength strength,
                  const std::function<void()>& completionCallback),
                 (override));
-    MOCK_METHOD(vibrator::HalResult<void>, performComposedEffect,
+    MOCK_METHOD(vibrator::HalResult<milliseconds>, performComposedEffect,
                 (const std::vector<CompositeEffect>& primitiveEffects,
                  const std::function<void()>& completionCallback),
                 (override));
@@ -143,7 +143,7 @@
                 .WillRepeatedly(Return(durationResult));
         EXPECT_CALL(*mMockHal.get(), performComposedEffect(Eq(compositeEffects), _))
                 .Times(Exactly(cardinality))
-                .WillRepeatedly(Return(voidResult));
+                .WillRepeatedly(Return(durationResult));
 
         if (cardinality > 1) {
             // One reconnection call after each failure.
@@ -208,7 +208,10 @@
     ASSERT_TRUE(performEffectResult.isOk());
     ASSERT_EQ(100ms, performEffectResult.value());
 
-    ASSERT_TRUE(mController->performComposedEffect(compositeEffects, []() {}).isOk());
+    auto performComposedEffectResult =
+            mController->performComposedEffect(compositeEffects, []() {});
+    ASSERT_TRUE(performComposedEffectResult.isOk());
+    ASSERT_EQ(100ms, performComposedEffectResult.value());
 
     ASSERT_EQ(1, mConnectCounter);
 }
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index f85fa10..8b5caa5 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -506,10 +506,21 @@
         EXPECT_CALL(*mMockHal.get(), compose(Eq(emptyEffects), _))
                 .Times(Exactly(1))
                 .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+
+        EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::CLICK), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(1), Return(Status())));
         EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _))
                 .Times(Exactly(1))
                 .WillRepeatedly(Return(
                         Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)));
+
+        EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
+        EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
+                .Times(Exactly(1))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(3), Return(Status())));
         EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
                 .Times(Exactly(1))
                 .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)));
@@ -520,6 +531,7 @@
 
     auto result = mWrapper->performComposedEffect(emptyEffects, callback);
     ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(0ms, result.value());
     ASSERT_EQ(1, *callbackCounter.get());
 
     result = mWrapper->performComposedEffect(singleEffect, callback);
@@ -532,3 +544,40 @@
     // Callback not triggered on failure
     ASSERT_EQ(1, *callbackCounter.get());
 }
+
+TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedCachesPrimitiveDurationsAndIgnoresFailures) {
+    std::vector<CompositeEffect> multipleEffects;
+    multipleEffects.push_back(
+            vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 10ms, 0.5f));
+    multipleEffects.push_back(
+            vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 100ms, 1.0f));
+
+    EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::SPIN), _))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<1>(1), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::THUD), _))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<1>(2), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _))
+            .Times(Exactly(3))
+            .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status())));
+
+    std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
+    auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+
+    auto result = mWrapper->performComposedEffect(multipleEffects, callback);
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(111ms, result.value()); // Failed primitive duration counted as 0.
+    ASSERT_EQ(1, *callbackCounter.get());
+
+    result = mWrapper->performComposedEffect(multipleEffects, callback);
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(113ms, result.value()); // Second fetch succeeds and returns primitive duration.
+    ASSERT_EQ(2, *callbackCounter.get());
+
+    result = mWrapper->performComposedEffect(multipleEffects, callback);
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(113ms, result.value()); // Cached durations not fetched again, same duration returned.
+    ASSERT_EQ(3, *callbackCounter.get());
+}
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index cb845a0..020b520 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1093,13 +1093,6 @@
         return VK_ERROR_SURFACE_LOST_KHR;
     }
 
-    err = native_window_set_buffer_count(window, 0);
-    if (err != android::OK) {
-        ALOGE("native_window_set_buffer_count(0) failed: %s (%d)",
-              strerror(-err), err);
-        return VK_ERROR_SURFACE_LOST_KHR;
-    }
-
     int swap_interval =
         create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? 0 : 1;
     err = window->setSwapInterval(window, swap_interval);
@@ -1707,7 +1700,7 @@
                 if (err != android::OK) {
                     ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
                     swapchain_result = WorstPresentResult(
-                        swapchain_result, VK_ERROR_OUT_OF_DATE_KHR);
+                        swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
                 } else {
                     if (img.dequeue_fence >= 0) {
                         close(img.dequeue_fence);