Merge "Implement SkiaGLRenderEngine::cleanFramebufferCache" into sc-dev
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 3184843..cedff0b 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -62,7 +62,7 @@
 
 using std::string;
 
-#define MAX_SYS_FILES 11
+#define MAX_SYS_FILES 12
 
 const char* k_traceTagsProperty = "debug.atrace.tags.enableflags";
 const char* k_userInitiatedTraceProperty = "debug.atrace.user_initiated";
@@ -177,6 +177,7 @@
         { OPT,      "events/power/suspend_resume/enable" },
         { OPT,      "events/cpuhp/cpuhp_enter/enable" },
         { OPT,      "events/cpuhp/cpuhp_exit/enable" },
+        { OPT,      "events/cpuhp/cpuhp_pause/enable" },
     } },
     { "membus",     "Memory Bus Utilization", 0, {
         { REQ,      "events/memory_bus/enable" },
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 006e532..37fc9a9 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -57,6 +57,8 @@
     chmod 0666 /sys/kernel/tracing/events/cpuhp/cpuhp_enter/enable
     chmod 0666 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_exit/enable
     chmod 0666 /sys/kernel/tracing/events/cpuhp/cpuhp_exit/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_pause/enable
+    chmod 0666 /sys/kernel/tracing/events/cpuhp/cpuhp_pause/enable
     chmod 0666 /sys/kernel/debug/tracing/events/power/gpu_frequency/enable
     chmod 0666 /sys/kernel/tracing/events/power/gpu_frequency/enable
     chmod 0666 /sys/kernel/debug/tracing/events/power/suspend_resume/enable
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index a999c9f..7ab2a8d 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -3027,11 +3027,14 @@
 }
 
 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());
+    // Currently WindowManagerService and InputMethodManagerSerivice support WinScope protocol.
+    for (const auto& service : {"window", "input_method"}) {
+        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", service, "tracing", "save-for-bugreport"},
+            CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
+    }
 }
 
 void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) {
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index c62d302..0712c0a 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -411,7 +411,6 @@
 }
 
 TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) {
-    SectionExists("HIGH connectivity", /* bytes= */ 3000);
     SectionExists("connectivity", /* bytes= */ 5000);
 }
 
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 3c53c02..bbbe6f7 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -495,7 +495,7 @@
 
     Rect r = Rect(cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
             cc.rectangle().bottom());
-    t.setCrop_legacy(mLayers[id], r);
+    t.setCrop(mLayers[id], r);
 }
 
 void Replayer::setCornerRadius(SurfaceComposerClient::Transaction& t,
diff --git a/include/android/input.h b/include/android/input.h
index 7973487..bb98beb 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -166,6 +166,9 @@
 
     /** Capture event */
     AINPUT_EVENT_TYPE_CAPTURE = 4,
+
+    /** Drag event */
+    AINPUT_EVENT_TYPE_DRAG = 5,
 };
 
 /**
@@ -675,7 +678,7 @@
     /**
      * Axis constant: The movement of y position of a motion event.
      *
-     * Same as {@link RELATIVE_X}, but for y position.
+     * Same as {@link AMOTION_EVENT_AXIS_RELATIVE_X}, but for y position.
      */
     AMOTION_EVENT_AXIS_RELATIVE_Y = 28,
     /**
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 6447844..92b79c7 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -448,12 +448,14 @@
             float           data[16];
             ASensorVector   vector;
             ASensorVector   acceleration;
+            ASensorVector   gyro;
             ASensorVector   magnetic;
             float           temperature;
             float           distance;
             float           light;
             float           pressure;
             float           relative_humidity;
+            AUncalibratedEvent uncalibrated_acceleration;
             AUncalibratedEvent uncalibrated_gyro;
             AUncalibratedEvent uncalibrated_magnetic;
             AMetaDataEvent meta_data;
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index b836581..c17f822 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -467,3 +467,5 @@
 __END_DECLS
 
 #endif // ANDROID_SURFACE_CONTROL_H
+
+/** @} */
diff --git a/include/android/surface_texture.h b/include/android/surface_texture.h
index b227b32..4757254 100644
--- a/include/android/surface_texture.h
+++ b/include/android/surface_texture.h
@@ -176,3 +176,5 @@
 __END_DECLS
 
 #endif /* ANDROID_NATIVE_SURFACE_TEXTURE_H */
+
+/** @} */
diff --git a/include/android/surface_texture_jni.h b/include/android/surface_texture_jni.h
index e40686d..8448d8c 100644
--- a/include/android/surface_texture_jni.h
+++ b/include/android/surface_texture_jni.h
@@ -53,3 +53,5 @@
 __END_DECLS
 
 #endif /* ANDROID_NATIVE_SURFACE_TEXTURE_JNI_H */
+
+/** @} */
diff --git a/include/input/Input.h b/include/input/Input.h
index aa42db8..f9fe6b9 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -792,6 +792,30 @@
     bool mPointerCaptureEnabled;
 };
 
+/*
+ * Drag events.
+ */
+class DragEvent : public InputEvent {
+public:
+    virtual ~DragEvent() {}
+
+    virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_DRAG; }
+
+    inline bool isExiting() const { return mIsExiting; }
+
+    inline float getX() const { return mX; }
+
+    inline float getY() const { return mY; }
+
+    void initialize(int32_t id, float x, float y, bool isExiting);
+
+    void initialize(const DragEvent& from);
+
+protected:
+    bool mIsExiting;
+    float mX, mY;
+};
+
 /**
  * Base class for verified events.
  * Do not create a VerifiedInputEvent explicitly.
@@ -855,6 +879,7 @@
     virtual MotionEvent* createMotionEvent() = 0;
     virtual FocusEvent* createFocusEvent() = 0;
     virtual CaptureEvent* createCaptureEvent() = 0;
+    virtual DragEvent* createDragEvent() = 0;
 };
 
 /*
@@ -870,12 +895,14 @@
     virtual MotionEvent* createMotionEvent() override { return &mMotionEvent; }
     virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; }
     virtual CaptureEvent* createCaptureEvent() override { return &mCaptureEvent; }
+    virtual DragEvent* createDragEvent() override { return &mDragEvent; }
 
 private:
     KeyEvent mKeyEvent;
     MotionEvent mMotionEvent;
     FocusEvent mFocusEvent;
     CaptureEvent mCaptureEvent;
+    DragEvent mDragEvent;
 };
 
 /*
@@ -890,6 +917,7 @@
     virtual MotionEvent* createMotionEvent() override;
     virtual FocusEvent* createFocusEvent() override;
     virtual CaptureEvent* createCaptureEvent() override;
+    virtual DragEvent* createDragEvent() override;
 
     void recycle(InputEvent* event);
 
@@ -900,6 +928,7 @@
     std::queue<std::unique_ptr<MotionEvent>> mMotionEventPool;
     std::queue<std::unique_ptr<FocusEvent>> mFocusEventPool;
     std::queue<std::unique_ptr<CaptureEvent>> mCaptureEventPool;
+    std::queue<std::unique_ptr<DragEvent>> mDragEventPool;
 };
 
 } // namespace android
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index ba9ae20..3e5674e 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/result.h>
 #include <android-base/unique_fd.h>
 
 #include <binder/IBinder.h>
@@ -69,6 +70,7 @@
         FINISHED,
         FOCUS,
         CAPTURE,
+        DRAG,
     };
 
     struct Header {
@@ -183,6 +185,16 @@
 
             inline size_t size() const { return sizeof(Capture); }
         } capture;
+
+        struct Drag {
+            int32_t eventId;
+            float x;
+            float y;
+            bool isExiting;
+            uint8_t empty[3];
+
+            inline size_t size() const { return sizeof(Drag); }
+        } drag;
     } __attribute__((aligned(8))) body;
 
     bool isValid(size_t actualSize) const;
@@ -354,20 +366,33 @@
      */
     status_t publishCaptureEvent(uint32_t seq, int32_t eventId, bool pointerCaptureEnabled);
 
-    /* Receives the finished signal from the consumer in reply to the original dispatch signal.
-     * If a signal was received, returns the message sequence number,
-     * whether the consumer handled the message, and the time the event was first read by the
-     * consumer.
-     *
-     * The returned sequence number is never 0 unless the operation failed.
+    /* Publishes a drag event to the input channel.
      *
      * Returns OK on success.
-     * Returns WOULD_BLOCK if there is no signal present.
+     * Returns WOULD_BLOCK if the channel is full.
      * Returns DEAD_OBJECT if the channel's peer has been closed.
      * Other errors probably indicate that the channel is broken.
      */
-    status_t receiveFinishedSignal(
-            const std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)>& callback);
+    status_t publishDragEvent(uint32_t seq, int32_t eventId, float x, float y, bool isExiting);
+
+    struct Finished {
+        uint32_t seq;
+        bool handled;
+        nsecs_t consumeTime;
+    };
+
+    /* Receives the finished signal from the consumer in reply to the original dispatch signal.
+     * If a signal was received, returns a Finished object.
+     *
+     * The returned sequence number is never 0 unless the operation failed.
+     *
+     * Returned error codes:
+     *         OK on success.
+     *         WOULD_BLOCK if there is no signal present.
+     *         DEAD_OBJECT if the channel's peer has been closed.
+     *         Other errors probably indicate that the channel is broken.
+     */
+    android::base::Result<Finished> receiveFinishedSignal();
 
 private:
     std::shared_ptr<InputChannel> mChannel;
@@ -601,6 +626,7 @@
     static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg);
     static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg);
     static void initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg);
+    static void initializeDragEvent(DragEvent* event, const InputMessage* msg);
     static void addSample(MotionEvent* event, const InputMessage* msg);
     static bool canAddSample(const Batch& batch, const InputMessage* msg);
     static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 6df04f0..a17e482 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -214,6 +214,7 @@
         "-misc-redundant-expression",
         "-misc-unused-using-decls",
         "performance*",
+        "-performance-no-int-to-ptr",
         "portability*",
     ],
 
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index f1085cf..0386d66 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -138,7 +138,11 @@
         OP_RECORD_AUDIO_HOTWORD = 102,
         // Ops 103-105 are currently unused in native, and intentionally omitted
         OP_RECORD_AUDIO_OUTPUT = 106,
-        _NUM_OP = 107
+        OP_SCHEDULE_EXACT_ALARM = 107,
+        OP_FINE_LOCATION_SOURCE = 108,
+        OP_COARSE_LOCATION_SOURCE = 109,
+        OP_MANAGE_MEDIA = 110,
+        _NUM_OP = 111
     };
 
     AppOpsManager();
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 05eb64b..6c44726 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -82,7 +82,10 @@
      */
     template <class T, class... Args>
     static std::shared_ptr<T> make(Args&&... args) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
         T* t = new T(std::forward<Args>(args)...);
+#pragma clang diagnostic pop
         // warning: Potential leak of memory pointed to by 't' [clang-analyzer-unix.Malloc]
         return t->template ref<T>();  // NOLINT(clang-analyzer-unix.Malloc)
     }
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index d53a88f..3899d47 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -33,13 +33,13 @@
 
 /// Binder action to perform.
 ///
-/// This must be a number between [`IBinder::FIRST_CALL_TRANSACTION`] and
-/// [`IBinder::LAST_CALL_TRANSACTION`].
+/// This must be a number between [`FIRST_CALL_TRANSACTION`] and
+/// [`LAST_CALL_TRANSACTION`].
 pub type TransactionCode = u32;
 
 /// Additional operation flags.
 ///
-/// `IBinder::FLAG_*` values.
+/// `FLAG_*` values.
 pub type TransactionFlags = u32;
 
 /// Super-trait for Binder interfaces.
@@ -85,20 +85,22 @@
     fn get_class() -> InterfaceClass;
 }
 
-/// Interface of binder local or remote objects.
+/// First transaction code available for user commands (inclusive)
+pub const FIRST_CALL_TRANSACTION: TransactionCode = sys::FIRST_CALL_TRANSACTION;
+/// Last transaction code available for user commands (inclusive)
+pub const LAST_CALL_TRANSACTION: TransactionCode = sys::LAST_CALL_TRANSACTION;
+
+/// Corresponds to TF_ONE_WAY -- an asynchronous call.
+pub const FLAG_ONEWAY: TransactionFlags = sys::FLAG_ONEWAY;
+/// Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call is made.
+pub const FLAG_CLEAR_BUF: TransactionFlags = sys::FLAG_CLEAR_BUF;
+
+/// Internal interface of binder local or remote objects for making
+/// transactions.
 ///
-/// This trait corresponds to the interface of the C++ `IBinder` class.
-pub trait IBinder {
-    /// First transaction code available for user commands (inclusive)
-    const FIRST_CALL_TRANSACTION: TransactionCode = sys::FIRST_CALL_TRANSACTION;
-    /// Last transaction code available for user commands (inclusive)
-    const LAST_CALL_TRANSACTION: TransactionCode = sys::LAST_CALL_TRANSACTION;
-
-    /// Corresponds to TF_ONE_WAY -- an asynchronous call.
-    const FLAG_ONEWAY: TransactionFlags = sys::FLAG_ONEWAY;
-    /// Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call is made.
-    const FLAG_CLEAR_BUF: TransactionFlags = sys::FLAG_CLEAR_BUF;
-
+/// This trait corresponds to the parts of the interface of the C++ `IBinder`
+/// class which are internal implementation details.
+pub trait IBinderInternal: IBinder {
     /// Is this object still alive?
     fn is_binder_alive(&self) -> bool;
 
@@ -122,19 +124,24 @@
     /// * `data` - [`Parcel`] with input data
     /// * `reply` - Optional [`Parcel`] for reply data
     /// * `flags` - Transaction flags, e.g. marking the transaction as
-    /// asynchronous ([`FLAG_ONEWAY`](IBinder::FLAG_ONEWAY))
+    ///   asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY))
     fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
         &self,
         code: TransactionCode,
         flags: TransactionFlags,
         input_callback: F,
     ) -> Result<Parcel>;
+}
 
+/// Interface of binder local or remote objects.
+///
+/// This trait corresponds to the parts of the interface of the C++ `IBinder`
+/// class which are public.
+pub trait IBinder {
     /// Register the recipient for a notification if this binder
     /// goes away. If this binder object unexpectedly goes away
     /// (typically because its hosting process has been killed),
-    /// then DeathRecipient::binder_died() will be called with a reference
-    /// to this.
+    /// then the `DeathRecipient`'s callback will be called.
     ///
     /// You will only receive death notifications for remote binders,
     /// as local binders by definition can't die without you dying as well.
@@ -142,11 +149,6 @@
     /// INVALID_OPERATION code being returned and nothing happening.
     ///
     /// This link always holds a weak reference to its recipient.
-    ///
-    /// You will only receive a weak reference to the dead
-    /// binder. You should not try to promote this to a strong reference.
-    /// (Nor should you need to, as there is nothing useful you can
-    /// directly do with it now that it has passed on.)
     fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>;
 
     /// Remove a previously registered death notification.
@@ -222,7 +224,8 @@
             // the number of u16 elements before the null terminator.
 
             let raw_descriptor: *const c_char = sys::AIBinder_Class_getDescriptor(self.0);
-            CStr::from_ptr(raw_descriptor).to_str()
+            CStr::from_ptr(raw_descriptor)
+                .to_str()
                 .expect("Expected valid UTF-8 string from AIBinder_Class_getDescriptor")
                 .into()
         }
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 43a237a..5bbd2a3 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -107,8 +107,9 @@
 pub mod parcel;
 
 pub use crate::binder::{
-    FromIBinder, IBinder, Interface, InterfaceClass, Remotable, Strong, TransactionCode,
-    TransactionFlags, Weak,
+    FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Remotable, Strong,
+    TransactionCode, TransactionFlags, Weak, FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY,
+    LAST_CALL_TRANSACTION,
 };
 pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
 pub use native::add_service;
@@ -123,8 +124,8 @@
     pub use super::parcel::ParcelFileDescriptor;
     pub use super::{add_service, get_interface};
     pub use super::{
-        ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode, Strong, ThreadState,
-        Weak, WpIBinder,
+        DeathRecipient, ExceptionCode, IBinder, Interface, ProcessState, SpIBinder, Status,
+        StatusCode, Strong, ThreadState, Weak, WpIBinder,
     };
 
     /// Binder result containing a [`Status`] on error.
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 132e075..52036f5 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -17,7 +17,8 @@
 //! Rust API for interacting with a remote binder service.
 
 use crate::binder::{
-    AsNative, FromIBinder, IBinder, Interface, InterfaceClass, Strong, TransactionCode, TransactionFlags,
+    AsNative, FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Strong,
+    TransactionCode, TransactionFlags,
 };
 use crate::error::{status_result, Result, StatusCode};
 use crate::parcel::{
@@ -26,8 +27,8 @@
 };
 use crate::sys;
 
-use std::convert::TryInto;
 use std::cmp::Ordering;
+use std::convert::TryInto;
 use std::ffi::{c_void, CString};
 use std::fmt;
 use std::os::unix::io::AsRawFd;
@@ -211,7 +212,7 @@
     }
 }
 
-impl<T: AsNative<sys::AIBinder>> IBinder for T {
+impl<T: AsNative<sys::AIBinder>> IBinderInternal for T {
     /// Perform a binder transaction
     fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
         &self,
@@ -300,9 +301,7 @@
     }
 
     fn set_requesting_sid(&mut self, enable: bool) {
-        unsafe {
-            sys::AIBinder_setRequestingSid(self.as_native_mut(), enable)
-        };
+        unsafe { sys::AIBinder_setRequestingSid(self.as_native_mut(), enable) };
     }
 
     fn dump<F: AsRawFd>(&mut self, fp: &F, args: &[&str]) -> Result<()> {
@@ -351,7 +350,9 @@
         status_result(status)?;
         Ok(ibinder)
     }
+}
 
+impl<T: AsNative<sys::AIBinder>> IBinder for T {
     fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> {
         status_result(unsafe {
             // Safety: `SpIBinder` guarantees that `self` always contains a
@@ -472,7 +473,10 @@
             // WpIBinder object from it.
             sys::AIBinder_Weak_clone(self.0)
         };
-        assert!(!ptr.is_null(), "Unexpected null pointer from AIBinder_Weak_clone");
+        assert!(
+            !ptr.is_null(),
+            "Unexpected null pointer from AIBinder_Weak_clone"
+        );
         Self(ptr)
     }
 }
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 719229c..60e3502 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -18,7 +18,10 @@
 
 use binder::declare_binder_interface;
 use binder::parcel::Parcel;
-use binder::{Binder, IBinder, Interface, SpIBinder, StatusCode, ThreadState, TransactionCode};
+use binder::{
+    Binder, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode,
+    FIRST_CALL_TRANSACTION,
+};
 use std::convert::{TryFrom, TryInto};
 
 /// Name of service runner.
@@ -83,7 +86,7 @@
 
 #[repr(u32)]
 enum TestTransactionCode {
-    Test = SpIBinder::FIRST_CALL_TRANSACTION,
+    Test = FIRST_CALL_TRANSACTION,
     GetSelinuxContext,
 }
 
@@ -196,7 +199,6 @@
 
 impl ITestSameDescriptor for Binder<BnTestSameDescriptor> {}
 
-
 #[cfg(test)]
 mod tests {
     use selinux_bindgen as selinux_sys;
@@ -209,9 +211,12 @@
     use std::thread;
     use std::time::Duration;
 
-    use binder::{Binder, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode, Strong};
+    use binder::{
+        Binder, DeathRecipient, FromIBinder, IBinder, IBinderInternal, Interface, SpIBinder,
+        StatusCode, Strong,
+    };
 
-    use super::{BnTest, ITest, ITestSameDescriptor, RUST_SERVICE_BINARY, TestService};
+    use super::{BnTest, ITest, ITestSameDescriptor, TestService, RUST_SERVICE_BINARY};
 
     pub struct ScopedServiceProcess(Child);
 
@@ -290,7 +295,9 @@
         };
         assert_eq!(
             test_client.get_selinux_context().unwrap(),
-            expected_context.to_str().expect("context was invalid UTF-8"),
+            expected_context
+                .to_str()
+                .expect("context was invalid UTF-8"),
         );
     }
 
@@ -479,18 +486,22 @@
 
         // This should succeed although we will have to treat the service as
         // remote.
-        let _interface: Strong<dyn ITestSameDescriptor> = FromIBinder::try_from(service.as_binder())
-            .expect("Could not re-interpret service as the ITestSameDescriptor interface");
+        let _interface: Strong<dyn ITestSameDescriptor> =
+            FromIBinder::try_from(service.as_binder())
+                .expect("Could not re-interpret service as the ITestSameDescriptor interface");
     }
 
     /// Test that we can round-trip a rust service through a generic IBinder
     #[test]
     fn reassociate_rust_binder() {
         let service_name = "testing_service";
-        let service_ibinder = BnTest::new_binder(TestService { s: service_name.to_string() })
-            .as_binder();
+        let service_ibinder = BnTest::new_binder(TestService {
+            s: service_name.to_string(),
+        })
+        .as_binder();
 
-        let service: Strong<dyn ITest> = service_ibinder.into_interface()
+        let service: Strong<dyn ITest> = service_ibinder
+            .into_interface()
             .expect("Could not reassociate the generic ibinder");
 
         assert_eq!(service.test().unwrap(), service_name);
@@ -499,7 +510,9 @@
     #[test]
     fn weak_binder_upgrade() {
         let service_name = "testing_service";
-        let service = BnTest::new_binder(TestService { s: service_name.to_string() });
+        let service = BnTest::new_binder(TestService {
+            s: service_name.to_string(),
+        });
 
         let weak = Strong::downgrade(&service);
 
@@ -512,7 +525,9 @@
     fn weak_binder_upgrade_dead() {
         let service_name = "testing_service";
         let weak = {
-            let service = BnTest::new_binder(TestService { s: service_name.to_string() });
+            let service = BnTest::new_binder(TestService {
+                s: service_name.to_string(),
+            });
 
             Strong::downgrade(&service)
         };
@@ -523,7 +538,9 @@
     #[test]
     fn weak_binder_clone() {
         let service_name = "testing_service";
-        let service = BnTest::new_binder(TestService { s: service_name.to_string() });
+        let service = BnTest::new_binder(TestService {
+            s: service_name.to_string(),
+        });
 
         let weak = Strong::downgrade(&service);
         let cloned = weak.clone();
@@ -539,8 +556,12 @@
     #[test]
     #[allow(clippy::eq_op)]
     fn binder_ord() {
-        let service1 = BnTest::new_binder(TestService { s: "testing_service1".to_string() });
-        let service2 = BnTest::new_binder(TestService { s: "testing_service2".to_string() });
+        let service1 = BnTest::new_binder(TestService {
+            s: "testing_service1".to_string(),
+        });
+        let service2 = BnTest::new_binder(TestService {
+            s: "testing_service2".to_string(),
+        });
 
         assert!(!(service1 < service1));
         assert!(!(service1 > service1));
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 82c9268..f778232 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -169,8 +169,6 @@
 
     mNumAcquired = 0;
     mNumFrameAvailable = 0;
-    mPendingReleaseItem.item = BufferItem();
-    mPendingReleaseItem.releaseFence = nullptr;
 }
 
 BLASTBufferQueue::~BLASTBufferQueue() {
@@ -242,7 +240,6 @@
         std::unique_lock _lock{mMutex};
         ATRACE_CALL();
         BQA_LOGV("transactionCallback");
-        mInitialCallbackReceived = true;
 
         if (!stats.empty()) {
             mTransformHint = stats[0].transformHint;
@@ -255,38 +252,20 @@
                                             stats[0].frameEventStats.compositorTiming,
                                             stats[0].latchTime,
                                             stats[0].frameEventStats.dequeueReadyTime);
-        }
-        if (mPendingReleaseItem.item.mGraphicBuffer != nullptr) {
-            if (!stats.empty()) {
-                mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
-            } else {
-                BQA_LOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
-                mPendingReleaseItem.releaseFence = nullptr;
+            currFrameNumber = stats[0].frameEventStats.frameNumber;
+
+            if (mTransactionCompleteCallback &&
+                currFrameNumber >= mTransactionCompleteFrameNumber) {
+                if (currFrameNumber > mTransactionCompleteFrameNumber) {
+                    BQA_LOGE("transactionCallback received for a newer framenumber=%" PRIu64
+                             " than expected=%" PRIu64,
+                             currFrameNumber, mTransactionCompleteFrameNumber);
+                }
+                transactionCompleteCallback = std::move(mTransactionCompleteCallback);
+                mTransactionCompleteFrameNumber = 0;
             }
-            mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item,
-                                               mPendingReleaseItem.releaseFence
-                                                       ? mPendingReleaseItem.releaseFence
-                                                       : Fence::NO_FENCE);
-            mNumAcquired--;
-            mPendingReleaseItem.item = BufferItem();
-            mPendingReleaseItem.releaseFence = nullptr;
         }
 
-        if (mSubmitted.empty()) {
-            BQA_LOGE("ERROR: callback with no corresponding submitted buffer item");
-        }
-        mPendingReleaseItem.item = std::move(mSubmitted.front());
-        mSubmitted.pop();
-
-        processNextBufferLocked(false /* useNextTransaction */);
-
-        currFrameNumber = mPendingReleaseItem.item.mFrameNumber;
-        if (mTransactionCompleteCallback && mTransactionCompleteFrameNumber == currFrameNumber) {
-            transactionCompleteCallback = std::move(mTransactionCompleteCallback);
-            mTransactionCompleteFrameNumber = 0;
-        }
-
-        mCallbackCV.notify_all();
         decStrong((void*)transactionCallbackThunk);
     }
 
@@ -295,15 +274,46 @@
     }
 }
 
+// Unlike transactionCallbackThunk the release buffer callback does not extend the life of the
+// BBQ. This is because if the BBQ is destroyed, then the buffers will be released by the client.
+// So we pass in a weak pointer to the BBQ and if it still alive, then we release the buffer.
+// Otherwise, this is a no-op.
+static void releaseBufferCallbackThunk(wp<BLASTBufferQueue> context, uint64_t graphicBufferId,
+                                       const sp<Fence>& releaseFence) {
+    sp<BLASTBufferQueue> blastBufferQueue = context.promote();
+    ALOGV("releaseBufferCallbackThunk graphicBufferId=%" PRIu64 " blastBufferQueue=%s",
+          graphicBufferId, blastBufferQueue ? "alive" : "dead");
+    if (blastBufferQueue) {
+        blastBufferQueue->releaseBufferCallback(graphicBufferId, releaseFence);
+    }
+}
+
+void BLASTBufferQueue::releaseBufferCallback(uint64_t graphicBufferId,
+                                             const sp<Fence>& releaseFence) {
+    ATRACE_CALL();
+    std::unique_lock _lock{mMutex};
+    BQA_LOGV("releaseBufferCallback graphicBufferId=%" PRIu64, graphicBufferId);
+
+    auto it = mSubmitted.find(graphicBufferId);
+    if (it == mSubmitted.end()) {
+        BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %" PRIu64,
+                 graphicBufferId);
+        return;
+    }
+
+    mBufferItemConsumer->releaseBuffer(it->second, releaseFence);
+    mSubmitted.erase(it);
+    mNumAcquired--;
+    processNextBufferLocked(false /* useNextTransaction */);
+    mCallbackCV.notify_all();
+}
+
 void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
     ATRACE_CALL();
-    BQA_LOGV("processNextBufferLocked useNextTransaction=%s", toString(useNextTransaction));
-
     // If the next transaction is set, we want to guarantee the our acquire will not fail, so don't
     // include the extra buffer when checking if we can acquire the next buffer.
     const bool includeExtraAcquire = !useNextTransaction;
     if (mNumFrameAvailable == 0 || maxBuffersAcquired(includeExtraAcquire)) {
-        BQA_LOGV("processNextBufferLocked waiting for frame available or callback");
         mCallbackCV.notify_all();
         return;
     }
@@ -353,7 +363,7 @@
     }
 
     mNumAcquired++;
-    mSubmitted.push(bufferItem);
+    mSubmitted[buffer->getId()] = bufferItem;
 
     bool needsDisconnect = false;
     mBufferItemConsumer->getConnectionEvents(bufferItem.mFrameNumber, &needsDisconnect);
@@ -369,7 +379,10 @@
     mLastBufferScalingMode = bufferItem.mScalingMode;
     mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
 
-    t->setBuffer(mSurfaceControl, buffer);
+    auto releaseBufferCallback =
+            std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
+                      std::placeholders::_1, std::placeholders::_2);
+    t->setBuffer(mSurfaceControl, buffer, releaseBufferCallback);
     t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
     t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
     t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage);
@@ -427,9 +440,12 @@
     }
 
     BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
-             " applyTransaction=%s mTimestamp=%" PRId64 " mPendingTransactions.size=%d",
+             " applyTransaction=%s mTimestamp=%" PRId64 "%s mPendingTransactions.size=%d"
+             " graphicBufferId=%" PRIu64,
              mSize.width, mSize.height, bufferItem.mFrameNumber, toString(applyTransaction),
-             bufferItem.mTimestamp, static_cast<uint32_t>(mPendingTransactions.size()));
+             bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp ? "(auto)" : "",
+             static_cast<uint32_t>(mPendingTransactions.size()),
+             bufferItem.mGraphicBuffer->getId());
 }
 
 Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
@@ -444,18 +460,17 @@
     std::unique_lock _lock{mMutex};
 
     const bool nextTransactionSet = mNextTransaction != nullptr;
-    BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s mFlushShadowQueue=%s",
-             item.mFrameNumber, toString(nextTransactionSet), toString(mFlushShadowQueue));
-
-    if (nextTransactionSet || mFlushShadowQueue) {
+    if (nextTransactionSet) {
         while (mNumFrameAvailable > 0 || maxBuffersAcquired(false /* includeExtraAcquire */)) {
             BQA_LOGV("waiting in onFrameAvailable...");
             mCallbackCV.wait(_lock);
         }
     }
-    mFlushShadowQueue = false;
     // add to shadow queue
     mNumFrameAvailable++;
+
+    BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber,
+             toString(nextTransactionSet));
     processNextBufferLocked(nextTransactionSet /* useNextTransaction */);
 }
 
@@ -514,14 +529,12 @@
 }
 
 // Check if we have acquired the maximum number of buffers.
-// As a special case, we wait for the first callback before acquiring the second buffer so we
-// can ensure the first buffer is presented if multiple buffers are queued in succession.
 // Consumer can acquire an additional buffer if that buffer is not droppable. Set
 // includeExtraAcquire is true to include this buffer to the count. Since this depends on the state
 // of the buffer, the next acquire may return with NO_BUFFER_AVAILABLE.
 bool BLASTBufferQueue::maxBuffersAcquired(bool includeExtraAcquire) const {
     int maxAcquiredBuffers = MAX_ACQUIRED_BUFFERS + (includeExtraAcquire ? 2 : 1);
-    return mNumAcquired == maxAcquiredBuffers || (!mInitialCallbackReceived && mNumAcquired == 1);
+    return mNumAcquired == maxAcquiredBuffers;
 }
 
 class BBQSurface : public Surface {
diff --git a/libs/gui/FrameTimelineInfo.cpp b/libs/gui/FrameTimelineInfo.cpp
index f400774..9231a57 100644
--- a/libs/gui/FrameTimelineInfo.cpp
+++ b/libs/gui/FrameTimelineInfo.cpp
@@ -21,6 +21,7 @@
 #include <android/os/IInputConstants.h>
 #include <gui/FrameTimelineInfo.h>
 #include <gui/LayerState.h>
+#include <private/gui/ParcelUtils.h>
 #include <utils/Errors.h>
 
 #include <cmath>
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 989abd9..ceab6ec 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -28,6 +28,7 @@
 #include <gui/ISurfaceComposerClient.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/LayerState.h>
+#include <private/gui/ParcelUtils.h>
 #include <stdint.h>
 #include <sys/types.h>
 #include <system/graphics.h>
@@ -935,7 +936,8 @@
         return NO_ERROR;
     }
 
-    status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) override {
+    status_t setDisplayBrightness(const sp<IBinder>& displayToken,
+                                  const gui::DisplayBrightness& brightness) override {
         Parcel data, reply;
         status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         if (error != NO_ERROR) {
@@ -947,7 +949,7 @@
             ALOGE("setDisplayBrightness: failed to write display token: %d", error);
             return error;
         }
-        error = data.writeFloat(brightness);
+        error = data.writeParcelable(brightness);
         if (error != NO_ERROR) {
             ALOGE("setDisplayBrightness: failed to write brightness: %d", error);
             return error;
@@ -1832,8 +1834,8 @@
                 ALOGE("setDisplayBrightness: failed to read display token: %d", error);
                 return error;
             }
-            float brightness = -1.0f;
-            error = data.readFloat(&brightness);
+            gui::DisplayBrightness brightness;
+            error = data.readParcelable(&brightness);
             if (error != NO_ERROR) {
                 ALOGE("setDisplayBrightness: failed to read brightness: %d", error);
                 return error;
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 0ded936..b42793b 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -17,9 +17,10 @@
 #define LOG_TAG "ITransactionCompletedListener"
 //#define LOG_NDEBUG 0
 
-#include <gui/LayerState.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/ITransactionCompletedListener.h>
+#include <gui/LayerState.h>
+#include <private/gui/ParcelUtils.h>
 
 namespace android {
 
@@ -27,7 +28,8 @@
 
 enum class Tag : uint32_t {
     ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
-    LAST = ON_TRANSACTION_COMPLETED,
+    ON_RELEASE_BUFFER,
+    LAST = ON_RELEASE_BUFFER,
 };
 
 } // Anonymous namespace
@@ -122,6 +124,7 @@
     for (const auto& data : jankData) {
         SAFE_PARCEL(output->writeParcelable, data);
     }
+    SAFE_PARCEL(output->writeUint64, previousBufferId);
     return NO_ERROR;
 }
 
@@ -144,6 +147,7 @@
         SAFE_PARCEL(input->readParcelable, &data);
         jankData.push_back(data);
     }
+    SAFE_PARCEL(input->readUint64, &previousBufferId);
     return NO_ERROR;
 }
 
@@ -245,6 +249,12 @@
                                          onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED,
                                                                   stats);
     }
+
+    void onReleaseBuffer(uint64_t graphicBufferId, sp<Fence> releaseFence) override {
+        callRemoteAsync<decltype(
+                &ITransactionCompletedListener::onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER,
+                                                                  graphicBufferId, releaseFence);
+    }
 };
 
 // Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
@@ -263,6 +273,8 @@
         case Tag::ON_TRANSACTION_COMPLETED:
             return callLocalAsync(data, reply,
                                   &ITransactionCompletedListener::onTransactionCompleted);
+        case Tag::ON_RELEASE_BUFFER:
+            return callLocalAsync(data, reply, &ITransactionCompletedListener::onReleaseBuffer);
     }
 }
 
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 288bf92..665086f 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -23,6 +23,7 @@
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/ISurfaceComposerClient.h>
 #include <gui/LayerState.h>
+#include <private/gui/ParcelUtils.h>
 #include <utils/Errors.h>
 
 #include <cmath>
@@ -41,7 +42,6 @@
         flags(0),
         mask(0),
         reserved(0),
-        crop_legacy(Rect::INVALID_RECT),
         cornerRadius(0.0f),
         backgroundBlurRadius(0),
         barrierFrameNumber(0),
@@ -64,7 +64,8 @@
         fixedTransformHint(ui::Transform::ROT_INVALID),
         frameNumber(0),
         frameTimelineInfo(),
-        autoRefresh(false) {
+        autoRefresh(false),
+        releaseBufferListener(nullptr) {
     matrix.dsdx = matrix.dtdy = 1.0f;
     matrix.dsdy = matrix.dtdx = 0.0f;
     hdrMetadata.validTypes = 0;
@@ -85,7 +86,7 @@
     SAFE_PARCEL(output.writeUint32, flags);
     SAFE_PARCEL(output.writeUint32, mask);
     SAFE_PARCEL(matrix.write, output);
-    SAFE_PARCEL(output.write, crop_legacy);
+    SAFE_PARCEL(output.write, crop);
     SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, barrierSurfaceControl_legacy);
     SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, reparentSurfaceControl);
     SAFE_PARCEL(output.writeUint64, barrierFrameNumber);
@@ -153,6 +154,7 @@
     SAFE_PARCEL(output.writeUint64, frameNumber);
     SAFE_PARCEL(frameTimelineInfo.write, output);
     SAFE_PARCEL(output.writeBool, autoRefresh);
+    SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(releaseBufferListener));
 
     SAFE_PARCEL(output.writeUint32, blurRegions.size());
     for (auto region : blurRegions) {
@@ -191,7 +193,7 @@
     SAFE_PARCEL(input.readUint32, &mask);
 
     SAFE_PARCEL(matrix.read, input);
-    SAFE_PARCEL(input.read, crop_legacy);
+    SAFE_PARCEL(input.read, crop);
     SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &barrierSurfaceControl_legacy);
     SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &reparentSurfaceControl);
     SAFE_PARCEL(input.readUint64, &barrierFrameNumber);
@@ -276,6 +278,12 @@
     SAFE_PARCEL(frameTimelineInfo.read, input);
     SAFE_PARCEL(input.readBool, &autoRefresh);
 
+    tmpBinder = nullptr;
+    SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
+    if (tmpBinder) {
+        releaseBufferListener = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
+    }
+
     uint32_t numRegions = 0;
     SAFE_PARCEL(input.readUint32, &numRegions);
     blurRegions.clear();
@@ -407,10 +415,6 @@
         what |= eLayerStackChanged;
         layerStack = other.layerStack;
     }
-    if (other.what & eCropChanged_legacy) {
-        what |= eCropChanged_legacy;
-        crop_legacy = other.crop_legacy;
-    }
     if (other.what & eCornerRadiusChanged) {
         what |= eCornerRadiusChanged;
         cornerRadius = other.cornerRadius;
@@ -548,6 +552,13 @@
         what |= eAutoRefreshChanged;
         autoRefresh = other.autoRefresh;
     }
+    if (other.what & eReleaseBufferListenerChanged) {
+        if (releaseBufferListener) {
+            ALOGW("Overriding releaseBufferListener");
+        }
+        what |= eReleaseBufferListenerChanged;
+        releaseBufferListener = other.releaseBufferListener;
+    }
     if (other.what & eStretchChanged) {
         what |= eStretchChanged;
         stretchEffect = other.stretchEffect;
diff --git a/libs/gui/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp
index f3849bc..e91f74f 100644
--- a/libs/gui/ScreenCaptureResults.cpp
+++ b/libs/gui/ScreenCaptureResults.cpp
@@ -16,6 +16,8 @@
 
 #include <gui/ScreenCaptureResults.h>
 
+#include <private/gui/ParcelUtils.h>
+
 namespace android::gui {
 
 status_t ScreenCaptureResults::writeToParcel(android::Parcel* parcel) const {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 5e8ab92..7dc5e85 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -39,6 +39,7 @@
 #include <gui/LayerState.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
+#include <private/gui/ParcelUtils.h>
 #include <ui/DisplayMode.h>
 #include <ui/DynamicDisplayInfo.h>
 
@@ -195,6 +196,17 @@
     }
 }
 
+void TransactionCompletedListener::setReleaseBufferCallback(uint64_t graphicBufferId,
+                                                            ReleaseBufferCallback listener) {
+    std::scoped_lock<std::mutex> lock(mMutex);
+    mReleaseBufferCallbacks[graphicBufferId] = listener;
+}
+
+void TransactionCompletedListener::removeReleaseBufferCallback(uint64_t graphicBufferId) {
+    std::scoped_lock<std::mutex> lock(mMutex);
+    mReleaseBufferCallbacks.erase(graphicBufferId);
+}
+
 void TransactionCompletedListener::addSurfaceStatsListener(void* context, void* cookie,
         sp<SurfaceControl> surfaceControl, SurfaceStatsCallback listener) {
     std::lock_guard<std::mutex> lock(mMutex);
@@ -275,6 +287,20 @@
                             .surfaceControls[surfaceStats.surfaceControl]
                             ->setTransformHint(surfaceStats.transformHint);
                 }
+                // If there is buffer id set, we look up any pending client release buffer callbacks
+                // and call them. This is a performance optimization when we have a transaction
+                // callback and a release buffer callback happening at the same time to avoid an
+                // additional ipc call from the server.
+                if (surfaceStats.previousBufferId) {
+                    ReleaseBufferCallback callback =
+                            popReleaseBufferCallbackLocked(surfaceStats.previousBufferId);
+                    if (callback) {
+                        callback(surfaceStats.previousBufferId,
+                                 surfaceStats.previousReleaseFence
+                                         ? surfaceStats.previousReleaseFence
+                                         : Fence::NO_FENCE);
+                    }
+                }
             }
 
             callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
@@ -297,6 +323,32 @@
     }
 }
 
+void TransactionCompletedListener::onReleaseBuffer(uint64_t graphicBufferId,
+                                                   sp<Fence> releaseFence) {
+    ReleaseBufferCallback callback;
+    {
+        std::scoped_lock<std::mutex> lock(mMutex);
+        callback = popReleaseBufferCallbackLocked(graphicBufferId);
+    }
+    if (!callback) {
+        ALOGE("Could not call release buffer callback, buffer not found %" PRIu64, graphicBufferId);
+        return;
+    }
+    callback(graphicBufferId, releaseFence);
+}
+
+ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked(
+        uint64_t graphicBufferId) {
+    ReleaseBufferCallback callback;
+    auto itr = mReleaseBufferCallbacks.find(graphicBufferId);
+    if (itr == mReleaseBufferCallbacks.end()) {
+        return nullptr;
+    }
+    callback = itr->second;
+    mReleaseBufferCallbacks.erase(itr);
+    return callback;
+}
+
 // ---------------------------------------------------------------------------
 
 void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId);
@@ -419,9 +471,8 @@
         mForceSynchronous(other.mForceSynchronous),
         mTransactionNestCount(other.mTransactionNestCount),
         mAnimation(other.mAnimation),
-        mEarlyWakeup(other.mEarlyWakeup),
-        mExplicitEarlyWakeupStart(other.mExplicitEarlyWakeupStart),
-        mExplicitEarlyWakeupEnd(other.mExplicitEarlyWakeupEnd),
+        mEarlyWakeupStart(other.mEarlyWakeupStart),
+        mEarlyWakeupEnd(other.mEarlyWakeupEnd),
         mContainsBuffer(other.mContainsBuffer),
         mDesiredPresentTime(other.mDesiredPresentTime),
         mIsAutoTimestamp(other.mIsAutoTimestamp),
@@ -450,9 +501,8 @@
     const uint32_t forceSynchronous = parcel->readUint32();
     const uint32_t transactionNestCount = parcel->readUint32();
     const bool animation = parcel->readBool();
-    const bool earlyWakeup = parcel->readBool();
-    const bool explicitEarlyWakeupStart = parcel->readBool();
-    const bool explicitEarlyWakeupEnd = parcel->readBool();
+    const bool earlyWakeupStart = parcel->readBool();
+    const bool earlyWakeupEnd = parcel->readBool();
     const bool containsBuffer = parcel->readBool();
     const int64_t desiredPresentTime = parcel->readInt64();
     const bool isAutoTimestamp = parcel->readBool();
@@ -527,9 +577,8 @@
     mForceSynchronous = forceSynchronous;
     mTransactionNestCount = transactionNestCount;
     mAnimation = animation;
-    mEarlyWakeup = earlyWakeup;
-    mExplicitEarlyWakeupStart = explicitEarlyWakeupStart;
-    mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd;
+    mEarlyWakeupStart = earlyWakeupStart;
+    mEarlyWakeupEnd = earlyWakeupEnd;
     mContainsBuffer = containsBuffer;
     mDesiredPresentTime = desiredPresentTime;
     mIsAutoTimestamp = isAutoTimestamp;
@@ -559,9 +608,8 @@
     parcel->writeUint32(mForceSynchronous);
     parcel->writeUint32(mTransactionNestCount);
     parcel->writeBool(mAnimation);
-    parcel->writeBool(mEarlyWakeup);
-    parcel->writeBool(mExplicitEarlyWakeupStart);
-    parcel->writeBool(mExplicitEarlyWakeupEnd);
+    parcel->writeBool(mEarlyWakeupStart);
+    parcel->writeBool(mEarlyWakeupEnd);
     parcel->writeBool(mContainsBuffer);
     parcel->writeInt64(mDesiredPresentTime);
     parcel->writeBool(mIsAutoTimestamp);
@@ -639,9 +687,8 @@
     mInputWindowCommands.merge(other.mInputWindowCommands);
 
     mContainsBuffer |= other.mContainsBuffer;
-    mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup;
-    mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart;
-    mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd;
+    mEarlyWakeupStart = mEarlyWakeupStart || other.mEarlyWakeupStart;
+    mEarlyWakeupEnd = mEarlyWakeupEnd || other.mEarlyWakeupEnd;
     mApplyToken = other.mApplyToken;
 
     mFrameTimelineInfo.merge(other.mFrameTimelineInfo);
@@ -659,9 +706,8 @@
     mForceSynchronous = 0;
     mTransactionNestCount = 0;
     mAnimation = false;
-    mEarlyWakeup = false;
-    mExplicitEarlyWakeupStart = false;
-    mExplicitEarlyWakeupEnd = false;
+    mEarlyWakeupStart = false;
+    mEarlyWakeupEnd = false;
     mDesiredPresentTime = 0;
     mIsAutoTimestamp = true;
     mFrameTimelineInfo.clear();
@@ -780,17 +826,14 @@
     if (mAnimation) {
         flags |= ISurfaceComposer::eAnimation;
     }
-    if (mEarlyWakeup) {
-        flags |= ISurfaceComposer::eEarlyWakeup;
-    }
 
-    // If both mExplicitEarlyWakeupStart and mExplicitEarlyWakeupEnd are set
+    // If both mEarlyWakeupStart and mEarlyWakeupEnd are set
     // it is equivalent for none
-    if (mExplicitEarlyWakeupStart && !mExplicitEarlyWakeupEnd) {
-        flags |= ISurfaceComposer::eExplicitEarlyWakeupStart;
+    if (mEarlyWakeupStart && !mEarlyWakeupEnd) {
+        flags |= ISurfaceComposer::eEarlyWakeupStart;
     }
-    if (mExplicitEarlyWakeupEnd && !mExplicitEarlyWakeupStart) {
-        flags |= ISurfaceComposer::eExplicitEarlyWakeupEnd;
+    if (mEarlyWakeupEnd && !mEarlyWakeupStart) {
+        flags |= ISurfaceComposer::eEarlyWakeupEnd;
     }
 
     sp<IBinder> applyToken = mApplyToken
@@ -841,16 +884,12 @@
     mAnimation = true;
 }
 
-void SurfaceComposerClient::Transaction::setEarlyWakeup() {
-    mEarlyWakeup = true;
+void SurfaceComposerClient::Transaction::setEarlyWakeupStart() {
+    mEarlyWakeupStart = true;
 }
 
-void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupStart() {
-    mExplicitEarlyWakeupStart = true;
-}
-
-void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupEnd() {
-    mExplicitEarlyWakeupEnd = true;
+void SurfaceComposerClient::Transaction::setEarlyWakeupEnd() {
+    mEarlyWakeupEnd = true;
 }
 
 layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) {
@@ -1048,15 +1087,15 @@
     return *this;
 }
 
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop_legacy(
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop(
         const sp<SurfaceControl>& sc, const Rect& crop) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->what |= layer_state_t::eCropChanged_legacy;
-    s->crop_legacy = crop;
+    s->what |= layer_state_t::eCropChanged;
+    s->crop = crop;
 
     registerSurfaceControlForCallback(sc);
     return *this;
@@ -1204,20 +1243,6 @@
     return *this;
 }
 
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop(
-        const sp<SurfaceControl>& sc, const Rect& crop) {
-    layer_state_t* s = getLayerState(sc);
-    if (!s) {
-        mStatus = BAD_INDEX;
-        return *this;
-    }
-    s->what |= layer_state_t::eCropChanged;
-    s->crop = crop;
-
-    registerSurfaceControlForCallback(sc);
-    return *this;
-}
-
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrame(
         const sp<SurfaceControl>& sc, const Rect& frame) {
     layer_state_t* s = getLayerState(sc);
@@ -1233,17 +1258,20 @@
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
-        const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer) {
+        const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
+        ReleaseBufferCallback callback) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
+    removeReleaseBufferCallback(s);
     s->what |= layer_state_t::eBufferChanged;
     s->buffer = buffer;
     if (mIsAutoTimestamp) {
         mDesiredPresentTime = systemTime();
     }
+    setReleaseBufferCallback(s, callback);
 
     registerSurfaceControlForCallback(sc);
 
@@ -1251,6 +1279,34 @@
     return *this;
 }
 
+void SurfaceComposerClient::Transaction::removeReleaseBufferCallback(layer_state_t* s) {
+    if (!s->releaseBufferListener) {
+        return;
+    }
+
+    s->what &= ~static_cast<uint64_t>(layer_state_t::eReleaseBufferListenerChanged);
+    s->releaseBufferListener = nullptr;
+    TransactionCompletedListener::getInstance()->removeReleaseBufferCallback(s->buffer->getId());
+}
+
+void SurfaceComposerClient::Transaction::setReleaseBufferCallback(layer_state_t* s,
+                                                                  ReleaseBufferCallback callback) {
+    if (!callback) {
+        return;
+    }
+
+    if (!s->buffer) {
+        ALOGW("Transaction::setReleaseBufferCallback"
+              "ignored trying to set a callback on a null buffer.");
+        return;
+    }
+
+    s->what |= layer_state_t::eReleaseBufferListenerChanged;
+    s->releaseBufferListener = TransactionCompletedListener::getIInstance();
+    auto listener = TransactionCompletedListener::getInstance();
+    listener->setReleaseBufferCallback(s->buffer->getId(), callback);
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAcquireFence(
         const sp<SurfaceControl>& sc, const sp<Fence>& fence) {
     layer_state_t* s = getLayerState(sc);
@@ -1458,7 +1514,7 @@
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeometry(
         const sp<SurfaceControl>& sc, const Rect& source, const Rect& dst, int transform) {
-    setCrop_legacy(sc, source);
+    setCrop(sc, source);
 
     int x = dst.left;
     int y = dst.top;
@@ -1998,7 +2054,7 @@
 }
 
 status_t SurfaceComposerClient::setDisplayBrightness(const sp<IBinder>& displayToken,
-                                                     float brightness) {
+                                                     const gui::DisplayBrightness& brightness) {
     return ComposerService::getComposerService()->setDisplayBrightness(displayToken, brightness);
 }
 
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 1dcfe2e..7e2f8f9 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -39,6 +39,7 @@
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 #include <gui/SurfaceControl.h>
+#include <private/gui/ParcelUtils.h>
 
 namespace android {
 
diff --git a/libs/gui/aidl/android/gui/DisplayBrightness.aidl b/libs/gui/aidl/android/gui/DisplayBrightness.aidl
new file mode 100644
index 0000000..bdb8c63
--- /dev/null
+++ b/libs/gui/aidl/android/gui/DisplayBrightness.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+/** @hide */
+parcelable DisplayBrightness {
+    // Range 0-1f, the desired sdr white point brightness
+    float sdrWhitePoint = 0f;
+
+    // The SDR white point in nits. -1 if unknown
+    float sdrWhitePointNits = -1f;
+
+    // Range 0-1f, the desired brightness of the display itself. -1f to turn the backlight off
+    float displayBrightness = 0f;
+
+    // The desired brightness of the display in nits. -1 if unknown
+    float displayBrightnessNits = -1f;
+}
\ No newline at end of file
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index dd8e714..0173ffd 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -89,13 +89,14 @@
 
     void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
             const std::vector<SurfaceControlStats>& stats);
+    void releaseBufferCallback(uint64_t graphicBufferId, const sp<Fence>& releaseFence);
     void setNextTransaction(SurfaceComposerClient::Transaction *t);
     void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
     void setTransactionCompleteCallback(uint64_t frameNumber,
                                         std::function<void(int64_t)>&& transactionCompleteCallback);
 
     void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format);
-    void flushShadowQueue() { mFlushShadowQueue = true; }
+    void flushShadowQueue() {}
 
     status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
     status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
@@ -132,16 +133,10 @@
 
     int32_t mNumFrameAvailable GUARDED_BY(mMutex);
     int32_t mNumAcquired GUARDED_BY(mMutex);
-    bool mInitialCallbackReceived GUARDED_BY(mMutex) = false;
-    struct PendingReleaseItem {
-        BufferItem item;
-        sp<Fence> releaseFence;
-    };
 
-    std::queue<const BufferItem> mSubmitted GUARDED_BY(mMutex);
-    // Keep a reference to the currently presented buffer so we can release it when the next buffer
-    // is ready to be presented.
-    PendingReleaseItem mPendingReleaseItem GUARDED_BY(mMutex);
+    // Keep a reference to the submitted buffers so we can release when surfaceflinger drops the
+    // buffer or the buffer has been presented and a new buffer is ready to be presented.
+    std::unordered_map<uint64_t /* bufferId */, BufferItem> mSubmitted GUARDED_BY(mMutex);
 
     ui::Size mSize GUARDED_BY(mMutex);
     ui::Size mRequestedSize GUARDED_BY(mMutex);
@@ -157,9 +152,6 @@
     std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
             mPendingTransactions GUARDED_BY(mMutex);
 
-    // If set to true, the next queue buffer will wait until the shadow queue has been processed by
-    // the adapter.
-    bool mFlushShadowQueue = false;
     // Last requested auto refresh state set by the producer. The state indicates that the consumer
     // should acquire the next frame as soon as it can and not wait for a frame to become available.
     // This is only relevant for shared buffer mode.
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 88cfe4b..35990d1 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <android/gui/DisplayBrightness.h>
 #include <android/gui/IFpsListener.h>
 #include <android/gui/IScreenCaptureListener.h>
 #include <android/gui/ITransactionTraceListener.h>
@@ -89,18 +90,15 @@
         eSynchronous = 0x01,
         eAnimation = 0x02,
 
-        // DEPRECATED - use eExplicitEarlyWakeup[Start|End]
-        eEarlyWakeup = 0x04,
-
         // Explicit indication that this transaction and others to follow will likely result in a
         // lot of layers being composed, and thus, SurfaceFlinger should wake-up earlier to avoid
         // missing frame deadlines. In this case SurfaceFlinger will wake up at
         // (sf vsync offset - debug.sf.early_phase_offset_ns). SurfaceFlinger will continue to be
-        // in the early configuration until it receives eExplicitEarlyWakeupEnd. These flags are
+        // in the early configuration until it receives eEarlyWakeupEnd. These flags are
         // expected to be used by WindowManager only and are guarded by
         // android.permission.ACCESS_SURFACE_FLINGER
-        eExplicitEarlyWakeupStart = 0x08,
-        eExplicitEarlyWakeupEnd = 0x10,
+        eEarlyWakeupStart = 0x08,
+        eEarlyWakeupEnd = 0x10,
     };
 
     enum VsyncSource {
@@ -415,15 +413,15 @@
      * displayToken
      *      The token of the display whose brightness is set.
      * brightness
-     *      A number between 0.0f (minimum brightness) and 1.0 (maximum brightness), or -1.0f to
-     *      turn the backlight off.
+     *      The DisplayBrightness info to set on the desired display.
      *
      * Returns NO_ERROR upon success. Otherwise,
      *      NAME_NOT_FOUND    if the display is invalid, or
      *      BAD_VALUE         if the brightness is invalid, or
      *      INVALID_OPERATION if brightness operations are not supported.
      */
-    virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) = 0;
+    virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken,
+                                          const gui::DisplayBrightness& brightness) = 0;
 
     /*
      * Sends a power boost to the composer. This function is asynchronous.
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index cb17cee..098760e 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -87,13 +87,14 @@
     SurfaceStats() = default;
     SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence,
                  uint32_t hint, FrameEventHistoryStats frameEventStats,
-                 std::vector<JankData> jankData)
+                 std::vector<JankData> jankData, uint64_t previousBufferId)
           : surfaceControl(sc),
             acquireTime(time),
             previousReleaseFence(prevReleaseFence),
             transformHint(hint),
             eventStats(frameEventStats),
-            jankData(std::move(jankData)) {}
+            jankData(std::move(jankData)),
+            previousBufferId(previousBufferId) {}
 
     sp<IBinder> surfaceControl;
     nsecs_t acquireTime = -1;
@@ -101,6 +102,7 @@
     uint32_t transformHint = 0;
     FrameEventHistoryStats eventStats;
     std::vector<JankData> jankData;
+    uint64_t previousBufferId;
 };
 
 class TransactionStats : public Parcelable {
@@ -139,6 +141,8 @@
     DECLARE_META_INTERFACE(TransactionCompletedListener)
 
     virtual void onTransactionCompleted(ListenerStats stats) = 0;
+
+    virtual void onReleaseBuffer(uint64_t graphicBufferId, sp<Fence> releaseFence) = 0;
 };
 
 class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index b273805..99b3ea5 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -16,23 +16,7 @@
 
 #ifndef ANDROID_SF_LAYER_STATE_H
 #define ANDROID_SF_LAYER_STATE_H
-#define SAFE_PARCEL(FUNC, ...)                                                            \
-    {                                                                                     \
-        status_t error = FUNC(__VA_ARGS__);                                               \
-        if (error) {                                                                      \
-            ALOGE("ERROR(%d). Failed to call parcel %s(%s)", error, #FUNC, #__VA_ARGS__); \
-            return error;                                                                 \
-        }                                                                                 \
-    }
 
-#define SAFE_PARCEL_READ_SIZE(FUNC, COUNT, SIZE)                             \
-    {                                                                        \
-        SAFE_PARCEL(FUNC, COUNT);                                            \
-        if (static_cast<unsigned int>(*COUNT) > SIZE) {                      \
-            ALOGE("ERROR(BAD_VALUE). %s was greater than dataSize", #COUNT); \
-            return BAD_VALUE;                                                \
-        }                                                                    \
-    }
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -98,9 +82,9 @@
         eTransparentRegionChanged = 0x00000020,
         eFlagsChanged = 0x00000040,
         eLayerStackChanged = 0x00000080,
-        eCropChanged_legacy = 0x00000100,
+        /* was eCropChanged_legacy, now available 0x00000100, */
         eDeferTransaction_legacy = 0x00000200,
-        /* was ScalingModeChanged, now available 0x00000400, */
+        eReleaseBufferListenerChanged = 0x00000400,
         eShadowRadiusChanged = 0x00000800,
         eReparentChildren = 0x00001000,
         /* was eDetachChildren, now available 0x00002000, */
@@ -167,7 +151,6 @@
     uint32_t mask;
     uint8_t reserved;
     matrix22_t matrix;
-    Rect crop_legacy;
     float cornerRadius;
     uint32_t backgroundBlurRadius;
     sp<SurfaceControl> barrierSurfaceControl_legacy;
@@ -249,6 +232,11 @@
 
     // Stretch effect to be applied to this layer
     StretchEffect stretchEffect;
+
+    // Listens to when the buffer is safe to be released. This is used for blast
+    // layers only. The callback includes a release fence as well as the graphic
+    // buffer id to identify the buffer.
+    sp<ITransactionCompletedListener> releaseBufferListener;
 };
 
 struct ComposerState {
diff --git a/libs/gui/include/gui/ScreenCaptureResults.h b/libs/gui/include/gui/ScreenCaptureResults.h
index 0ccc6e8..99c35c1 100644
--- a/libs/gui/include/gui/ScreenCaptureResults.h
+++ b/libs/gui/include/gui/ScreenCaptureResults.h
@@ -16,15 +16,6 @@
 
 #pragma once
 
-#define SAFE_PARCEL(FUNC, ...)                                                            \
-    {                                                                                     \
-        status_t error = FUNC(__VA_ARGS__);                                               \
-        if (error) {                                                                      \
-            ALOGE("ERROR(%d). Failed to call parcel %s(%s)", error, #FUNC, #__VA_ARGS__); \
-            return error;                                                                 \
-        }                                                                                 \
-    }
-
 #include <binder/Parcel.h>
 #include <binder/Parcelable.h>
 #include <ui/Fence.h>
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index de88943..a91de9c 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -80,6 +80,9 @@
 using TransactionCompletedCallback =
         std::function<void(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
                            const std::vector<SurfaceControlStats>& /*stats*/)>;
+using ReleaseBufferCallback =
+        std::function<void(uint64_t /* graphicsBufferId */, const sp<Fence>& /*releaseFence*/)>;
+
 using SurfaceStatsCallback =
         std::function<void(void* /*context*/, nsecs_t /*latchTime*/,
                            const sp<Fence>& /*presentFence*/,
@@ -209,7 +212,8 @@
      *      BAD_VALUE         if the brightness value is invalid, or
      *      INVALID_OPERATION if brightness operaetions are not supported.
      */
-    static status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness);
+    static status_t setDisplayBrightness(const sp<IBinder>& displayToken,
+                                         const gui::DisplayBrightness& brightness);
 
     /*
      * Sends a power boost to the composer. This function is asynchronous.
@@ -349,9 +353,8 @@
         uint32_t mForceSynchronous = 0;
         uint32_t mTransactionNestCount = 0;
         bool mAnimation = false;
-        bool mEarlyWakeup = false;
-        bool mExplicitEarlyWakeupStart = false;
-        bool mExplicitEarlyWakeupEnd = false;
+        bool mEarlyWakeupStart = false;
+        bool mEarlyWakeupEnd = false;
 
         // Indicates that the Transaction contains a buffer that should be cached
         bool mContainsBuffer = false;
@@ -387,6 +390,8 @@
 
         void cacheBuffers();
         void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
+        void setReleaseBufferCallback(layer_state_t* state, ReleaseBufferCallback callback);
+        void removeReleaseBufferCallback(layer_state_t* state);
 
     public:
         Transaction();
@@ -436,7 +441,7 @@
                 float alpha);
         Transaction& setMatrix(const sp<SurfaceControl>& sc,
                 float dsdx, float dtdx, float dtdy, float dsdy);
-        Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop);
+        Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
         Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
         Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc,
                                              int backgroundBlurRadius);
@@ -469,9 +474,9 @@
         Transaction& setTransform(const sp<SurfaceControl>& sc, uint32_t transform);
         Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
                                                   bool transformToDisplayInverse);
-        Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
         Transaction& setFrame(const sp<SurfaceControl>& sc, const Rect& frame);
-        Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer);
+        Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
+                               ReleaseBufferCallback callback = nullptr);
         Transaction& setCachedBuffer(const sp<SurfaceControl>& sc, int32_t bufferId);
         Transaction& setAcquireFence(const sp<SurfaceControl>& sc, const sp<Fence>& fence);
         Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
@@ -561,9 +566,8 @@
                                   const Rect& layerStackRect, const Rect& displayRect);
         void setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height);
         void setAnimationTransaction();
-        void setEarlyWakeup();
-        void setExplicitEarlyWakeupStart();
-        void setExplicitEarlyWakeupEnd();
+        void setEarlyWakeupStart();
+        void setEarlyWakeupEnd();
     };
 
     status_t clearLayerFrameStats(const sp<IBinder>& token) const;
@@ -650,6 +654,8 @@
 
     std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex);
     std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex);
+    std::unordered_map<uint64_t /* graphicsBufferId */, ReleaseBufferCallback>
+            mReleaseBufferCallbacks GUARDED_BY(mMutex);
     std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry>
                 mSurfaceStatsListeners GUARDED_BY(mMutex);
 
@@ -683,8 +689,15 @@
                 SurfaceStatsCallback listener);
     void removeSurfaceStatsListener(void* context, void* cookie);
 
-    // Overrides BnTransactionCompletedListener's onTransactionCompleted
+    void setReleaseBufferCallback(uint64_t /* graphicsBufferId */, ReleaseBufferCallback);
+    void removeReleaseBufferCallback(uint64_t /* graphicsBufferId */);
+
+    // BnTransactionCompletedListener overrides
     void onTransactionCompleted(ListenerStats stats) override;
+    void onReleaseBuffer(uint64_t /* graphicsBufferId */, sp<Fence> releaseFence) override;
+
+private:
+    ReleaseBufferCallback popReleaseBufferCallbackLocked(uint64_t /* graphicsBufferId */);
 };
 
 } // namespace android
diff --git a/libs/gui/include/private/gui/ParcelUtils.h b/libs/gui/include/private/gui/ParcelUtils.h
new file mode 100644
index 0000000..1cdd07e
--- /dev/null
+++ b/libs/gui/include/private/gui/ParcelUtils.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstring>
+
+#define SAFE_PARCEL(FUNC, ...)                                                                   \
+    {                                                                                            \
+        status_t error = FUNC(__VA_ARGS__);                                                      \
+        if (error) {                                                                             \
+            ALOGE("ERROR(%s, %d). Failed to call parcel %s(%s)", strerror(-error), error, #FUNC, \
+                  #__VA_ARGS__);                                                                 \
+            return error;                                                                        \
+        }                                                                                        \
+    }
+
+#define SAFE_PARCEL_READ_SIZE(FUNC, COUNT, SIZE)                             \
+    {                                                                        \
+        SAFE_PARCEL(FUNC, COUNT);                                            \
+        if (static_cast<unsigned int>(*COUNT) > SIZE) {                      \
+            ALOGE("ERROR(BAD_VALUE). %s was greater than dataSize", #COUNT); \
+            return BAD_VALUE;                                                \
+        }                                                                    \
+    }
\ No newline at end of file
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index fb07a19..fe48d88 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -73,13 +73,34 @@
 
     void waitForCallbacks() {
         std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
-        while (mBlastBufferQueueAdapter->mSubmitted.size() > 0) {
+        // Wait until all but one of the submitted buffers have been released.
+        while (mBlastBufferQueueAdapter->mSubmitted.size() > 1) {
             mBlastBufferQueueAdapter->mCallbackCV.wait(lock);
         }
     }
 
+    void setTransactionCompleteCallback(int64_t frameNumber) {
+        mBlastBufferQueueAdapter->setTransactionCompleteCallback(frameNumber, [&](int64_t frame) {
+            std::unique_lock lock{mMutex};
+            mLastTransactionCompleteFrameNumber = frame;
+            mCallbackCV.notify_all();
+        });
+    }
+
+    void waitForCallback(int64_t frameNumber) {
+        std::unique_lock lock{mMutex};
+        // Wait until all but one of the submitted buffers have been released.
+        while (mLastTransactionCompleteFrameNumber < frameNumber) {
+            mCallbackCV.wait(lock);
+        }
+    }
+
 private:
     sp<BLASTBufferQueue> mBlastBufferQueueAdapter;
+
+    std::mutex mMutex;
+    std::condition_variable mCallbackCV;
+    int64_t mLastTransactionCompleteFrameNumber = -1;
 };
 
 class BLASTBufferQueueTest : public ::testing::Test {
@@ -128,7 +149,7 @@
         mCaptureArgs.dataspace = ui::Dataspace::V0_SRGB;
     }
 
-    void setUpProducer(BLASTBufferQueueHelper adapter, sp<IGraphicBufferProducer>& producer) {
+    void setUpProducer(BLASTBufferQueueHelper& adapter, sp<IGraphicBufferProducer>& producer) {
         producer = adapter.getIGraphicBufferProducer();
         setUpProducer(producer);
     }
@@ -205,10 +226,10 @@
                     EXPECT_GE(epsilon, abs(g - *(pixel + 1)));
                     EXPECT_GE(epsilon, abs(b - *(pixel + 2)));
                 }
+                ASSERT_EQ(false, ::testing::Test::HasFailure());
             }
         }
         captureBuf->unlock();
-        ASSERT_EQ(false, ::testing::Test::HasFailure());
     }
 
     static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
@@ -315,7 +336,8 @@
 
     nsecs_t desiredPresentTime = systemTime() + nsecs_t(5 * 1e8);
     IGraphicBufferProducer::QueueBufferOutput qbOutput;
-    IGraphicBufferProducer::QueueBufferInput input(desiredPresentTime, false, HAL_DATASPACE_UNKNOWN,
+    IGraphicBufferProducer::QueueBufferInput input(desiredPresentTime, true /* autotimestamp */,
+                                                   HAL_DATASPACE_UNKNOWN,
                                                    Rect(mDisplayWidth, mDisplayHeight),
                                                    NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
                                                    Fence::NO_FENCE);
@@ -351,7 +373,8 @@
     buf->unlock();
 
     IGraphicBufferProducer::QueueBufferOutput qbOutput;
-    IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+    IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+                                                   HAL_DATASPACE_UNKNOWN,
                                                    Rect(mDisplayWidth, mDisplayHeight),
                                                    NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
                                                    Fence::NO_FENCE);
@@ -396,7 +419,8 @@
                                               nullptr, nullptr);
         ASSERT_EQ(NO_ERROR, ret);
         IGraphicBufferProducer::QueueBufferOutput qbOutput;
-        IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+        IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+                                                       HAL_DATASPACE_UNKNOWN,
                                                        Rect(mDisplayWidth, mDisplayHeight),
                                                        NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
                                                        Fence::NO_FENCE);
@@ -429,7 +453,8 @@
     buf->unlock();
 
     IGraphicBufferProducer::QueueBufferOutput qbOutput;
-    IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+    IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+                                                   HAL_DATASPACE_UNKNOWN,
                                                    Rect(mDisplayWidth, mDisplayHeight / 2),
                                                    NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
                                                    Fence::NO_FENCE);
@@ -458,7 +483,7 @@
     ASSERT_NE(nullptr, bg.get());
     Transaction t;
     t.setLayerStack(bg, 0)
-            .setCrop_legacy(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+            .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
             .setColor(bg, half3{0, 0, 0})
             .setLayer(bg, 0)
             .apply();
@@ -486,7 +511,8 @@
     buf->unlock();
 
     IGraphicBufferProducer::QueueBufferOutput qbOutput;
-    IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+    IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+                                                   HAL_DATASPACE_UNKNOWN,
                                                    Rect(bufferSideLength, finalCropSideLength),
                                                    NATIVE_WINDOW_SCALING_MODE_SCALE_CROP, 0,
                                                    Fence::NO_FENCE);
@@ -537,7 +563,8 @@
         ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
         ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
         IGraphicBufferProducer::QueueBufferOutput qbOutput;
-        IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+        IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+                                                       HAL_DATASPACE_UNKNOWN,
                                                        Rect(mDisplayWidth, mDisplayHeight),
                                                        NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
                                                        Fence::NO_FENCE);
@@ -577,7 +604,7 @@
     sp<IGraphicBufferProducer> slowIgbProducer;
     setUpProducer(slowAdapter, slowIgbProducer);
     nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
-    queueBuffer(slowIgbProducer, 0 /* r */, 0 /* g */, 0 /* b */, presentTimeDelay);
+    queueBuffer(slowIgbProducer, 0 /* r */, 255 /* g */, 0 /* b */, presentTimeDelay);
 
     BLASTBufferQueueHelper fastAdapter(bgSurface, mDisplayWidth, mDisplayHeight);
     sp<IGraphicBufferProducer> fastIgbProducer;
@@ -617,7 +644,8 @@
         fillQuadrants(buf);
 
         IGraphicBufferProducer::QueueBufferOutput qbOutput;
-        IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+        IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+                                                       HAL_DATASPACE_UNKNOWN,
                                                        Rect(bufWidth, bufHeight),
                                                        NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
                                                        tr, Fence::NO_FENCE);
@@ -838,6 +866,7 @@
     IGraphicBufferProducer::QueueBufferOutput qbOutput;
     nsecs_t requestedPresentTimeA = 0;
     nsecs_t postedTimeA = 0;
+    adapter.setTransactionCompleteCallback(1);
     setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true);
     history.applyDelta(qbOutput.frameTimestamps);
 
@@ -848,7 +877,7 @@
     ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
     ASSERT_GE(events->postedTime, postedTimeA);
 
-    adapter.waitForCallbacks();
+    adapter.waitForCallback(1);
 
     // queue another buffer so we query for frame event deltas
     nsecs_t requestedPresentTimeB = 0;
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index d65a40b..59e5c13 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -195,7 +195,7 @@
         t.setInputWindowInfo(mSurfaceControl, mInputInfo);
         t.setLayer(mSurfaceControl, LAYER_BASE);
         t.setPosition(mSurfaceControl, x, y);
-        t.setCrop_legacy(mSurfaceControl, crop);
+        t.setCrop(mSurfaceControl, crop);
         t.setAlpha(mSurfaceControl, 1);
         t.apply(true);
     }
@@ -670,7 +670,7 @@
     parentSurface->showAt(100, 100);
 
     nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
-        t.setCrop_legacy(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
+        t.setCrop(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
         t.reparent(sc, parentSurface->mSurfaceControl);
     });
 
@@ -694,7 +694,7 @@
     parentSurface->showAt(50, 50);
 
     nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
-        t.setCrop_legacy(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
+        t.setCrop(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
         t.reparent(sc, parentSurface->mSurfaceControl);
     });
 
diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp
index 5c1bebb..0cd150d 100644
--- a/libs/gui/tests/SamplingDemo.cpp
+++ b/libs/gui/tests/SamplingDemo.cpp
@@ -46,8 +46,7 @@
 
         SurfaceComposerClient::Transaction{}
                 .setLayer(mButton, 0x7fffffff)
-                .setCrop_legacy(mButton,
-                                {0, 0, width - 2 * BUTTON_PADDING, height - 2 * BUTTON_PADDING})
+                .setCrop(mButton, {0, 0, width - 2 * BUTTON_PADDING, height - 2 * BUTTON_PADDING})
                 .setPosition(mButton, samplingArea.left + BUTTON_PADDING,
                              samplingArea.top + BUTTON_PADDING)
                 .setColor(mButton, half3{1, 1, 1})
@@ -59,9 +58,8 @@
 
         SurfaceComposerClient::Transaction{}
                 .setLayer(mButtonBlend, 0x7ffffffe)
-                .setCrop_legacy(mButtonBlend,
-                                {0, 0, width - 2 * SAMPLE_AREA_PADDING,
-                                 height - 2 * SAMPLE_AREA_PADDING})
+                .setCrop(mButtonBlend,
+                         {0, 0, width - 2 * SAMPLE_AREA_PADDING, height - 2 * SAMPLE_AREA_PADDING})
                 .setPosition(mButtonBlend, samplingArea.left + SAMPLE_AREA_PADDING,
                              samplingArea.top + SAMPLE_AREA_PADDING)
                 .setColor(mButtonBlend, half3{1, 1, 1})
@@ -77,7 +75,7 @@
 
             SurfaceComposerClient::Transaction{}
                     .setLayer(mSamplingArea, 0x7ffffffd)
-                    .setCrop_legacy(mSamplingArea, {0, 0, 100, 32})
+                    .setCrop(mSamplingArea, {0, 0, 100, 32})
                     .setPosition(mSamplingArea, 490, 1606)
                     .setColor(mSamplingArea, half3{0, 1, 0})
                     .setAlpha(mSamplingArea, 0.1)
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index e8fb71d..8d7f8c9 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -811,7 +811,7 @@
         return NO_ERROR;
     }
     status_t setDisplayBrightness(const sp<IBinder>& /*displayToken*/,
-                                  float /*brightness*/) override {
+                                  const gui::DisplayBrightness& /*brightness*/) override {
         return NO_ERROR;
     }
 
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 0a00d68..5600eb3 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -92,6 +92,9 @@
         case AINPUT_EVENT_TYPE_CAPTURE: {
             return "CAPTURE";
         }
+        case AINPUT_EVENT_TYPE_DRAG: {
+            return "DRAG";
+        }
     }
     return "UNKNOWN";
 }
@@ -770,6 +773,23 @@
     mPointerCaptureEnabled = from.mPointerCaptureEnabled;
 }
 
+// --- DragEvent ---
+
+void DragEvent::initialize(int32_t id, float x, float y, bool isExiting) {
+    InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
+                           ADISPLAY_ID_NONE, INVALID_HMAC);
+    mIsExiting = isExiting;
+    mX = x;
+    mY = y;
+}
+
+void DragEvent::initialize(const DragEvent& from) {
+    InputEvent::initialize(from);
+    mIsExiting = from.mIsExiting;
+    mX = from.mX;
+    mY = from.mY;
+}
+
 // --- PooledInputEventFactory ---
 
 PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) :
@@ -815,6 +835,15 @@
     return event;
 }
 
+DragEvent* PooledInputEventFactory::createDragEvent() {
+    if (mDragEventPool.empty()) {
+        return new DragEvent();
+    }
+    DragEvent* event = mDragEventPool.front().release();
+    mDragEventPool.pop();
+    return event;
+}
+
 void PooledInputEventFactory::recycle(InputEvent* event) {
     switch (event->getType()) {
     case AINPUT_EVENT_TYPE_KEY:
@@ -842,6 +871,12 @@
             return;
         }
         break;
+    case AINPUT_EVENT_TYPE_DRAG:
+        if (mDragEventPool.size() < mMaxPoolSize) {
+            mDragEventPool.push(std::unique_ptr<DragEvent>(static_cast<DragEvent*>(event)));
+            return;
+        }
+        break;
     }
     delete event;
 }
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index ee2daec..c2a3cf1 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -108,6 +108,8 @@
                 return true;
             case Type::CAPTURE:
                 return true;
+            case Type::DRAG:
+                return true;
         }
     }
     return false;
@@ -125,6 +127,8 @@
             return sizeof(Header) + body.focus.size();
         case Type::CAPTURE:
             return sizeof(Header) + body.capture.size();
+        case Type::DRAG:
+            return sizeof(Header) + body.drag.size();
     }
     return sizeof(Header);
 }
@@ -249,6 +253,13 @@
             msg->body.capture.pointerCaptureEnabled = body.capture.pointerCaptureEnabled;
             break;
         }
+        case InputMessage::Type::DRAG: {
+            msg->body.drag.eventId = body.drag.eventId;
+            msg->body.drag.x = body.drag.x;
+            msg->body.drag.y = body.drag.y;
+            msg->body.drag.isExiting = body.drag.isExiting;
+            break;
+        }
     }
 }
 
@@ -599,24 +610,45 @@
     return mChannel->sendMessage(&msg);
 }
 
-status_t InputPublisher::receiveFinishedSignal(
-        const std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)>& callback) {
+status_t InputPublisher::publishDragEvent(uint32_t seq, int32_t eventId, float x, float y,
+                                          bool isExiting) {
+    if (ATRACE_ENABLED()) {
+        std::string message =
+                StringPrintf("publishDragEvent(inputChannel=%s, x=%f, y=%f, isExiting=%s)",
+                             mChannel->getName().c_str(), x, y, toString(isExiting));
+        ATRACE_NAME(message.c_str());
+    }
+
+    InputMessage msg;
+    msg.header.type = InputMessage::Type::DRAG;
+    msg.header.seq = seq;
+    msg.body.drag.eventId = eventId;
+    msg.body.drag.isExiting = isExiting;
+    msg.body.drag.x = x;
+    msg.body.drag.y = y;
+    return mChannel->sendMessage(&msg);
+}
+
+android::base::Result<InputPublisher::Finished> InputPublisher::receiveFinishedSignal() {
     if (DEBUG_TRANSPORT_ACTIONS) {
-        ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str());
+        ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__);
     }
 
     InputMessage msg;
     status_t result = mChannel->receiveMessage(&msg);
     if (result) {
-        return result;
+        return android::base::Error(result);
     }
     if (msg.header.type != InputMessage::Type::FINISHED) {
         ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer",
               mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str());
-        return UNKNOWN_ERROR;
+        return android::base::Error(UNKNOWN_ERROR);
     }
-    callback(msg.header.seq, msg.body.finished.handled, msg.body.finished.consumeTime);
-    return OK;
+    return Finished{
+            .seq = msg.header.seq,
+            .handled = msg.body.finished.handled,
+            .consumeTime = msg.body.finished.consumeTime,
+    };
 }
 
 // --- InputConsumer ---
@@ -779,6 +811,16 @@
                 *outEvent = captureEvent;
                 break;
             }
+
+            case InputMessage::Type::DRAG: {
+                DragEvent* dragEvent = factory->createDragEvent();
+                if (!dragEvent) return NO_MEMORY;
+
+                initializeDragEvent(dragEvent, &mMsg);
+                *outSeq = mMsg.header.seq;
+                *outEvent = dragEvent;
+                break;
+            }
         }
     }
     return OK;
@@ -1236,6 +1278,11 @@
     event->initialize(msg->body.capture.eventId, msg->body.capture.pointerCaptureEnabled);
 }
 
+void InputConsumer::initializeDragEvent(DragEvent* event, const InputMessage* msg) {
+    event->initialize(msg->body.drag.eventId, msg->body.drag.x, msg->body.drag.y,
+                      msg->body.drag.isExiting);
+}
+
 void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
     uint32_t pointerCount = msg->body.motion.pointerCount;
     PointerProperties pointerProperties[pointerCount];
@@ -1346,6 +1393,12 @@
                                                                         .pointerCaptureEnabled));
                     break;
                 }
+                case InputMessage::Type::DRAG: {
+                    out += android::base::StringPrintf("x=%.1f y=%.1f, isExiting=%s",
+                                                       msg.body.drag.x, msg.body.drag.y,
+                                                       toString(msg.body.drag.isExiting));
+                    break;
+                }
             }
             out += "\n";
         }
diff --git a/libs/input/tests/Flags_test.cpp b/libs/input/tests/Flags_test.cpp
index 0dbb4cf..6de030f 100644
--- a/libs/input/tests/Flags_test.cpp
+++ b/libs/input/tests/Flags_test.cpp
@@ -148,6 +148,11 @@
     ASSERT_NE(flags1, flags2);
 }
 
+TEST(Flags, GetValue) {
+    Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+    ASSERT_EQ(flags.get(), 0x3);
+}
+
 TEST(Flags, String_NoFlags) {
     Flags<TestFlags> flags;
     ASSERT_EQ(flags.string(), "0x0");
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index e7e566d..fc31715 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -27,6 +27,8 @@
 #include <utils/StopWatch.h>
 #include <utils/Timers.h>
 
+using android::base::Result;
+
 namespace android {
 
 class InputPublisherAndConsumerTest : public testing::Test {
@@ -52,6 +54,7 @@
     void PublishAndConsumeMotionEvent();
     void PublishAndConsumeFocusEvent();
     void PublishAndConsumeCaptureEvent();
+    void PublishAndConsumeDragEvent();
 };
 
 TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -121,23 +124,13 @@
     ASSERT_EQ(OK, status)
             << "consumer sendFinishedSignal should return OK";
 
-    uint32_t finishedSeq = 0;
-    bool handled = false;
-    nsecs_t consumeTime;
-    status = mPublisher->receiveFinishedSignal(
-            [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
-                                                   nsecs_t inConsumeTime) -> void {
-                finishedSeq = inSeq;
-                handled = inHandled;
-                consumeTime = inConsumeTime;
-            });
-    ASSERT_EQ(OK, status)
-            << "publisher receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, finishedSeq)
-            << "publisher receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_TRUE(handled)
-            << "publisher receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(consumeTime, publishTime)
+    Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+    ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
+    ASSERT_EQ(seq, result->seq)
+            << "receiveFinishedSignal should have returned the original sequence number";
+    ASSERT_TRUE(result->handled)
+            << "receiveFinishedSignal should have set handled to consumer's reply";
+    ASSERT_GE(result->consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
@@ -271,23 +264,13 @@
     ASSERT_EQ(OK, status)
             << "consumer sendFinishedSignal should return OK";
 
-    uint32_t finishedSeq = 0;
-    bool handled = true;
-    nsecs_t consumeTime;
-    status = mPublisher->receiveFinishedSignal(
-            [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
-                                                   nsecs_t inConsumeTime) -> void {
-                finishedSeq = inSeq;
-                handled = inHandled;
-                consumeTime = inConsumeTime;
-            });
-    ASSERT_EQ(OK, status)
-            << "publisher receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, finishedSeq)
-            << "publisher receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_FALSE(handled)
-            << "publisher receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(consumeTime, publishTime)
+    Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+    ASSERT_TRUE(result.ok()) << "receiveFinishedSignal should return OK";
+    ASSERT_EQ(seq, result->seq)
+            << "receiveFinishedSignal should have returned the original sequence number";
+    ASSERT_FALSE(result->handled)
+            << "receiveFinishedSignal should have set handled to consumer's reply";
+    ASSERT_GE(result->consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
@@ -301,7 +284,7 @@
     const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     status = mPublisher->publishFocusEvent(seq, eventId, hasFocus, inTouchMode);
-    ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK";
+    ASSERT_EQ(OK, status) << "publisher publishFocusEvent should return OK";
 
     uint32_t consumeSeq;
     InputEvent* event;
@@ -321,22 +304,14 @@
     status = mConsumer->sendFinishedSignal(seq, true);
     ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
 
-    uint32_t finishedSeq = 0;
-    bool handled = false;
-    nsecs_t consumeTime;
-    status = mPublisher->receiveFinishedSignal(
-            [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
-                                                   nsecs_t inConsumeTime) -> void {
-                finishedSeq = inSeq;
-                handled = inHandled;
-                consumeTime = inConsumeTime;
-            });
-    ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, finishedSeq)
-            << "publisher receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_TRUE(handled)
-            << "publisher receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(consumeTime, publishTime)
+    Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+
+    ASSERT_TRUE(result.ok()) << "receiveFinishedSignal should return OK";
+    ASSERT_EQ(seq, result->seq)
+            << "receiveFinishedSignal should have returned the original sequence number";
+    ASSERT_TRUE(result->handled)
+            << "receiveFinishedSignal should have set handled to consumer's reply";
+    ASSERT_GE(result->consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
@@ -349,7 +324,7 @@
     const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     status = mPublisher->publishCaptureEvent(seq, eventId, captureEnabled);
-    ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK";
+    ASSERT_EQ(OK, status) << "publisher publishCaptureEvent should return OK";
 
     uint32_t consumeSeq;
     InputEvent* event;
@@ -368,22 +343,55 @@
     status = mConsumer->sendFinishedSignal(seq, true);
     ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
 
-    uint32_t finishedSeq = 0;
-    bool handled = false;
-    nsecs_t consumeTime;
-    status = mPublisher->receiveFinishedSignal(
-            [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
-                                                   nsecs_t inConsumeTime) -> void {
-                finishedSeq = inSeq;
-                handled = inHandled;
-                consumeTime = inConsumeTime;
-            });
-    ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, finishedSeq)
+    android::base::Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+    ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
+    ASSERT_EQ(seq, result->seq)
+            << "receiveFinishedSignal should have returned the original sequence number";
+    ASSERT_TRUE(result->handled)
+            << "receiveFinishedSignal should have set handled to consumer's reply";
+    ASSERT_GE(result->consumeTime, publishTime)
+            << "finished signal's consume time should be greater than publish time";
+}
+
+void InputPublisherAndConsumerTest::PublishAndConsumeDragEvent() {
+    status_t status;
+
+    constexpr uint32_t seq = 15;
+    int32_t eventId = InputEvent::nextId();
+    constexpr bool isExiting = false;
+    constexpr float x = 10;
+    constexpr float y = 15;
+    const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+    status = mPublisher->publishDragEvent(seq, eventId, x, y, isExiting);
+    ASSERT_EQ(OK, status) << "publisher publishDragEvent should return OK";
+
+    uint32_t consumeSeq;
+    InputEvent* event;
+    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+    ASSERT_EQ(OK, status) << "consumer consume should return OK";
+
+    ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
+    ASSERT_EQ(AINPUT_EVENT_TYPE_DRAG, event->getType())
+            << "consumer should have returned a drag event";
+
+    const DragEvent& dragEvent = static_cast<const DragEvent&>(*event);
+    EXPECT_EQ(seq, consumeSeq);
+    EXPECT_EQ(eventId, dragEvent.getId());
+    EXPECT_EQ(isExiting, dragEvent.isExiting());
+    EXPECT_EQ(x, dragEvent.getX());
+    EXPECT_EQ(y, dragEvent.getY());
+
+    status = mConsumer->sendFinishedSignal(seq, true);
+    ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
+
+    android::base::Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+    ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
+    ASSERT_EQ(seq, result->seq)
             << "publisher receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_TRUE(handled)
+    ASSERT_TRUE(result->handled)
             << "publisher receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(consumeTime, publishTime)
+    ASSERT_GE(result->consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
@@ -403,6 +411,10 @@
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeCaptureEvent());
 }
 
+TEST_F(InputPublisherAndConsumerTest, PublishDragEvent_EndToEnd) {
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
+}
+
 TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {
     status_t status;
     const size_t pointerCount = 1;
@@ -468,6 +480,7 @@
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeCaptureEvent());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
 }
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 8f43608..3d80b38 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -87,6 +87,12 @@
   CHECK_OFFSET(InputMessage::Body::Capture, pointerCaptureEnabled, 4);
   CHECK_OFFSET(InputMessage::Body::Capture, empty, 5);
 
+  CHECK_OFFSET(InputMessage::Body::Drag, eventId, 0);
+  CHECK_OFFSET(InputMessage::Body::Drag, x, 4);
+  CHECK_OFFSET(InputMessage::Body::Drag, y, 8);
+  CHECK_OFFSET(InputMessage::Body::Drag, isExiting, 12);
+  CHECK_OFFSET(InputMessage::Body::Drag, empty, 13);
+
   CHECK_OFFSET(InputMessage::Body::Finished, handled, 0);
   CHECK_OFFSET(InputMessage::Body::Finished, empty, 1);
   CHECK_OFFSET(InputMessage::Body::Finished, consumeTime, 8);
@@ -110,6 +116,7 @@
     static_assert(sizeof(InputMessage::Body::Finished) == 16);
     static_assert(sizeof(InputMessage::Body::Focus) == 8);
     static_assert(sizeof(InputMessage::Body::Capture) == 8);
+    static_assert(sizeof(InputMessage::Body::Drag) == 16);
 }
 
 // --- VerifiedInputEvent ---
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index eb3b434..026b19a 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -84,6 +84,7 @@
     name: "librenderengine_skia_sources",
     srcs: [
         "skia/AutoBackendTexture.cpp",
+        "skia/Cache.cpp",
         "skia/ColorSpaces.cpp",
         "skia/SkiaRenderEngine.cpp",
         "skia/SkiaGLRenderEngine.cpp",
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 79839c1..0c5a851 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -85,5 +85,15 @@
 
 RenderEngine::~RenderEngine() = default;
 
+void RenderEngine::validateInputBufferUsage(const sp<GraphicBuffer>& buffer) {
+    LOG_ALWAYS_FATAL_IF(!(buffer->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE),
+                        "input buffer not gpu readable");
+}
+
+void RenderEngine::validateOutputBufferUsage(const sp<GraphicBuffer>& buffer) {
+    LOG_ALWAYS_FATAL_IF(!(buffer->getUsage() & GraphicBuffer::USAGE_HW_RENDER),
+                        "output buffer not gpu writeable");
+}
+
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 397f038..a2963a7 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -515,7 +515,7 @@
     return mDrawingBuffer.get();
 }
 
-void GLESRenderEngine::primeCache() const {
+void GLESRenderEngine::primeCache() {
     ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
                                            mUseColorManagement, mPrecacheToneMapperShaderOnly);
 }
@@ -1125,6 +1125,8 @@
         return BAD_VALUE;
     }
 
+    validateOutputBufferUsage(buffer);
+
     std::unique_ptr<BindNativeBufferAsFramebuffer> fbo;
     // Gathering layers that requested blur, we'll need them to decide when to render to an
     // offscreen buffer, and when to render to the native buffer.
@@ -1249,6 +1251,7 @@
             isOpaque = layer->source.buffer.isOpaque;
 
             sp<GraphicBuffer> gBuf = layer->source.buffer.buffer;
+            validateInputBufferUsage(gBuf);
             bindExternalTextureBuffer(layer->source.buffer.textureName, gBuf,
                                       layer->source.buffer.fence);
 
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 7496b74..06a1722 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -57,7 +57,7 @@
                      EGLSurface protectedStub);
     ~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
 
-    void primeCache() const override;
+    void primeCache() override;
     void genTextures(size_t count, uint32_t* names) override;
     void deleteTextures(size_t count, uint32_t const* names) override;
     void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 572d348..7c51f1b 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -97,7 +97,7 @@
     // This interface, while still in use until a suitable replacement is built,
     // should be considered deprecated, minus some methods which still may be
     // used to support legacy behavior.
-    virtual void primeCache() const = 0;
+    virtual void primeCache() = 0;
 
     // dump the extension strings. always call the base class.
     virtual void dump(std::string& result) = 0;
@@ -201,6 +201,9 @@
     // we should not allow in general, so remove this.
     RenderEngineType getRenderEngineType() const { return mRenderEngineType; }
 
+    static void validateInputBufferUsage(const sp<GraphicBuffer>&);
+    static void validateOutputBufferUsage(const sp<GraphicBuffer>&);
+
 protected:
     friend class threaded::RenderEngineThreaded;
     const RenderEngineType mRenderEngineType;
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 895ba3f..5f75b81 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -35,7 +35,7 @@
     RenderEngine();
     ~RenderEngine() override;
 
-    MOCK_CONST_METHOD0(primeCache, void());
+    MOCK_METHOD0(primeCache, void());
     MOCK_METHOD1(dump, void(std::string&));
     MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
     MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
new file mode 100644
index 0000000..4fdae74
--- /dev/null
+++ b/libs/renderengine/skia/Cache.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Cache.h"
+#include "AutoBackendTexture.h"
+#include "SkiaRenderEngine.h"
+#include "android-base/unique_fd.h"
+#include "renderengine/DisplaySettings.h"
+#include "renderengine/LayerSettings.h"
+#include "ui/GraphicBuffer.h"
+#include "ui/GraphicTypes.h"
+#include "ui/PixelFormat.h"
+#include "ui/Rect.h"
+#include "utils/Timers.h"
+
+namespace android::renderengine::skia {
+
+static void drawShadowLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+                            sp<GraphicBuffer> dstBuffer) {
+    // Somewhat arbitrary dimensions, but on screen and slightly shorter, based
+    // on actual use.
+    FloatRect rect(0, 0, display.physicalDisplay.width(), display.physicalDisplay.height() - 30);
+    LayerSettings layer{
+            .geometry =
+                    Geometry{
+                            .boundaries = rect,
+                            .roundedCornersCrop = rect,
+                    },
+            .shadow =
+                    ShadowSettings{
+                            .ambientColor = vec4(0, 0, 0, 0.00935997f),
+                            .spotColor = vec4(0, 0, 0, 0.0455841f),
+                            .lightPos = vec3(370.508f, -1527.03f, 1650.f),
+                            .lightRadius = 2200.0f,
+                            .length = 0.955342f,
+                    },
+    };
+
+    auto layers = std::vector<const LayerSettings*>{&layer};
+    // The identity matrix will generate the fast shaders, and the second matrix
+    // (based on one seen while going from dialer to the home screen) will
+    // generate the slower (more general case) version. If we also need a
+    // slow version without color correction, we should use this matrix with
+    // display.outputDataspace set to SRGB.
+    for (const mat4 transform : { mat4(), mat4(0.728872f,   0.f,          0.f, 0.f,
+                                               0.f,         0.727627f,    0.f, 0.f,
+                                               0.f,         0.f,          1.f, 0.f,
+                                               167.355743f, 1852.257812f, 0.f, 1.f) }) {
+        layer.geometry.positionTransform = transform;
+        renderengine->drawLayers(display, layers, dstBuffer, false /* useFrameBufferCache*/,
+                                 base::unique_fd(), nullptr);
+    }
+}
+
+static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+                            sp<GraphicBuffer> dstBuffer, sp<GraphicBuffer> srcBuffer) {
+    const Rect& displayRect = display.physicalDisplay;
+    FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+    LayerSettings layer{
+            .geometry =
+                    Geometry{
+                            .boundaries = rect,
+                            // This matrix is based on actual data seen when opening the dialer.
+                            // What seems to be important in matching the actual use cases are:
+                            // - it is not identity
+                            // - the layer will be drawn (not clipped out etc)
+                            .positionTransform = mat4(.19f,     .0f, .0f,   .0f,
+                                                      .0f,     .19f, .0f,   .0f,
+                                                      .0f,      .0f, 1.f,   .0f,
+                                                      169.f, 1527.f, .0f,   1.f),
+                            .roundedCornersCrop = rect,
+                    },
+            .source = PixelSource{.buffer =
+                                          Buffer{
+                                                  .buffer = srcBuffer,
+                                                  .maxMasteringLuminance = 1000.f,
+                                                  .maxContentLuminance = 1000.f,
+                                          }},
+    };
+
+    // Test both drawRect and drawRRect
+    auto layers = std::vector<const LayerSettings*>{&layer};
+    for (float roundedCornersRadius : {0.0f, 500.f}) {
+        layer.geometry.roundedCornersRadius = roundedCornersRadius;
+        // No need to check UNKNOWN, which is treated as SRGB.
+        for (auto dataspace : {ui::Dataspace::SRGB, ui::Dataspace::DISPLAY_P3}) {
+            layer.sourceDataspace = dataspace;
+            for (bool isOpaque : {true, false}) {
+                layer.source.buffer.isOpaque = isOpaque;
+                for (auto alpha : {half(.23999f), half(1.0f)}) {
+                    layer.alpha = alpha;
+                    renderengine->drawLayers(display, layers, dstBuffer,
+                                             false /* useFrameBufferCache*/, base::unique_fd(),
+                                             nullptr);
+                }
+            }
+        }
+    }
+}
+
+void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
+    const nsecs_t timeBefore = systemTime();
+    // The dimensions should not matter, so long as we draw inside them.
+    const Rect displayRect(0, 0, 1080, 2340);
+    DisplaySettings display{
+            .physicalDisplay = displayRect,
+            .clip = displayRect,
+            .maxLuminance = 500,
+            .outputDataspace = ui::Dataspace::DISPLAY_P3,
+    };
+
+    const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+
+    sp<GraphicBuffer> dstBuffer =
+            new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
+                              usage, "primeShaderCache_dst");
+    // This buffer will be the source for the call to drawImageLayers. Draw
+    // something to it as a placeholder for what an app draws. We should draw
+    // something, but the details are not important. We only need one version of
+    // the shadow's SkSL, so draw it here, giving us both a placeholder image
+    // and a chance to compile the shadow's SkSL.
+    sp<GraphicBuffer> srcBuffer =
+            new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
+                              usage, "drawImageLayer_src");
+    drawShadowLayer(renderengine, display, srcBuffer);
+
+    drawImageLayers(renderengine, display, dstBuffer, srcBuffer);
+    const nsecs_t timeAfter = systemTime();
+    const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
+    ALOGD("shader cache generated in %f ms\n", compileTimeMs);
+}
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/Cache.h b/libs/renderengine/skia/Cache.h
new file mode 100644
index 0000000..437571e
--- /dev/null
+++ b/libs/renderengine/skia/Cache.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android::renderengine::skia {
+
+class SkiaRenderEngine;
+
+class Cache {
+public:
+    static void primeShaderCache(SkiaRenderEngine*);
+
+private:
+    Cache() = default;
+};
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index acd3dcf..4d10d74 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -40,6 +40,7 @@
 #include <ui/DebugUtils.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/Trace.h>
+#include "Cache.h"
 
 #include <cmath>
 #include <cstdint>
@@ -224,6 +225,10 @@
     return engine;
 }
 
+void SkiaGLRenderEngine::primeCache() {
+    Cache::primeShaderCache(this);
+}
+
 EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
     status_t err;
     EGLConfig config;
@@ -596,6 +601,8 @@
         return BAD_VALUE;
     }
 
+    validateOutputBufferUsage(buffer);
+
     auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
     auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
     AHardwareBuffer_Desc bufferDesc;
@@ -642,9 +649,8 @@
     if (mBlurFilter) {
         bool requiresCompositionLayer = false;
         for (const auto& layer : layers) {
-            if (layer->backgroundBlurRadius > 0) {
-                // when skbug.com/11208 and b/176903027 are resolved we can add the additional
-                // restriction for layer->backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius
+            if (layer->backgroundBlurRadius > 0 &&
+                layer->backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius) {
                 requiresCompositionLayer = true;
             }
             for (auto region : layer->blurRegions) {
@@ -813,6 +819,7 @@
         SkPaint paint;
         if (layer->source.buffer.buffer) {
             ATRACE_NAME("DrawImage");
+            validateInputBufferUsage(layer->source.buffer.buffer);
             const auto& item = layer->source.buffer;
             std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr;
             auto iter = mTextureCache.find(item.buffer->getId());
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 1c3a633..15d834d 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -50,6 +50,7 @@
                        EGLSurface protectedPlaceholder);
     ~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
 
+    void primeCache() override;
     void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
     void unbindExternalTextureBuffer(uint64_t bufferId) override;
     status_t drawLayers(const DisplaySettings& display,
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 79a1040..f403725 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -39,7 +39,7 @@
     SkiaRenderEngine(RenderEngineType type) : RenderEngine(type) {}
     ~SkiaRenderEngine() override {}
 
-    virtual void primeCache() const override{};
+    virtual void primeCache() override{};
     virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override{};
     virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
     virtual void cacheExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/){};
@@ -64,4 +64,4 @@
 } // namespace renderengine
 } // namespace android
 
-#endif /* SF_GLESRENDERENGINE_H_ */
\ No newline at end of file
+#endif /* SF_GLESRENDERENGINE_H_ */
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 7c7d165..6a91c7c 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -86,7 +86,7 @@
     }
 }
 
-void RenderEngineThreaded::primeCache() const {
+void RenderEngineThreaded::primeCache() {
     std::promise<void> resultPromise;
     std::future<void> resultFuture = resultPromise.get_future();
     {
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index d362e17..df0551d 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -42,7 +42,7 @@
 
     RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type);
     ~RenderEngineThreaded() override;
-    void primeCache() const override;
+    void primeCache() override;
 
     void dump(std::string& result) override;
 
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index b2ddb42..7bd0c6b 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -113,6 +113,8 @@
 
     void setPointerCapture(bool enabled) override {}
 
+    void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}
+
     InputDispatcherConfiguration mConfig;
 };
 
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index a19b04f..5270b8a 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -130,6 +130,23 @@
                         pointerCaptureEnabled ? "true" : "false");
 }
 
+// --- DragEntry ---
+
+// Drag notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries
+DragEntry::DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting,
+                     float x, float y)
+      : EventEntry(id, Type::DRAG, eventTime, POLICY_FLAG_PASS_TO_USER),
+        connectionToken(connectionToken),
+        isExiting(isExiting),
+        x(x),
+        y(y) {}
+
+DragEntry::~DragEntry() {}
+
+std::string DragEntry::getDescription() const {
+    return StringPrintf("DragEntry(isExiting=%s, x=%f, y=%f)", isExiting ? "true" : "false", x, y);
+}
+
 // --- KeyEntry ---
 
 KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index ed17e68..f3ef64b 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -38,6 +38,7 @@
         MOTION,
         SENSOR,
         POINTER_CAPTURE_CHANGED,
+        DRAG,
     };
 
     int32_t id;
@@ -111,6 +112,18 @@
     virtual ~PointerCaptureChangedEntry();
 };
 
+struct DragEntry : EventEntry {
+    sp<IBinder> connectionToken;
+    bool isExiting;
+    float x, y;
+
+    DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting, float x,
+              float y);
+    std::string getDescription() const override;
+
+    ~DragEntry() override;
+};
+
 struct KeyEntry : EventEntry {
     int32_t deviceId;
     uint32_t source;
@@ -274,6 +287,8 @@
     int32_t pid;
     nsecs_t consumeTime; // time when the event was consumed by InputConsumer
     int32_t displayId;
+    float x;
+    float y;
 };
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 19f8694..397a9d7 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -80,6 +80,7 @@
 #define INDENT4 "        "
 
 using android::base::HwTimeoutMultiplier;
+using android::base::Result;
 using android::base::StringPrintf;
 using android::os::BlockUntrustedTouchesMode;
 using android::os::IInputConstants;
@@ -332,10 +333,11 @@
                                                           int32_t inputTargetFlags) {
     if (eventEntry->type == EventEntry::Type::MOTION) {
         const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
-        if (motionEntry.source & AINPUT_SOURCE_CLASS_JOYSTICK) {
+        if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) == 0) {
             const ui::Transform identityTransform;
-            // Use identity transform for joystick events events because they don't depend on
-            // the window info
+            // Use identity transform for events that are not pointer events because their axes
+            // values do not represent on-screen coordinates, so they should not have any window
+            // transformations applied to them.
             return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, identityTransform,
                                                    1.0f /*globalScaleFactor*/);
         }
@@ -751,6 +753,14 @@
             break;
         }
 
+        case EventEntry::Type::DRAG: {
+            std::shared_ptr<DragEntry> typedEntry =
+                    std::static_pointer_cast<DragEntry>(mPendingEvent);
+            dispatchDragLocked(currentTime, typedEntry);
+            done = true;
+            break;
+        }
+
         case EventEntry::Type::KEY: {
             std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
             if (isAppSwitchDue) {
@@ -919,7 +929,8 @@
         case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET:
         case EventEntry::Type::SENSOR:
-        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+        case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+        case EventEntry::Type::DRAG: {
             // nothing to do
             break;
         }
@@ -941,7 +952,8 @@
 sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
                                                                  int32_t y, TouchState* touchState,
                                                                  bool addOutsideTargets,
-                                                                 bool addPortalWindows) {
+                                                                 bool addPortalWindows,
+                                                                 bool ignoreDragWindow) {
     if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {
         LOG_ALWAYS_FATAL(
                 "Must provide a valid touch state if adding portal windows or outside targets");
@@ -949,6 +961,9 @@
     // Traverse windows from front to back to find touched window.
     const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
     for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
+        if (ignoreDragWindow && haveSameToken(windowHandle, touchState->dragWindow)) {
+            continue;
+        }
         const InputWindowInfo* windowInfo = windowHandle->getInfo();
         if (windowInfo->displayId == displayId) {
             auto flags = windowInfo->flags;
@@ -1058,7 +1073,8 @@
         case EventEntry::Type::SENSOR: {
             break;
         }
-        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+        case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+        case EventEntry::Type::DRAG: {
             break;
         }
         case EventEntry::Type::FOCUS:
@@ -1552,6 +1568,35 @@
     return true;
 }
 
+void InputDispatcher::enqueueDragEventLocked(const sp<InputWindowHandle>& windowHandle,
+                                             bool isExiting, const MotionEntry& motionEntry) {
+    // If the window needs enqueue a drag event, the pointerCount should be 1 and the action should
+    // be AMOTION_EVENT_ACTION_MOVE, that could guarantee the first pointer is always valid.
+    LOG_ALWAYS_FATAL_IF(motionEntry.pointerCount != 1);
+    PointerCoords pointerCoords;
+    pointerCoords.copyFrom(motionEntry.pointerCoords[0]);
+    pointerCoords.transform(windowHandle->getInfo()->transform);
+
+    std::unique_ptr<DragEntry> dragEntry =
+            std::make_unique<DragEntry>(mIdGenerator.nextId(), motionEntry.eventTime,
+                                        windowHandle->getToken(), isExiting, pointerCoords.getX(),
+                                        pointerCoords.getY());
+
+    enqueueInboundEventLocked(std::move(dragEntry));
+}
+
+void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) {
+    std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
+    if (channel == nullptr) {
+        return; // Window has gone away
+    }
+    InputTarget target;
+    target.inputChannel = channel;
+    target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+    entry->dispatchInProgress = true;
+    dispatchEventLocked(currentTime, entry, {target});
+}
+
 void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
@@ -1658,7 +1703,8 @@
         case EventEntry::Type::FOCUS:
         case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET:
-        case EventEntry::Type::SENSOR: {
+        case EventEntry::Type::SENSOR:
+        case EventEntry::Type::DRAG: {
             ALOGE("%s events do not have a target display", NamedEnum::string(entry.type).c_str());
             return ADISPLAY_ID_NONE;
         }
@@ -2015,6 +2061,8 @@
             goto Failed;
         }
 
+        addDragEventLocked(entry, tempTouchState);
+
         // Check whether touches should slip outside of the current foreground window.
         if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
             tempTouchState.isSlippery()) {
@@ -2270,6 +2318,42 @@
     return injectionResult;
 }
 
+void InputDispatcher::addDragEventLocked(const MotionEntry& entry, TouchState& state) {
+    if (entry.pointerCount != 1 || !state.dragWindow) {
+        return;
+    }
+
+    int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK;
+    int32_t x = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+    int32_t y = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+    if (maskedAction == AMOTION_EVENT_ACTION_MOVE) {
+        const sp<InputWindowHandle> hoverWindowHandle =
+                findTouchedWindowAtLocked(entry.displayId, x, y, &state,
+                                          false /*addOutsideTargets*/, false /*addPortalWindows*/,
+                                          true /*ignoreDragWindow*/);
+        // enqueue drag exit if needed.
+        if (hoverWindowHandle != state.dragHoverWindowHandle &&
+            !haveSameToken(hoverWindowHandle, state.dragHoverWindowHandle)) {
+            if (state.dragHoverWindowHandle != nullptr) {
+                enqueueDragEventLocked(state.dragHoverWindowHandle, true /*isExiting*/, entry);
+            }
+            state.dragHoverWindowHandle = hoverWindowHandle;
+        }
+        // enqueue drag location if needed.
+        if (hoverWindowHandle != nullptr) {
+            enqueueDragEventLocked(hoverWindowHandle, false /*isExiting*/, entry);
+        }
+    } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
+               maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+        if (state.dragHoverWindowHandle && maskedAction == AMOTION_EVENT_ACTION_UP) {
+            vec2 local = state.dragHoverWindowHandle->getInfo()->transform.transform(x, y);
+            notifyDropWindowLocked(state.dragHoverWindowHandle->getToken(), local.x, local.y);
+        }
+        state.dragWindow = nullptr;
+        state.dragHoverWindowHandle = nullptr;
+    }
+}
+
 void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
                                             int32_t targetFlags, BitSet32 pointerIds,
                                             std::vector<InputTarget>& inputTargets) {
@@ -2541,7 +2625,8 @@
 
 void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
     if (eventEntry.type == EventEntry::Type::FOCUS ||
-        eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED) {
+        eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED ||
+        eventEntry.type == EventEntry::Type::DRAG) {
         // Focus or pointer capture changed events are passed to apps, but do not represent user
         // activity.
         return;
@@ -2583,7 +2668,8 @@
         case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET:
         case EventEntry::Type::SENSOR:
-        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+        case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+        case EventEntry::Type::DRAG: {
             LOG_ALWAYS_FATAL("%s events are not user activity",
                              NamedEnum::string(eventEntry.type).c_str());
             break;
@@ -2797,7 +2883,8 @@
             break;
         }
         case EventEntry::Type::FOCUS:
-        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+        case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+        case EventEntry::Type::DRAG: {
             break;
         }
         case EventEntry::Type::SENSOR: {
@@ -3020,6 +3107,15 @@
                 break;
             }
 
+            case EventEntry::Type::DRAG: {
+                const DragEntry& dragEntry = static_cast<const DragEntry&>(eventEntry);
+                status = connection->inputPublisher.publishDragEvent(dispatchEntry->seq,
+                                                                     dragEntry.id, dragEntry.x,
+                                                                     dragEntry.y,
+                                                                     dragEntry.isExiting);
+                break;
+            }
+
             case EventEntry::Type::CONFIGURATION_CHANGED:
             case EventEntry::Type::DEVICE_RESET:
             case EventEntry::Type::SENSOR: {
@@ -3192,17 +3288,17 @@
 
             nsecs_t currentTime = now();
             bool gotOne = false;
-            status_t status;
+            status_t status = OK;
             for (;;) {
-                std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)> callback =
-                        std::bind(&InputDispatcher::finishDispatchCycleLocked, d, currentTime,
-                                  connection, std::placeholders::_1, std::placeholders::_2,
-                                  std::placeholders::_3);
-
-                status = connection->inputPublisher.receiveFinishedSignal(callback);
-                if (status) {
+                Result<InputPublisher::Finished> result =
+                        connection->inputPublisher.receiveFinishedSignal();
+                if (!result.ok()) {
+                    status = result.error().code();
                     break;
                 }
+                const InputPublisher::Finished& finished = *result;
+                d->finishDispatchCycleLocked(currentTime, connection, finished.seq,
+                                             finished.handled, finished.consumeTime);
                 gotOne = true;
             }
             if (gotOne) {
@@ -3317,7 +3413,8 @@
                 break;
             }
             case EventEntry::Type::FOCUS:
-            case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+            case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+            case EventEntry::Type::DRAG: {
                 LOG_ALWAYS_FATAL("Canceling %s events is not supported",
                                  NamedEnum::string(cancelationEventEntry->type).c_str());
                 break;
@@ -3382,7 +3479,8 @@
             case EventEntry::Type::CONFIGURATION_CHANGED:
             case EventEntry::Type::DEVICE_RESET:
             case EventEntry::Type::POINTER_CAPTURE_CHANGED:
-            case EventEntry::Type::SENSOR: {
+            case EventEntry::Type::SENSOR:
+            case EventEntry::Type::DRAG: {
                 LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
                                  NamedEnum::string(downEventEntry->type).c_str());
                 break;
@@ -4346,6 +4444,15 @@
                 ++i;
             }
         }
+
+        // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. we
+        // could just clear the state here.
+        if (state.dragWindow &&
+            std::find(windowHandles.begin(), windowHandles.end(), state.dragWindow) ==
+                    windowHandles.end()) {
+            state.dragWindow = nullptr;
+            state.dragHoverWindowHandle = nullptr;
+        }
     }
 
     // Release information for windows that are no longer present.
@@ -4536,7 +4643,8 @@
     mBlockUntrustedTouchesMode = mode;
 }
 
-bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
+bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+                                         bool isDragDrop) {
     if (fromToken == toToken) {
         if (DEBUG_FOCUS) {
             ALOGD("Trivial transfer to same window.");
@@ -4580,6 +4688,11 @@
                              InputTarget::FLAG_DISPATCH_AS_IS);
                     state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
 
+                    // Store the dragging window.
+                    if (isDragDrop) {
+                        state.dragWindow = toWindowHandle;
+                    }
+
                     found = true;
                     goto Found;
                 }
@@ -4890,8 +5003,7 @@
     }
 }
 
-base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(
-        const std::string& name) {
+Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
 #if DEBUG_CHANNEL_CREATION
     ALOGD("channel '%s' ~ createInputChannel", name.c_str());
 #endif
@@ -4920,8 +5032,10 @@
     return clientChannel;
 }
 
-base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(
-        int32_t displayId, bool isGestureMonitor, const std::string& name, int32_t pid) {
+Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_t displayId,
+                                                                          bool isGestureMonitor,
+                                                                          const std::string& name,
+                                                                          int32_t pid) {
     std::shared_ptr<InputChannel> serverChannel;
     std::unique_ptr<InputChannel> clientChannel;
     status_t result = openInputChannelPair(name, serverChannel, clientChannel);
@@ -5192,6 +5306,15 @@
     postCommandLocked(std::move(commandEntry));
 }
 
+void InputDispatcher::notifyDropWindowLocked(const sp<IBinder>& token, float x, float y) {
+    std::unique_ptr<CommandEntry> commandEntry =
+            std::make_unique<CommandEntry>(&InputDispatcher::doNotifyDropWindowLockedInterruptible);
+    commandEntry->newToken = token;
+    commandEntry->x = x;
+    commandEntry->y = y;
+    postCommandLocked(std::move(commandEntry));
+}
+
 void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
     if (connection == nullptr) {
         LOG_ALWAYS_FATAL("Caller must check for nullness");
@@ -5301,6 +5424,13 @@
     mLock.lock();
 }
 
+void InputDispatcher::doNotifyDropWindowLockedInterruptible(CommandEntry* commandEntry) {
+    sp<IBinder> newToken = commandEntry->newToken;
+    mLock.unlock();
+    mPolicy->notifyDropWindow(newToken, commandEntry->x, commandEntry->y);
+    mLock.lock();
+}
+
 void InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) {
     mLock.unlock();
 
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 83094c2..593ec23 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -120,8 +120,8 @@
     virtual void setMaximumObscuringOpacityForTouch(float opacity) override;
     virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
 
-    virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
-                                    const sp<IBinder>& toToken) override;
+    virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+                                    bool isDragDrop = false) override;
 
     virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
             const std::string& name) override;
@@ -185,6 +185,9 @@
     // Enqueues a focus event.
     void enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
                                  const std::string& reason) REQUIRES(mLock);
+    // Enqueues a drag event.
+    void enqueueDragEventLocked(const sp<InputWindowHandle>& windowToken, bool isExiting,
+                                const MotionEntry& motionEntry) REQUIRES(mLock);
 
     // Adds an event to a queue of recent events for debugging purposes.
     void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
@@ -204,7 +207,8 @@
     sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
                                                     TouchState* touchState,
                                                     bool addOutsideTargets = false,
-                                                    bool addPortalWindows = false) REQUIRES(mLock);
+                                                    bool addPortalWindows = false,
+                                                    bool ignoreDragWindow = false) REQUIRES(mLock);
 
     // All registered connections mapped by channel file descriptor.
     std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
@@ -387,6 +391,7 @@
                              const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
     void dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry,
                               DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
+    void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) REQUIRES(mLock);
     void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
     void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry);
 
@@ -489,10 +494,12 @@
                                    std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
     void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId,
                                           float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
-
     void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
     bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
                                   const InjectionState* injectionState);
+    // Enqueue a drag event if needed, and update the touch state.
+    // Uses findTouchedWindowTargetsLocked to make the decision
+    void addDragEventLocked(const MotionEntry& entry, TouchState& state) REQUIRES(mLock);
 
     struct TouchOcclusionInfo {
         bool hasBlockingOcclusion;
@@ -585,6 +592,7 @@
     void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock);
     void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus)
             REQUIRES(mLock);
+    void notifyDropWindowLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock);
     void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
     void onAnrLocked(std::shared_ptr<InputApplicationHandle> application) REQUIRES(mLock);
     void onUntrustedTouchLocked(const std::string& obscuringPackage) REQUIRES(mLock);
@@ -600,6 +608,8 @@
             REQUIRES(mLock);
     void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+    void doNotifyDropWindowLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+
     // ANR-related callbacks - start
     void doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 81b3cf0..4165f49 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -49,6 +49,8 @@
     windows = other.windows;
     portalWindows = other.portalWindows;
     gestureMonitors = other.gestureMonitors;
+    dragHoverWindowHandle = other.dragHoverWindowHandle;
+    dragWindow = other.dragWindow;
 }
 
 void TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 623c6a8..d7a561c 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -41,6 +41,11 @@
 
     std::vector<TouchedMonitor> gestureMonitors;
 
+    // The last drag hover window which could receive the drag event.
+    sp<InputWindowHandle> dragHoverWindowHandle;
+    // The window being dragged.
+    sp<InputWindowHandle> dragWindow;
+
     TouchState();
     ~TouchState();
     void reset();
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 3491893..b601dfc 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -149,8 +149,8 @@
      *
      * Returns true on success.  False if the window did not actually have touch focus.
      */
-    virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
-
+    virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+                                    bool isDragDrop) = 0;
     /**
      * Sets focus on the specified window.
      */
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 439d85e..219f45a 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -157,6 +157,9 @@
      * InputDispatcher is solely responsible for updating the Pointer Capture state.
      */
     virtual void setPointerCapture(bool enabled) = 0;
+
+    /* Notifies the policy that the drag window has moved over to another window */
+    virtual void notifyDropWindow(const sp<IBinder>& token, float x, float y) = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
index 18d0226..743587c 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -67,6 +67,7 @@
     cflags: ["-Wall", "-Werror"],
 
     shared_libs: [
+        "libbase",
         "libbinder",
         "libinputflingerhost",
         "libutils",
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 8a2828c..1af70a4 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -68,6 +68,7 @@
         "libcutils",
         "libinput",
         "liblog",
+        "libstatslog",
         "libui",
         "libutils",
     ],
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 1a7fcd5..07011f5 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -37,13 +37,14 @@
 
 // #define LOG_NDEBUG 0
 #include <android-base/file.h>
-#include <android-base/strings.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <cutils/properties.h>
 #include <input/KeyCharacterMap.h>
 #include <input/KeyLayoutMap.h>
 #include <input/VirtualKeyMap.h>
 #include <openssl/sha.h>
+#include <statslog.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
 #include <utils/Timers.h>
@@ -1461,7 +1462,7 @@
             for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
                  it++) {
                 std::unique_ptr<TouchVideoDevice>& videoDevice = *it;
-                if (tryAddVideoDevice(*device, videoDevice)) {
+                if (tryAddVideoDeviceLocked(*device, videoDevice)) {
                     // videoDevice was transferred to 'device'
                     it = mUnattachedVideoDevices.erase(it);
                     break;
@@ -1771,6 +1772,13 @@
     }
 }
 
+void EventHub::reportDeviceAddedForStatisticsLocked(const InputDeviceIdentifier& identifier,
+                                                    Flags<InputDeviceClass> classes) {
+    android::util::stats_write(android::util::INPUTDEVICE_REGISTERED, identifier.name.c_str(),
+                               identifier.vendor, identifier.product, identifier.version,
+                               identifier.bus, identifier.uniqueId.c_str(), classes.get());
+}
+
 void EventHub::openDeviceLocked(const std::string& devicePath) {
     // If an input device happens to register around the time when EventHub's constructor runs, it
     // is possible that the same input event node (for example, /dev/input/event3) will be noticed
@@ -2076,7 +2084,7 @@
     }
     // Transfer ownership of this video device to a matching input device
     for (const auto& [id, device] : mDevices) {
-        if (tryAddVideoDevice(*device, videoDevice)) {
+        if (tryAddVideoDeviceLocked(*device, videoDevice)) {
             return; // 'device' now owns 'videoDevice'
         }
     }
@@ -2088,8 +2096,8 @@
     mUnattachedVideoDevices.push_back(std::move(videoDevice));
 }
 
-bool EventHub::tryAddVideoDevice(EventHub::Device& device,
-                                 std::unique_ptr<TouchVideoDevice>& videoDevice) {
+bool EventHub::tryAddVideoDeviceLocked(EventHub::Device& device,
+                                       std::unique_ptr<TouchVideoDevice>& videoDevice) {
     if (videoDevice->getName() != device.identifier.name) {
         return false;
     }
@@ -2163,6 +2171,7 @@
 }
 
 void EventHub::addDeviceLocked(std::unique_ptr<Device> device) {
+    reportDeviceAddedForStatisticsLocked(device->identifier, device->classes);
     mOpeningDevices.push_back(std::move(device));
 }
 
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 5e5f85e..2afaa85 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -570,8 +570,8 @@
     /**
      * Create a new device for the provided path.
      */
-    void openDeviceLocked(const std::string& devicePath);
-    void openVideoDeviceLocked(const std::string& devicePath);
+    void openDeviceLocked(const std::string& devicePath) REQUIRES(mLock);
+    void openVideoDeviceLocked(const std::string& devicePath) REQUIRES(mLock);
     /**
      * Try to associate a video device with an input device. If the association succeeds,
      * the videoDevice is moved into the input device. 'videoDevice' will become null if this
@@ -579,39 +579,42 @@
      * Return true if the association succeeds.
      * Return false otherwise.
      */
-    bool tryAddVideoDevice(Device& device, std::unique_ptr<TouchVideoDevice>& videoDevice);
-    void createVirtualKeyboardLocked();
-    void addDeviceLocked(std::unique_ptr<Device> device);
-    void assignDescriptorLocked(InputDeviceIdentifier& identifier);
+    bool tryAddVideoDeviceLocked(Device& device, std::unique_ptr<TouchVideoDevice>& videoDevice)
+            REQUIRES(mLock);
+    void createVirtualKeyboardLocked() REQUIRES(mLock);
+    void addDeviceLocked(std::unique_ptr<Device> device) REQUIRES(mLock);
+    void assignDescriptorLocked(InputDeviceIdentifier& identifier) REQUIRES(mLock);
 
-    void closeDeviceByPathLocked(const std::string& devicePath);
-    void closeVideoDeviceByPathLocked(const std::string& devicePath);
-    void closeDeviceLocked(Device& device);
-    void closeAllDevicesLocked();
+    void closeDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock);
+    void closeVideoDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock);
+    void closeDeviceLocked(Device& device) REQUIRES(mLock);
+    void closeAllDevicesLocked() REQUIRES(mLock);
 
     status_t registerFdForEpoll(int fd);
     status_t unregisterFdFromEpoll(int fd);
-    status_t registerDeviceForEpollLocked(Device& device);
-    void registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice);
-    status_t unregisterDeviceFromEpollLocked(Device& device);
-    void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice);
+    status_t registerDeviceForEpollLocked(Device& device) REQUIRES(mLock);
+    void registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice) REQUIRES(mLock);
+    status_t unregisterDeviceFromEpollLocked(Device& device) REQUIRES(mLock);
+    void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice) REQUIRES(mLock);
 
-    status_t scanDirLocked(const std::string& dirname);
-    status_t scanVideoDirLocked(const std::string& dirname);
-    void scanDevicesLocked();
-    status_t readNotifyLocked();
+    status_t scanDirLocked(const std::string& dirname) REQUIRES(mLock);
+    status_t scanVideoDirLocked(const std::string& dirname) REQUIRES(mLock);
+    void scanDevicesLocked() REQUIRES(mLock);
+    status_t readNotifyLocked() REQUIRES(mLock);
 
-    Device* getDeviceByDescriptorLocked(const std::string& descriptor) const;
-    Device* getDeviceLocked(int32_t deviceId) const;
-    Device* getDeviceByPathLocked(const std::string& devicePath) const;
+    Device* getDeviceByDescriptorLocked(const std::string& descriptor) const REQUIRES(mLock);
+    Device* getDeviceLocked(int32_t deviceId) const REQUIRES(mLock);
+    Device* getDeviceByPathLocked(const std::string& devicePath) const REQUIRES(mLock);
     /**
      * Look through all available fd's (both for input devices and for video devices),
      * and return the device pointer.
      */
-    Device* getDeviceByFdLocked(int fd) const;
+    Device* getDeviceByFdLocked(int fd) const REQUIRES(mLock);
 
-    int32_t getNextControllerNumberLocked(const std::string& name);
-    void releaseControllerNumberLocked(int32_t num);
+    int32_t getNextControllerNumberLocked(const std::string& name) REQUIRES(mLock);
+    void releaseControllerNumberLocked(int32_t num) REQUIRES(mLock);
+    void reportDeviceAddedForStatisticsLocked(const InputDeviceIdentifier& identifier,
+                                              Flags<InputDeviceClass> classes) REQUIRES(mLock);
 
     // Protect all internal state.
     mutable std::mutex mLock;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index bb12be7..d6bd823 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -188,11 +188,29 @@
 
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
         mOrientation = DISPLAY_ORIENTATION_0;
-        if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
-            std::optional<DisplayViewport> internalViewport =
-                    config->getDisplayViewportByType(ViewportType::INTERNAL);
-            if (internalViewport) {
-                mOrientation = internalViewport->orientation;
+        const bool isOrientedDevice =
+                (mParameters.orientationAware && mParameters.hasAssociatedDisplay);
+
+        if (isPerWindowInputRotationEnabled()) {
+            // When per-window input rotation is enabled, InputReader works in the un-rotated
+            // coordinate space, so we don't need to do anything if the device is already
+            // orientation-aware. If the device is not orientation-aware, then we need to apply the
+            // inverse rotation of the display so that when the display rotation is applied later
+            // as a part of the per-window transform, we get the expected screen coordinates.
+            if (!isOrientedDevice) {
+                std::optional<DisplayViewport> internalViewport =
+                        config->getDisplayViewportByType(ViewportType::INTERNAL);
+                if (internalViewport) {
+                    mOrientation = getInverseRotation(internalViewport->orientation);
+                }
+            }
+        } else {
+            if (isOrientedDevice) {
+                std::optional<DisplayViewport> internalViewport =
+                        config->getDisplayViewportByType(ViewportType::INTERNAL);
+                if (internalViewport) {
+                    mOrientation = internalViewport->orientation;
+                }
             }
         }
 
@@ -294,11 +312,8 @@
     float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale;
     bool moved = deltaX != 0 || deltaY != 0;
 
-    // Rotate delta according to orientation if needed.
-    if (mParameters.orientationAware && mParameters.hasAssociatedDisplay &&
-        (deltaX != 0.0f || deltaY != 0.0f)) {
-        rotateDelta(mOrientation, &deltaX, &deltaY);
-    }
+    // Rotate delta according to orientation.
+    rotateDelta(mOrientation, &deltaX, &deltaY);
 
     // Move the pointer.
     PointerProperties pointerProperties;
@@ -326,7 +341,15 @@
             mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
 
             if (moved) {
-                mPointerController->move(deltaX, deltaY);
+                float dx = deltaX;
+                float dy = deltaY;
+                if (isPerWindowInputRotationEnabled()) {
+                    // Rotate the delta from InputReader's un-rotated coordinate space to
+                    // PointerController's rotated coordinate space that is oriented with the
+                    // viewport.
+                    rotateDelta(getInverseRotation(mOrientation), &dx, &dy);
+                }
+                mPointerController->move(dx, dy);
             }
 
             if (buttonsChanged) {
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index 5344227..1843b03 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -17,6 +17,7 @@
 #ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
 #define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
 
+#include <android-base/properties.h>
 #include <input/DisplayViewport.h>
 #include <stdint.h>
 
@@ -28,6 +29,26 @@
 
 // --- Static Definitions ---
 
+// When per-window input rotation is enabled, display transformations such as rotation and
+// projection are part of the input window's transform. This means InputReader should work in the
+// un-rotated coordinate space.
+static bool isPerWindowInputRotationEnabled() {
+    static const bool PER_WINDOW_INPUT_ROTATION =
+            base::GetBoolProperty("persist.debug.per_window_input_rotation", false);
+    return PER_WINDOW_INPUT_ROTATION;
+}
+
+static int32_t getInverseRotation(int32_t orientation) {
+    switch (orientation) {
+        case DISPLAY_ORIENTATION_90:
+            return DISPLAY_ORIENTATION_270;
+        case DISPLAY_ORIENTATION_270:
+            return DISPLAY_ORIENTATION_90;
+        default:
+            return orientation;
+    }
+}
+
 static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) {
     float temp;
     switch (orientation) {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 1a7ddee..13712ee 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -28,6 +28,30 @@
 
 namespace android {
 
+namespace {
+
+// Rotates the given point (x, y) by the supplied orientation. The width and height are the
+// dimensions of the surface prior to this rotation being applied.
+void rotatePoint(int32_t orientation, float& x, float& y, int32_t width, int32_t height) {
+    rotateDelta(orientation, &x, &y);
+    switch (orientation) {
+        case DISPLAY_ORIENTATION_90:
+            y += width;
+            break;
+        case DISPLAY_ORIENTATION_180:
+            x += width;
+            y += height;
+            break;
+        case DISPLAY_ORIENTATION_270:
+            x += height;
+            break;
+        default:
+            break;
+    }
+}
+
+} // namespace
+
 // --- Constants ---
 
 // Maximum amount of latency to add to touch events while waiting for data from an
@@ -729,8 +753,20 @@
             mSurfaceRight = mSurfaceLeft + naturalLogicalWidth;
             mSurfaceBottom = mSurfaceTop + naturalLogicalHeight;
 
-            mSurfaceOrientation =
-                    mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0;
+            if (isPerWindowInputRotationEnabled()) {
+                // When per-window input rotation is enabled, InputReader works in the un-rotated
+                // coordinate space, so we don't need to do anything if the device is already
+                // orientation-aware. If the device is not orientation-aware, then we need to apply
+                // the inverse rotation of the display so that when the display rotation is applied
+                // later as a part of the per-window transform, we get the expected screen
+                // coordinates.
+                mSurfaceOrientation = mParameters.orientationAware
+                        ? DISPLAY_ORIENTATION_0
+                        : getInverseRotation(mViewport.orientation);
+            } else {
+                mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation
+                                                                   : DISPLAY_ORIENTATION_0;
+            }
         } else {
             mPhysicalWidth = rawWidth;
             mPhysicalHeight = rawHeight;
@@ -1637,10 +1673,9 @@
     mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
 
     mPointerController->setButtonState(mCurrentRawState.buttonState);
-    mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
-                                 mCurrentCookedState.cookedPointerData.idToIndex,
-                                 mCurrentCookedState.cookedPointerData.touchingIdBits,
-                                 mViewport.displayId);
+    setTouchSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
+                  mCurrentCookedState.cookedPointerData.idToIndex,
+                  mCurrentCookedState.cookedPointerData.touchingIdBits, mViewport.displayId);
 }
 
 bool TouchInputMapper::isTouchScreen() {
@@ -2378,10 +2413,9 @@
         }
 
         if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
-            mPointerController->setSpots(mPointerGesture.currentGestureCoords,
-                                         mPointerGesture.currentGestureIdToIndex,
-                                         mPointerGesture.currentGestureIdBits,
-                                         mPointerController->getDisplayId());
+            setTouchSpots(mPointerGesture.currentGestureCoords,
+                          mPointerGesture.currentGestureIdToIndex,
+                          mPointerGesture.currentGestureIdBits, mPointerController->getDisplayId());
         }
     } else {
         mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
@@ -2525,8 +2559,7 @@
         // the pointer is hovering again even if the user is not currently touching
         // the touch pad.  This ensures that a view will receive a fresh hover enter
         // event after a tap.
-        float x, y;
-        mPointerController->getPosition(&x, &y);
+        auto [x, y] = getMouseCursorPosition();
 
         PointerProperties pointerProperties;
         pointerProperties.clear();
@@ -2783,13 +2816,12 @@
             // Move the pointer using a relative motion.
             // When using spots, the click will occur at the position of the anchor
             // spot and all other spots will move there.
-            mPointerController->move(deltaX, deltaY);
+            moveMouseCursor(deltaX, deltaY);
         } else {
             mPointerVelocityControl.reset();
         }
 
-        float x, y;
-        mPointerController->getPosition(&x, &y);
+        auto [x, y] = getMouseCursorPosition();
 
         mPointerGesture.currentGestureMode = PointerGesture::Mode::BUTTON_CLICK_OR_DRAG;
         mPointerGesture.currentGestureIdBits.clear();
@@ -2815,8 +2847,7 @@
              mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) &&
             lastFingerCount == 1) {
             if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
-                float x, y;
-                mPointerController->getPosition(&x, &y);
+                auto [x, y] = getMouseCursorPosition();
                 if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
                     fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
 #if DEBUG_GESTURES
@@ -2884,8 +2915,7 @@
         mPointerGesture.currentGestureMode = PointerGesture::Mode::HOVER;
         if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
             if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
-                float x, y;
-                mPointerController->getPosition(&x, &y);
+                auto [x, y] = getMouseCursorPosition();
                 if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
                     fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
                     mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG;
@@ -2919,7 +2949,7 @@
 
             // Move the pointer using a relative motion.
             // When using spots, the hover or drag will occur at the position of the anchor spot.
-            mPointerController->move(deltaX, deltaY);
+            moveMouseCursor(deltaX, deltaY);
         } else {
             mPointerVelocityControl.reset();
         }
@@ -2941,8 +2971,7 @@
             down = false;
         }
 
-        float x, y;
-        mPointerController->getPosition(&x, &y);
+        auto [x, y] = getMouseCursorPosition();
 
         mPointerGesture.currentGestureIdBits.clear();
         mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
@@ -3015,8 +3044,9 @@
             mCurrentRawState.rawPointerData
                     .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX,
                                                    &mPointerGesture.referenceTouchY);
-            mPointerController->getPosition(&mPointerGesture.referenceGestureX,
-                                            &mPointerGesture.referenceGestureY);
+            auto [x, y] = getMouseCursorPosition();
+            mPointerGesture.referenceGestureX = x;
+            mPointerGesture.referenceGestureY = y;
         }
 
         // Clear the reference deltas for fingers not yet included in the reference calculation.
@@ -3354,14 +3384,13 @@
     if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
         uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit();
         uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id];
-        float x = mCurrentCookedState.cookedPointerData.pointerCoords[index].getX();
-        float y = mCurrentCookedState.cookedPointerData.pointerCoords[index].getY();
-        mPointerController->setPosition(x, y);
+        setMouseCursorPosition(mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(),
+                               mCurrentCookedState.cookedPointerData.pointerCoords[index].getY());
 
         hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id);
         down = !hovering;
 
-        mPointerController->getPosition(&x, &y);
+        auto [x, y] = getMouseCursorPosition();
         mPointerSimple.currentCoords.copyFrom(
                 mCurrentCookedState.cookedPointerData.pointerCoords[index]);
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
@@ -3402,7 +3431,7 @@
             rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
             mPointerVelocityControl.move(when, &deltaX, &deltaY);
 
-            mPointerController->move(deltaX, deltaY);
+            moveMouseCursor(deltaX, deltaY);
         } else {
             mPointerVelocityControl.reset();
         }
@@ -3410,8 +3439,7 @@
         down = isPointerDown(mCurrentRawState.buttonState);
         hovering = !down;
 
-        float x, y;
-        mPointerController->getPosition(&x, &y);
+        auto [x, y] = getMouseCursorPosition();
         mPointerSimple.currentCoords.copyFrom(
                 mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]);
         mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
@@ -3451,9 +3479,7 @@
     }
     int32_t displayId = mPointerController->getDisplayId();
 
-    float xCursorPosition;
-    float yCursorPosition;
-    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+    auto [xCursorPosition, yCursorPosition] = getMouseCursorPosition();
 
     if (mPointerSimple.down && !down) {
         mPointerSimple.down = false;
@@ -3619,7 +3645,9 @@
     float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     if (mDeviceMode == DeviceMode::POINTER) {
-        mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+        auto [x, y] = getMouseCursorPosition();
+        xCursorPosition = x;
+        yCursorPosition = y;
     }
     const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
     const int32_t deviceId = getDeviceId();
@@ -3969,4 +3997,63 @@
     return std::nullopt;
 }
 
+void TouchInputMapper::moveMouseCursor(float dx, float dy) const {
+    if (isPerWindowInputRotationEnabled()) {
+        // Convert from InputReader's un-rotated coordinate space to PointerController's coordinate
+        // space that is oriented with the viewport.
+        rotateDelta(mViewport.orientation, &dx, &dy);
+    }
+
+    mPointerController->move(dx, dy);
+}
+
+std::pair<float, float> TouchInputMapper::getMouseCursorPosition() const {
+    float x = 0;
+    float y = 0;
+    mPointerController->getPosition(&x, &y);
+
+    if (!isPerWindowInputRotationEnabled()) return {x, y};
+    if (!mViewport.isValid()) return {x, y};
+
+    // Convert from PointerController's rotated coordinate space that is oriented with the viewport
+    // to InputReader's un-rotated coordinate space.
+    const int32_t orientation = getInverseRotation(mViewport.orientation);
+    rotatePoint(orientation, x, y, mViewport.deviceWidth, mViewport.deviceHeight);
+    return {x, y};
+}
+
+void TouchInputMapper::setMouseCursorPosition(float x, float y) const {
+    if (isPerWindowInputRotationEnabled() && mViewport.isValid()) {
+        // Convert from InputReader's un-rotated coordinate space to PointerController's rotated
+        // coordinate space that is oriented with the viewport.
+        rotatePoint(mViewport.orientation, x, y, mRawSurfaceWidth, mRawSurfaceHeight);
+    }
+
+    mPointerController->setPosition(x, y);
+}
+
+void TouchInputMapper::setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+                                     BitSet32 spotIdBits, int32_t displayId) {
+    std::array<PointerCoords, MAX_POINTERS> outSpotCoords{};
+
+    for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
+        const uint32_t index = spotIdToIndex[idBits.clearFirstMarkedBit()];
+        float x = spotCoords[index].getX();
+        float y = spotCoords[index].getY();
+        float pressure = spotCoords[index].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
+
+        if (isPerWindowInputRotationEnabled()) {
+            // Convert from InputReader's un-rotated coordinate space to PointerController's rotated
+            // coordinate space.
+            rotatePoint(mViewport.orientation, x, y, mRawSurfaceWidth, mRawSurfaceHeight);
+        }
+
+        outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+        outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+        outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
+    }
+
+    mPointerController->setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits, displayId);
+}
+
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 9b84ed5..5146299 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -776,6 +776,14 @@
 
     const char* modeToString(DeviceMode deviceMode);
     void rotateAndScale(float& x, float& y);
+
+    // Wrapper methods for interfacing with PointerController. These are used to convert points
+    // between the coordinate spaces used by InputReader and PointerController, if they differ.
+    void moveMouseCursor(float dx, float dy) const;
+    std::pair<float, float> getMouseCursorPosition() const;
+    void setMouseCursorPosition(float x, float y) const;
+    void setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+                       BitSet32 spotIdBits, int32_t displayId);
 };
 
 } // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 51f6f5d..cedda6e 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -266,6 +266,11 @@
         mPointerCaptureEnabled.reset();
     }
 
+    void assertDropTargetEquals(const sp<IBinder>& targetToken) {
+        std::scoped_lock lock(mLock);
+        ASSERT_EQ(targetToken, mDropTargetWindowToken);
+    }
+
 private:
     std::mutex mLock;
     std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -284,6 +289,8 @@
     std::queue<int32_t> mResponsiveMonitorPids GUARDED_BY(mLock);
     std::condition_variable mNotifyAnr;
 
+    sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock);
+
     void notifyConfigurationChanged(nsecs_t when) override {
         std::scoped_lock lock(mLock);
         mConfigurationChangedTime = when;
@@ -392,6 +399,11 @@
         mPointerCaptureChangedCondition.notify_all();
     }
 
+    void notifyDropWindow(const sp<IBinder>& token, float x, float y) override {
+        std::scoped_lock lock(mLock);
+        mDropTargetWindowToken = token;
+    }
+
     void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action,
                                          int32_t displayId) {
         std::scoped_lock lock(mLock);
@@ -766,6 +778,9 @@
             case AINPUT_EVENT_TYPE_CAPTURE: {
                 FAIL() << "Use 'consumeCaptureEvent' for CAPTURE events";
             }
+            case AINPUT_EVENT_TYPE_DRAG: {
+                FAIL() << "Use 'consumeDragEvent' for DRAG events";
+            }
             default: {
                 FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType;
             }
@@ -803,6 +818,23 @@
         EXPECT_EQ(hasCapture, captureEvent.getPointerCaptureEnabled());
     }
 
+    void consumeDragEvent(bool isExiting, float x, float y) {
+        const InputEvent* event = consume();
+        ASSERT_NE(nullptr, event) << mName.c_str()
+                                  << ": consumer should have returned non-NULL event.";
+        ASSERT_EQ(AINPUT_EVENT_TYPE_DRAG, event->getType())
+                << "Got " << inputEventTypeToString(event->getType())
+                << " event instead of DRAG event";
+
+        EXPECT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
+                << mName.c_str() << ": event displayId should always be NONE.";
+
+        const auto& dragEvent = static_cast<const DragEvent&>(*event);
+        EXPECT_EQ(isExiting, dragEvent.isExiting());
+        EXPECT_EQ(x, dragEvent.getX());
+        EXPECT_EQ(y, dragEvent.getY());
+    }
+
     void assertNoEvents() {
         InputEvent* event = consume();
         if (event == nullptr) {
@@ -905,7 +937,7 @@
         mInfo.frameTop = frame.top;
         mInfo.frameRight = frame.right;
         mInfo.frameBottom = frame.bottom;
-        mInfo.transform.set(frame.left, frame.top);
+        mInfo.transform.set(-frame.left, -frame.top);
         mInfo.touchableRegion.clear();
         mInfo.addTouchableRegion(frame);
     }
@@ -1003,6 +1035,10 @@
                                      expectedFlags);
     }
 
+    void consumeDragEvent(bool isExiting, float x, float y) {
+        mInputReceiver->consumeDragEvent(isExiting, x, y);
+    }
+
     std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
         if (mInputReceiver == nullptr) {
             ADD_FAILURE() << "Invalid receive event on window with no receiver";
@@ -2183,7 +2219,7 @@
     EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
 }
 
-TEST_F(InputDispatcherTest, NonPointerMotionEvent_JoystickNotTransformed) {
+TEST_F(InputDispatcherTest, NonPointerMotionEvent_NotTransformed) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
@@ -2203,28 +2239,41 @@
     // Second, we consume focus event if it is right or wrong according to onFocusChangedLocked.
     window->consumeFocusEvent(true);
 
-    NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE,
-                                                     AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_DEFAULT);
-    mDispatcher->notifyMotion(&motionArgs);
+    constexpr const std::array nonPointerSources = {AINPUT_SOURCE_TRACKBALL,
+                                                    AINPUT_SOURCE_MOUSE_RELATIVE,
+                                                    AINPUT_SOURCE_JOYSTICK};
+    for (const int source : nonPointerSources) {
+        // Notify motion with a non-pointer source.
+        NotifyMotionArgs motionArgs =
+                generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, source, ADISPLAY_ID_DEFAULT);
+        mDispatcher->notifyMotion(&motionArgs);
 
-    // Third, we consume motion event.
-    InputEvent* event = window->consume();
-    ASSERT_NE(event, nullptr);
-    ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
-            << name.c_str() << "expected " << inputEventTypeToString(AINPUT_EVENT_TYPE_MOTION)
-            << " event, got " << inputEventTypeToString(event->getType()) << " event";
+        InputEvent* event = window->consume();
+        ASSERT_NE(event, nullptr);
+        ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
+                << name.c_str() << "expected " << inputEventTypeToString(AINPUT_EVENT_TYPE_MOTION)
+                << " event, got " << inputEventTypeToString(event->getType()) << " event";
 
-    const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
-    EXPECT_EQ(AINPUT_EVENT_TYPE_MOTION, motionEvent.getAction());
+        const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+        EXPECT_EQ(AMOTION_EVENT_ACTION_MOVE, motionEvent.getAction());
+        EXPECT_EQ(motionArgs.pointerCount, motionEvent.getPointerCount());
 
-    float expectedX = motionArgs.pointerCoords[0].getX();
-    float expectedY = motionArgs.pointerCoords[0].getY();
+        float expectedX = motionArgs.pointerCoords[0].getX();
+        float expectedY = motionArgs.pointerCoords[0].getY();
 
-    // Finally we test if the axis values from the final motion event are not transformed
-    EXPECT_EQ(expectedX, motionEvent.getX(0)) << "expected " << expectedX << " for x coord of "
-                                              << name.c_str() << ", got " << motionEvent.getX(0);
-    EXPECT_EQ(expectedY, motionEvent.getY(0)) << "expected " << expectedY << " for y coord of "
-                                              << name.c_str() << ", got " << motionEvent.getY(0);
+        // Ensure the axis values from the final motion event are not transformed.
+        EXPECT_EQ(expectedX, motionEvent.getX(0))
+                << "expected " << expectedX << " for x coord of " << name.c_str() << ", got "
+                << motionEvent.getX(0);
+        EXPECT_EQ(expectedY, motionEvent.getY(0))
+                << "expected " << expectedY << " for y coord of " << name.c_str() << ", got "
+                << motionEvent.getY(0);
+        // Ensure the raw and transformed axis values for the motion event are the same.
+        EXPECT_EQ(motionEvent.getRawX(0), motionEvent.getX(0))
+                << "expected raw and transformed X-axis values to be equal";
+        EXPECT_EQ(motionEvent.getRawY(0), motionEvent.getY(0))
+                << "expected raw and transformed Y-axis values to be equal";
+    }
 }
 
 /**
@@ -4669,4 +4718,119 @@
     mTouchWindow->consumeAnyMotionDown();
 }
 
+class InputDispatcherDragTests : public InputDispatcherTest {
+protected:
+    std::shared_ptr<FakeApplicationHandle> mApp;
+    sp<FakeWindowHandle> mWindow;
+    sp<FakeWindowHandle> mSecondWindow;
+    sp<FakeWindowHandle> mDragWindow;
+
+    void SetUp() override {
+        InputDispatcherTest::SetUp();
+        mApp = std::make_shared<FakeApplicationHandle>();
+        mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+        mWindow->setFrame(Rect(0, 0, 100, 100));
+        mWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+        mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
+        mSecondWindow->setFrame(Rect(100, 0, 200, 100));
+        mSecondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
+    }
+
+    // Start performing drag, we will create a drag window and transfer touch to it.
+    void performDrag() {
+        ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+                  injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                   {50, 50}))
+                << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+        // Window should receive motion event.
+        mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+        // The drag window covers the entire display
+        mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT);
+        mDispatcher->setInputWindows(
+                {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
+
+        // Transfer touch focus to the drag window
+        mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(),
+                                        true /* isDragDrop */);
+        mWindow->consumeMotionCancel();
+        mDragWindow->consumeMotionDown();
+    }
+};
+
+TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
+    performDrag();
+
+    // Move on window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+    mWindow->consumeDragEvent(false, 50, 50);
+    mSecondWindow->assertNoEvents();
+
+    // Move to another window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+    mWindow->consumeDragEvent(true, 150, 50);
+    mSecondWindow->consumeDragEvent(false, 50, 50);
+
+    // Move back to original window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+    mWindow->consumeDragEvent(false, 50, 50);
+    mSecondWindow->consumeDragEvent(true, -50, 50);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+    mWindow->assertNoEvents();
+    mSecondWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherDragTests, DragAndDrop) {
+    performDrag();
+
+    // Move on window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+    mWindow->consumeDragEvent(false, 50, 50);
+    mSecondWindow->assertNoEvents();
+
+    // Move to another window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+    mWindow->consumeDragEvent(true, 150, 50);
+    mSecondWindow->consumeDragEvent(false, 50, 50);
+
+    // drop to another window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                             {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+    mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
+    mWindow->assertNoEvents();
+    mSecondWindow->assertNoEvents();
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 23893ea..d8e8b52 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -686,9 +686,9 @@
         ALOGD_IF(DEBUG_CONNECTIONS, "enable index=%zd", info.batchParams.indexOfKey(ident));
 
         if (isClientDisabledLocked(ident)) {
-            ALOGE("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d",
+            ALOGW("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d",
                     ident, handle);
-            return INVALID_OPERATION;
+            return NO_ERROR;
         }
 
         if (info.batchParams.indexOfKey(ident) >= 0) {
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index d215298..5fed79f 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -182,7 +182,7 @@
     bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
 
     // Returns true if the next buffer should be presented at the expected present time
-    bool shouldPresentNow(nsecs_t expectedPresentTime) const final;
+    bool shouldPresentNow(nsecs_t expectedPresentTime) const;
 
     // Returns true if the next buffer should be presented at the expected present time,
     // overridden by BufferStateLayer and BufferQueueLayer for implementation
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index e017cdc..b8aad61 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -41,6 +41,16 @@
 namespace android {
 
 using PresentState = frametimeline::SurfaceFrame::PresentState;
+namespace {
+void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
+                               const sp<GraphicBuffer>& buffer, const sp<Fence>& releaseFence) {
+    if (!listener) {
+        return;
+    }
+    listener->onReleaseBuffer(buffer->getId(), releaseFence ? releaseFence : Fence::NO_FENCE);
+}
+} // namespace
+
 // clang-format off
 const std::array<float, 16> BufferStateLayer::IDENTITY_MATRIX{
         1, 0, 0, 0,
@@ -65,7 +75,10 @@
         // RenderEngine may have been using the buffer as an external texture
         // after the client uncached the buffer.
         auto& engine(mFlinger->getRenderEngine());
-        engine.unbindExternalTextureBuffer(mBufferInfo.mBuffer->getId());
+        const uint64_t bufferId = mBufferInfo.mBuffer->getId();
+        engine.unbindExternalTextureBuffer(bufferId);
+        callReleaseBufferCallback(mDrawingState.releaseBufferListener, mBufferInfo.mBuffer,
+                                  mBufferInfo.mFence);
     }
 }
 
@@ -74,6 +87,7 @@
     if (ch == nullptr) {
         return OK;
     }
+    ch->previousBufferId = mPreviousBufferId;
     if (!ch->previousReleaseFence.get()) {
         ch->previousReleaseFence = fence;
         return OK;
@@ -177,6 +191,8 @@
         // leak.
         ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak",
               mName.c_str());
+        std::string miniDump = mPendingJankClassifications.front()->miniDump();
+        ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str());
         mPendingJankClassifications.pop_front();
     }
     mPendingJankClassifications.emplace_back(surfaceFrame);
@@ -188,6 +204,19 @@
         handle->dequeueReadyTime = dequeueReadyTime;
     }
 
+    // If there are multiple transactions in this frame, set the previous id on the earliest
+    // transacton. We don't need to pass in the released buffer id to multiple transactions.
+    // The buffer id does not have to correspond to any particular transaction as long as the
+    // listening end point is the same but the client expects the first transaction callback that
+    // replaces the presented buffer to contain the release fence. This follows the same logic.
+    // see BufferStateLayer::onLayerDisplayed.
+    for (auto& handle : mDrawingState.callbackHandles) {
+        if (handle->releasePreviousBuffer) {
+            handle->previousBufferId = mPreviousBufferId;
+            break;
+        }
+    }
+
     std::vector<JankData> jankData;
     jankData.reserve(mPendingJankClassifications.size());
     while (!mPendingJankClassifications.empty()
@@ -255,8 +284,8 @@
 }
 
 bool BufferStateLayer::setTransform(uint32_t transform) {
-    if (mCurrentState.transform == transform) return false;
-    mCurrentState.transform = transform;
+    if (mCurrentState.bufferTransform == transform) return false;
+    mCurrentState.bufferTransform = transform;
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
@@ -308,17 +337,17 @@
         h = frame.bottom;
     }
 
-    if (mCurrentState.active.transform.tx() == x && mCurrentState.active.transform.ty() == y &&
-        mCurrentState.active.w == w && mCurrentState.active.h == h) {
+    if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y &&
+        mCurrentState.width == w && mCurrentState.height == h) {
         return false;
     }
 
     if (!frame.isValid()) {
         x = y = w = h = 0;
     }
-    mCurrentState.active.transform.set(x, y);
-    mCurrentState.active.w = w;
-    mCurrentState.active.h = h;
+    mCurrentState.transform.set(x, y);
+    mCurrentState.width = w;
+    mCurrentState.height = h;
 
     mCurrentState.sequence++;
     mCurrentState.modified = true;
@@ -342,8 +371,8 @@
 bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
                                  nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp,
                                  const client_cache_t& clientCacheId, uint64_t frameNumber,
-                                 std::optional<nsecs_t> dequeueTime,
-                                 const FrameTimelineInfo& info) {
+                                 std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info,
+                                 const sp<ITransactionCompletedListener>& releaseBufferListener) {
     ATRACE_CALL();
 
     if (mCurrentState.buffer) {
@@ -351,7 +380,10 @@
         if (mCurrentState.buffer != mDrawingState.buffer) {
             // If mCurrentState has a buffer, and we are about to update again
             // before swapping to drawing state, then the first buffer will be
-            // dropped and we should decrement the pending buffer count.
+            // dropped and we should decrement the pending buffer count and
+            // call any release buffer callbacks if set.
+            callReleaseBufferCallback(mCurrentState.releaseBufferListener, mCurrentState.buffer,
+                                      mCurrentState.acquireFence);
             decrementPendingBufferCount();
             if (mCurrentState.bufferSurfaceFrameTX != nullptr) {
                 addSurfaceFrameDroppedForBuffer(mCurrentState.bufferSurfaceFrameTX);
@@ -359,9 +391,8 @@
             }
         }
     }
-
     mCurrentState.frameNumber = frameNumber;
-
+    mCurrentState.releaseBufferListener = releaseBufferListener;
     mCurrentState.buffer = buffer;
     mCurrentState.clientCacheId = clientCacheId;
     mCurrentState.modified = true;
@@ -373,7 +404,16 @@
     mCurrentState.desiredPresentTime = desiredPresentTime;
     mCurrentState.isAutoTimestamp = isAutoTimestamp;
 
-    mFlinger->mScheduler->recordLayerHistory(this, isAutoTimestamp ? 0 : desiredPresentTime,
+    const nsecs_t presentTime = [&] {
+        if (!isAutoTimestamp) return desiredPresentTime;
+
+        const auto prediction =
+                mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(info.vsyncId);
+        if (prediction.has_value()) return prediction->presentTime;
+
+        return static_cast<nsecs_t>(0);
+    }();
+    mFlinger->mScheduler->recordLayerHistory(this, presentTime,
                                              LayerHistory::LayerUpdateType::Buffer);
 
     addFrameEvent(acquireFence, postTime, isAutoTimestamp ? 0 : desiredPresentTime);
@@ -779,7 +819,7 @@
     mBufferInfo.mDesiredPresentTime = s.desiredPresentTime;
     mBufferInfo.mFenceTime = std::make_shared<FenceTime>(s.acquireFence);
     mBufferInfo.mFence = s.acquireFence;
-    mBufferInfo.mTransform = s.transform;
+    mBufferInfo.mTransform = s.bufferTransform;
     mBufferInfo.mDataspace = translateDataspace(s.dataspace);
     mBufferInfo.mCrop = computeCrop(s);
     mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
@@ -846,10 +886,10 @@
     const State& s(getDrawingState());
     if (radius <= 0 || (getActiveWidth(s) == UINT32_MAX && getActiveHeight(s) == UINT32_MAX))
         return RoundedCornerState();
-    return RoundedCornerState(FloatRect(static_cast<float>(s.active.transform.tx()),
-                                        static_cast<float>(s.active.transform.ty()),
-                                        static_cast<float>(s.active.transform.tx() + s.active.w),
-                                        static_cast<float>(s.active.transform.ty() + s.active.h)),
+    return RoundedCornerState(FloatRect(static_cast<float>(s.transform.tx()),
+                                        static_cast<float>(s.transform.ty()),
+                                        static_cast<float>(s.transform.tx() + s.width),
+                                        static_cast<float>(s.transform.ty() + s.height)),
                               radius);
 }
 
@@ -863,7 +903,7 @@
     uint32_t bufferHeight = s.buffer->height;
 
     // Undo any transformations on the buffer and return the result.
-    if (s.transform & ui::Transform::ROT_90) {
+    if (s.bufferTransform & ui::Transform::ROT_90) {
         std::swap(bufferWidth, bufferHeight);
     }
 
@@ -887,15 +927,16 @@
     ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers);
 }
 
-uint32_t BufferStateLayer::doTransaction(uint32_t flags) {
-    if (mDrawingState.buffer != nullptr && mDrawingState.buffer != mBufferInfo.mBuffer) {
+void BufferStateLayer::bufferMayChange(sp<GraphicBuffer>& newBuffer) {
+    if (mDrawingState.buffer != nullptr && mDrawingState.buffer != mBufferInfo.mBuffer &&
+        newBuffer != mDrawingState.buffer) {
         // If we are about to update mDrawingState.buffer but it has not yet latched
-        // then we will drop a buffer and should decrement the pending buffer count.
-        // This logic may not work perfectly in the face of a BufferStateLayer being the
-        // deferred side of a deferred transaction, but we don't expect this use case.
+        // then we will drop a buffer and should decrement the pending buffer count and
+        // call any release buffer callbacks if set.
+        callReleaseBufferCallback(mDrawingState.releaseBufferListener, mDrawingState.buffer,
+                                  mDrawingState.acquireFence);
         decrementPendingBufferCount();
     }
-    return Layer::doTransaction(flags);
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 003edd5..036e8d2 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -55,11 +55,9 @@
     void pushPendingState() override;*/
     bool applyPendingStates(Layer::State* stateToCommit) override;
 
-    uint32_t getActiveWidth(const Layer::State& s) const override { return s.active.w; }
-    uint32_t getActiveHeight(const Layer::State& s) const override { return s.active.h; }
-    ui::Transform getActiveTransform(const Layer::State& s) const override {
-        return s.active.transform;
-    }
+    uint32_t getActiveWidth(const Layer::State& s) const override { return s.width; }
+    uint32_t getActiveHeight(const Layer::State& s) const override { return s.height; }
+    ui::Transform getActiveTransform(const Layer::State& s) const override { return s.transform; }
     Region getActiveTransparentRegion(const Layer::State& s) const override {
         return s.transparentRegionHint;
     }
@@ -72,7 +70,8 @@
     bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime,
                    nsecs_t desiredPresentTime, bool isAutoTimestamp,
                    const client_cache_t& clientCacheId, uint64_t frameNumber,
-                   std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info) override;
+                   std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info,
+                   const sp<ITransactionCompletedListener>& transactionListener) override;
     bool setAcquireFence(const sp<Fence>& fence) override;
     bool setDataspace(ui::Dataspace dataspace) override;
     bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
@@ -91,7 +90,6 @@
                    bool /*allowNonRectPreservingTransforms*/) override {
         return false;
     }
-    bool setCrop_legacy(const Rect& /*crop*/) override { return false; }
     void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/,
                                       uint64_t /*frameNumber*/) override {}
     void deferTransactionUntil_legacy(const sp<Layer>& /*barrierLayer*/,
@@ -114,10 +112,12 @@
 
     // See mPendingBufferTransactions
     void decrementPendingBufferCount();
-    uint32_t doTransaction(uint32_t flags) override;
+    void bufferMayChange(sp<GraphicBuffer>& newBuffer) override;
     std::atomic<int32_t>* getPendingBufferCounter() override { return &mPendingBufferTransactions; }
     std::string getPendingBufferCounterName() override { return mBlastTransactionName; }
 
+    bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const override { return true; }
+
 protected:
     void gatherBufferInfo() override;
     uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
@@ -171,6 +171,9 @@
 
     mutable bool mCurrentStateModified = false;
     bool mReleasePreviousBuffer = false;
+
+    // Stores the last set acquire fence signal time used to populate the callback handle's acquire
+    // time.
     nsecs_t mCallbackHandleAcquireTime = -1;
 
     std::deque<std::shared_ptr<android::frametimeline::SurfaceFrame>> mPendingJankClassifications;
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index f9e5b9a..08147ed 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -103,6 +103,10 @@
     test_suites: ["device-tests"],
     defaults: ["libcompositionengine_defaults"],
     srcs: [
+        "tests/planner/CachedSetTest.cpp",
+        "tests/planner/FlattenerTest.cpp",
+        "tests/planner/LayerStateTest.cpp",
+        "tests/planner/PredictorTest.cpp",
         "tests/CompositionEngineTest.cpp",
         "tests/DisplayColorProfileTest.cpp",
         "tests/DisplayTest.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 5f834be..a3e84e2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -89,6 +89,7 @@
         sp<GraphicBuffer> buffer = nullptr;
         sp<Fence> acquireFence = nullptr;
         Rect displayFrame = {};
+        ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
     } overrideInfo;
 
     /*
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index 00424b2..b0e42b7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -17,15 +17,12 @@
 #pragma once
 
 #include <compositionengine/impl/planner/LayerState.h>
+#include <renderengine/RenderEngine.h>
 
 #include <chrono>
 
 namespace android {
 
-namespace renderengine {
-class RenderEngine;
-} // namespace renderengine
-
 namespace compositionengine::impl::planner {
 
 std::string durationString(std::chrono::milliseconds duration);
@@ -63,8 +60,9 @@
     const Layer& getFirstLayer() const { return mLayers[0]; }
     const Rect& getBounds() const { return mBounds; }
     size_t getAge() const { return mAge; }
-    const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+    const sp<GraphicBuffer>& getBuffer() const { return mTexture.getBuffer(); }
     const sp<Fence>& getDrawFence() const { return mDrawFence; }
+    ui::Dataspace getOutputDataspace() const { return mOutputDataspace; }
 
     NonBufferHash getNonBufferHash() const;
 
@@ -82,7 +80,8 @@
 
     void setLastUpdate(std::chrono::steady_clock::time_point now) { mLastUpdate = now; }
     void append(const CachedSet& other) {
-        mBuffer = nullptr;
+        mTexture.setBuffer(nullptr, nullptr);
+        mOutputDataspace = ui::Dataspace::UNKNOWN;
         mDrawFence = nullptr;
 
         mLayers.insert(mLayers.end(), other.mLayers.cbegin(), other.mLayers.cend());
@@ -93,7 +92,8 @@
     }
     void incrementAge() { ++mAge; }
 
-    void render(renderengine::RenderEngine&);
+    // Renders the cached set with the supplied output dataspace.
+    void render(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
 
     void dump(std::string& result) const;
 
@@ -105,8 +105,34 @@
     std::vector<Layer> mLayers;
     Rect mBounds = Rect::EMPTY_RECT;
     size_t mAge = 0;
-    sp<GraphicBuffer> mBuffer;
+
+    class Texture {
+    public:
+        ~Texture() { setBuffer(nullptr, nullptr); }
+
+        void setBuffer(const sp<GraphicBuffer>& buffer, renderengine::RenderEngine* re) {
+            if (mRE && mBuffer) {
+                mRE->unbindExternalTextureBuffer(mBuffer->getId());
+            }
+
+            mBuffer = buffer;
+            mRE = re;
+
+            if (mRE && mBuffer) {
+                mRE->cacheExternalTextureBuffer(mBuffer);
+            }
+        }
+
+        const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+
+    private:
+        sp<GraphicBuffer> mBuffer = nullptr;
+        renderengine::RenderEngine* mRE = nullptr;
+    };
+
+    Texture mTexture;
     sp<Fence> mDrawFence;
+    ui::Dataspace mOutputDataspace;
 
     static const bool sDebugHighlighLayers;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 6c86408..5b9a9f0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -39,9 +39,11 @@
 
     void setDisplaySize(ui::Size size) { mDisplaySize = size; }
 
-    NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash);
+    NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash,
+                                std::chrono::steady_clock::time_point now);
 
-    void renderCachedSets(renderengine::RenderEngine&);
+    // Renders the newest cached sets with the supplied output dataspace
+    void renderCachedSets(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
 
     void reset();
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index d19ac62..a3beadc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -40,6 +40,7 @@
 
 // clang-format off
 enum class LayerStateField : uint32_t {
+    None            = 0u,
     Id              = 1u << 0,
     Name            = 1u << 1,
     DisplayFrame    = 1u << 2,
@@ -173,8 +174,12 @@
     // Returns which fields were updated
     Flags<LayerStateField> update(compositionengine::OutputLayer*);
 
+    // Computes a hash for this LayerState.
+    // The hash is only computed from NonUniqueFields.
     size_t getHash(Flags<LayerStateField> skipFields) const;
 
+    // Returns the bit-set of differing fields between this LayerState and another LayerState.
+    // This bit-set is based on NonUniqueFields only
     Flags<LayerStateField> getDifferingFields(const LayerState& other,
                                               Flags<LayerStateField> skipFields) const;
 
@@ -273,26 +278,14 @@
                            }};
 
     using DataspaceState = OutputLayerState<ui::Dataspace, LayerStateField::Dataspace>;
-    DataspaceState mDataspace{[](auto layer) { return layer->getState().dataspace; },
-                              DataspaceState::getHalToStrings()};
+    DataspaceState mOutputDataspace{[](auto layer) { return layer->getState().dataspace; },
+                                    DataspaceState::getHalToStrings()};
 
     // TODO(b/180638831): Buffer format
 
     // Output-independent per-frame state
 
-    OutputLayerState<mat4, LayerStateField::ColorTransform>
-            mColorTransform{[](auto layer) {
-                                const auto state = layer->getLayerFE().getCompositionState();
-                                return state->colorTransformIsIdentity ? mat4{}
-                                                                       : state->colorTransform;
-                            },
-                            [](const mat4& mat) {
-                                using namespace std::string_literals;
-                                std::vector<std::string> split =
-                                        base::Split(std::string(mat.asString().string()), "\n"s);
-                                split.pop_back(); // Strip the last (empty) line
-                                return split;
-                            }};
+    OutputLayerState<mat4, LayerStateField::ColorTransform> mColorTransform;
 
     // TODO(b/180638831): Surface damage
 
@@ -348,7 +341,7 @@
     std::array<const StateInterface*, 13> getNonUniqueFields() const {
         return {
                 &mDisplayFrame,   &mSourceCrop,      &mZOrder,         &mBufferTransform,
-                &mBlendMode,      &mAlpha,           &mVisibleRegion,  &mDataspace,
+                &mBlendMode,      &mAlpha,           &mVisibleRegion,  &mOutputDataspace,
                 &mColorTransform, &mCompositionType, &mSidebandStream, &mBuffer,
                 &mSolidColor,
         };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index e96abb7..89de34d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -59,7 +59,7 @@
             compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers);
 
     // The planner will call to the Flattener to render any pending cached set
-    void renderCachedSets(renderengine::RenderEngine&);
+    void renderCachedSets(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
 
     void dump(const Vector<String16>& args, std::string&);
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
index 422af77..fe486d3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
@@ -24,16 +24,28 @@
 public:
     LayerStack(const std::vector<const LayerState*>& layers) : mLayers(copyLayers(layers)) {}
 
+    // Describes an approximate match between two layer stacks
     struct ApproximateMatch {
         bool operator==(const ApproximateMatch& other) const {
             return differingIndex == other.differingIndex &&
                     differingFields == other.differingFields;
         }
 
+        // The index of the single differing layer between the two stacks.
+        // This implies that only one layer is allowed to differ in an approximate match.
         size_t differingIndex;
+        // Set of fields that differ for the differing layer in the approximate match.
         Flags<LayerStateField> differingFields;
     };
 
+    // Returns an approximate match when comparing this layer stack with the provided list of
+    // layers, for the purposes of scoring how closely the two layer stacks will match composition
+    // strategies.
+    //
+    // If the two layer stacks are identical, then an approximate match is still returned, but the
+    // differing fields will be empty to represent an exact match.
+    //
+    // If the two layer stacks differ by too much, then an empty optional is returned.
     std::optional<ApproximateMatch> getApproximateMatch(
             const std::vector<const LayerState*>& other) const;
 
@@ -108,6 +120,10 @@
     }
     friend bool operator!=(const Plan& lhs, const Plan& rhs) { return !(lhs == rhs); }
 
+    friend std::ostream& operator<<(std::ostream& os, const Plan& plan) {
+        return os << to_string(plan);
+    }
+
 private:
     std::vector<hardware::graphics::composer::hal::Composition> mLayerTypes;
 };
@@ -146,6 +162,10 @@
         }
     }
 
+    friend std::ostream& operator<<(std::ostream& os, const Type& type) {
+        return os << to_string(type);
+    }
+
     Prediction(const std::vector<const LayerState*>& layers, Plan plan)
           : mExampleLayerStack(layers), mPlan(std::move(plan)) {}
 
@@ -205,11 +225,25 @@
         NonBufferHash hash;
         Plan plan;
         Prediction::Type type;
+
+        friend bool operator==(const PredictedPlan& lhs, const PredictedPlan& rhs) {
+            return lhs.hash == rhs.hash && lhs.plan == rhs.plan && lhs.type == rhs.type;
+        }
     };
 
-    std::optional<PredictedPlan> getPredictedPlan(const std::vector<const LayerState*>&,
-                                                  NonBufferHash) const;
+    // Retrieves the predicted plan based on a layer stack alongside its hash.
+    //
+    // If the exact layer stack has previously been seen by the predictor, then report the plan used
+    // for that layer stack.
+    //
+    // Otherwise, try to match to the best approximate stack to retireve the most likely plan.
+    std::optional<PredictedPlan> getPredictedPlan(const std::vector<const LayerState*>& layers,
+                                                  NonBufferHash hash) const;
 
+    // Records a comparison between the predicted plan and the resulting plan, alongside the layer
+    // stack we used.
+    //
+    // This method is intended to help with scoring how effective the prediction engine is.
     void recordResult(std::optional<PredictedPlan> predictedPlan, NonBufferHash flattenedHash,
                       const std::vector<const LayerState*>&, bool hasSkippedLayers, Plan result);
 
@@ -275,4 +309,13 @@
     mutable size_t mMissCount = 0;
 };
 
+// Defining PrintTo helps with Google Tests.
+inline void PrintTo(Predictor::PredictedPlan plan, ::std::ostream* os) {
+    *os << "PredictedPlan {";
+    *os << "\n    .hash = " << plan.hash;
+    *os << "\n    .plan = " << plan.plan;
+    *os << "\n    .type = " << plan.type;
+    *os << "\n}";
+}
+
 } // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index dc1aacc..ded2dcc 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1252,7 +1252,7 @@
 
 void Output::renderCachedSets() {
     if (mPlanner) {
-        mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine());
+        mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine(), getState().dataspace);
     }
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 54784a2..b364649 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -681,6 +681,7 @@
                                               .buffer = getState().overrideInfo.buffer,
                                               .fence = getState().overrideInfo.acquireFence,
                                       }};
+    settings.sourceDataspace = getState().overrideInfo.dataspace;
     settings.alpha = 1.0f;
 
     return {static_cast<LayerFE::LayerSettings>(settings)};
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index 165e320..45dce98 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -68,6 +68,10 @@
     dumpVal(out, "bufferTransform", toString(bufferTransform), bufferTransform);
     dumpVal(out, "dataspace", toString(dataspace), dataspace);
     dumpVal(out, "z-index", z);
+    dumpVal(out, "override buffer", overrideInfo.buffer.get());
+    dumpVal(out, "override acquire fence", overrideInfo.acquireFence.get());
+    dumpVal(out, "override display frame", overrideInfo.displayFrame);
+    dumpVal(out, "override dataspace", toString(overrideInfo.dataspace), overrideInfo.dataspace);
 
     if (hwc) {
         dumpHwc(*hwc, out);
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index ab3fe9e..137697b 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -79,10 +79,11 @@
         return mFingerprint;
     }
 
-    // TODO(b/181192080): Add all fields which contribute to geometry of override layer (e.g.,
-    // dataspace)
+    // TODO(b/182614524): We sometimes match this with LayerState hashes. Determine if that is
+    // necessary (and therefore we need to match implementations).
     size_t hash = 0;
     android::hashCombineSingle(hash, mBounds);
+    android::hashCombineSingle(hash, mOutputDataspace);
     return hash;
 }
 
@@ -126,7 +127,7 @@
 }
 
 bool CachedSet::hasReadyBuffer() const {
-    return mBuffer != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
+    return mTexture.getBuffer() != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
 }
 
 std::vector<CachedSet> CachedSet::decompose() const {
@@ -148,10 +149,11 @@
     }
 }
 
-void CachedSet::render(renderengine::RenderEngine& renderEngine) {
+void CachedSet::render(renderengine::RenderEngine& renderEngine, ui::Dataspace outputDataspace) {
     renderengine::DisplaySettings displaySettings{
             .physicalDisplay = Rect(0, 0, mBounds.getWidth(), mBounds.getHeight()),
             .clip = mBounds,
+            .outputDataspace = outputDataspace,
     };
 
     Region clearRegion = Region::INVALID_REGION;
@@ -163,8 +165,7 @@
             .supportsProtectedContent = false,
             .clearRegion = clearRegion,
             .viewport = viewport,
-            // TODO(181192086): Propagate the Output's dataspace instead of using UNKNOWN
-            .dataspace = ui::Dataspace::UNKNOWN,
+            .dataspace = outputDataspace,
             .realContentIsVisible = true,
             .clearContent = false,
             .disableBlurs = false,
@@ -209,12 +210,14 @@
                                                  HAL_PIXEL_FORMAT_RGBA_8888, 1, usageFlags);
     LOG_ALWAYS_FATAL_IF(buffer->initCheck() != OK);
     base::unique_fd drawFence;
+
     status_t result = renderEngine.drawLayers(displaySettings, layerSettingsPointers, buffer, false,
                                               base::unique_fd(), &drawFence);
 
     if (result == NO_ERROR) {
-        mBuffer = buffer;
+        mTexture.setBuffer(buffer, &renderEngine);
         mDrawFence = new Fence(drawFence.release());
+        mOutputDataspace = outputDataspace;
     }
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 0c09714..30b5761 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -28,9 +28,7 @@
 namespace android::compositionengine::impl::planner {
 
 NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers,
-                                       NonBufferHash hash) {
-    const auto now = std::chrono::steady_clock::now();
-
+                                       NonBufferHash hash, time_point now) {
     const size_t unflattenedDisplayCost = calculateDisplayCost(layers);
     mUnflattenedDisplayCost += unflattenedDisplayCost;
 
@@ -53,12 +51,13 @@
     return hash;
 }
 
-void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine) {
+void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine,
+                                 ui::Dataspace outputDataspace) {
     if (!mNewCachedSet) {
         return;
     }
 
-    mNewCachedSet->render(renderEngine);
+    mNewCachedSet->render(renderEngine, outputDataspace);
 }
 
 void Flattener::reset() {
@@ -225,6 +224,7 @@
                                 .buffer = mNewCachedSet->getBuffer(),
                                 .acquireFence = mNewCachedSet->getDrawFence(),
                                 .displayFrame = mNewCachedSet->getBounds(),
+                                .dataspace = mNewCachedSet->getOutputDataspace(),
                         };
                         ++incomingLayerIter;
                     }
@@ -255,6 +255,7 @@
                         .buffer = currentLayerIter->getBuffer(),
                         .acquireFence = currentLayerIter->getDrawFence(),
                         .displayFrame = currentLayerIter->getBounds(),
+                        .dataspace = currentLayerIter->getOutputDataspace(),
                 };
                 ++incomingLayerIter;
             }
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index 7cf4819..222b2be 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -16,9 +16,28 @@
 
 #include <compositionengine/impl/planner/LayerState.h>
 
+namespace {
+extern "C" const char* __attribute__((unused)) __asan_default_options() {
+    return "detect_container_overflow=0";
+}
+} // namespace
+
 namespace android::compositionengine::impl::planner {
 
-LayerState::LayerState(compositionengine::OutputLayer* layer) : mOutputLayer(layer) {
+LayerState::LayerState(compositionengine::OutputLayer* layer)
+      : mOutputLayer(layer),
+        mColorTransform({[](auto layer) {
+                             const auto state = layer->getLayerFE().getCompositionState();
+                             return state->colorTransformIsIdentity ? mat4{}
+                                                                    : state->colorTransform;
+                         },
+                         [](const mat4& mat) {
+                             using namespace std::string_literals;
+                             std::vector<std::string> split =
+                                     base::Split(std::string(mat.asString().string()), "\n"s);
+                             split.pop_back(); // Strip the last (empty) line
+                             return split;
+                         }}) {
     update(layer);
 }
 
@@ -139,7 +158,8 @@
             lhs.mSourceCrop == rhs.mSourceCrop && lhs.mZOrder == rhs.mZOrder &&
             lhs.mBufferTransform == rhs.mBufferTransform && lhs.mBlendMode == rhs.mBlendMode &&
             lhs.mAlpha == rhs.mAlpha && lhs.mVisibleRegion == rhs.mVisibleRegion &&
-            lhs.mDataspace == rhs.mDataspace && lhs.mColorTransform == rhs.mColorTransform &&
+            lhs.mOutputDataspace == rhs.mOutputDataspace &&
+            lhs.mColorTransform == rhs.mColorTransform &&
             lhs.mCompositionType == rhs.mCompositionType &&
             lhs.mSidebandStream == rhs.mSidebandStream && lhs.mBuffer == rhs.mBuffer &&
             (lhs.mCompositionType.get() != hal::Composition::SOLID_COLOR ||
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 52efff5..87721c7 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -89,7 +89,8 @@
                    });
 
     const NonBufferHash hash = getNonBufferHash(mCurrentLayers);
-    mFlattenedHash = mFlattener.flattenLayers(mCurrentLayers, hash);
+    mFlattenedHash =
+            mFlattener.flattenLayers(mCurrentLayers, hash, std::chrono::steady_clock::now());
     const bool layersWereFlattened = hash != mFlattenedHash;
     ALOGV("[%s] Initial hash %zx flattened hash %zx", __func__, hash, mFlattenedHash);
 
@@ -132,8 +133,9 @@
                             finalPlan);
 }
 
-void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine) {
-    mFlattener.renderCachedSets(renderEngine);
+void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine,
+                               ui::Dataspace outputDataspace) {
+    mFlattener.renderCachedSets(renderEngine, outputDataspace);
 }
 
 void Planner::dump(const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
index ba5e64d..07920b8 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
@@ -50,8 +50,8 @@
             return std::nullopt;
         }
 
-        // If layers are not identical, but we already have a prior approximate match,
-        // the LayerStacks differ by too much, so return nothing
+        // If layers are not identical, but we already detected a prior approximate match for a
+        // previous layer, the LayerStacks differ by too much, so return nothing
         if (approximateMatch) {
             return std::nullopt;
         }
@@ -72,6 +72,10 @@
         }
     }
 
+    if (approximateMatch) {
+        return approximateMatch;
+    }
+
     // If we make it through the layer-by-layer comparison without an approximate match,
     // it means that all layers were either identical or had client-composited layers in common,
     // which don't affect the composition strategy, so return a successful result with
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
new file mode 100644
index 0000000..377f817
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/impl/planner/CachedSet.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/OutputLayer.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+
+namespace android::compositionengine {
+using namespace std::chrono_literals;
+
+using testing::_;
+using testing::DoAll;
+using testing::Invoke;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SetArgPointee;
+
+using impl::planner::CachedSet;
+using impl::planner::LayerState;
+using impl::planner::LayerStateField;
+
+namespace {
+
+class CachedSetTest : public testing::Test {
+public:
+    CachedSetTest() = default;
+    void SetUp() override;
+    void TearDown() override;
+
+protected:
+    const std::chrono::steady_clock::time_point kStartTime = std::chrono::steady_clock::now();
+
+    struct TestLayer {
+        mock::OutputLayer outputLayer;
+        impl::OutputLayerCompositionState outputLayerCompositionState;
+        // LayerFE inherits from RefBase and must be held by an sp<>
+        sp<mock::LayerFE> layerFE;
+        LayerFECompositionState layerFECompositionState;
+
+        std::unique_ptr<LayerState> layerState;
+        std::unique_ptr<CachedSet::Layer> cachedSetLayer;
+    };
+
+    static constexpr size_t kNumLayers = 5;
+    std::vector<std::unique_ptr<TestLayer>> mTestLayers;
+
+    android::renderengine::mock::RenderEngine mRenderEngine;
+};
+
+void CachedSetTest::SetUp() {
+    for (size_t i = 0; i < kNumLayers; i++) {
+        auto testLayer = std::make_unique<TestLayer>();
+        auto pos = static_cast<int32_t>(i);
+        testLayer->outputLayerCompositionState.displayFrame = Rect(pos, pos, pos + 1, pos + 1);
+
+        testLayer->layerFE = sp<mock::LayerFE>::make();
+
+        EXPECT_CALL(*testLayer->layerFE, getSequence)
+                .WillRepeatedly(Return(static_cast<int32_t>(i)));
+        EXPECT_CALL(*testLayer->layerFE, getDebugName).WillRepeatedly(Return("testLayer"));
+        EXPECT_CALL(*testLayer->layerFE, getCompositionState)
+                .WillRepeatedly(Return(&testLayer->layerFECompositionState));
+        EXPECT_CALL(testLayer->outputLayer, getLayerFE)
+                .WillRepeatedly(ReturnRef(*testLayer->layerFE));
+        EXPECT_CALL(testLayer->outputLayer, getState)
+                .WillRepeatedly(ReturnRef(testLayer->outputLayerCompositionState));
+
+        testLayer->layerState = std::make_unique<LayerState>(&testLayer->outputLayer);
+        testLayer->layerState->incrementFramesSinceBufferUpdate();
+        testLayer->cachedSetLayer =
+                std::make_unique<CachedSet::Layer>(testLayer->layerState.get(), kStartTime);
+
+        mTestLayers.emplace_back(std::move(testLayer));
+    }
+}
+
+void CachedSetTest::TearDown() {
+    mTestLayers.clear();
+}
+
+void expectEqual(const CachedSet& cachedSet, const CachedSet::Layer& layer) {
+    EXPECT_EQ(layer.getHash(), cachedSet.getFingerprint());
+    EXPECT_EQ(layer.getLastUpdate(), cachedSet.getLastUpdate());
+    EXPECT_EQ(layer.getDisplayFrame(), cachedSet.getBounds());
+    EXPECT_EQ(1u, cachedSet.getLayerCount());
+    EXPECT_EQ(layer.getState(), cachedSet.getFirstLayer().getState());
+    EXPECT_EQ(0u, cachedSet.getAge());
+    EXPECT_EQ(layer.getHash(), cachedSet.getNonBufferHash());
+}
+
+void expectEqual(const CachedSet& cachedSet, const LayerState& layerState,
+                 std::chrono::steady_clock::time_point lastUpdate) {
+    CachedSet::Layer layer(&layerState, lastUpdate);
+    expectEqual(cachedSet, layer);
+}
+
+void expectNoBuffer(const CachedSet& cachedSet) {
+    EXPECT_EQ(nullptr, cachedSet.getBuffer());
+    EXPECT_EQ(nullptr, cachedSet.getDrawFence());
+    EXPECT_FALSE(cachedSet.hasReadyBuffer());
+}
+
+void expectReadyBuffer(const CachedSet& cachedSet) {
+    EXPECT_NE(nullptr, cachedSet.getBuffer());
+    EXPECT_NE(nullptr, cachedSet.getDrawFence());
+    EXPECT_TRUE(cachedSet.hasReadyBuffer());
+}
+
+TEST_F(CachedSetTest, createFromLayer) {
+    CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+    CachedSet cachedSet(layer);
+    expectEqual(cachedSet, layer);
+    expectNoBuffer(cachedSet);
+}
+
+TEST_F(CachedSetTest, createFromLayerState) {
+    LayerState& layerState = *mTestLayers[0]->layerState.get();
+    CachedSet cachedSet(&layerState, kStartTime);
+    expectEqual(cachedSet, layerState, kStartTime);
+    expectNoBuffer(cachedSet);
+}
+
+TEST_F(CachedSetTest, addLayer) {
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+
+    CachedSet cachedSet(layer1);
+    cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+    EXPECT_EQ(layer1.getHash(), cachedSet.getFingerprint());
+    EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
+    EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getBounds());
+    EXPECT_EQ(2u, cachedSet.getLayerCount());
+    EXPECT_EQ(0u, cachedSet.getAge());
+    expectNoBuffer(cachedSet);
+    // TODO(b/181192080): check that getNonBufferHash returns the correct hash value
+    // EXPECT_EQ(android::hashCombine(layer1.getHash(), layer2.getHash()),
+    // cachedSet.getNonBufferHash());
+}
+
+TEST_F(CachedSetTest, decompose) {
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+    CachedSet cachedSet(layer1);
+    cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+    cachedSet.addLayer(layer3.getState(), kStartTime + 20ms);
+
+    std::vector<CachedSet> decomposed = cachedSet.decompose();
+    EXPECT_EQ(3u, decomposed.size());
+    expectEqual(decomposed[0], *layer1.getState(), kStartTime);
+    expectNoBuffer(decomposed[0]);
+
+    expectEqual(decomposed[1], *layer2.getState(), kStartTime + 10ms);
+    expectNoBuffer(decomposed[1]);
+
+    expectEqual(decomposed[2], *layer3.getState(), kStartTime + 20ms);
+    expectNoBuffer(decomposed[2]);
+}
+
+TEST_F(CachedSetTest, setLastUpdate) {
+    LayerState& layerState = *mTestLayers[0]->layerState.get();
+    CachedSet cachedSet(&layerState, kStartTime);
+    cachedSet.setLastUpdate(kStartTime + 10ms);
+    expectEqual(cachedSet, layerState, kStartTime + 10ms);
+}
+
+TEST_F(CachedSetTest, incrementAge) {
+    CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+    CachedSet cachedSet(layer);
+    EXPECT_EQ(0u, cachedSet.getAge());
+    cachedSet.incrementAge();
+    EXPECT_EQ(1u, cachedSet.getAge());
+    cachedSet.incrementAge();
+    EXPECT_EQ(2u, cachedSet.getAge());
+}
+
+TEST_F(CachedSetTest, hasBufferUpdate_NoUpdate) {
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+    CachedSet cachedSet(layer1);
+    cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+    cachedSet.addLayer(layer3.getState(), kStartTime + 20ms);
+
+    std::vector<const LayerState*> incomingLayers = {
+            layer1.getState(),
+            layer2.getState(),
+            layer3.getState(),
+    };
+
+    EXPECT_FALSE(cachedSet.hasBufferUpdate(incomingLayers.begin()));
+}
+
+TEST_F(CachedSetTest, hasBufferUpdate_BufferUpdate) {
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+    CachedSet cachedSet(layer1);
+    cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+    cachedSet.addLayer(layer3.getState(), kStartTime + 20ms);
+
+    mTestLayers[1]->layerState->resetFramesSinceBufferUpdate();
+
+    std::vector<const LayerState*> incomingLayers = {
+            layer1.getState(),
+            layer2.getState(),
+            layer3.getState(),
+    };
+
+    EXPECT_TRUE(cachedSet.hasBufferUpdate(incomingLayers.begin()));
+}
+
+TEST_F(CachedSetTest, append) {
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+
+    CachedSet cachedSet1(layer1);
+    CachedSet cachedSet2(layer2);
+    cachedSet1.addLayer(layer3.getState(), kStartTime + 10ms);
+    cachedSet1.append(cachedSet2);
+
+    EXPECT_EQ(layer1.getHash(), cachedSet1.getFingerprint());
+    EXPECT_EQ(kStartTime, cachedSet1.getLastUpdate());
+    EXPECT_EQ(Rect(0, 0, 3, 3), cachedSet1.getBounds());
+    EXPECT_EQ(3u, cachedSet1.getLayerCount());
+    EXPECT_EQ(0u, cachedSet1.getAge());
+    expectNoBuffer(cachedSet1);
+    // TODO(b/181192080): check that getNonBufferHash returns the correct hash value
+    // EXPECT_EQ(android::hashCombine(layer1.getHash(), layer2.getHash()),
+    // cachedSet1.getNonBufferHash());
+}
+
+TEST_F(CachedSetTest, updateAge_NoUpdate) {
+    CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+
+    CachedSet cachedSet(layer);
+    cachedSet.incrementAge();
+    EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
+    EXPECT_EQ(1u, cachedSet.getAge());
+
+    cachedSet.updateAge(kStartTime + 10ms);
+    EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
+    EXPECT_EQ(1u, cachedSet.getAge());
+}
+
+TEST_F(CachedSetTest, updateAge_BufferUpdate) {
+    CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+    mTestLayers[0]->layerState->resetFramesSinceBufferUpdate();
+
+    CachedSet cachedSet(layer);
+    cachedSet.incrementAge();
+    EXPECT_EQ(kStartTime, cachedSet.getLastUpdate());
+    EXPECT_EQ(1u, cachedSet.getAge());
+
+    cachedSet.updateAge(kStartTime + 10ms);
+    EXPECT_EQ(kStartTime + 10ms, cachedSet.getLastUpdate());
+    EXPECT_EQ(0u, cachedSet.getAge());
+}
+
+TEST_F(CachedSetTest, render) {
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE;
+    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE;
+
+    CachedSet cachedSet(layer1);
+    cachedSet.append(CachedSet(layer2));
+
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+    clientCompList1.push_back({});
+    clientCompList1[0].alpha = 0.5f;
+
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+    clientCompList2.push_back({});
+    clientCompList2[0].alpha = 0.75f;
+
+    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+                                const std::vector<const renderengine::LayerSettings*>& layers,
+                                const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
+                                base::unique_fd*) -> size_t {
+        EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.physicalDisplay);
+        EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.clip);
+        EXPECT_EQ(0.5f, layers[0]->alpha);
+        EXPECT_EQ(0.75f, layers[1]->alpha);
+        EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
+
+        return NO_ERROR;
+    };
+
+    EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
+    EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    EXPECT_CALL(mRenderEngine, cacheExternalTextureBuffer(_));
+    cachedSet.render(mRenderEngine, ui::Dataspace::SRGB);
+    expectReadyBuffer(cachedSet);
+
+    // Now check that appending a new cached set properly cleans up RenderEngine resources.
+    EXPECT_CALL(mRenderEngine, unbindExternalTextureBuffer(_));
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+    cachedSet.append(CachedSet(layer3));
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
new file mode 100644
index 0000000..bd77559
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -0,0 +1,450 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/impl/planner/CachedSet.h>
+#include <compositionengine/impl/planner/Flattener.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/impl/planner/Predictor.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/OutputLayer.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+
+namespace android::compositionengine {
+using namespace std::chrono_literals;
+using impl::planner::Flattener;
+using impl::planner::LayerState;
+using impl::planner::NonBufferHash;
+using impl::planner::Predictor;
+
+using testing::_;
+using testing::ByMove;
+using testing::ByRef;
+using testing::DoAll;
+using testing::Invoke;
+using testing::Return;
+using testing::ReturnRef;
+using testing::Sequence;
+using testing::SetArgPointee;
+
+namespace {
+
+class FlattenerTest : public testing::Test {
+public:
+    FlattenerTest() : mFlattener(std::make_unique<Flattener>(mPredictor)) {}
+    void SetUp() override;
+
+protected:
+    void initializeOverrideBuffer(const std::vector<const LayerState*>& layers);
+    void initializeFlattener(const std::vector<const LayerState*>& layers);
+    void expectAllLayersFlattened(const std::vector<const LayerState*>& layers);
+
+    // TODO(b/181192467): Once Flattener starts to do something useful with Predictor,
+    // mPredictor should be mocked and checked for expectations.
+    Predictor mPredictor;
+
+    // mRenderEngine may be held as a pointer to mFlattener, so mFlattener must be destroyed first.
+    renderengine::mock::RenderEngine mRenderEngine;
+    std::unique_ptr<Flattener> mFlattener;
+
+    const std::chrono::steady_clock::time_point kStartTime = std::chrono::steady_clock::now();
+    std::chrono::steady_clock::time_point mTime = kStartTime;
+
+    struct TestLayer {
+        std::string name;
+        mock::OutputLayer outputLayer;
+        impl::OutputLayerCompositionState outputLayerCompositionState;
+        // LayerFE inherits from RefBase and must be held by an sp<>
+        sp<mock::LayerFE> layerFE;
+        LayerFECompositionState layerFECompositionState;
+
+        std::unique_ptr<LayerState> layerState;
+    };
+
+    static constexpr size_t kNumLayers = 5;
+    std::vector<std::unique_ptr<TestLayer>> mTestLayers;
+};
+
+void FlattenerTest::SetUp() {
+    for (size_t i = 0; i < kNumLayers; i++) {
+        auto testLayer = std::make_unique<TestLayer>();
+        auto pos = static_cast<int32_t>(i);
+        std::stringstream ss;
+        ss << "testLayer" << i;
+        testLayer->name = ss.str();
+
+        testLayer->outputLayerCompositionState.displayFrame = Rect(pos, pos, pos + 1, pos + 1);
+
+        testLayer->layerFECompositionState.buffer =
+                new GraphicBuffer(100, 100, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                  GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+                                          GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE,
+                                  "output");
+
+        testLayer->layerFE = sp<mock::LayerFE>::make();
+
+        EXPECT_CALL(*testLayer->layerFE, getSequence)
+                .WillRepeatedly(Return(static_cast<int32_t>(i)));
+        EXPECT_CALL(*testLayer->layerFE, getDebugName)
+                .WillRepeatedly(Return(testLayer->name.c_str()));
+        EXPECT_CALL(*testLayer->layerFE, getCompositionState)
+                .WillRepeatedly(Return(&testLayer->layerFECompositionState));
+
+        std::vector<LayerFE::LayerSettings> clientCompositionList = {
+                LayerFE::LayerSettings{},
+        };
+
+        EXPECT_CALL(*testLayer->layerFE, prepareClientCompositionList)
+                .WillRepeatedly(Return(clientCompositionList));
+        EXPECT_CALL(testLayer->outputLayer, getLayerFE)
+                .WillRepeatedly(ReturnRef(*testLayer->layerFE));
+        EXPECT_CALL(testLayer->outputLayer, getState)
+                .WillRepeatedly(ReturnRef(testLayer->outputLayerCompositionState));
+        EXPECT_CALL(testLayer->outputLayer, editState)
+                .WillRepeatedly(ReturnRef(testLayer->outputLayerCompositionState));
+
+        testLayer->layerState = std::make_unique<LayerState>(&testLayer->outputLayer);
+        testLayer->layerState->incrementFramesSinceBufferUpdate();
+
+        mTestLayers.emplace_back(std::move(testLayer));
+    }
+}
+
+void FlattenerTest::initializeOverrideBuffer(const std::vector<const LayerState*>& layers) {
+    for (const auto layer : layers) {
+        layer->getOutputLayer()->editState().overrideInfo = {};
+    }
+}
+
+void FlattenerTest::initializeFlattener(const std::vector<const LayerState*>& layers) {
+    // layer stack is unknown, reset current geomentry
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+    // same geometry, update the internal layer stack
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+}
+
+void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
+    // layers would be flattened but the buffer would not be overridden
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+    for (const auto layer : layers) {
+        EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
+    }
+
+    // the new flattened layer is replaced
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+    const auto buffer = layers[0]->getOutputLayer()->getState().overrideInfo.buffer;
+    EXPECT_NE(nullptr, buffer);
+    for (const auto layer : layers) {
+        EXPECT_EQ(buffer, layer->getOutputLayer()->getState().overrideInfo.buffer);
+    }
+}
+
+TEST_F(FlattenerTest, flattenLayers_NewLayerStack) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    auto& layerState2 = mTestLayers[1]->layerState;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+    };
+    initializeFlattener(layers);
+}
+
+TEST_F(FlattenerTest, flattenLayers_ActiveLayersAreNotFlattened) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    auto& layerState2 = mTestLayers[1]->layerState;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // layers cannot be flattened yet, since they are still active
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+}
+
+TEST_F(FlattenerTest, flattenLayers_basicFlatten) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    auto& layerState2 = mTestLayers[1]->layerState;
+    auto& layerState3 = mTestLayers[2]->layerState;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+            layerState3.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // make all layers inactive
+    mTime += 200ms;
+    expectAllLayersFlattened(layers);
+}
+
+TEST_F(FlattenerTest, flattenLayers_FlattenedLayersStayFlattenWhenNoUpdate) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState3 = mTestLayers[2]->layerState;
+    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+            layerState3.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // make all layers inactive
+    mTime += 200ms;
+    expectAllLayersFlattened(layers);
+
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+    EXPECT_NE(nullptr, overrideBuffer1);
+    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+    EXPECT_EQ(overrideBuffer2, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_addLayerToFlattenedCauseReset) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState3 = mTestLayers[2]->layerState;
+    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+    std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+    };
+
+    initializeFlattener(layers);
+    // make all layers inactive
+    mTime += 200ms;
+
+    initializeOverrideBuffer(layers);
+    expectAllLayersFlattened(layers);
+
+    // add a new layer to the stack, this will cause all the flatenner to reset
+    layers.push_back(layerState3.get());
+
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+    EXPECT_EQ(nullptr, overrideBuffer1);
+    EXPECT_EQ(nullptr, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState3 = mTestLayers[2]->layerState;
+    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+            layerState3.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // make all layers inactive
+    mTime += 200ms;
+    expectAllLayersFlattened(layers);
+
+    // Layer 1 posted a buffer update, layers would be decomposed, and a new drawFrame would be
+    // caleed for Layer2 and Layer3
+    layerState1->resetFramesSinceBufferUpdate();
+
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+    EXPECT_EQ(nullptr, overrideBuffer1);
+    EXPECT_EQ(nullptr, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+    EXPECT_EQ(nullptr, overrideBuffer1);
+    EXPECT_NE(nullptr, overrideBuffer2);
+    EXPECT_EQ(overrideBuffer2, overrideBuffer3);
+
+    layerState1->incrementFramesSinceBufferUpdate();
+    mTime += 200ms;
+
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+    EXPECT_EQ(nullptr, overrideBuffer1);
+    EXPECT_NE(nullptr, overrideBuffer2);
+    EXPECT_EQ(overrideBuffer2, overrideBuffer3);
+
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+    EXPECT_NE(nullptr, overrideBuffer1);
+    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+    EXPECT_EQ(overrideBuffer2, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState3 = mTestLayers[2]->layerState;
+    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState4 = mTestLayers[3]->layerState;
+    const auto& overrideBuffer4 = layerState4->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState5 = mTestLayers[4]->layerState;
+    const auto& overrideBuffer5 = layerState5->getOutputLayer()->getState().overrideInfo.buffer;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(), layerState2.get(), layerState3.get(),
+            layerState4.get(), layerState5.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // make all layers inactive
+    mTime += 200ms;
+    expectAllLayersFlattened(layers);
+
+    // Layer 3 posted a buffer update, layers would be decomposed, and a new drawFrame would be
+    // called for Layer1 and Layer2
+    layerState3->resetFramesSinceBufferUpdate();
+
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+    EXPECT_EQ(nullptr, overrideBuffer1);
+    EXPECT_EQ(nullptr, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+    EXPECT_EQ(nullptr, overrideBuffer4);
+    EXPECT_EQ(nullptr, overrideBuffer5);
+
+    // Layers 1 and 2 will be flattened a new drawFrame would be called for Layer4 and Layer5
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+    EXPECT_NE(nullptr, overrideBuffer1);
+    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+    EXPECT_EQ(nullptr, overrideBuffer4);
+    EXPECT_EQ(nullptr, overrideBuffer5);
+
+    // Layers 4 and 5 will be flattened
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+    EXPECT_NE(nullptr, overrideBuffer1);
+    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+    EXPECT_NE(nullptr, overrideBuffer4);
+    EXPECT_EQ(overrideBuffer4, overrideBuffer5);
+
+    layerState3->incrementFramesSinceBufferUpdate();
+    mTime += 200ms;
+
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+    EXPECT_NE(nullptr, overrideBuffer1);
+    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+    EXPECT_NE(nullptr, overrideBuffer4);
+    EXPECT_EQ(overrideBuffer4, overrideBuffer5);
+
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+
+    EXPECT_NE(nullptr, overrideBuffer1);
+    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+    EXPECT_EQ(overrideBuffer2, overrideBuffer3);
+    EXPECT_EQ(overrideBuffer3, overrideBuffer4);
+    EXPECT_EQ(overrideBuffer4, overrideBuffer5);
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
new file mode 100644
index 0000000..8f235ab
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -0,0 +1,1020 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LayerStateTest"
+
+#include <compositionengine/impl/OutputLayer.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/OutputLayer.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+namespace android::compositionengine::impl::planner {
+namespace {
+
+using testing::Return;
+using testing::ReturnRef;
+
+const std::string sDebugName = std::string("Test LayerFE");
+const std::string sDebugNameTwo = std::string("Test LayerFE2");
+const constexpr int32_t sSequenceId = 12345;
+const constexpr int32_t sSequenceIdTwo = 123456;
+const Rect sRectOne = Rect(10, 20, 30, 40);
+const Rect sRectTwo = Rect(40, 30, 20, 10);
+const FloatRect sFloatRectOne = FloatRect(100.f, 200.f, 300.f, 400.f);
+const FloatRect sFloatRectTwo = FloatRect(400.f, 300.f, 200.f, 100.f);
+const constexpr int32_t sZOne = 100;
+const constexpr int32_t sZTwo = 101;
+const constexpr float sAlphaOne = 0.25f;
+const constexpr float sAlphaTwo = 0.5f;
+const Region sRegionOne = Region(sRectOne);
+const Region sRegionTwo = Region(sRectTwo);
+const mat4 sMat4One = mat4::scale(vec4(2.f, 3.f, 1.f, 1.f));
+native_handle_t* const sFakeSidebandStreamOne = reinterpret_cast<native_handle_t*>(10);
+native_handle_t* const sFakeSidebandStreamTwo = reinterpret_cast<native_handle_t*>(11);
+const half4 sHalf4One = half4(0.2f, 0.3f, 0.4f, 0.5f);
+const half4 sHalf4Two = half4(0.5f, 0.4f, 0.43, 0.2f);
+
+struct LayerStateTest : public testing::Test {
+    LayerStateTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    ~LayerStateTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    void setupMocksForLayer(mock::OutputLayer& layer, mock::LayerFE& layerFE,
+                            const OutputLayerCompositionState& outputLayerState,
+                            const LayerFECompositionState& layerFEState,
+                            int32_t sequenceId = sSequenceId,
+                            const std::string& debugName = sDebugName) {
+        EXPECT_CALL(layer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
+        EXPECT_CALL(layer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+        EXPECT_CALL(layerFE, getSequence()).WillRepeatedly(Return(sequenceId));
+        EXPECT_CALL(layerFE, getDebugName()).WillRepeatedly(Return(debugName.c_str()));
+        EXPECT_CALL(layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+    }
+
+    mock::LayerFE mLayerFE;
+    mock::OutputLayer mOutputLayer;
+    std::unique_ptr<LayerState> mLayerState;
+};
+
+TEST_F(LayerStateTest, getOutputLayer) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_EQ(&mOutputLayer, mLayerState->getOutputLayer());
+}
+
+TEST_F(LayerStateTest, getId) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_EQ(sSequenceId, mLayerState->getId());
+}
+
+TEST_F(LayerStateTest, updateId) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionState, sSequenceIdTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(sSequenceIdTwo, mLayerState->getId());
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Id), updates);
+}
+
+TEST_F(LayerStateTest, compareId) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionState, sSequenceIdTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getId(), otherLayerState->getId());
+
+    // Id is a unique field, so it's not computed in the hash for a layer state.
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::Id),
+              otherLayerState->getHash(LayerStateField::Id));
+
+    // Similarly, Id cannot be included in differing fields.
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Id));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Id));
+
+    EXPECT_FALSE(mLayerState->compare(*otherLayerState));
+    EXPECT_FALSE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getName) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_EQ(sDebugName, mLayerState->getName());
+}
+
+TEST_F(LayerStateTest, updateName) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionState, sSequenceId, sDebugNameTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(sDebugNameTwo, mLayerState->getName());
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Name), updates);
+}
+
+TEST_F(LayerStateTest, compareName) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionState, sSequenceId, sDebugNameTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getName(), otherLayerState->getName());
+
+    // Name is a unique field, so it's not computed in the hash for a layer state.
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::Name),
+              otherLayerState->getHash(LayerStateField::Name));
+
+    // Similarly, Name cannot be included in differing fields.
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Name));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Name));
+
+    EXPECT_FALSE(mLayerState->compare(*otherLayerState));
+    EXPECT_FALSE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getDisplayFrame) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.displayFrame = sRectOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_EQ(sRectOne, mLayerState->getDisplayFrame());
+}
+
+TEST_F(LayerStateTest, updateDisplayFrame) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.displayFrame = sRectOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.displayFrame = sRectTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(sRectTwo, mLayerState->getDisplayFrame());
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::DisplayFrame), updates);
+}
+
+TEST_F(LayerStateTest, compareDisplayFrame) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.displayFrame = sRectOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.displayFrame = sRectTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getDisplayFrame(), otherLayerState->getDisplayFrame());
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::DisplayFrame),
+              otherLayerState->getHash(LayerStateField::DisplayFrame));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::DisplayFrame),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::DisplayFrame));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::DisplayFrame),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::DisplayFrame));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getCompositionType) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.compositionType =
+            hardware::graphics::composer::hal::Composition::DEVICE;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_EQ(hardware::graphics::composer::hal::Composition::DEVICE,
+              mLayerState->getCompositionType());
+}
+
+TEST_F(LayerStateTest, getCompositionType_forcedClient) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.forceClientComposition = true;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.compositionType =
+            hardware::graphics::composer::hal::Composition::DEVICE;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_EQ(hardware::graphics::composer::hal::Composition::CLIENT,
+              mLayerState->getCompositionType());
+}
+
+TEST_F(LayerStateTest, updateCompositionType) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.compositionType =
+            hardware::graphics::composer::hal::Composition::DEVICE;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.compositionType =
+            hardware::graphics::composer::hal::Composition::SOLID_COLOR;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(hardware::graphics::composer::hal::Composition::SOLID_COLOR,
+              mLayerState->getCompositionType());
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType), updates);
+}
+
+TEST_F(LayerStateTest, compareCompositionType) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.compositionType =
+            hardware::graphics::composer::hal::Composition::DEVICE;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.compositionType =
+            hardware::graphics::composer::hal::Composition::SOLID_COLOR;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getCompositionType(), otherLayerState->getCompositionType());
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::CompositionType),
+              otherLayerState->getHash(LayerStateField::CompositionType));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::CompositionType));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::CompositionType));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getBuffer) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.buffer = new GraphicBuffer();
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_EQ(layerFECompositionState.buffer, mLayerState->getBuffer());
+}
+
+TEST_F(LayerStateTest, updateBuffer) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.buffer = new GraphicBuffer();
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(layerFECompositionStateTwo.buffer, mLayerState->getBuffer());
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer), updates);
+}
+
+TEST_F(LayerStateTest, compareBuffer) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.buffer = new GraphicBuffer();
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getBuffer(), otherLayerState->getBuffer());
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::Buffer),
+              otherLayerState->getHash(LayerStateField::Buffer));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Buffer));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Buffer));
+
+    // Buffers are explicitly excluded from comparison
+    EXPECT_FALSE(mLayerState->compare(*otherLayerState));
+    EXPECT_FALSE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateSourceCrop) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.sourceCrop = sFloatRectOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.sourceCrop = sFloatRectTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SourceCrop), updates);
+}
+
+TEST_F(LayerStateTest, compareSourceCrop) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.sourceCrop = sFloatRectOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.sourceCrop = sFloatRectTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::SourceCrop),
+              otherLayerState->getHash(LayerStateField::SourceCrop));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SourceCrop),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SourceCrop));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SourceCrop),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::SourceCrop));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateZOrder) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.z = sZOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.z = sZTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder), updates);
+}
+
+TEST_F(LayerStateTest, compareZOrder) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.z = sZOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.z = sZTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::ZOrder),
+              otherLayerState->getHash(LayerStateField::ZOrder));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::ZOrder));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::ZOrder));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateBufferTransform) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.bufferTransform = Hwc2::Transform::FLIP_H;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.bufferTransform = Hwc2::Transform::FLIP_V;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BufferTransform), updates);
+}
+
+TEST_F(LayerStateTest, compareBufferTransform) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.bufferTransform = Hwc2::Transform::FLIP_H;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.bufferTransform = Hwc2::Transform::FLIP_V;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::BufferTransform),
+              otherLayerState->getHash(LayerStateField::BufferTransform));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BufferTransform),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::BufferTransform));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BufferTransform),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::BufferTransform));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateBlendMode) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.blendMode = hal::BlendMode::COVERAGE;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.blendMode = hal::BlendMode::PREMULTIPLIED;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlendMode), updates);
+}
+
+TEST_F(LayerStateTest, compareBlendMode) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.blendMode = hal::BlendMode::COVERAGE;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.blendMode = hal::BlendMode::PREMULTIPLIED;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::BlendMode),
+              otherLayerState->getHash(LayerStateField::BlendMode));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlendMode),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::BlendMode));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlendMode),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::BlendMode));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateAlpha) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.alpha = sAlphaOne;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.alpha = sAlphaTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Alpha), updates);
+}
+
+TEST_F(LayerStateTest, compareAlpha) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.alpha = sAlphaOne;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.alpha = sAlphaTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::Alpha),
+              otherLayerState->getHash(LayerStateField::Alpha));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Alpha),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Alpha));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Alpha),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Alpha));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateVisibleRegion) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.visibleRegion = sRegionOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.visibleRegion = sRegionTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::VisibleRegion), updates);
+}
+
+TEST_F(LayerStateTest, compareVisibleRegion) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.visibleRegion = sRegionOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.visibleRegion = sRegionTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::VisibleRegion),
+              otherLayerState->getHash(LayerStateField::VisibleRegion));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::VisibleRegion),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::VisibleRegion));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::VisibleRegion),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::VisibleRegion));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateDataspace) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.dataspace = ui::Dataspace::SRGB;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.dataspace = ui::Dataspace::DISPLAY_P3;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Dataspace), updates);
+}
+
+TEST_F(LayerStateTest, compareDataspace) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.dataspace = ui::Dataspace::SRGB;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.dataspace = ui::Dataspace::DISPLAY_P3;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::Dataspace),
+              otherLayerState->getHash(LayerStateField::Dataspace));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Dataspace),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Dataspace));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Dataspace),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Dataspace));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateColorTransform) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.colorTransformIsIdentity = true;
+    layerFECompositionState.colorTransform = mat4();
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.colorTransformIsIdentity = false;
+    layerFECompositionStateTwo.colorTransform = sMat4One;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ColorTransform), updates);
+}
+
+TEST_F(LayerStateTest, compareColorTransform) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.colorTransformIsIdentity = true;
+    layerFECompositionState.colorTransform = mat4();
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.colorTransformIsIdentity = false;
+    layerFECompositionStateTwo.colorTransform = sMat4One;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::ColorTransform),
+              otherLayerState->getHash(LayerStateField::ColorTransform));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ColorTransform),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::ColorTransform));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ColorTransform),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::ColorTransform));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateSidebandStream) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.sidebandStream = NativeHandle::create(sFakeSidebandStreamOne, false);
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.sidebandStream = NativeHandle::create(sFakeSidebandStreamTwo, false);
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SidebandStream), updates);
+}
+
+TEST_F(LayerStateTest, compareSidebandStream) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.sidebandStream = NativeHandle::create(sFakeSidebandStreamOne, false);
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.sidebandStream = NativeHandle::create(sFakeSidebandStreamTwo, false);
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::SidebandStream),
+              otherLayerState->getHash(LayerStateField::SidebandStream));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SidebandStream),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SidebandStream));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SidebandStream),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::SidebandStream));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateSolidColor) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.color = sHalf4One;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.color = sHalf4Two;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SolidColor), updates);
+}
+
+TEST_F(LayerStateTest, compareSolidColor) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.color = sHalf4One;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.color = sHalf4Two;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::SolidColor),
+              otherLayerState->getHash(LayerStateField::SolidColor));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SolidColor),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SolidColor));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SolidColor),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::SolidColor));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, dumpDoesNotCrash) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    std::string dump;
+    mLayerState->dump(dump);
+    EXPECT_TRUE(dump.size() > 0);
+}
+
+TEST_F(LayerStateTest, framesSinceBufferUpdate) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    EXPECT_EQ(0, mLayerState->getFramesSinceBufferUpdate());
+    mLayerState->incrementFramesSinceBufferUpdate();
+    EXPECT_EQ(1, mLayerState->getFramesSinceBufferUpdate());
+    mLayerState->resetFramesSinceBufferUpdate();
+    EXPECT_EQ(0, mLayerState->getFramesSinceBufferUpdate());
+}
+
+TEST_F(LayerStateTest, getNonBufferHash_doesNotCommute) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.displayFrame = sRectOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.displayFrame = sRectTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(getNonBufferHash({mLayerState.get(), otherLayerState.get()}),
+              getNonBufferHash({otherLayerState.get(), mLayerState.get()}));
+}
+
+TEST_F(LayerStateTest, getNonBufferHash_isIdempotent) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.displayFrame = sRectOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.displayFrame = sRectTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_EQ(getNonBufferHash({mLayerState.get(), otherLayerState.get()}),
+              getNonBufferHash({mLayerState.get(), otherLayerState.get()}));
+}
+
+TEST_F(LayerStateTest, getNonBufferHash_filtersOutBuffers) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.buffer = new GraphicBuffer();
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_EQ(getNonBufferHash({mLayerState.get()}), getNonBufferHash({otherLayerState.get()}));
+}
+
+} // namespace
+} // namespace android::compositionengine::impl::planner
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
new file mode 100644
index 0000000..43e119f
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
@@ -0,0 +1,528 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DisplayHardware/Hal.h"
+#undef LOG_TAG
+#define LOG_TAG "PredictorTest"
+
+#include <compositionengine/impl/planner/Predictor.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/OutputLayer.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+namespace android::compositionengine::impl::planner {
+namespace {
+
+const FloatRect sFloatRectOne = FloatRect(100.f, 200.f, 300.f, 400.f);
+const FloatRect sFloatRectTwo = FloatRect(400.f, 300.f, 200.f, 100.f);
+const Rect sRectOne = Rect(1, 2, 3, 4);
+const Rect sRectTwo = Rect(4, 3, 2, 1);
+const constexpr int32_t sZOne = 100;
+const constexpr int32_t sZTwo = 101;
+const constexpr float sAlphaOne = 0.25f;
+const constexpr float sAlphaTwo = 0.5f;
+const Region sRegionOne = Region(sRectOne);
+const Region sRegionTwo = Region(sRectTwo);
+const mat4 sMat4One = mat4::scale(vec4(2.f, 3.f, 1.f, 1.f));
+
+using testing::Return;
+using testing::ReturnRef;
+
+const std::string sDebugName = std::string("Test LayerFE");
+const constexpr int32_t sSequenceId = 12345;
+
+void setupMocksForLayer(mock::OutputLayer& layer, mock::LayerFE& layerFE,
+                        const OutputLayerCompositionState& outputLayerState,
+                        const LayerFECompositionState& layerFEState) {
+    EXPECT_CALL(layer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
+    EXPECT_CALL(layer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+    EXPECT_CALL(layerFE, getSequence()).WillRepeatedly(Return(sSequenceId));
+    EXPECT_CALL(layerFE, getDebugName()).WillRepeatedly(Return(sDebugName.c_str()));
+    EXPECT_CALL(layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+}
+
+struct LayerStackTest : public testing::Test {
+    LayerStackTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    ~LayerStackTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+};
+
+TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchSizeDifferences) {
+    mock::OutputLayer outputLayerOne;
+    mock::LayerFE layerFEOne;
+    OutputLayerCompositionState outputLayerCompositionStateOne;
+    LayerFECompositionState layerFECompositionStateOne;
+    setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+                       layerFECompositionStateOne);
+    LayerState layerStateOne(&outputLayerOne);
+
+    mock::OutputLayer outputLayerTwo;
+    mock::LayerFE layerFETwo;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    LayerFECompositionState layerFECompositionStateTwo;
+    setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+                       layerFECompositionStateTwo);
+    LayerState layerStateTwo(&outputLayerTwo);
+
+    mock::OutputLayer outputLayerThree;
+    mock::LayerFE layerFEThree;
+    OutputLayerCompositionState outputLayerCompositionStateThree;
+    LayerFECompositionState layerFECompositionStateThree;
+    setupMocksForLayer(outputLayerThree, layerFEThree, outputLayerCompositionStateThree,
+                       layerFECompositionStateThree);
+    LayerState layerStateThree(&outputLayerThree);
+
+    LayerStack stack({&layerStateOne});
+
+    EXPECT_FALSE(stack.getApproximateMatch({}));
+    EXPECT_FALSE(stack.getApproximateMatch({&layerStateOne, &layerStateThree}));
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchDifferentCompositionTypes) {
+    mock::OutputLayer outputLayerOne;
+    mock::LayerFE layerFEOne;
+    OutputLayerCompositionState outputLayerCompositionStateOne;
+    LayerFECompositionState layerFECompositionStateOne;
+    layerFECompositionStateOne.compositionType = hal::Composition::DEVICE;
+    setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+                       layerFECompositionStateOne);
+    LayerState layerStateOne(&outputLayerOne);
+
+    mock::OutputLayer outputLayerTwo;
+    mock::LayerFE layerFETwo;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.compositionType = hal::Composition::SOLID_COLOR;
+    setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+                       layerFECompositionStateTwo);
+    LayerState layerStateTwo(&outputLayerTwo);
+
+    LayerStack stack({&layerStateOne});
+
+    EXPECT_FALSE(stack.getApproximateMatch({&layerStateTwo}));
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInSingleLayer) {
+    mock::OutputLayer outputLayerOne;
+    mock::LayerFE layerFEOne;
+    OutputLayerCompositionState outputLayerCompositionStateOne{
+            .sourceCrop = sFloatRectOne,
+    };
+    LayerFECompositionState layerFECompositionStateOne;
+    setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+                       layerFECompositionStateOne);
+    LayerState layerStateOne(&outputLayerOne);
+
+    mock::OutputLayer outputLayerTwo;
+    mock::LayerFE layerFETwo;
+    OutputLayerCompositionState outputLayerCompositionStateTwo{
+            .sourceCrop = sFloatRectTwo,
+    };
+    LayerFECompositionState layerFECompositionStateTwo;
+    setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+                       layerFECompositionStateTwo);
+    LayerState layerStateTwo(&outputLayerTwo);
+
+    LayerStack stack({&layerStateOne});
+
+    const auto match = stack.getApproximateMatch({&layerStateTwo});
+    EXPECT_TRUE(match);
+    LayerStack::ApproximateMatch expectedMatch;
+    expectedMatch.differingIndex = 0;
+    expectedMatch.differingFields = LayerStateField::SourceCrop;
+    EXPECT_EQ(expectedMatch, *match);
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInMultiLayerStack) {
+    mock::OutputLayer outputLayerOne;
+    mock::LayerFE layerFEOne;
+    OutputLayerCompositionState outputLayerCompositionStateOne{
+            .sourceCrop = sFloatRectOne,
+    };
+    LayerFECompositionState layerFECompositionStateOne;
+    setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+                       layerFECompositionStateOne);
+    LayerState layerStateOne(&outputLayerOne);
+
+    mock::OutputLayer outputLayerTwo;
+    mock::LayerFE layerFETwo;
+    OutputLayerCompositionState outputLayerCompositionStateTwo{
+            .sourceCrop = sFloatRectTwo,
+    };
+    LayerFECompositionState layerFECompositionStateTwo;
+    setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+                       layerFECompositionStateTwo);
+    LayerState layerStateTwo(&outputLayerTwo);
+
+    LayerStack stack({&layerStateOne, &layerStateOne});
+
+    const auto match = stack.getApproximateMatch({&layerStateOne, &layerStateTwo});
+    EXPECT_TRUE(match);
+    LayerStack::ApproximateMatch expectedMatch;
+    expectedMatch.differingIndex = 1;
+    expectedMatch.differingFields = LayerStateField::SourceCrop;
+    EXPECT_EQ(expectedMatch, *match);
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchManyDifferences) {
+    mock::OutputLayer outputLayerOne;
+    mock::LayerFE layerFEOne;
+    OutputLayerCompositionState outputLayerCompositionStateOne{
+            .visibleRegion = sRegionOne,
+            .displayFrame = sRectOne,
+            .sourceCrop = sFloatRectOne,
+            .dataspace = ui::Dataspace::SRGB,
+            .z = sZOne,
+    };
+    LayerFECompositionState layerFECompositionStateOne;
+    layerFECompositionStateOne.alpha = sAlphaOne;
+    layerFECompositionStateOne.colorTransformIsIdentity = true;
+    setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+                       layerFECompositionStateOne);
+    LayerState layerStateOne(&outputLayerOne);
+
+    mock::OutputLayer outputLayerTwo;
+    mock::LayerFE layerFETwo;
+    OutputLayerCompositionState outputLayerCompositionStateTwo{
+            .visibleRegion = sRegionTwo,
+            .displayFrame = sRectTwo,
+            .sourceCrop = sFloatRectTwo,
+            .dataspace = ui::Dataspace::DISPLAY_P3,
+            .z = sZTwo,
+    };
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.alpha = sAlphaTwo;
+    layerFECompositionStateTwo.colorTransformIsIdentity = false;
+    layerFECompositionStateTwo.colorTransform = sMat4One;
+    setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+                       layerFECompositionStateTwo);
+    LayerState layerStateTwo(&outputLayerTwo);
+
+    LayerStack stack({&layerStateOne});
+
+    EXPECT_FALSE(stack.getApproximateMatch({&layerStateTwo}));
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_exactMatchesSameBuffer) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    mock::OutputLayer outputLayerOne;
+    mock::LayerFE layerFEOne;
+    OutputLayerCompositionState outputLayerCompositionStateOne;
+    LayerFECompositionState layerFECompositionStateOne;
+    layerFECompositionStateOne.buffer = buffer;
+    setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+                       layerFECompositionStateOne);
+    LayerState layerStateOne(&outputLayerOne);
+
+    mock::OutputLayer outputLayerTwo;
+    mock::LayerFE layerFETwo;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.buffer = buffer;
+    setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+                       layerFECompositionStateTwo);
+    LayerState layerStateTwo(&outputLayerTwo);
+
+    LayerStack stack({&layerStateOne});
+
+    const auto match = stack.getApproximateMatch({&layerStateTwo});
+    EXPECT_TRUE(match);
+    LayerStack::ApproximateMatch expectedMatch;
+    expectedMatch.differingIndex = 0;
+    expectedMatch.differingFields = LayerStateField::None;
+    EXPECT_EQ(expectedMatch, *match);
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_alwaysMatchesClientComposition) {
+    mock::OutputLayer outputLayerOne;
+    mock::LayerFE layerFEOne;
+    OutputLayerCompositionState outputLayerCompositionStateOne{
+            .visibleRegion = sRegionOne,
+            .forceClientComposition = true,
+            .displayFrame = sRectOne,
+            .sourceCrop = sFloatRectOne,
+            .dataspace = ui::Dataspace::SRGB,
+            .z = sZOne,
+    };
+    LayerFECompositionState layerFECompositionStateOne;
+    layerFECompositionStateOne.buffer = new GraphicBuffer();
+    layerFECompositionStateOne.alpha = sAlphaOne;
+    layerFECompositionStateOne.colorTransformIsIdentity = true;
+    setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+                       layerFECompositionStateOne);
+    LayerState layerStateOne(&outputLayerOne);
+
+    mock::OutputLayer outputLayerTwo;
+    mock::LayerFE layerFETwo;
+    OutputLayerCompositionState outputLayerCompositionStateTwo{
+            .visibleRegion = sRegionTwo,
+            .forceClientComposition = true,
+            .displayFrame = sRectTwo,
+            .sourceCrop = sFloatRectTwo,
+            .dataspace = ui::Dataspace::DISPLAY_P3,
+            .z = sZTwo,
+    };
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    layerFECompositionStateTwo.alpha = sAlphaTwo;
+    layerFECompositionStateTwo.colorTransformIsIdentity = false;
+    layerFECompositionStateTwo.colorTransform = sMat4One;
+    setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+                       layerFECompositionStateTwo);
+    LayerState layerStateTwo(&outputLayerTwo);
+
+    LayerStack stack({&layerStateOne});
+
+    const auto match = stack.getApproximateMatch({&layerStateTwo});
+    EXPECT_TRUE(match);
+    LayerStack::ApproximateMatch expectedMatch;
+    expectedMatch.differingIndex = 0;
+    expectedMatch.differingFields = LayerStateField::None;
+    EXPECT_EQ(expectedMatch, *match);
+}
+
+TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchMultipleApproximations) {
+    mock::OutputLayer outputLayerOne;
+    mock::LayerFE layerFEOne;
+    OutputLayerCompositionState outputLayerCompositionStateOne{
+            .sourceCrop = sFloatRectOne,
+    };
+    LayerFECompositionState layerFECompositionStateOne;
+    layerFECompositionStateOne.buffer = new GraphicBuffer();
+    setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+                       layerFECompositionStateOne);
+    LayerState layerStateOne(&outputLayerOne);
+
+    mock::OutputLayer outputLayerTwo;
+    mock::LayerFE layerFETwo;
+    OutputLayerCompositionState outputLayerCompositionStateTwo{
+            .sourceCrop = sFloatRectTwo,
+    };
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+                       layerFECompositionStateTwo);
+    LayerState layerStateTwo(&outputLayerTwo);
+
+    EXPECT_TRUE(LayerStack({&layerStateOne}).getApproximateMatch({&layerStateTwo}));
+
+    LayerStack stack({&layerStateOne, &layerStateOne});
+    EXPECT_FALSE(stack.getApproximateMatch({&layerStateTwo, &layerStateTwo}));
+}
+
+struct PredictionTest : public testing::Test {
+    PredictionTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    ~PredictionTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+};
+
+TEST_F(PredictionTest, constructPrediction) {
+    Plan plan;
+    plan.addLayerType(hal::Composition::DEVICE);
+
+    Prediction prediction({}, plan);
+
+    EXPECT_EQ(plan, prediction.getPlan());
+
+    // check that dump doesn't crash
+    std::string result;
+    prediction.dump(result);
+}
+
+TEST_F(PredictionTest, recordHits) {
+    Prediction prediction({}, {});
+
+    const constexpr uint32_t kExactMatches = 2;
+    for (uint32_t i = 0; i < kExactMatches; i++) {
+        prediction.recordHit(Prediction::Type::Exact);
+    }
+
+    const constexpr uint32_t kApproximateMatches = 3;
+    for (uint32_t i = 0; i < kApproximateMatches; i++) {
+        prediction.recordHit(Prediction::Type::Approximate);
+    }
+
+    EXPECT_EQ(kExactMatches, prediction.getHitCount(Prediction::Type::Exact));
+    EXPECT_EQ(kApproximateMatches, prediction.getHitCount(Prediction::Type::Approximate));
+    EXPECT_EQ(kExactMatches + kApproximateMatches, prediction.getHitCount(Prediction::Type::Total));
+}
+
+TEST_F(PredictionTest, recordMisses) {
+    Prediction prediction({}, {});
+
+    const constexpr uint32_t kExactMatches = 2;
+    for (uint32_t i = 0; i < kExactMatches; i++) {
+        prediction.recordMiss(Prediction::Type::Exact);
+    }
+
+    const constexpr uint32_t kApproximateMatches = 3;
+    for (uint32_t i = 0; i < kApproximateMatches; i++) {
+        prediction.recordMiss(Prediction::Type::Approximate);
+    }
+
+    EXPECT_EQ(kExactMatches, prediction.getMissCount(Prediction::Type::Exact));
+    EXPECT_EQ(kApproximateMatches, prediction.getMissCount(Prediction::Type::Approximate));
+    EXPECT_EQ(kExactMatches + kApproximateMatches,
+              prediction.getMissCount(Prediction::Type::Total));
+}
+
+struct PredictorTest : public testing::Test {
+    PredictorTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    ~PredictorTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+};
+
+TEST_F(PredictorTest, getPredictedPlan_emptyLayersWithoutExactMatch_returnsNullopt) {
+    Predictor predictor;
+    EXPECT_FALSE(predictor.getPredictedPlan({}, 0));
+}
+
+TEST_F(PredictorTest, getPredictedPlan_recordCandidateAndRetrieveExactMatch) {
+    mock::OutputLayer outputLayerOne;
+    mock::LayerFE layerFEOne;
+    OutputLayerCompositionState outputLayerCompositionStateOne;
+    LayerFECompositionState layerFECompositionStateOne;
+    layerFECompositionStateOne.compositionType = hal::Composition::DEVICE;
+    setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+                       layerFECompositionStateOne);
+    LayerState layerStateOne(&outputLayerOne);
+
+    Plan plan;
+    plan.addLayerType(hal::Composition::DEVICE);
+
+    Predictor predictor;
+
+    NonBufferHash hash = getNonBufferHash({&layerStateOne});
+
+    predictor.recordResult(std::nullopt, hash, {&layerStateOne}, false, plan);
+
+    auto predictedPlan = predictor.getPredictedPlan({}, hash);
+    EXPECT_TRUE(predictedPlan);
+    Predictor::PredictedPlan expectedPlan{hash, plan, Prediction::Type::Exact};
+    EXPECT_EQ(expectedPlan, predictedPlan);
+}
+
+TEST_F(PredictorTest, getPredictedPlan_recordCandidateAndRetrieveApproximateMatch) {
+    mock::OutputLayer outputLayerOne;
+    mock::LayerFE layerFEOne;
+    OutputLayerCompositionState outputLayerCompositionStateOne{
+            .sourceCrop = sFloatRectOne,
+    };
+    LayerFECompositionState layerFECompositionStateOne;
+    setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+                       layerFECompositionStateOne);
+    LayerState layerStateOne(&outputLayerOne);
+
+    mock::OutputLayer outputLayerTwo;
+    mock::LayerFE layerFETwo;
+    OutputLayerCompositionState outputLayerCompositionStateTwo{
+            .sourceCrop = sFloatRectTwo,
+    };
+    LayerFECompositionState layerFECompositionStateTwo;
+    setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+                       layerFECompositionStateTwo);
+    LayerState layerStateTwo(&outputLayerTwo);
+
+    Plan plan;
+    plan.addLayerType(hal::Composition::DEVICE);
+
+    Predictor predictor;
+
+    NonBufferHash hashOne = getNonBufferHash({&layerStateOne});
+    NonBufferHash hashTwo = getNonBufferHash({&layerStateTwo});
+
+    predictor.recordResult(std::nullopt, hashOne, {&layerStateOne}, false, plan);
+
+    auto predictedPlan = predictor.getPredictedPlan({&layerStateTwo}, hashTwo);
+    EXPECT_TRUE(predictedPlan);
+    Predictor::PredictedPlan expectedPlan{hashOne, plan, Prediction::Type::Approximate};
+    EXPECT_EQ(expectedPlan, predictedPlan);
+}
+
+TEST_F(PredictorTest, recordMissedPlan_skipsApproximateMatch) {
+    mock::OutputLayer outputLayerOne;
+    mock::LayerFE layerFEOne;
+    OutputLayerCompositionState outputLayerCompositionStateOne{
+            .sourceCrop = sFloatRectOne,
+    };
+    LayerFECompositionState layerFECompositionStateOne;
+    setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
+                       layerFECompositionStateOne);
+    LayerState layerStateOne(&outputLayerOne);
+
+    mock::OutputLayer outputLayerTwo;
+    mock::LayerFE layerFETwo;
+    OutputLayerCompositionState outputLayerCompositionStateTwo{
+            .sourceCrop = sFloatRectTwo,
+    };
+    LayerFECompositionState layerFECompositionStateTwo;
+    setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
+                       layerFECompositionStateTwo);
+    LayerState layerStateTwo(&outputLayerTwo);
+
+    Plan plan;
+    plan.addLayerType(hal::Composition::DEVICE);
+
+    Predictor predictor;
+
+    NonBufferHash hashOne = getNonBufferHash({&layerStateOne});
+    NonBufferHash hashTwo = getNonBufferHash({&layerStateTwo});
+
+    predictor.recordResult(std::nullopt, hashOne, {&layerStateOne}, false, plan);
+
+    auto predictedPlan = predictor.getPredictedPlan({&layerStateTwo}, hashTwo);
+    ASSERT_TRUE(predictedPlan);
+    EXPECT_EQ(Prediction::Type::Approximate, predictedPlan->type);
+
+    Plan planTwo;
+    planTwo.addLayerType(hal::Composition::CLIENT);
+    predictor.recordResult(predictedPlan, hashTwo, {&layerStateTwo}, false, planTwo);
+    // Now trying to retrieve the predicted plan again returns a nullopt instead.
+    // TODO(b/158790260): Even though this is enforced in this test, we might want to reassess this.
+    // One of the implications around this implementation is that if we miss a prediction then we
+    // can never actually correct our mistake if we see the same layer stack again, which doesn't
+    // seem robust.
+    auto predictedPlanTwo = predictor.getPredictedPlan({&layerStateTwo}, hashTwo);
+    EXPECT_FALSE(predictedPlanTwo);
+}
+
+} // namespace
+} // namespace android::compositionengine::impl::planner
\ No newline at end of file
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 03e38f3..b1dff8d 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -296,7 +296,7 @@
                            frametimeline::TimelineItem&& predictions,
                            std::shared_ptr<TimeStats> timeStats,
                            JankClassificationThresholds thresholds,
-                           TraceCookieCounter* traceCookieCounter)
+                           TraceCookieCounter* traceCookieCounter, bool isBuffer)
       : mToken(frameTimelineInfo.vsyncId),
         mInputEventId(frameTimelineInfo.inputEventId),
         mOwnerPid(ownerPid),
@@ -310,7 +310,8 @@
         mActuals({0, 0, 0}),
         mTimeStats(timeStats),
         mJankClassificationThresholds(thresholds),
-        mTraceCookieCounter(*traceCookieCounter) {}
+        mTraceCookieCounter(*traceCookieCounter),
+        mIsBuffer(isBuffer) {}
 
 void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) {
     std::scoped_lock lock(mMutex);
@@ -395,6 +396,20 @@
     return mDropTime;
 }
 
+void SurfaceFrame::promoteToBuffer() {
+    std::scoped_lock lock(mMutex);
+    LOG_ALWAYS_FATAL_IF(mIsBuffer == true,
+                        "Trying to promote an already promoted BufferSurfaceFrame from layer %s "
+                        "with token %" PRId64 "",
+                        mDebugName.c_str(), mToken);
+    mIsBuffer = true;
+}
+
+bool SurfaceFrame::getIsBuffer() const {
+    std::scoped_lock lock(mMutex);
+    return mIsBuffer;
+}
+
 void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) const {
     std::scoped_lock lock(mMutex);
     StringAppendF(&result, "%s", indent.c_str());
@@ -407,6 +422,8 @@
     StringAppendF(&result, "%s", indent.c_str());
     StringAppendF(&result, "Token: %" PRId64 "\n", mToken);
     StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Is Buffer?: %d\n", mIsBuffer);
+    StringAppendF(&result, "%s", indent.c_str());
     StringAppendF(&result, "Owner Pid : %d\n", mOwnerPid);
     StringAppendF(&result, "%s", indent.c_str());
     StringAppendF(&result, "Scheduled rendering rate: %d fps\n",
@@ -444,37 +461,41 @@
     dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
 }
 
-void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate,
-                             nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta) {
+std::string SurfaceFrame::miniDump() const {
     std::scoped_lock lock(mMutex);
+    std::string result;
+    StringAppendF(&result, "Layer - %s\n", mDebugName.c_str());
+    StringAppendF(&result, "Token: %" PRId64 "\n", mToken);
+    StringAppendF(&result, "Is Buffer?: %d\n", mIsBuffer);
+    StringAppendF(&result, "Present State : %s\n", toString(mPresentState).c_str());
+    StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
+    StringAppendF(&result, "Jank Type : %s\n", jankTypeBitmaskToString(mJankType).c_str());
+    StringAppendF(&result, "Present Metadata : %s\n", toString(mFramePresentMetadata).c_str());
+    StringAppendF(&result, "Finish Metadata: %s\n", toString(mFrameReadyMetadata).c_str());
+    StringAppendF(&result, "Present time: %" PRId64 "", mActuals.presentTime);
+    return result;
+}
 
-    if (mPresentState != PresentState::Presented) {
-        // No need to update dropped buffers
+void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate,
+                                      nsecs_t& deadlineDelta) {
+    if (mPredictionState == PredictionState::Expired ||
+        mActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
+        // Cannot do any classification for invalid present time.
+        // For prediction expired case, we do not know what happened here to classify this
+        // correctly. This could potentially be AppDeadlineMissed but that's assuming no app will
+        // request frames 120ms apart.
+        mJankType = JankType::Unknown;
+        deadlineDelta = -1;
         return;
     }
 
-    mActuals.presentTime = presentTime;
-    // Jank Analysis for SurfaceFrame
     if (mPredictionState == PredictionState::None) {
         // Cannot do jank classification on frames that don't have a token.
         return;
     }
-    if (mPredictionState == PredictionState::Expired) {
-        // We do not know what happened here to classify this correctly. This could
-        // potentially be AppDeadlineMissed but that's assuming no app will request frames
-        // 120ms apart.
-        mJankType = JankType::Unknown;
-        mFramePresentMetadata = FramePresentMetadata::UnknownPresent;
-        mFrameReadyMetadata = FrameReadyMetadata::UnknownFinish;
-        const constexpr nsecs_t kAppDeadlineDelta = -1;
-        mTimeStats->incrementJankyFrames({refreshRate, mRenderRate, mOwnerUid, mLayerName,
-                                          mJankType, displayDeadlineDelta, displayPresentDelta,
-                                          kAppDeadlineDelta});
-        return;
-    }
 
+    deadlineDelta = mActuals.endTime - mPredictions.endTime;
     const nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
-    const nsecs_t deadlineDelta = mActuals.endTime - mPredictions.endTime;
     const nsecs_t deltaToVsync = refreshRate.getPeriodNsecs() > 0
             ? std::abs(presentDelta) % refreshRate.getPeriodNsecs()
             : 0;
@@ -558,8 +579,28 @@
             }
         }
     }
-    mTimeStats->incrementJankyFrames({refreshRate, mRenderRate, mOwnerUid, mLayerName, mJankType,
-                                      displayDeadlineDelta, displayPresentDelta, deadlineDelta});
+}
+
+void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate,
+                             nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta) {
+    std::scoped_lock lock(mMutex);
+
+    if (mPresentState != PresentState::Presented) {
+        // No need to update dropped buffers
+        return;
+    }
+
+    mActuals.presentTime = presentTime;
+    nsecs_t deadlineDelta = 0;
+
+    classifyJankLocked(displayFrameJankType, refreshRate, deadlineDelta);
+
+    if (mPredictionState != PredictionState::None) {
+        // Only update janky frames if the app used vsync predictions
+        mTimeStats->incrementJankyFrames({refreshRate, mRenderRate, mOwnerUid, mLayerName,
+                                          mJankType, displayDeadlineDelta, displayPresentDelta,
+                                          deadlineDelta});
+    }
 }
 
 void SurfaceFrame::tracePredictions(int64_t displayFrameToken) const {
@@ -735,13 +776,14 @@
 
 std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
         const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId,
-        std::string layerName, std::string debugName) {
+        std::string layerName, std::string debugName, bool isBuffer) {
     ATRACE_CALL();
     if (frameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
         return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
                                               std::move(layerName), std::move(debugName),
                                               PredictionState::None, TimelineItem(), mTimeStats,
-                                              mJankClassificationThresholds, &mTraceCookieCounter);
+                                              mJankClassificationThresholds, &mTraceCookieCounter,
+                                              isBuffer);
     }
     std::optional<TimelineItem> predictions =
             mTokenManager.getPredictionsForToken(frameTimelineInfo.vsyncId);
@@ -750,12 +792,13 @@
                                               std::move(layerName), std::move(debugName),
                                               PredictionState::Valid, std::move(*predictions),
                                               mTimeStats, mJankClassificationThresholds,
-                                              &mTraceCookieCounter);
+                                              &mTraceCookieCounter, isBuffer);
     }
     return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
                                           std::move(layerName), std::move(debugName),
                                           PredictionState::Expired, TimelineItem(), mTimeStats,
-                                          mJankClassificationThresholds, &mTraceCookieCounter);
+                                          mJankClassificationThresholds, &mTraceCookieCounter,
+                                          isBuffer);
 }
 
 FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats,
@@ -826,25 +869,28 @@
     mSurfaceFlingerActuals.endTime = actualEndTime;
 }
 
-void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) {
-    mSurfaceFlingerActuals.presentTime = signalTime;
-    if (mPredictionState == PredictionState::Expired) {
-        // Cannot do jank classification with expired predictions
+void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync) {
+    if (mPredictionState == PredictionState::Expired ||
+        mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
+        // Cannot do jank classification with expired predictions or invalid signal times.
         mJankType = JankType::Unknown;
+        deadlineDelta = -1;
+        deltaToVsync = -1;
         return;
     }
 
     // Delta between the expected present and the actual present
     const nsecs_t presentDelta =
             mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime;
-    const nsecs_t deadlineDelta =
+    deadlineDelta =
             mSurfaceFlingerActuals.endTime - (mSurfaceFlingerPredictions.endTime - mHwcDuration);
 
     // How far off was the presentDelta when compared to the vsyncPeriod. Used in checking if there
     // was a prediction error or not.
-    nsecs_t deltaToVsync = mRefreshRate.getPeriodNsecs() > 0
+    deltaToVsync = mRefreshRate.getPeriodNsecs() > 0
             ? std::abs(presentDelta) % mRefreshRate.getPeriodNsecs()
             : 0;
+
     if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
         mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
                                                  : FramePresentMetadata::EarlyPresent;
@@ -922,6 +968,14 @@
             mJankType = JankType::Unknown;
         }
     }
+}
+
+void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) {
+    mSurfaceFlingerActuals.presentTime = signalTime;
+    nsecs_t deadlineDelta = 0;
+    nsecs_t deltaToVsync = 0;
+    classifyJank(deadlineDelta, deltaToVsync);
+
     for (auto& surfaceFrame : mSurfaceFrames) {
         surfaceFrame->onPresent(signalTime, mJankType, mRefreshRate, deadlineDelta, deltaToVsync);
     }
@@ -1084,11 +1138,9 @@
                 continue;
             }
         }
-        if (signalTime != Fence::SIGNAL_TIME_INVALID) {
-            auto& displayFrame = pendingPresentFence.second;
-            displayFrame->onPresent(signalTime);
-            displayFrame->trace(mSurfaceFlingerPid);
-        }
+        auto& displayFrame = pendingPresentFence.second;
+        displayFrame->onPresent(signalTime);
+        displayFrame->trace(mSurfaceFlingerPid);
 
         mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
         --i;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 7c6a0cc..3cf35f0 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -159,7 +159,7 @@
                  int32_t layerId, std::string layerName, std::string debugName,
                  PredictionState predictionState, TimelineItem&& predictions,
                  std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
-                 TraceCookieCounter* traceCookieCounter);
+                 TraceCookieCounter* traceCookieCounter, bool isBuffer);
     ~SurfaceFrame() = default;
 
     // Returns std::nullopt if the frame hasn't been classified yet.
@@ -181,6 +181,10 @@
     void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0);
     void setRenderRate(Fps renderRate);
 
+    // When a bufferless SurfaceFrame is promoted to a buffer SurfaceFrame, we also have to update
+    // isBuffer.
+    void promoteToBuffer();
+
     // Functions called by FrameTimeline
     // BaseTime is the smallest timestamp in this SurfaceFrame.
     // Used for dumping all timestamps relative to the oldest, making it easy to read.
@@ -192,6 +196,8 @@
                    nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta);
     // All the timestamps are dumped relative to the baseTime
     void dump(std::string& result, const std::string& indent, nsecs_t baseTime) const;
+    // Dumps only the layer, token, is buffer, jank metadata, prediction and present states.
+    std::string miniDump() const;
     // Emits a packet for perfetto tracing. The function body will be executed only if tracing is
     // enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding
     // DisplayFrame at the trace processor side.
@@ -206,6 +212,7 @@
     FrameReadyMetadata getFrameReadyMetadata() const;
     FramePresentMetadata getFramePresentMetadata() const;
     nsecs_t getDropTime() const;
+    bool getIsBuffer() const;
 
     // For prediction expired frames, this delta is subtracted from the actual end time to get a
     // start time decent enough to see in traces.
@@ -216,6 +223,8 @@
 private:
     void tracePredictions(int64_t displayFrameToken) const;
     void traceActuals(int64_t displayFrameToken) const;
+    void classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate,
+                            nsecs_t& deadlineDelta) REQUIRES(mMutex);
 
     const int64_t mToken;
     const int32_t mInputEventId;
@@ -251,6 +260,9 @@
     // TraceCookieCounter is used to obtain the cookie for sendig trace packets to perfetto. Using a
     // reference here because the counter is owned by FrameTimeline, which outlives SurfaceFrame.
     TraceCookieCounter& mTraceCookieCounter;
+    // Tells if the SurfaceFrame is representing a buffer or a transaction without a
+    // buffer(animations)
+    bool mIsBuffer;
 };
 
 /*
@@ -270,7 +282,7 @@
     // Debug name is the human-readable debugging string for dumpsys.
     virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
             const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
-            int32_t layerId, std::string layerName, std::string debugName) = 0;
+            int32_t layerId, std::string layerName, std::string debugName, bool isBuffer) = 0;
 
     // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
     // composited into one display frame.
@@ -355,7 +367,7 @@
         // Sets the token, vsyncPeriod, predictions and SF start time.
         void onSfWakeUp(int64_t token, Fps refreshRate, std::optional<TimelineItem> predictions,
                         nsecs_t wakeUpTime);
-        // Sets the appropriate metadata, classifies the jank and returns the classified jankType.
+        // Sets the appropriate metadata and classifies the jank.
         void onPresent(nsecs_t signalTime);
         // Adds the provided SurfaceFrame to the current display frame.
         void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame);
@@ -383,6 +395,7 @@
         void dump(std::string& result, nsecs_t baseTime) const;
         void tracePredictions(pid_t surfaceFlingerPid) const;
         void traceActuals(pid_t surfaceFlingerPid) const;
+        void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync);
 
         int64_t mToken = FrameTimelineInfo::INVALID_VSYNC_ID;
 
@@ -428,7 +441,7 @@
     frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
     std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
             const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
-            int32_t layerId, std::string layerName, std::string debugName) override;
+            int32_t layerId, std::string layerName, std::string debugName, bool isBuffer) override;
     void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
     void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override;
     void setSfPresent(nsecs_t sfPresentTime,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 061ad0e..cd3e8ad 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -102,18 +102,18 @@
     mCurrentState.active_legacy.h = args.h;
     mCurrentState.flags = layerFlags;
     mCurrentState.active_legacy.transform.set(0, 0);
-    mCurrentState.crop_legacy.makeInvalid();
-    mCurrentState.requestedCrop_legacy = mCurrentState.crop_legacy;
+    mCurrentState.crop.makeInvalid();
+    mCurrentState.requestedCrop = mCurrentState.crop;
     mCurrentState.z = 0;
     mCurrentState.color.a = 1.0f;
     mCurrentState.layerStack = 0;
     mCurrentState.sequence = 0;
     mCurrentState.requested_legacy = mCurrentState.active_legacy;
-    mCurrentState.active.w = UINT32_MAX;
-    mCurrentState.active.h = UINT32_MAX;
-    mCurrentState.active.transform.set(0, 0);
+    mCurrentState.width = UINT32_MAX;
+    mCurrentState.height = UINT32_MAX;
+    mCurrentState.transform.set(0, 0);
     mCurrentState.frameNumber = 0;
-    mCurrentState.transform = 0;
+    mCurrentState.bufferTransform = 0;
     mCurrentState.transformToDisplayInverse = false;
     mCurrentState.crop.makeInvalid();
     mCurrentState.acquireFence = new Fence(-1);
@@ -949,13 +949,12 @@
                  "            requested={ wh={%4u,%4u} }}\n",
                  this, getName().c_str(), getBufferTransform(), getEffectiveScalingMode(),
                  stateToCommit->active_legacy.w, stateToCommit->active_legacy.h,
-                 stateToCommit->crop_legacy.left, stateToCommit->crop_legacy.top,
-                 stateToCommit->crop_legacy.right, stateToCommit->crop_legacy.bottom,
-                 stateToCommit->crop_legacy.getWidth(), stateToCommit->crop_legacy.getHeight(),
-                 stateToCommit->requested_legacy.w, stateToCommit->requested_legacy.h,
-                 s.active_legacy.w, s.active_legacy.h, s.crop_legacy.left, s.crop_legacy.top,
-                 s.crop_legacy.right, s.crop_legacy.bottom, s.crop_legacy.getWidth(),
-                 s.crop_legacy.getHeight(), s.requested_legacy.w, s.requested_legacy.h);
+                 stateToCommit->crop.left, stateToCommit->crop.top, stateToCommit->crop.right,
+                 stateToCommit->crop.bottom, stateToCommit->crop.getWidth(),
+                 stateToCommit->crop.getHeight(), stateToCommit->requested_legacy.w,
+                 stateToCommit->requested_legacy.h, s.active_legacy.w, s.active_legacy.h,
+                 s.crop.left, s.crop.top, s.crop.right, s.crop.bottom, s.crop.getWidth(),
+                 s.crop.getHeight(), s.requested_legacy.w, s.requested_legacy.h);
     }
 
     // Don't let Layer::doTransaction update the drawing state
@@ -1049,6 +1048,9 @@
         c.callbackHandles.push_back(handle);
     }
 
+    // Allow BufferStateLayer to release any unlatched buffers in drawing state.
+    bufferMayChange(c.buffer);
+
     // Commit the transaction
     commitTransaction(c);
     mPendingStatesSnapshot = mPendingStates;
@@ -1058,6 +1060,15 @@
 }
 
 void Layer::commitTransaction(State& stateToCommit) {
+    if (auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX;
+        mDrawingState.buffer != stateToCommit.buffer && bufferSurfaceFrame != nullptr &&
+        bufferSurfaceFrame->getPresentState() != PresentState::Presented) {
+        // If the previous buffer was committed but not latched (refreshPending - happens during
+        // back to back invalidates), it gets silently dropped here. Mark the corresponding
+        // SurfaceFrame as dropped to prevent it from getting stuck in the pending classification
+        // list.
+        addSurfaceFrameDroppedForBuffer(bufferSurfaceFrame);
+    }
     mDrawingState = stateToCommit;
 
     // Set the present state for all bufferlessSurfaceFramesTX to Presented. The
@@ -1326,11 +1337,11 @@
     return true;
 }
 
-bool Layer::setCrop_legacy(const Rect& crop) {
-    if (mCurrentState.requestedCrop_legacy == crop) return false;
+bool Layer::setCrop(const Rect& crop) {
+    if (mCurrentState.requestedCrop == crop) return false;
     mCurrentState.sequence++;
-    mCurrentState.requestedCrop_legacy = crop;
-    mCurrentState.crop_legacy = crop;
+    mCurrentState.requestedCrop = crop;
+    mCurrentState.crop = crop;
 
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
@@ -1537,6 +1548,7 @@
         // Promote the bufferlessSurfaceFrame to a bufferSurfaceFrameTX
         mCurrentState.bufferSurfaceFrameTX = it->second;
         mCurrentState.bufferlessSurfaceFramesTX.erase(it);
+        mCurrentState.bufferSurfaceFrameTX->promoteToBuffer();
         mCurrentState.bufferSurfaceFrameTX->setActualQueueTime(postTime);
     } else {
         mCurrentState.bufferSurfaceFrameTX =
@@ -1596,7 +1608,8 @@
     auto surfaceFrame =
             mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
                                                                  getSequence(), mName,
-                                                                 mTransactionName);
+                                                                 mTransactionName,
+                                                                 /*isBuffer*/ false);
     // For Transactions, the post time is considered to be both queue and acquire fence time.
     surfaceFrame->setActualQueueTime(postTime);
     surfaceFrame->setAcquireFenceTime(postTime);
@@ -1612,7 +1625,8 @@
         const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName) {
     auto surfaceFrame =
             mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
-                                                                 getSequence(), mName, debugName);
+                                                                 getSequence(), mName, debugName,
+                                                                 /*isBuffer*/ true);
     // For buffers, acquire fence time will set during latch.
     surfaceFrame->setActualQueueTime(queueTime);
     const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
@@ -1732,7 +1746,7 @@
     info.mZ = ds.z;
     info.mWidth = ds.active_legacy.w;
     info.mHeight = ds.active_legacy.h;
-    info.mCrop = ds.crop_legacy;
+    info.mCrop = ds.crop;
     info.mColor = ds.color;
     info.mFlags = ds.flags;
     info.mPixelFormat = getPixelFormat();
@@ -2431,7 +2445,7 @@
     const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
     const State& state = useDrawing ? mDrawingState : mCurrentState;
 
-    ui::Transform requestedTransform = state.active_legacy.transform;
+    ui::Transform requestedTransform = state.transform;
 
     if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
         layerInfo->set_id(sequence);
@@ -2460,11 +2474,10 @@
                                                    return layerInfo->mutable_requested_position();
                                                });
 
-        LayerProtoHelper::writeSizeToProto(state.active_legacy.w, state.active_legacy.h,
+        LayerProtoHelper::writeSizeToProto(state.width, state.height,
                                            [&]() { return layerInfo->mutable_size(); });
 
-        LayerProtoHelper::writeToProto(state.crop_legacy,
-                                       [&]() { return layerInfo->mutable_crop(); });
+        LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); });
 
         layerInfo->set_is_opaque(isOpaque(state));
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 34a9f39..85ff479 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -214,8 +214,8 @@
         bool modified;
 
         // Crop is expressed in layer space coordinate.
-        Rect crop_legacy;
-        Rect requestedCrop_legacy;
+        Rect crop;
+        Rect requestedCrop;
 
         // If set, defers this state update until the identified Layer
         // receives a frame with the given frameNumber
@@ -250,12 +250,13 @@
 
         // The fields below this point are only used by BufferStateLayer
         uint64_t frameNumber;
-        Geometry active;
+        uint32_t width;
+        uint32_t height;
+        ui::Transform transform;
 
-        uint32_t transform;
+        uint32_t bufferTransform;
         bool transformToDisplayInverse;
 
-        Rect crop;
         Region transparentRegionHint;
 
         sp<GraphicBuffer> buffer;
@@ -311,6 +312,7 @@
         // When the transaction was posted
         nsecs_t postTime;
 
+        sp<ITransactionCompletedListener> releaseBufferListener;
         // SurfaceFrame that tracks the timeline of Transactions that contain a Buffer. Only one
         // such SurfaceFrame exists because only one buffer can be presented on the layer per vsync.
         // If multiple buffers are queued, the prior ones will be dropped, along with the
@@ -421,7 +423,7 @@
     // space for top-level layers.
     virtual bool setPosition(float x, float y);
     // Buffer space
-    virtual bool setCrop_legacy(const Rect& crop);
+    virtual bool setCrop(const Rect& crop);
 
     // TODO(b/38182121): Could we eliminate the various latching modes by
     // using the layer hierarchy?
@@ -460,13 +462,13 @@
     // Used only to set BufferStateLayer state
     virtual bool setTransform(uint32_t /*transform*/) { return false; };
     virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
-    virtual bool setCrop(const Rect& /*crop*/) { return false; };
     virtual bool setFrame(const Rect& /*frame*/) { return false; };
     virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/,
                            nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
                            bool /*isAutoTimestamp*/, const client_cache_t& /*clientCacheId*/,
                            uint64_t /* frameNumber */, std::optional<nsecs_t> /* dequeueTime */,
-                           const FrameTimelineInfo& /*info*/) {
+                           const FrameTimelineInfo& /*info*/,
+                           const sp<ITransactionCompletedListener>& /* releaseBufferListener */) {
         return false;
     };
     virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
@@ -547,7 +549,7 @@
     virtual Region getActiveTransparentRegion(const Layer::State& s) const {
         return s.activeTransparentRegion_legacy;
     }
-    virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
+    virtual Rect getCrop(const Layer::State& s) const { return s.crop; }
     virtual bool needsFiltering(const DisplayDevice*) const { return false; }
 
     // True if this layer requires filtering
@@ -774,6 +776,12 @@
     virtual uint32_t doTransaction(uint32_t transactionFlags);
 
     /*
+     * Called before updating the drawing state buffer. Used by BufferStateLayer to release any
+     * unlatched buffers in the drawing state.
+     */
+    virtual void bufferMayChange(sp<GraphicBuffer>& /* newBuffer */){};
+
+    /*
      * Remove relative z for the layer if its relative parent is not part of the
      * provided layer tree.
      */
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
index 053b7f7..1c0263b 100644
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -80,24 +80,23 @@
             // recompute visible region
             mRecomputeVisibleRegions = true;
 
-            if (mFront.crop_legacy != mFront.requestedCrop_legacy) {
-                mFront.crop_legacy = mFront.requestedCrop_legacy;
-                mCurrent.crop_legacy = mFront.requestedCrop_legacy;
+            if (mFront.crop != mFront.requestedCrop) {
+                mFront.crop = mFront.requestedCrop;
+                mCurrent.crop = mFront.requestedCrop;
                 mRecomputeVisibleRegions = true;
             }
         }
 
         ALOGD_IF(DEBUG_RESIZE,
                  "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
-                 "  drawing={ active_legacy   ={ wh={%4u,%4u} crop_legacy={%4d,%4d,%4d,%4d} "
+                 "  drawing={ active_legacy   ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} "
                  "(%4d,%4d) "
                  "}\n"
                  "            requested_legacy={ wh={%4u,%4u} }}\n",
                  mName.c_str(), bufWidth, bufHeight, item.mTransform, item.mScalingMode,
-                 mFront.active_legacy.w, mFront.active_legacy.h, mFront.crop_legacy.left,
-                 mFront.crop_legacy.top, mFront.crop_legacy.right, mFront.crop_legacy.bottom,
-                 mFront.crop_legacy.getWidth(), mFront.crop_legacy.getHeight(),
-                 mFront.requested_legacy.w, mFront.requested_legacy.h);
+                 mFront.active_legacy.w, mFront.active_legacy.h, mFront.crop.left, mFront.crop.top,
+                 mFront.crop.right, mFront.crop.bottom, mFront.crop.getWidth(),
+                 mFront.crop.getHeight(), mFront.requested_legacy.w, mFront.requested_legacy.h);
     }
 
     if (!isFixedSize && !mStickyTransformSet) {
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index b29c624..1d00cc3 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -241,7 +241,8 @@
     auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame];
     mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
                       mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
-                      std::nullopt /* dequeueTime */, FrameTimelineInfo{});
+                      std::nullopt /* dequeueTime */, FrameTimelineInfo{},
+                      nullptr /* releaseBufferListener */);
 
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
@@ -254,7 +255,8 @@
     auto buffer = buffers[mFrame];
     mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
                       mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
-                      std::nullopt /* dequeueTime */, FrameTimelineInfo{});
+                      std::nullopt /* dequeueTime */, FrameTimelineInfo{},
+                      nullptr /* releaseBufferListener */);
 
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index ba43e70..6553efe 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -354,7 +354,7 @@
             return mTokenManager->generateTokenForPredictions(
                     {timestamp, deadlineTimestamp, expectedVSyncTimestamp});
         }
-        return static_cast<int64_t>(0);
+        return FrameTimelineInfo::INVALID_VSYNC_ID;
     }();
 
     mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
@@ -494,10 +494,16 @@
                 const auto now = systemTime(SYSTEM_TIME_MONOTONIC);
                 const auto deadlineTimestamp = now + timeout.count();
                 const auto expectedVSyncTime = deadlineTimestamp + timeout.count();
-                // TODO(b/162890590): use TokenManager to populate vsyncId
+                const int64_t vsyncId = [&] {
+                    if (mTokenManager != nullptr) {
+                        return mTokenManager->generateTokenForPredictions(
+                                {now, deadlineTimestamp, expectedVSyncTime});
+                    }
+                    return FrameTimelineInfo::INVALID_VSYNC_ID;
+                }();
                 mPendingEvents.push_back(makeVSync(mVSyncState->displayId, now,
                                                    ++mVSyncState->count, expectedVSyncTime,
-                                                   deadlineTimestamp, /*vsyncId=*/0));
+                                                   deadlineTimestamp, vsyncId));
             }
         }
     }
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
index 1f821be..194d808 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
@@ -50,24 +50,23 @@
         TransactionSchedule schedule) {
     switch (schedule) {
         case Schedule::EarlyStart:
-            ALOGW_IF(mExplicitEarlyWakeup, "%s: Duplicate EarlyStart", __FUNCTION__);
-            mExplicitEarlyWakeup = true;
+            ALOGW_IF(mEarlyWakeup, "%s: Duplicate EarlyStart", __FUNCTION__);
+            mEarlyWakeup = true;
             break;
         case Schedule::EarlyEnd:
-            ALOGW_IF(!mExplicitEarlyWakeup, "%s: Unexpected EarlyEnd", __FUNCTION__);
-            mExplicitEarlyWakeup = false;
+            ALOGW_IF(!mEarlyWakeup, "%s: Unexpected EarlyEnd", __FUNCTION__);
+            mEarlyWakeup = false;
             break;
-        case Schedule::Early:
         case Schedule::Late:
-            // No change to mExplicitEarlyWakeup for non-explicit states.
+            // No change to mEarlyWakeup for non-explicit states.
             break;
     }
 
     if (mTraceDetailedInfo) {
-        ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup);
+        ATRACE_INT("mEarlyWakeup", mEarlyWakeup);
     }
 
-    if (!mExplicitEarlyWakeup && (schedule == Schedule::Early || schedule == Schedule::EarlyEnd)) {
+    if (!mEarlyWakeup && schedule == Schedule::EarlyEnd) {
         mEarlyTransactionFrames = MIN_EARLY_TRANSACTION_FRAMES;
         mEarlyTransactionStartTime = mNow();
     }
@@ -129,8 +128,8 @@
 const VsyncModulator::VsyncConfig& VsyncModulator::getNextVsyncConfig() const {
     // Early offsets are used if we're in the middle of a refresh rate
     // change, or if we recently begin a transaction.
-    if (mExplicitEarlyWakeup || mTransactionSchedule == Schedule::EarlyEnd ||
-        mEarlyTransactionFrames > 0 || mRefreshRateChangePending) {
+    if (mEarlyWakeup || mTransactionSchedule == Schedule::EarlyEnd || mEarlyTransactionFrames > 0 ||
+        mRefreshRateChangePending) {
         return mVsyncConfigSet.early;
     } else if (mEarlyGpuFrames > 0) {
         return mVsyncConfigSet.earlyGpu;
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
index 355a14a..fcde279 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -30,7 +30,6 @@
 // fixed number of frames, respectively.
 enum class TransactionSchedule {
     Late,  // Default.
-    Early, // Deprecated.
     EarlyStart,
     EarlyEnd
 };
@@ -114,7 +113,7 @@
 
     using Schedule = TransactionSchedule;
     std::atomic<Schedule> mTransactionSchedule = Schedule::Late;
-    std::atomic<bool> mExplicitEarlyWakeup = false;
+    std::atomic<bool> mEarlyWakeup = false;
 
     std::atomic<bool> mRefreshRateChangePending = false;
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 593855e..ed27783 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -23,6 +23,7 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "SurfaceFlinger.h"
+#include "TraceUtils.h"
 
 #include <android-base/properties.h>
 #include <android/configuration.h>
@@ -79,7 +80,6 @@
 #include <utils/String16.h>
 #include <utils/String8.h>
 #include <utils/Timers.h>
-#include <utils/Trace.h>
 #include <utils/misc.h>
 
 #include <algorithm>
@@ -914,11 +914,6 @@
     }
 
     info->activeDisplayModeId = static_cast<int32_t>(display->getActiveMode()->getId().value());
-    if (display->isPrimary()) {
-        if (const auto mode = getDesiredActiveMode()) {
-            info->activeDisplayModeId = static_cast<int32_t>(mode->modeId.value());
-        }
-    }
 
     const auto& supportedModes = display->getSupportedModes();
     info->supportedDisplayModes.clear();
@@ -1467,14 +1462,16 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) {
+status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken,
+                                              const gui::DisplayBrightness& brightness) {
     if (!displayToken) {
         return BAD_VALUE;
     }
 
     return ftl::chain(schedule([=]() MAIN_THREAD {
                if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
-                   return getHwComposer().setDisplayBrightness(*displayId, brightness);
+                   return getHwComposer().setDisplayBrightness(*displayId,
+                                                               brightness.displayBrightness);
                } else {
                    ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
                    return ftl::yield<status_t>(NAME_NOT_FOUND);
@@ -1692,7 +1689,12 @@
 }
 
 void SurfaceFlinger::onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime) {
-    ATRACE_CALL();
+    const auto vsyncIn = [&] {
+        if (!ATRACE_ENABLED()) return 0.f;
+        return (expectedVSyncTime - systemTime()) / 1e6f;
+    }();
+
+    ATRACE_FORMAT("onMessageReceived %" PRId64 " vsyncIn %.2fms", vsyncId, vsyncIn);
     switch (what) {
         case MessageQueue::INVALIDATE: {
             onMessageInvalidate(vsyncId, expectedVSyncTime);
@@ -1879,7 +1881,12 @@
             // underestimated.
             mFrameStartTime = frameStart;
         }
-        signalRefresh();
+
+        // Run the refresh immediately after invalidate as there is no point going thru the message
+        // queue again, and to ensure that we actually refresh the screen instead of handling
+        // other messages that were queued us already in the MessageQueue.
+        mRefreshPending = true;
+        onMessageRefresh();
     }
 }
 
@@ -2973,7 +2980,7 @@
     mRefreshRateConfigs = std::make_unique<
             scheduler::RefreshRateConfigs>(displayState.physical->supportedModes,
                                            displayState.physical->activeMode->getId(),
-                                           android::sysprop::enable_frame_rate_override(true));
+                                           android::sysprop::enable_frame_rate_override(false));
     const auto currRefreshRate = displayState.physical->activeMode->getFps();
     mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, currRefreshRate,
                                                                       hal::PowerMode::OFF);
@@ -3041,9 +3048,8 @@
 
 void SurfaceFlinger::commitTransaction() {
     commitTransactionLocked();
-    mTransactionPending = false;
+    signalSynchronousTransactions();
     mAnimTransactionPending = false;
-    mTransactionCV.broadcast();
 }
 
 void SurfaceFlinger::commitTransactionLocked() {
@@ -3312,15 +3318,16 @@
                 auto& [applyToken, transactionQueue] = *it;
 
                 while (!transactionQueue.empty()) {
-                    const auto& transaction = transactionQueue.front();
+                    auto& transaction = transactionQueue.front();
                     if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
                                                        transaction.isAutoTimestamp,
                                                        transaction.desiredPresentTime,
-                                                       transaction.states, pendingBuffers)) {
+                                                       transaction.originUid, transaction.states,
+                                                       pendingBuffers)) {
                         setTransactionFlags(eTransactionFlushNeeded);
                         break;
                     }
-                    transactions.push_back(transaction);
+                    transactions.emplace_back(std::move(transaction));
                     transactionQueue.pop();
                 }
 
@@ -3337,17 +3344,18 @@
             // Case 2: push to pending when there exist a pending queue.
             // Case 3: others are ready to apply.
             while (!mTransactionQueue.empty()) {
-                const auto& transaction = mTransactionQueue.front();
+                auto& transaction = mTransactionQueue.front();
                 bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) !=
                         mPendingTransactionQueues.end();
                 if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
                                                    transaction.isAutoTimestamp,
                                                    transaction.desiredPresentTime,
-                                                   transaction.states, pendingBuffers) ||
+                                                   transaction.originUid, transaction.states,
+                                                   pendingBuffers) ||
                     pendingTransactions) {
-                    mPendingTransactionQueues[transaction.applyToken].push(transaction);
+                    mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
                 } else {
-                    transactions.push_back(transaction);
+                    transactions.emplace_back(std::move(transaction));
                 }
                 mTransactionQueue.pop();
                 ATRACE_INT("TransactionQueue", mTransactionQueue.size());
@@ -3363,25 +3371,36 @@
                                   transaction.postTime, transaction.permissions,
                                   transaction.hasListenerCallbacks, transaction.listenerCallbacks,
                                   transaction.originPid, transaction.originUid, transaction.id);
+            if (transaction.transactionCommittedSignal) {
+                mTransactionCommittedSignals.emplace_back(
+                        std::move(transaction.transactionCommittedSignal));
+            }
         }
     }
 }
 
 bool SurfaceFlinger::transactionFlushNeeded() {
     Mutex::Autolock _l(mQueueLock);
-    return !mPendingTransactionQueues.empty();
+    return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty();
 }
 
 bool SurfaceFlinger::transactionIsReadyToBeApplied(
         const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
-        const Vector<ComposerState>& states,
+        uid_t originUid, const Vector<ComposerState>& states,
         std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers) {
+    ATRACE_CALL();
     const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
     bool ready = true;
     // Do not present if the desiredPresentTime has not passed unless it is more than one second
     // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
     if (!isAutoTimestamp && desiredPresentTime >= expectedPresentTime &&
         desiredPresentTime < expectedPresentTime + s2ns(1)) {
+        ATRACE_NAME("not current");
+        ready = false;
+    }
+
+    if (!mScheduler->isVsyncValid(expectedPresentTime, originUid)) {
+        ATRACE_NAME("!isVsyncValid");
         ready = false;
     }
 
@@ -3404,15 +3423,12 @@
             continue;
         }
 
+        ATRACE_NAME(layer->getName().c_str());
+
         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;
-        }
-
-        if (!mScheduler->isVsyncValid(expectedPresentTime, layer->getOwnerUid())) {
-            ATRACE_NAME("!isVsyncValidForUid");
             ready = false;
         }
 
@@ -3421,16 +3437,16 @@
             // transaction in the queue.
             const bool hasPendingBuffer = pendingBuffers.find(s.surface) != pendingBuffers.end();
             if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
+                ATRACE_NAME("hasPendingBuffer");
                 ready = false;
             }
             pendingBuffers.insert(s.surface);
         }
-        pendingBuffers.insert(s.surface);
     }
     return ready;
 }
 
-void SurfaceFlinger::queueTransaction(TransactionState state) {
+void SurfaceFlinger::queueTransaction(TransactionState& state) {
     Mutex::Autolock _l(mQueueLock);
 
     // If its TransactionQueue already has a pending TransactionState or if it is pending
@@ -3450,52 +3466,41 @@
         }
     }
 
+    // Generate a CountDownLatch pending state if this is a synchronous transaction.
+    if ((state.flags & eSynchronous) || state.inputWindowCommands.syncInputWindows) {
+        state.transactionCommittedSignal = std::make_shared<CountDownLatch>(
+                (state.inputWindowCommands.syncInputWindows ? 2 : 1));
+    }
+
     mTransactionQueue.emplace(state);
     ATRACE_INT("TransactionQueue", mTransactionQueue.size());
 
-    // TODO(b/159125966): Remove eEarlyWakeup completely as no client should use this flag
-    if (state.flags & eEarlyWakeup) {
-        ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
-    }
-
-    if (!(state.permissions & Permission::ACCESS_SURFACE_FLINGER) &&
-        (state.flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
-        ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
-        state.flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
-    }
-
     const auto schedule = [](uint32_t flags) {
-        if (flags & eEarlyWakeup) return TransactionSchedule::Early;
-        if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
-        if (flags & eExplicitEarlyWakeupStart) return TransactionSchedule::EarlyStart;
+        if (flags & eEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
+        if (flags & eEarlyWakeupStart) return TransactionSchedule::EarlyStart;
         return TransactionSchedule::Late;
     }(state.flags);
 
     setTransactionFlags(eTransactionFlushNeeded, schedule);
 }
 
-void SurfaceFlinger::waitForSynchronousTransaction(bool synchronous, bool syncInput) {
-    Mutex::Autolock _l(mStateLock);
-    if (synchronous) {
-        mTransactionPending = true;
+void SurfaceFlinger::waitForSynchronousTransaction(
+        const CountDownLatch& transactionCommittedSignal) {
+    // applyTransactionState is called on the main SF thread.  While a given process may wish
+    // to wait on synchronous transactions, the main SF thread should apply the transaction and
+    // set the value to notify this after committed.
+    if (!transactionCommittedSignal.wait_until(std::chrono::seconds(5))) {
+        ALOGE("setTransactionState timed out!");
     }
-    if (syncInput) {
-        mPendingSyncInputWindows = true;
-    }
+}
 
-    // applyTransactionState can be called by either the main SF thread or by
-    // another process through setTransactionState.  While a given process may wish
-    // to wait on synchronous transactions, the main SF thread should never
-    // be blocked.  Therefore, we only wait if isMainThread is false.
-    while (mTransactionPending || mPendingSyncInputWindows) {
-        status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
-        if (CC_UNLIKELY(err != NO_ERROR)) {
-            // just in case something goes wrong in SF, return to the
-            // called after a few seconds.
-            ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out!");
-            mTransactionPending = false;
-            mPendingSyncInputWindows = false;
-            break;
+void SurfaceFlinger::signalSynchronousTransactions() {
+    for (auto it = mTransactionCommittedSignals.begin();
+         it != mTransactionCommittedSignals.end();) {
+        if ((*it)->countDown() == 0) {
+            it = mTransactionCommittedSignals.erase(it);
+        } else {
+            it++;
         }
     }
 }
@@ -3524,21 +3529,30 @@
         permissions |= Permission::ROTATE_SURFACE_FLINGER;
     }
 
+    if (!(permissions & Permission::ACCESS_SURFACE_FLINGER) &&
+        (flags & (eEarlyWakeupStart | eEarlyWakeupEnd))) {
+        ALOGE("Only WindowManager is allowed to use eEarlyWakeup[Start|End] flags");
+        flags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
+    }
+
     const int64_t postTime = systemTime();
 
     IPCThreadState* ipc = IPCThreadState::self();
     const int originPid = ipc->getCallingPid();
     const int originUid = ipc->getCallingUid();
+    TransactionState state{frameTimelineInfo,  states,
+                           displays,           flags,
+                           applyToken,         inputWindowCommands,
+                           desiredPresentTime, isAutoTimestamp,
+                           uncacheBuffer,      postTime,
+                           permissions,        hasListenerCallbacks,
+                           listenerCallbacks,  originPid,
+                           originUid,          transactionId};
+    queueTransaction(state);
 
-    queueTransaction({frameTimelineInfo, states, displays, flags, applyToken, inputWindowCommands,
-                      desiredPresentTime, isAutoTimestamp, uncacheBuffer, postTime, permissions,
-                      hasListenerCallbacks, listenerCallbacks, originPid, originUid,
-                      transactionId});
-
-    const bool synchronous = flags & eSynchronous;
-    const bool syncInput = inputWindowCommands.syncInputWindows;
-    if (synchronous || syncInput) {
-        waitForSynchronousTransaction(synchronous, syncInput);
+    // Check the pending state to make sure the transaction is synchronous.
+    if (state.transactionCommittedSignal) {
+        waitForSynchronousTransaction(*state.transactionCommittedSignal);
     }
 
     return NO_ERROR;
@@ -3836,9 +3850,6 @@
         if (layer->setFlags(s.flags, s.mask))
             flags |= eTraversalNeeded;
     }
-    if (what & layer_state_t::eCropChanged_legacy) {
-        if (layer->setCrop_legacy(s.crop_legacy)) flags |= eTraversalNeeded;
-    }
     if (what & layer_state_t::eCornerRadiusChanged) {
         if (layer->setCornerRadius(s.cornerRadius))
             flags |= eTraversalNeeded;
@@ -4015,7 +4026,8 @@
                 : layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1;
 
         if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp,
-                             s.cachedBuffer, frameNumber, dequeueBufferTimestamp, info)) {
+                             s.cachedBuffer, frameNumber, dequeueBufferTimestamp, info,
+                             s.releaseBufferListener)) {
             flags |= eTraversalNeeded;
         }
     } else if (info.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
@@ -6078,10 +6090,7 @@
 
 void SurfaceFlinger::setInputWindowsFinished() {
     Mutex::Autolock _l(mStateLock);
-
-    mPendingSyncInputWindows = false;
-
-    mTransactionCV.broadcast();
+    signalSynchronousTransactions();
 }
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 40d63b2..a5b06df 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -474,6 +474,41 @@
         hal::Connection connection = hal::Connection::INVALID;
     };
 
+    class CountDownLatch {
+    public:
+        explicit CountDownLatch(int32_t count) : mCount(count) {}
+
+        int32_t countDown() {
+            std::unique_lock<std::mutex> lock(mMutex);
+            if (mCount == 0) {
+                return 0;
+            }
+            if (--mCount == 0) {
+                mCountDownComplete.notify_all();
+            }
+            return mCount;
+        }
+
+        // Return true if triggered.
+        bool wait_until(const std::chrono::seconds& timeout) const {
+            std::unique_lock<std::mutex> lock(mMutex);
+            const auto untilTime = std::chrono::system_clock::now() + timeout;
+            while (mCount != 0) {
+                // Conditional variables can be woken up sporadically, so we check count
+                // to verify the wakeup was triggered by |countDown|.
+                if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+    private:
+        int32_t mCount;
+        mutable std::condition_variable mCountDownComplete;
+        mutable std::mutex mMutex;
+    };
+
     struct TransactionState {
         TransactionState(const FrameTimelineInfo& frameTimelineInfo,
                          const Vector<ComposerState>& composerStates,
@@ -517,6 +552,7 @@
         int originPid;
         int originUid;
         uint64_t id;
+        std::shared_ptr<CountDownLatch> transactionCommittedSignal;
     };
 
     template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
@@ -644,7 +680,8 @@
                                         float* outAppRequestRefreshRateMax) override;
     status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
                                          bool* outSupport) const override;
-    status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) override;
+    status_t setDisplayBrightness(const sp<IBinder>& displayToken,
+                                  const gui::DisplayBrightness& brightness) override;
     status_t notifyPowerBoost(int32_t boostId) override;
     status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
                                      float lightPosY, float lightPosZ, float lightRadius) override;
@@ -801,7 +838,7 @@
     void commitOffscreenLayers();
     bool transactionIsReadyToBeApplied(
             const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
-            const Vector<ComposerState>& states,
+            uid_t originUid, const Vector<ComposerState>& states,
             std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers)
             REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
@@ -1080,8 +1117,9 @@
     status_t CheckTransactCodeCredentials(uint32_t code);
 
     // Add transaction to the Transaction Queue
-    void queueTransaction(TransactionState state) EXCLUDES(mQueueLock);
-    void waitForSynchronousTransaction(bool synchronous, bool syncInput) EXCLUDES(mStateLock);
+    void queueTransaction(TransactionState& state) EXCLUDES(mQueueLock);
+    void waitForSynchronousTransaction(const CountDownLatch& transactionCommittedSignal);
+    void signalSynchronousTransactions();
 
     /*
      * Generic Layer Metadata
@@ -1111,8 +1149,7 @@
     mutable Mutex mStateLock;
     State mCurrentState{LayerVector::StateSet::Current};
     std::atomic<int32_t> mTransactionFlags = 0;
-    Condition mTransactionCV;
-    bool mTransactionPending = false;
+    std::vector<std::shared_ptr<CountDownLatch>> mTransactionCommittedSignals;
     bool mAnimTransactionPending = false;
     SortedVector<sp<Layer>> mLayersPendingRemoval;
     bool mForceTraversal = false;
@@ -1324,7 +1361,6 @@
 
     sp<SetInputWindowsListener> mSetInputWindowsListener;
 
-    bool mPendingSyncInputWindows GUARDED_BY(mStateLock) = false;
     Hwc2::impl::PowerAdvisor mPowerAdvisor;
 
     // This should only be accessed on the main thread.
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index b0413f1..8a3be9f 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -137,7 +137,7 @@
     addTransparentRegionLocked(transaction, layerId,
                                layer->mCurrentState.activeTransparentRegion_legacy);
     addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack);
-    addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy);
+    addCropLocked(transaction, layerId, layer->mCurrentState.crop);
     addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius);
     addBackgroundBlurRadiusLocked(transaction, layerId, layer->mCurrentState.backgroundBlurRadius);
     addBlurRegionsLocked(transaction, layerId, layer->mCurrentState.blurRegions);
@@ -459,8 +459,8 @@
     if (state.what & layer_state_t::eLayerStackChanged) {
         addLayerStackLocked(transaction, layerId, state.layerStack);
     }
-    if (state.what & layer_state_t::eCropChanged_legacy) {
-        addCropLocked(transaction, layerId, state.crop_legacy);
+    if (state.what & layer_state_t::eCropChanged) {
+        addCropLocked(transaction, layerId, state.crop);
     }
     if (state.what & layer_state_t::eCornerRadiusChanged) {
         addCornerRadiusLocked(transaction, layerId, state.cornerRadius);
diff --git a/services/surfaceflinger/TraceUtils.h b/services/surfaceflinger/TraceUtils.h
new file mode 100644
index 0000000..90a34a5
--- /dev/null
+++ b/services/surfaceflinger/TraceUtils.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/183120308): This file is a copy of f/b/libs/hwui/utils/TraceUtils.h
+//  It should be migrated to a common place where both SF and hwui could use it.
+
+#pragma once
+
+#include <cutils/trace.h>
+#include <utils/Trace.h>
+
+#define ATRACE_FORMAT(fmt, ...)           \
+    TraceUtils::TraceEnder __traceEnder = \
+            (TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__), TraceUtils::TraceEnder())
+
+#define ATRACE_FORMAT_BEGIN(fmt, ...) TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__)
+
+namespace android {
+
+class TraceUtils {
+public:
+    class TraceEnder {
+    public:
+        ~TraceEnder() { ATRACE_END(); }
+    };
+
+    static void atraceFormatBegin(const char* fmt, ...) {
+        if (CC_LIKELY(!ATRACE_ENABLED())) return;
+
+        const int BUFFER_SIZE = 256;
+        va_list ap;
+        char buf[BUFFER_SIZE];
+
+        va_start(ap, fmt);
+        vsnprintf(buf, BUFFER_SIZE, fmt, ap);
+        va_end(ap);
+
+        ATRACE_BEGIN(buf);
+    }
+
+}; // class TraceUtils
+
+} /* namespace android */
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index a78510e..3590e76 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -201,7 +201,8 @@
                                           handle->dequeueReadyTime);
         transactionStats->surfaceStats.emplace_back(surfaceControl, handle->acquireTime,
                                                     handle->previousReleaseFence,
-                                                    handle->transformHint, eventStats, jankData);
+                                                    handle->transformHint, eventStats, jankData,
+                                                    handle->previousBufferId);
     }
     return NO_ERROR;
 }
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index a240c82..caa8a4f 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -50,6 +50,7 @@
     nsecs_t refreshStartTime = 0;
     nsecs_t dequeueReadyTime = 0;
     uint64_t frameNumber = 0;
+    uint64_t previousBufferId = 0;
 };
 
 class TransactionCallbackInvoker {
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 78187f7..b96725f 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -44,6 +44,7 @@
         "MultiDisplayLayerBounds_test.cpp",
         "RefreshRateOverlay_test.cpp",
         "RelativeZ_test.cpp",
+        "ReleaseBufferCallback_test.cpp",
         "ScreenCapture_test.cpp",
         "SetFrameRate_test.cpp",
         "SetGeometry_test.cpp",
diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp
index 4868c12..03f8e1a 100644
--- a/services/surfaceflinger/tests/BufferGenerator.cpp
+++ b/services/surfaceflinger/tests/BufferGenerator.cpp
@@ -70,7 +70,7 @@
         consumer->setDefaultBufferSize(width, height);
         consumer->setDefaultBufferFormat(format);
 
-        mBufferItemConsumer = new BufferItemConsumer(consumer, 0);
+        mBufferItemConsumer = new BufferItemConsumer(consumer, GraphicBuffer::USAGE_HW_TEXTURE);
 
         mListener = new BufferListener(consumer, callback);
         mBufferItemConsumer->setFrameAvailableListener(mListener);
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index 7a3c45d..f470eda 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -55,7 +55,7 @@
 
     EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
     asTransaction([&](Transaction& t) {
-        t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+        t.setCrop(effectLayer, Rect(0, 0, 400, 400));
         t.show(effectLayer);
     });
 
@@ -76,7 +76,7 @@
 
     EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
     asTransaction([&](Transaction& t) {
-        t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+        t.setCrop(effectLayer, Rect(0, 0, 400, 400));
         t.show(effectLayer);
     });
 
@@ -97,7 +97,7 @@
 
     EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
     asTransaction([&](Transaction& t) {
-        t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+        t.setCrop(effectLayer, Rect(0, 0, 400, 400));
         t.setColor(effectLayer,
                    half3{Color::GREEN.r / 255.0f, Color::GREEN.g / 255.0f,
                          Color::GREEN.b / 255.0f});
@@ -127,10 +127,10 @@
     asTransaction([&](Transaction& t) {
         t.setLayer(leftLayer, mLayerZBase + 1);
         t.reparent(leftLayer, mParentLayer);
-        t.setCrop_legacy(leftLayer, leftRect);
+        t.setCrop(leftLayer, leftRect);
         t.setLayer(rightLayer, mLayerZBase + 2);
         t.reparent(rightLayer, mParentLayer);
-        t.setCrop_legacy(rightLayer, rightRect);
+        t.setCrop(rightLayer, rightRect);
         t.show(leftLayer);
         t.show(rightLayer);
     });
@@ -148,7 +148,7 @@
         t.setLayer(blurLayer, mLayerZBase + 3);
         t.reparent(blurLayer, mParentLayer);
         t.setBackgroundBlurRadius(blurLayer, blurRadius);
-        t.setCrop_legacy(blurLayer, blurRect);
+        t.setCrop(blurLayer, blurRect);
         t.setFrame(blurLayer, blurRect);
         t.setAlpha(blurLayer, 0.0f);
         t.show(blurLayer);
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index aa1cce2..158801a 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
 #include <sys/epoll.h>
 
 #include <gui/DisplayEventReceiver.h>
@@ -25,6 +21,8 @@
 #include "LayerTransactionTest.h"
 #include "utils/CallbackUtils.h"
 
+using namespace std::chrono_literals;
+
 namespace android {
 
 using android::hardware::graphics::common::V1_1::BufferUsage;
@@ -801,7 +799,7 @@
     }
 
     // Try to present 100ms in the future
-    nsecs_t time = systemTime() + (100 * 1e6);
+    nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
 
     transaction.setDesiredPresentTime(time);
     transaction.apply();
@@ -825,7 +823,7 @@
     }
 
     // Try to present 100ms in the future
-    nsecs_t time = systemTime() + (100 * 1e6);
+    nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
 
     transaction.setDesiredPresentTime(time);
     transaction.apply();
@@ -842,7 +840,7 @@
     }
 
     // Try to present 33ms after the first frame
-    time += (33.3 * 1e6);
+    time += std::chrono::nanoseconds(33ms).count();
 
     transaction.setDesiredPresentTime(time);
     transaction.apply();
@@ -870,7 +868,7 @@
     }
 
     // Try to present 100ms in the future
-    nsecs_t time = systemTime() + (100 * 1e6);
+    nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
 
     transaction.setDesiredPresentTime(time);
     transaction.apply();
@@ -887,7 +885,7 @@
     }
 
     // Try to present 33ms before the previous frame
-    time -= (33.3 * 1e6);
+    time -= std::chrono::nanoseconds(33ms).count();
 
     transaction.setDesiredPresentTime(time);
     transaction.apply();
@@ -914,7 +912,7 @@
     }
 
     // Try to present 100ms in the past
-    nsecs_t time = systemTime() - (100 * 1e6);
+    nsecs_t time = systemTime() - std::chrono::nanoseconds(100ms).count();
 
     transaction.setDesiredPresentTime(time);
     transaction.apply();
@@ -948,6 +946,3 @@
 }
 
 } // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 52e1a4d..7505e6e 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -43,6 +43,9 @@
 
 protected:
     LayerRenderPathTestHarness mHarness;
+
+    static constexpr int64_t kUsageFlags = BufferUsage::CPU_READ_OFTEN |
+            BufferUsage::CPU_WRITE_OFTEN | BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE;
 };
 
 INSTANTIATE_TEST_CASE_P(LayerRenderTypeTransactionTests, LayerRenderTypeTransactionTest,
@@ -377,10 +380,7 @@
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
 
     ASSERT_NO_FATAL_FAILURE(
             TransactionUtils::fillGraphicBufferColor(buffer, top, Color::TRANSPARENT));
@@ -405,10 +405,7 @@
         shot->expectColor(bottom, Color::BLACK);
     }
 
-    buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                               BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                       BufferUsage::COMPOSER_OVERLAY,
-                               "test");
+    buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
 
     ASSERT_NO_FATAL_FAILURE(TransactionUtils::fillGraphicBufferColor(buffer, top, Color::RED));
     ASSERT_NO_FATAL_FAILURE(
@@ -518,7 +515,7 @@
                                                 ISurfaceComposerClient::eFXSurfaceEffect));
 
     Transaction()
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setCrop(colorLayer, Rect(0, 0, 32, 32))
             .setLayer(colorLayer, mLayerZBase + 1)
             .apply();
 
@@ -557,7 +554,7 @@
         case ISurfaceComposerClient::eFXSurfaceEffect:
             ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 0, 0, layerType));
             Transaction()
-                    .setCrop_legacy(layer, Rect(0, 0, width, height))
+                    .setCrop(layer, Rect(0, 0, width, height))
                     .setColor(layer, half3(1.0f, 0, 0))
                     .apply();
             expectedColor = fillColor;
@@ -568,7 +565,7 @@
                 ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, fillColor, width, height));
                 expectedColor = fillColor;
             }
-            Transaction().setCrop_legacy(layer, Rect(0, 0, width, height)).apply();
+            Transaction().setCrop(layer, Rect(0, 0, width, height)).apply();
             break;
         case ISurfaceComposerClient::eFXSurfaceBufferState:
             ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType));
@@ -730,7 +727,7 @@
                                     createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
                                                 ISurfaceComposerClient::eFXSurfaceEffect));
     Transaction()
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setCrop(colorLayer, Rect(0, 0, 32, 32))
             .setColor(colorLayer, half3(2.0f, 0.0f, 0.0f))
             .apply();
 
@@ -744,7 +741,7 @@
                                     createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
                                                 ISurfaceComposerClient::eFXSurfaceEffect));
     Transaction()
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setCrop(colorLayer, Rect(0, 0, 32, 32))
             .setColor(colorLayer, half3(1.0f, -1.0f, 0.5f))
             .apply();
 
@@ -759,7 +756,7 @@
     ASSERT_NO_FATAL_FAILURE(colorLayer =
                                     createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
                                                 ISurfaceComposerClient::eFXSurfaceEffect));
-    Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
+    Transaction().setCrop(colorLayer, Rect(0, 0, 32, 32)).apply();
 
     const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
     const float alpha = 0.25f;
@@ -786,7 +783,7 @@
     ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer("childWithColor", 0 /* buffer width */,
                                                      0 /* buffer height */,
                                                      ISurfaceComposerClient::eFXSurfaceEffect));
-    Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
+    Transaction().setCrop(colorLayer, Rect(0, 0, 32, 32)).apply();
     const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
     const float alpha = 0.25f;
     const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
@@ -943,7 +940,7 @@
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
     const Rect crop(8, 8, 24, 24);
 
-    Transaction().setCrop_legacy(layer, crop).apply();
+    Transaction().setCrop(layer, crop).apply();
     auto shot = getScreenCapture();
     shot->expectColor(crop, Color::RED);
     shot->expectBorder(crop, Color::BLACK);
@@ -969,13 +966,13 @@
 
     {
         SCOPED_TRACE("empty rect");
-        Transaction().setCrop_legacy(layer, Rect(8, 8, 8, 8)).apply();
+        Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply();
         getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     {
         SCOPED_TRACE("negative rect");
-        Transaction().setCrop_legacy(layer, Rect(8, 8, 0, 0)).apply();
+        Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply();
         getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 }
@@ -1004,7 +1001,7 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
-    Transaction().setCrop_legacy(layer, Rect(-128, -64, 128, 64)).apply();
+    Transaction().setCrop(layer, Rect(-128, -64, 128, 64)).apply();
     auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
@@ -1015,10 +1012,7 @@
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 64, PIXEL_FORMAT_RGBA_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
+            new GraphicBuffer(32, 64, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
     TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 16), Color::BLUE);
     TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 16, 32, 64), Color::RED);
 
@@ -1062,7 +1056,7 @@
 
     const Point position(32, 32);
     const Rect crop(8, 8, 24, 24);
-    Transaction().setPosition(layer, position.x, position.y).setCrop_legacy(layer, crop).apply();
+    Transaction().setPosition(layer, position.x, position.y).setCrop(layer, crop).apply();
     auto shot = getScreenCapture();
     shot->expectColor(crop + position, Color::RED);
     shot->expectBorder(crop + position, Color::BLACK);
@@ -1087,10 +1081,10 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
-    // crop_legacy is affected by matrix
+    // crop is affected by matrix
     Transaction()
             .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
-            .setCrop_legacy(layer, Rect(8, 8, 24, 24))
+            .setCrop(layer, Rect(8, 8, 24, 24))
             .apply();
     auto shot = getScreenCapture();
     shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
@@ -1102,8 +1096,8 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
-    // setCrop_legacy is applied immediately by default, with or without resize pending
-    Transaction().setCrop_legacy(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
+    // setCrop is applied immediately by default, with or without resize pending
+    Transaction().setCrop(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
     {
         SCOPED_TRACE("resize pending");
         auto shot = getScreenCapture();
@@ -1341,10 +1335,7 @@
 
     size_t idx = 0;
     for (auto& buffer : buffers) {
-        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                                   BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                           BufferUsage::COMPOSER_OVERLAY,
-                                   "test");
+        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
         Color color = colors[idx % colors.size()];
         TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
         idx++;
@@ -1377,10 +1368,7 @@
 
     size_t idx = 0;
     for (auto& buffer : buffers) {
-        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                                   BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                           BufferUsage::COMPOSER_OVERLAY,
-                                   "test");
+        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
         Color color = colors[idx % colors.size()];
         TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
         idx++;
@@ -1413,10 +1401,7 @@
 
     size_t idx = 0;
     for (auto& buffer : buffers) {
-        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                                   BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                           BufferUsage::COMPOSER_OVERLAY,
-                                   "test");
+        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
         Color color = colors[idx % colors.size()];
         TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
         idx++;
@@ -1499,10 +1484,7 @@
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
     TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
 
     sp<Fence> fence;
@@ -1528,10 +1510,7 @@
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
     TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
 
     sp<Fence> fence = Fence::NO_FENCE;
@@ -1549,10 +1528,7 @@
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
     TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
 
     Transaction().setBuffer(layer, buffer).setDataspace(layer, ui::Dataspace::UNKNOWN).apply();
@@ -1568,10 +1544,7 @@
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
     TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
 
     HdrMetadata hdrMetadata;
@@ -1589,10 +1562,7 @@
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
     TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
 
     Region region;
@@ -1610,10 +1580,7 @@
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
     TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
 
     Transaction().setBuffer(layer, buffer).setApi(layer, NATIVE_WINDOW_API_CPU).apply();
@@ -1629,7 +1596,7 @@
                                     createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
                                                 ISurfaceComposerClient::eFXSurfaceEffect));
     Transaction()
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setCrop(colorLayer, Rect(0, 0, 32, 32))
             .setLayer(colorLayer, mLayerZBase + 1)
             .apply();
     {
@@ -1686,8 +1653,8 @@
                                      ISurfaceComposerClient::eFXSurfaceEffect, parentLayer.get()));
 
     Transaction()
-            .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setCrop(parentLayer, Rect(0, 0, 100, 100))
+            .setCrop(colorLayer, Rect(0, 0, 32, 32))
             .setLayer(parentLayer, mLayerZBase + 1)
             .apply();
     {
@@ -1747,8 +1714,8 @@
                                      ISurfaceComposerClient::eFXSurfaceEffect, parentLayer.get()));
 
     Transaction()
-            .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setCrop(parentLayer, Rect(0, 0, 100, 100))
+            .setCrop(colorLayer, Rect(0, 0, 32, 32))
             .setLayer(parentLayer, mLayerZBase + 1)
             .apply();
     {
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 05729be..87c7b7d 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -139,7 +139,7 @@
         sp<GraphicBuffer> buffer =
                 new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
                                   BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                          BufferUsage::COMPOSER_OVERLAY,
+                                          BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE,
                                   "test");
         TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight),
                                                  color);
@@ -208,7 +208,7 @@
         sp<GraphicBuffer> buffer =
                 new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
                                   BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                          BufferUsage::COMPOSER_OVERLAY,
+                                          BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE,
                                   "test");
 
         ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0);
@@ -298,7 +298,7 @@
         // set layer stack (b/68888219)
         Transaction t;
         t.setDisplayLayerStack(mDisplay, mDisplayLayerStack);
-        t.setCrop_legacy(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight));
+        t.setCrop(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight));
         t.setLayerStack(mBlackBgSurface, mDisplayLayerStack);
         t.setColor(mBlackBgSurface, half3{0, 0, 0});
         t.setLayer(mBlackBgSurface, mLayerZBase);
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 44e7189..ac5e297 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -39,6 +39,9 @@
 
 protected:
     LayerRenderPathTestHarness mRenderPathHarness;
+
+    static constexpr int64_t kUsageFlags = BufferUsage::CPU_READ_OFTEN |
+            BufferUsage::CPU_WRITE_OFTEN | BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE;
 };
 
 ::testing::Environment* const binderEnv =
@@ -142,7 +145,7 @@
     sp<SurfaceControl> parent =
             LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
                                               ISurfaceComposerClient::eFXSurfaceContainer);
-    Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
+    Transaction().setCrop(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
     ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
@@ -196,7 +199,7 @@
     if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
         Transaction()
                 .setCornerRadius(layer, cornerRadius)
-                .setCrop_legacy(layer, Rect(0, 0, size, size))
+                .setCrop(layer, Rect(0, 0, size, size))
                 .apply();
     } else {
         Transaction()
@@ -233,13 +236,13 @@
 
     auto transaction = Transaction()
                                .setCornerRadius(parent, cornerRadius)
-                               .setCrop_legacy(parent, Rect(0, 0, size, size))
+                               .setCrop(parent, Rect(0, 0, size, size))
                                .reparent(child, parent)
                                .setPosition(child, 0, size)
                                // Rotate by half PI
                                .setMatrix(child, 0.0f, -1.0f, 1.0f, 0.0f);
     if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
-        transaction.setCrop_legacy(parent, Rect(0, 0, size, size));
+        transaction.setCrop(parent, Rect(0, 0, size, size));
     } else {
         transaction.setFrame(parent, Rect(0, 0, size, size));
     }
@@ -275,7 +278,7 @@
     if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
         Transaction()
                 .setCornerRadius(parent, cornerRadius)
-                .setCrop_legacy(parent, Rect(0, 0, size, size))
+                .setCrop(parent, Rect(0, 0, size, size))
                 .reparent(child, parent)
                 .setPosition(child, 0, size / 2)
                 .apply();
@@ -348,7 +351,7 @@
     Transaction()
             .setLayer(blurLayer, mLayerZBase + 3)
             .setBackgroundBlurRadius(blurLayer, blurRadius)
-            .setCrop_legacy(blurLayer, blurRect)
+            .setCrop(blurLayer, blurRect)
             .setFrame(blurLayer, blurRect)
             .setSize(blurLayer, blurRect.getWidth(), blurRect.getHeight())
             .setAlpha(blurLayer, 0.0f)
@@ -513,7 +516,7 @@
             .setLayer(layer, INT32_MAX - 1)
             .show(layer)
             .setLayerStack(behindLayer, mDisplayLayerStack)
-            .setCrop_legacy(behindLayer, crop)
+            .setCrop(behindLayer, crop)
             .setLayer(behindLayer, INT32_MAX - 2)
             .show(behindLayer)
             .apply();
@@ -521,10 +524,7 @@
     sp<Surface> surface = layer->getSurface();
 
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(width, height, PIXEL_FORMAT_RGBX_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
+            new GraphicBuffer(width, height, PIXEL_FORMAT_RGBX_8888, 1, kUsageFlags, "test");
     ASSERT_NO_FATAL_FAILURE(
             TransactionUtils::fillGraphicBufferColor(buffer, crop, Color::TRANSPARENT));
 
@@ -540,10 +540,7 @@
         shot->expectColor(crop, Color::BLACK);
     }
 
-    buffer = new GraphicBuffer(width, height, PIXEL_FORMAT_RGBA_8888, 1,
-                               BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                       BufferUsage::COMPOSER_OVERLAY,
-                               "test");
+    buffer = new GraphicBuffer(width, height, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
     ASSERT_NO_FATAL_FAILURE(
             TransactionUtils::fillGraphicBufferColor(buffer, crop, Color::TRANSPARENT));
 
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
index f8a0bc1..4753362 100644
--- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -43,7 +43,7 @@
     sp<SurfaceControl> parent =
             LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
                                               ISurfaceComposerClient::eFXSurfaceContainer);
-    Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
+    Transaction().setCrop(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
     sp<SurfaceControl> layerB;
@@ -84,8 +84,8 @@
             .setColor(parent, half3{0.0f, 0.0f, 0.0f})
             .show(childLayer)
             .show(parent)
-            .setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight))
-            .setCrop_legacy(childLayer, Rect(0, 0, 20, 30))
+            .setCrop(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+            .setCrop(childLayer, Rect(0, 0, 20, 30))
             .apply();
 
     Transaction().setRelativeLayer(childLayer, parent, -1).setLayer(childLayer, 1).apply();
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index ec826ae..e4a1f66 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -131,7 +131,7 @@
         asTransaction([&](Transaction& t) {
             t.setSize(mFGSurfaceControl, 64, 64);
             t.setPosition(mFGSurfaceControl, 64, 64);
-            t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
+            t.setCrop(mFGSurfaceControl, Rect(0, 0, 64, 64));
         });
 
         EXPECT_INITIAL_STATE("After restoring initial state");
@@ -225,7 +225,7 @@
                                                    PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
     TransactionUtils::fillSurfaceRGBA8(childBuffer, 200, 200, 200);
     SurfaceComposerClient::Transaction{}
-            .setCrop_legacy(childNoBuffer, Rect(0, 0, 10, 10))
+            .setCrop(childNoBuffer, Rect(0, 0, 10, 10))
             .show(childNoBuffer)
             .show(childBuffer)
             .apply(true);
@@ -234,9 +234,7 @@
         sc->expectChildColor(73, 73);
         sc->expectFGColor(74, 74);
     }
-    SurfaceComposerClient::Transaction{}
-            .setCrop_legacy(childNoBuffer, Rect(0, 0, 20, 20))
-            .apply(true);
+    SurfaceComposerClient::Transaction{}.setCrop(childNoBuffer, Rect(0, 0, 20, 20)).apply(true);
     {
         ScreenCapture::captureScreen(&sc);
         sc->expectChildColor(73, 73);
@@ -351,7 +349,7 @@
         t.show(mChild);
         t.setPosition(mChild, 0, 0);
         t.setPosition(mFGSurfaceControl, 0, 0);
-        t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5));
+        t.setCrop(mFGSurfaceControl, Rect(0, 0, 5, 5));
     });
 
     {
@@ -947,7 +945,7 @@
                           ISurfaceComposerClient::eFXSurfaceEffect, cropLayer.get());
     ASSERT_TRUE(colorLayer->isValid());
     asTransaction([&](Transaction& t) {
-        t.setCrop_legacy(cropLayer, Rect(5, 5, 10, 10));
+        t.setCrop(cropLayer, Rect(5, 5, 10, 10));
         t.setColor(colorLayer, half3{0, 0, 0});
         t.show(cropLayer);
         t.show(colorLayer);
@@ -1007,7 +1005,7 @@
         t.show(boundlessLayerRightShift);
         t.setPosition(boundlessLayerDownShift, 0, 32);
         t.show(boundlessLayerDownShift);
-        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        t.setCrop(colorLayer, Rect(0, 0, 64, 64));
         t.setColor(colorLayer, half3{0, 0, 0});
         t.show(colorLayer);
     });
@@ -1043,7 +1041,7 @@
         // expect the child layer to be cropped.
         t.setPosition(boundlessLayer, 32, 32);
         t.show(boundlessLayer);
-        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        t.setCrop(colorLayer, Rect(0, 0, 64, 64));
         // undo shift by parent
         t.setPosition(colorLayer, -32, -32);
         t.setColor(colorLayer, half3{0, 0, 0});
@@ -1074,7 +1072,7 @@
         t.setLayer(rootBoundlessLayer, INT32_MAX - 1);
         t.setPosition(rootBoundlessLayer, 32, 32);
         t.show(rootBoundlessLayer);
-        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        t.setCrop(colorLayer, Rect(0, 0, 64, 64));
         t.setColor(colorLayer, half3{0, 0, 0});
         t.show(colorLayer);
         t.hide(mFGSurfaceControl);
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index 16826c1..613b21e 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -36,7 +36,7 @@
         asTransaction([&](Transaction& t) {
             t.setDisplayLayerStack(display, 0);
             t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
-            t.setCrop_legacy(mChildLayer, Rect(0, 0, 400, 400)).show(mChildLayer);
+            t.setCrop(mChildLayer, Rect(0, 0, 400, 400)).show(mChildLayer);
             t.setPosition(mChildLayer, 50, 50);
             t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
             t.setFlags(mChildLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
@@ -58,7 +58,7 @@
             createColorLayer("Grandchild layer", Color::BLUE, mChildLayer.get());
     Transaction()
             .setFlags(grandchild, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque)
-            .setCrop_legacy(grandchild, Rect(0, 0, 200, 200))
+            .setCrop(grandchild, Rect(0, 0, 200, 200))
             .show(grandchild)
             .apply();
 
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index eaf54e3..08de01c 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -71,7 +71,7 @@
         ASSERT_TRUE(mColorLayer->isValid());
         asTransaction([&](Transaction& t) {
             t.setLayerStack(mColorLayer, layerStack);
-            t.setCrop_legacy(mColorLayer, Rect(0, 0, 30, 40));
+            t.setCrop(mColorLayer, Rect(0, 0, 30, 40));
             t.setLayer(mColorLayer, INT32_MAX - 2);
             t.setColor(mColorLayer,
                        half3{mExpectedColor.r / 255.0f, mExpectedColor.g / 255.0f,
diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
new file mode 100644
index 0000000..fb7d41c
--- /dev/null
+++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerTransactionTest.h"
+#include "utils/CallbackUtils.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+// b/181132765 - disabled until cuttlefish failures are investigated
+class ReleaseBufferCallbackHelper {
+public:
+    static void function(void* callbackContext, uint64_t graphicsBufferId,
+                         const sp<Fence>& releaseFence) {
+        if (!callbackContext) {
+            FAIL() << "failed to get callback context";
+        }
+        ReleaseBufferCallbackHelper* helper =
+                static_cast<ReleaseBufferCallbackHelper*>(callbackContext);
+        std::lock_guard lock(helper->mMutex);
+        helper->mCallbackDataQueue.emplace(graphicsBufferId, releaseFence);
+        helper->mConditionVariable.notify_all();
+    }
+
+    void getCallbackData(uint64_t* bufferId) {
+        std::unique_lock lock(mMutex);
+        if (mCallbackDataQueue.empty()) {
+            if (!mConditionVariable.wait_for(lock, std::chrono::seconds(3),
+                                             [&] { return !mCallbackDataQueue.empty(); })) {
+                FAIL() << "failed to get releaseBuffer callback";
+            }
+        }
+
+        auto callbackData = mCallbackDataQueue.front();
+        mCallbackDataQueue.pop();
+        *bufferId = callbackData.first;
+    }
+
+    void verifyNoCallbacks() {
+        // Wait to see if there are extra callbacks
+        std::this_thread::sleep_for(300ms);
+
+        std::lock_guard lock(mMutex);
+        EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received";
+        mCallbackDataQueue = {};
+    }
+
+    android::ReleaseBufferCallback getCallback() {
+        return std::bind(function, static_cast<void*>(this) /* callbackContext */,
+                         std::placeholders::_1, std::placeholders::_2);
+    }
+
+    std::mutex mMutex;
+    std::condition_variable mConditionVariable;
+    std::queue<std::pair<uint64_t, sp<Fence>>> mCallbackDataQueue;
+};
+
+class ReleaseBufferCallbackTest : public LayerTransactionTest {
+public:
+    virtual sp<SurfaceControl> createBufferStateLayer() {
+        return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
+    }
+
+    static void submitBuffer(const sp<SurfaceControl>& layer, sp<GraphicBuffer> buffer,
+                             sp<Fence> fence, CallbackHelper& callback,
+                             ReleaseBufferCallbackHelper& releaseCallback) {
+        Transaction t;
+        t.setBuffer(layer, buffer, releaseCallback.getCallback());
+        t.setAcquireFence(layer, fence);
+        t.addTransactionCompletedCallback(callback.function, callback.getContext());
+        t.apply();
+    }
+
+    static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult) {
+        CallbackData callbackData;
+        helper.getCallbackData(&callbackData);
+        expectedResult.verifyCallbackData(callbackData);
+    }
+
+    static void waitForReleaseBufferCallback(ReleaseBufferCallbackHelper& releaseCallback,
+                                             uint64_t expectedReleaseBufferId) {
+        uint64_t actualReleaseBufferId;
+        releaseCallback.getCallbackData(&actualReleaseBufferId);
+        EXPECT_EQ(expectedReleaseBufferId, actualReleaseBufferId);
+        releaseCallback.verifyNoCallbacks();
+    }
+    static ReleaseBufferCallbackHelper* getReleaseBufferCallbackHelper() {
+        static std::vector<ReleaseBufferCallbackHelper*> sCallbacks;
+        sCallbacks.emplace_back(new ReleaseBufferCallbackHelper());
+        return sCallbacks.back();
+    }
+
+    static sp<GraphicBuffer> getBuffer() {
+        return new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                                 BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                         BufferUsage::COMPOSER_OVERLAY,
+                                 "test");
+    }
+};
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_PresentBuffer) {
+    sp<SurfaceControl> layer = createBufferStateLayer();
+    CallbackHelper transactionCallback;
+    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+    // If a buffer is being presented, we should not emit a release callback.
+    sp<GraphicBuffer> firstBuffer = getBuffer();
+    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+    EXPECT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+
+    // if state doesn't change, no release callbacks are expected
+    Transaction t;
+    t.addTransactionCompletedCallback(transactionCallback.function,
+                                      transactionCallback.getContext());
+    t.apply();
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, ExpectedResult()));
+    EXPECT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+
+    // If a presented buffer is replaced, we should emit a release callback for the
+    // previously presented buffer.
+    sp<GraphicBuffer> secondBuffer = getBuffer();
+    submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+    expected = ExpectedResult();
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED,
+                        ExpectedResult::PreviousBuffer::RELEASED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+}
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_OffScreenLayer) {
+    sp<SurfaceControl> layer = createBufferStateLayer();
+
+    CallbackHelper transactionCallback;
+    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+    // If a buffer is being presented, we should not emit a release callback.
+    sp<GraphicBuffer> firstBuffer = getBuffer();
+    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+    releaseCallback->verifyNoCallbacks();
+
+    // If a layer is parented offscreen then it should not emit a callback since sf still owns
+    // the buffer and can render it again.
+    Transaction t;
+    t.reparent(layer, nullptr);
+    t.addTransactionCompletedCallback(transactionCallback.function,
+                                      transactionCallback.getContext());
+    t.apply();
+    expected = ExpectedResult();
+    expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED,
+                        ExpectedResult::PreviousBuffer::NOT_RELEASED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+    ASSERT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+
+    // If a presented buffer is replaced, we should emit a release callback for the
+    // previously presented buffer.
+    sp<GraphicBuffer> secondBuffer = getBuffer();
+    submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+    expected = ExpectedResult();
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED,
+                        ExpectedResult::PreviousBuffer::NOT_RELEASED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+
+    // If continue to submit buffer we continue to get release callbacks
+    sp<GraphicBuffer> thirdBuffer = getBuffer();
+    submitBuffer(layer, thirdBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+    expected = ExpectedResult();
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED,
+                        ExpectedResult::PreviousBuffer::NOT_RELEASED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBuffer->getId()));
+}
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_LayerLifecycle_layerdestroy) {
+    sp<SurfaceControl> layer = createBufferStateLayer();
+    CallbackHelper* transactionCallback = new CallbackHelper();
+    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+    // If a buffer is being presented, we should not emit a release callback.
+    sp<GraphicBuffer> firstBuffer = getBuffer();
+    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, *releaseCallback);
+    {
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::NOT_ACQUIRED);
+        ASSERT_NO_FATAL_FAILURE(waitForCallback(*transactionCallback, expected));
+        ASSERT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+    }
+
+    // Destroying a currently presenting layer emits a callback.
+    Transaction t;
+    t.reparent(layer, nullptr);
+    t.apply();
+    layer = nullptr;
+
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+}
+
+// Destroying a never presented layer emits a callback.
+TEST_F(ReleaseBufferCallbackTest, DISABLED_LayerLifecycle_OffScreenLayerDestroy) {
+    sp<SurfaceControl> layer = createBufferStateLayer();
+
+    // make layer offscreen
+    Transaction t;
+    t.reparent(layer, nullptr);
+    t.apply();
+
+    CallbackHelper* transactionCallback = new CallbackHelper();
+    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+    // Submitting a buffer does not emit a callback.
+    sp<GraphicBuffer> firstBuffer = getBuffer();
+    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, *releaseCallback);
+    {
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::NOT_ACQUIRED);
+        ASSERT_NO_FATAL_FAILURE(waitForCallback(*transactionCallback, expected));
+        ASSERT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+    }
+
+    // Submitting a second buffer will replace the drawing state buffer and emit a callback.
+    sp<GraphicBuffer> secondBuffer = getBuffer();
+    submitBuffer(layer, secondBuffer, Fence::NO_FENCE, *transactionCallback, *releaseCallback);
+    {
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::NOT_ACQUIRED);
+        ASSERT_NO_FATAL_FAILURE(waitForCallback(*transactionCallback, expected));
+        ASSERT_NO_FATAL_FAILURE(
+                waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+    }
+
+    // Destroying the offscreen layer emits a callback.
+    layer = nullptr;
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBuffer->getId()));
+}
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_FrameDropping) {
+    sp<SurfaceControl> layer = createBufferStateLayer();
+    CallbackHelper transactionCallback;
+    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+    // If a buffer is being presented, we should not emit a release callback.
+    sp<GraphicBuffer> firstBuffer = getBuffer();
+
+    // Try to present 100ms in the future
+    nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
+
+    Transaction t;
+    t.setBuffer(layer, firstBuffer, releaseCallback->getCallback());
+    t.setAcquireFence(layer, Fence::NO_FENCE);
+    t.addTransactionCompletedCallback(transactionCallback.function,
+                                      transactionCallback.getContext());
+    t.setDesiredPresentTime(time);
+    t.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+    EXPECT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+
+    // Dropping frames in transaction queue emits a callback
+    sp<GraphicBuffer> secondBuffer = getBuffer();
+    t.setBuffer(layer, secondBuffer, releaseCallback->getCallback());
+    t.setAcquireFence(layer, Fence::NO_FENCE);
+    t.addTransactionCompletedCallback(transactionCallback.function,
+                                      transactionCallback.getContext());
+    t.setDesiredPresentTime(time);
+    t.apply();
+
+    expected = ExpectedResult();
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED,
+                        ExpectedResult::PreviousBuffer::RELEASED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 4598f9d..b0753c8 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -328,7 +328,7 @@
 TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) {
     sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
     Rect layerCrop(0, 0, 10, 10);
-    SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true);
+    SurfaceComposerClient::Transaction().setCrop(child, layerCrop).show(child).apply(true);
 
     LayerCaptureArgs captureArgs;
     captureArgs.layerHandle = child->getHandle();
@@ -623,7 +623,7 @@
             .setLayerStack(layer, 0)
             .setLayer(layer, INT32_MAX)
             .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
-            .setCrop_legacy(layer, bounds)
+            .setCrop(layer, bounds)
             .apply();
 
     DisplayCaptureArgs captureArgs;
@@ -671,8 +671,8 @@
             .setLayer(layer, INT32_MAX)
             .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
             .setColor(childLayer, {childColor.r / 255, childColor.g / 255, childColor.b / 255})
-            .setCrop_legacy(layer, bounds)
-            .setCrop_legacy(childLayer, childBounds)
+            .setCrop(layer, bounds)
+            .setCrop(childLayer, childBounds)
             .apply();
 
     DisplayCaptureArgs captureArgs;
@@ -853,9 +853,7 @@
 }
 
 TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
-    SurfaceComposerClient::Transaction()
-            .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1))
-            .apply(true);
+    SurfaceComposerClient::Transaction().setCrop(mFGSurfaceControl, Rect(0, 0, 1, 1)).apply(true);
 
     // Even though the parent is cropped out we should still capture the child.
 
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index a20d5c6..d9cab42 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -370,7 +370,7 @@
 }
 
 void SurfaceInterceptorTest::cropUpdate(Transaction& t) {
-    t.setCrop_legacy(mBGSurfaceControl, CROP_UPDATE);
+    t.setCrop(mBGSurfaceControl, CROP_UPDATE);
 }
 
 void SurfaceInterceptorTest::matrixUpdate(Transaction& t) {
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 11bd9eb..820f248 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -1324,7 +1324,7 @@
         {
             TransactionScope ts(*sFakeComposer);
             Rect cropRect(16, 16, 32, 32);
-            ts.setCrop_legacy(mFGSurfaceControl, cropRect);
+            ts.setCrop(mFGSurfaceControl, cropRect);
         }
         ASSERT_EQ(2, sFakeComposer->getFrameCount());
 
@@ -1692,7 +1692,7 @@
             ts.show(mChild);
             ts.setPosition(mChild, 0, 0);
             ts.setPosition(Base::mFGSurfaceControl, 0, 0);
-            ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 5, 5));
+            ts.setCrop(Base::mFGSurfaceControl, Rect(0, 0, 5, 5));
         }
         // NOTE: The foreground surface would be occluded by the child
         // now, but is included in the stack because the child is
@@ -1915,7 +1915,7 @@
             TransactionScope ts(*Base::sFakeComposer);
             ts.setColor(Base::mChild,
                         {LIGHT_GRAY.r / 255.0f, LIGHT_GRAY.g / 255.0f, LIGHT_GRAY.b / 255.0f});
-            ts.setCrop_legacy(Base::mChild, Rect(0, 0, 10, 10));
+            ts.setCrop(Base::mChild, Rect(0, 0, 10, 10));
         }
 
         Base::sFakeComposer->runVSyncAndWait();
@@ -2010,7 +2010,7 @@
         TransactionScope ts(*Base::sFakeComposer);
         ts.setSize(Base::mFGSurfaceControl, 64, 64);
         ts.setPosition(Base::mFGSurfaceControl, 64, 64);
-        ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 64, 64));
+        ts.setCrop(Base::mFGSurfaceControl, Rect(0, 0, 64, 64));
     }
 
     void Test_SurfacePositionLatching() {
@@ -2043,7 +2043,7 @@
         {
             TransactionScope ts(*Base::sFakeComposer);
             ts.setSize(Base::mFGSurfaceControl, 128, 128);
-            ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 63, 63));
+            ts.setCrop(Base::mFGSurfaceControl, Rect(0, 0, 63, 63));
         }
 
         auto referenceFrame1 = Base::mBaseFrame;
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
index 6bc2318..6a7ec9b 100644
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CachingTest.cpp
@@ -31,7 +31,8 @@
 
 class SlotGenerationTest : public testing::Test {
 protected:
-    BufferStateLayer::HwcSlotGenerator mHwcSlotGenerator;
+    sp<BufferStateLayer::HwcSlotGenerator> mHwcSlotGenerator =
+            sp<BufferStateLayer::HwcSlotGenerator>::make();
     sp<GraphicBuffer> mBuffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
     sp<GraphicBuffer> mBuffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
     sp<GraphicBuffer> mBuffer3{new GraphicBuffer(10, 10, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
@@ -41,7 +42,7 @@
     sp<IBinder> binder = new BBinder();
     // test getting invalid client_cache_id
     client_cache_t id;
-    uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+    uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
     EXPECT_EQ(BufferQueue::INVALID_BUFFER_SLOT, slot);
 }
 
@@ -50,19 +51,19 @@
     client_cache_t id;
     id.token = binder;
     id.id = 0;
-    uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+    uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
     EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
 
     client_cache_t idB;
     idB.token = binder;
     idB.id = 1;
-    slot = mHwcSlotGenerator.getHwcCacheSlot(idB);
+    slot = mHwcSlotGenerator->getHwcCacheSlot(idB);
     EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
 
-    slot = mHwcSlotGenerator.getHwcCacheSlot(idB);
+    slot = mHwcSlotGenerator->getHwcCacheSlot(idB);
     EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
 
-    slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+    slot = mHwcSlotGenerator->getHwcCacheSlot(id);
     EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
 }
 
@@ -77,12 +78,12 @@
         id.id = cacheId;
         ids.push_back(id);
 
-        uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+        uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
         EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
         cacheId++;
     }
     for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
-        uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(ids[i]);
+        uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(ids[i]);
         EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
     }
 
@@ -90,7 +91,7 @@
         client_cache_t id;
         id.token = binder;
         id.id = cacheId;
-        uint32_t slot = mHwcSlotGenerator.getHwcCacheSlot(id);
+        uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
         EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
         cacheId++;
     }
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 256be27..4e1c0c7 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -836,8 +836,8 @@
     static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) {
         auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
         layerDrawingState.layerStack = DEFAULT_LAYER_STACK;
-        layerDrawingState.active.w = 100;
-        layerDrawingState.active.h = 100;
+        layerDrawingState.width = 100;
+        layerDrawingState.height = 100;
         layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
                                         LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
         layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */);
@@ -884,7 +884,7 @@
         });
 
         auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
-        layerDrawingState.crop_legacy = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
+        layerDrawingState.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
         return layer;
     }
 
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index f2cb951..d1385c0 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -193,10 +193,12 @@
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) {
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
-                                                       sLayerNameOne, sLayerNameOne);
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({}, sPidTwo, sUidOne, sLayerIdOne,
-                                                       sLayerNameOne, sLayerNameOne);
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
     EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
 }
@@ -204,7 +206,8 @@
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
-                                                       sLayerNameOne, sLayerNameOne);
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
 }
 
@@ -213,7 +216,8 @@
     flushTokens(systemTime() + maxTokenRetentionTime);
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
 
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
 }
@@ -222,7 +226,8 @@
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
 
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
     EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
@@ -233,7 +238,8 @@
     constexpr int32_t inputEventId = 1;
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({token1, inputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
 
     EXPECT_EQ(inputEventId, surfaceFrame->getInputEventId());
 }
@@ -243,7 +249,8 @@
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
 
     // Set up the display frame
     mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -271,11 +278,11 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdTwo, sLayerNameTwo,
-                                                       sLayerNameTwo);
+                                                       sLayerNameTwo, /*isBuffer*/ true);
     mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -317,7 +324,8 @@
         auto surfaceFrame =
                 mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId},
                                                            sPidOne, sUidOne, sLayerIdOne,
-                                                           sLayerNameOne, sLayerNameOne);
+                                                           sLayerNameOne, sLayerNameOne,
+                                                           /*isBuffer*/ true);
         mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
         surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
         mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -339,7 +347,7 @@
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
     surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -353,18 +361,20 @@
 }
 
 TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
-    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
-                                                                   "acquireFenceAfterQueue",
-                                                                   "acquireFenceAfterQueue");
+    auto surfaceFrame =
+            mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+                                                       "acquireFenceAfterQueue",
+                                                       "acquireFenceAfterQueue", /*isBuffer*/ true);
     surfaceFrame->setActualQueueTime(123);
     surfaceFrame->setAcquireFenceTime(456);
     EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
 }
 
 TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
-    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
-                                                                   "acquireFenceAfterQueue",
-                                                                   "acquireFenceAfterQueue");
+    auto surfaceFrame =
+            mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+                                                       "acquireFenceAfterQueue",
+                                                       "acquireFenceAfterQueue", /*isBuffer*/ true);
     surfaceFrame->setActualQueueTime(456);
     surfaceFrame->setAcquireFenceTime(123);
     EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
@@ -378,7 +388,8 @@
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
         auto surfaceFrame =
                 mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
-                                                           sLayerNameOne, sLayerNameOne);
+                                                           sLayerNameOne, sLayerNameOne,
+                                                           /*isBuffer*/ true);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
         mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
         surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -394,7 +405,8 @@
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
         auto surfaceFrame =
                 mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
-                                                           sLayerNameOne, sLayerNameOne);
+                                                           sLayerNameOne, sLayerNameOne,
+                                                           /*isBuffer*/ true);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
         mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
         surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -410,7 +422,8 @@
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
         auto surfaceFrame =
                 mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
-                                                           sLayerNameOne, sLayerNameOne);
+                                                           sLayerNameOne, sLayerNameOne,
+                                                           /*isBuffer*/ true);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
         mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
         surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -420,7 +433,54 @@
     EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
 }
 
+TEST_F(FrameTimelineTest, presentFenceSignaled_invalidSignalTime) {
+    Fps refreshRate = Fps::fromPeriodNsecs(11);
+
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne, /*isBuffer*/ true);
+    mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+    surfaceFrame1->setAcquireFenceTime(20);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+
+    mFrameTimeline->setSfPresent(59, presentFence1);
+    presentFence1->signalForTest(-1);
+    addEmptyDisplayFrame();
+
+    auto displayFrame0 = getDisplayFrame(0);
+    EXPECT_EQ(displayFrame0->getActuals().presentTime, -1);
+    EXPECT_EQ(displayFrame0->getJankType(), JankType::Unknown);
+    EXPECT_EQ(surfaceFrame1->getActuals().presentTime, -1);
+    EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown);
+}
+
 // Tests related to TimeStats
+TEST_F(FrameTimelineTest, presentFenceSignaled_doesNotReportForInvalidTokens) {
+    Fps refreshRate = Fps::fromPeriodNsecs(11);
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(0);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t surfaceFrameToken1 = -1;
+    int64_t sfToken1 = -1;
+
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne, /*isBuffer*/ true);
+    mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+    surfaceFrame1->setAcquireFenceTime(20);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    presentFence1->signalForTest(70);
+
+    mFrameTimeline->setSfPresent(59, presentFence1);
+}
+
 TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) {
     Fps refreshRate = Fps::fromPeriodNsecs(11);
     // Deadline delta is 2ms because, sf's adjusted deadline is 60 - composerTime(3) = 57ms.
@@ -437,7 +497,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
     surfaceFrame1->setAcquireFenceTime(20);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -461,7 +521,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     surfaceFrame1->setAcquireFenceTime(20);
@@ -485,7 +545,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(45);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
 
@@ -511,7 +571,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(50);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
 
@@ -537,7 +597,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(40);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
 
@@ -563,7 +623,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(40);
     mFrameTimeline->setSfWakeUp(sfToken1, 82, refreshRate);
 
@@ -590,7 +650,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(45);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
 
@@ -603,6 +663,43 @@
     EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed);
 }
 
+TEST_F(FrameTimelineTest, presentFenceSignaled_displayFramePredictionExpiredPresentsSurfaceFrame) {
+    Fps refreshRate = Fps::fromPeriodNsecs(11);
+    Fps renderRate = Fps::fromPeriodNsecs(30);
+
+    EXPECT_CALL(*mTimeStats,
+                incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne,
+                                                                sLayerNameOne, JankType::Unknown,
+                                                                -1, -1, 25}));
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne, /*isBuffer*/ true);
+    surfaceFrame1->setAcquireFenceTime(45);
+    // Trigger a prediction expiry
+    flushTokens(systemTime() + maxTokenRetentionTime);
+    mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    surfaceFrame1->setRenderRate(renderRate);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    presentFence1->signalForTest(90);
+    mFrameTimeline->setSfPresent(86, presentFence1);
+
+    auto displayFrame = getDisplayFrame(0);
+    EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown);
+    EXPECT_EQ(displayFrame->getFrameStartMetadata(), FrameStartMetadata::UnknownStart);
+    EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish);
+    EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
+
+    EXPECT_EQ(surfaceFrame1->getActuals().presentTime, 90);
+    EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown);
+}
+
 /*
  * Tracing Tests
  *
@@ -617,7 +714,8 @@
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
 
     // Set up the display frame
     mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -643,7 +741,8 @@
     int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
 
     // Set up the display frame
     mFrameTimeline->setSfWakeUp(token2, 20, Fps::fromPeriodNsecs(11));
@@ -688,7 +787,8 @@
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
-                                                       sLayerNameOne, sLayerNameOne);
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
 
     // Set up the display frame
     mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -1001,11 +1101,11 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setActualQueueTime(10);
     surfaceFrame1->setDropTime(15);
 
@@ -1161,7 +1261,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
                                                        sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setActualQueueTime(appEndTime);
     surfaceFrame1->setAcquireFenceTime(appEndTime);
 
@@ -1231,7 +1331,7 @@
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
     surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -1409,7 +1509,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(16);
     mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1429,7 +1529,7 @@
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame2->setAcquireFenceTime(36);
     mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1489,7 +1589,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(16);
     mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1509,7 +1609,7 @@
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame2->setAcquireFenceTime(36);
     mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1568,7 +1668,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(40);
     mFrameTimeline->setSfWakeUp(sfToken1, 42, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1612,7 +1712,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(26);
     mFrameTimeline->setSfWakeUp(sfToken1, 32, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1632,7 +1732,7 @@
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame2->setAcquireFenceTime(40);
     mFrameTimeline->setSfWakeUp(sfToken2, 43, Fps::fromPeriodNsecs(11));
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1686,7 +1786,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(50);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1706,7 +1806,7 @@
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame2->setAcquireFenceTime(84);
     mFrameTimeline->setSfWakeUp(sfToken2, 112, Fps::fromPeriodNsecs(30));
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
@@ -1764,7 +1864,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(50);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1784,7 +1884,7 @@
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame2->setAcquireFenceTime(80);
     mFrameTimeline->setSfWakeUp(sfToken2, 82, Fps::fromPeriodNsecs(30));
     // Setting previous latch time to 54, adjusted deadline will be 54 + vsyncTime(30) = 84
@@ -1839,7 +1939,8 @@
 
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1854,7 +1955,8 @@
     const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1863,7 +1965,8 @@
 
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -1878,7 +1981,8 @@
     const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1887,7 +1991,8 @@
 
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+                                                       sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+                                                       /*isBuffer*/ true);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -1902,7 +2007,8 @@
     const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1911,7 +2017,8 @@
 
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+                                                       sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+                                                       /*isBuffer*/ true);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -1929,7 +2036,8 @@
     const auto sixHundredMs = std::chrono::nanoseconds(600ms).count();
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1938,7 +2046,8 @@
 
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -1947,7 +2056,8 @@
 
     auto surfaceFrame3 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+                                                       sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+                                                       /*isBuffer*/ true);
     auto presentFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame3->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame3);
@@ -1956,7 +2066,8 @@
 
     auto surfaceFrame4 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame4->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame4);
@@ -1965,7 +2076,8 @@
 
     auto surfaceFrame5 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence5 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     // Dropped frames will be excluded from fps computation
     surfaceFrame5->setPresentState(SurfaceFrame::PresentState::Dropped);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index e46a270..38e503f 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -64,7 +64,7 @@
         }
     } mExpectDisableVsync{mSchedulerCallback};
 
-    TestableScheduler mScheduler{mConfigs, mSchedulerCallback};
+    TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback};
 
     Scheduler::ConnectionHandle mConnectionHandle;
     mock::EventThread* mEventThread;
@@ -85,8 +85,10 @@
     EXPECT_CALL(*mEventThread, createEventConnection(_, _))
             .WillRepeatedly(Return(mEventThreadConnection));
 
-    mConnectionHandle = mScheduler.createConnection(std::move(eventThread));
+    mConnectionHandle = mScheduler->createConnection(std::move(eventThread));
     EXPECT_TRUE(mConnectionHandle);
+
+    mFlinger.resetScheduler(mScheduler);
 }
 
 } // namespace
@@ -94,85 +96,84 @@
 TEST_F(SchedulerTest, invalidConnectionHandle) {
     Scheduler::ConnectionHandle handle;
 
-    const sp<IDisplayEventConnection> connection = mScheduler.createDisplayEventConnection(handle);
+    const sp<IDisplayEventConnection> connection = mScheduler->createDisplayEventConnection(handle);
 
     EXPECT_FALSE(connection);
-    EXPECT_FALSE(mScheduler.getEventConnection(handle));
+    EXPECT_FALSE(mScheduler->getEventConnection(handle));
 
     // The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
     EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
-    mScheduler.onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false);
+    mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false);
 
     EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
-    mScheduler.onScreenAcquired(handle);
+    mScheduler->onScreenAcquired(handle);
 
     EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
-    mScheduler.onScreenReleased(handle);
+    mScheduler->onScreenReleased(handle);
 
     std::string output;
     EXPECT_CALL(*mEventThread, dump(_)).Times(0);
-    mScheduler.dump(handle, output);
+    mScheduler->dump(handle, output);
     EXPECT_TRUE(output.empty());
 
     EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(0);
-    mScheduler.setDuration(handle, 10ns, 20ns);
+    mScheduler->setDuration(handle, 10ns, 20ns);
 }
 
 TEST_F(SchedulerTest, validConnectionHandle) {
     const sp<IDisplayEventConnection> connection =
-            mScheduler.createDisplayEventConnection(mConnectionHandle);
+            mScheduler->createDisplayEventConnection(mConnectionHandle);
 
     ASSERT_EQ(mEventThreadConnection, connection);
-    EXPECT_TRUE(mScheduler.getEventConnection(mConnectionHandle));
+    EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
 
     EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
-    mScheduler.onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false);
+    mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false);
 
     EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
-    mScheduler.onScreenAcquired(mConnectionHandle);
+    mScheduler->onScreenAcquired(mConnectionHandle);
 
     EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1);
-    mScheduler.onScreenReleased(mConnectionHandle);
+    mScheduler->onScreenReleased(mConnectionHandle);
 
     std::string output("dump");
     EXPECT_CALL(*mEventThread, dump(output)).Times(1);
-    mScheduler.dump(mConnectionHandle, output);
+    mScheduler->dump(mConnectionHandle, output);
     EXPECT_FALSE(output.empty());
 
     EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(1);
-    mScheduler.setDuration(mConnectionHandle, 10ns, 20ns);
+    mScheduler->setDuration(mConnectionHandle, 10ns, 20ns);
 
     static constexpr size_t kEventConnections = 5;
     EXPECT_CALL(*mEventThread, getEventThreadConnectionCount()).WillOnce(Return(kEventConnections));
-    EXPECT_EQ(kEventConnections, mScheduler.getEventThreadConnectionCount(mConnectionHandle));
+    EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
 }
 
 TEST_F(SchedulerTest, noLayerHistory) {
     // Layer history should not be created if there is a single config.
-    ASSERT_FALSE(mScheduler.hasLayerHistory());
+    ASSERT_FALSE(mScheduler->hasLayerHistory());
 
-    TestableSurfaceFlinger flinger;
-    mock::MockLayer layer(flinger.flinger());
+    sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
 
     // Content detection should be no-op.
-    mScheduler.registerLayer(&layer);
-    mScheduler.recordLayerHistory(&layer, 0, LayerHistory::LayerUpdateType::Buffer);
+    mScheduler->registerLayer(layer.get());
+    mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
 
     constexpr bool kPowerStateNormal = true;
-    mScheduler.setDisplayPowerState(kPowerStateNormal);
+    mScheduler->setDisplayPowerState(kPowerStateNormal);
 
     constexpr uint32_t kDisplayArea = 999'999;
-    mScheduler.onPrimaryDisplayAreaChanged(kDisplayArea);
+    mScheduler->onPrimaryDisplayAreaChanged(kDisplayArea);
 
     EXPECT_CALL(mSchedulerCallback, changeRefreshRate(_, _)).Times(0);
-    mScheduler.chooseRefreshRateForContent();
+    mScheduler->chooseRefreshRateForContent();
 }
 
 TEST_F(SchedulerTest, testDispatchCachedReportedMode) {
     // If the optional fields are cleared, the function should return before
     // onModeChange is called.
-    mScheduler.clearOptionalFieldsInFeatures();
-    EXPECT_NO_FATAL_FAILURE(mScheduler.dispatchCachedReportedMode());
+    mScheduler->clearOptionalFieldsInFeatures();
+    EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode());
     EXPECT_CALL(*mEventThread, onModeChanged(_, _, _)).Times(0);
 }
 
@@ -183,9 +184,9 @@
     // If the handle is incorrect, the function should return before
     // onModeChange is called.
     Scheduler::ConnectionHandle invalidHandle = {.id = 123};
-    EXPECT_NO_FATAL_FAILURE(mScheduler.onNonPrimaryDisplayModeChanged(invalidHandle,
-                                                                      PHYSICAL_DISPLAY_ID, modeId,
-                                                                      vsyncPeriod));
+    EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle,
+                                                                       PHYSICAL_DISPLAY_ID, modeId,
+                                                                       vsyncPeriod));
     EXPECT_CALL(*mEventThread, onModeChanged(_, _, _)).Times(0);
 }
 
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index dbadf75..b5ef0a1 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -123,7 +123,8 @@
                     traceTimestamp(layerId, bufferId, frameNumber, postTime,
                                    FrameTracer::FrameEvent::QUEUE, /*duration*/ 0));
         layer->setBuffer(buffer, fence, postTime, /*desiredPresentTime*/ 30, false, mClientCache,
-                         frameNumber, dequeueTime, FrameTimelineInfo{});
+                         frameNumber, dequeueTime, FrameTimelineInfo{},
+                         nullptr /* releaseBufferCallback */);
 
         commitTransaction(layer.get());
         bool computeVisisbleRegions;
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 363bd80..c75538f 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -116,6 +116,7 @@
         const auto surfaceFrame = layer->mCurrentState.bufferlessSurfaceFramesTX.at(/*token*/ 1);
         commitTransaction(layer.get());
         EXPECT_EQ(1, surfaceFrame->getToken());
+        EXPECT_EQ(false, surfaceFrame->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, surfaceFrame->getPresentState());
     }
 
@@ -125,7 +126,7 @@
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
         sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         acquireFence->signalForTest(12);
 
         commitTransaction(layer.get());
@@ -139,6 +140,7 @@
         layer->updateTexImage(computeVisisbleRegions, 15, 0);
 
         EXPECT_EQ(1, surfaceFrame->getToken());
+        EXPECT_EQ(true, surfaceFrame->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, surfaceFrame->getPresentState());
     }
 
@@ -149,7 +151,7 @@
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
         sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
         layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
         const auto droppedSurfaceFrame = layer->mCurrentState.bufferSurfaceFrameTX;
@@ -159,7 +161,7 @@
         sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
         nsecs_t start = systemTime();
         layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         nsecs_t end = systemTime();
         acquireFence2->signalForTest(12);
 
@@ -172,12 +174,14 @@
         layer->updateTexImage(computeVisisbleRegions, 15, 0);
 
         EXPECT_EQ(1, droppedSurfaceFrame->getToken());
+        EXPECT_EQ(true, droppedSurfaceFrame->getIsBuffer());
         EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame->getPresentState());
         EXPECT_EQ(0u, droppedSurfaceFrame->getActuals().endTime);
         auto dropTime = droppedSurfaceFrame->getDropTime();
         EXPECT_TRUE(dropTime > start && dropTime < end);
 
         EXPECT_EQ(1, presentedSurfaceFrame->getToken());
+        EXPECT_EQ(true, presentedSurfaceFrame->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, presentedSurfaceFrame->getPresentState());
     }
 
@@ -195,7 +199,7 @@
         sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
 
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         acquireFence->signalForTest(12);
 
         EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
@@ -204,6 +208,7 @@
 
         commitTransaction(layer.get());
         EXPECT_EQ(1, surfaceFrame->getToken());
+        EXPECT_EQ(true, surfaceFrame->getIsBuffer());
         // Buffers are presented only at latch time.
         EXPECT_EQ(PresentState::Unknown, surfaceFrame->getPresentState());
 
@@ -220,7 +225,7 @@
         sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
 
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
 
@@ -250,7 +255,7 @@
         sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
 
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 3, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 3, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         EXPECT_EQ(2u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
         const auto& bufferSurfaceFrameTX = layer->mCurrentState.bufferSurfaceFrameTX;
@@ -260,12 +265,15 @@
         commitTransaction(layer.get());
 
         EXPECT_EQ(1, bufferlessSurfaceFrame1->getToken());
+        EXPECT_EQ(false, bufferlessSurfaceFrame1->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame1->getPresentState());
 
         EXPECT_EQ(4, bufferlessSurfaceFrame2->getToken());
+        EXPECT_EQ(false, bufferlessSurfaceFrame2->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState());
 
         EXPECT_EQ(3, bufferSurfaceFrameTX->getToken());
+        EXPECT_EQ(true, bufferSurfaceFrameTX->getIsBuffer());
         // Buffers are presented only at latch time.
         EXPECT_EQ(PresentState::Unknown, bufferSurfaceFrameTX->getPresentState());
 
@@ -297,10 +305,12 @@
         commitTransaction(layer.get());
 
         EXPECT_EQ(1, bufferlessSurfaceFrame1->getToken());
+        EXPECT_EQ(false, bufferlessSurfaceFrame1->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame1->getPresentState());
         EXPECT_EQ(10, bufferlessSurfaceFrame1->getActuals().endTime);
 
         EXPECT_EQ(2, bufferlessSurfaceFrame2->getToken());
+        EXPECT_EQ(false, bufferlessSurfaceFrame2->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState());
         EXPECT_EQ(12, bufferlessSurfaceFrame2->getActuals().endTime);
     }
@@ -327,9 +337,11 @@
         commitTransaction(layer.get());
 
         EXPECT_EQ(1, bufferlessSurfaceFrame1->getToken());
+        EXPECT_EQ(false, bufferlessSurfaceFrame1->getIsBuffer());
         EXPECT_EQ(PresentState::Unknown, bufferlessSurfaceFrame1->getPresentState());
 
         EXPECT_EQ(1, bufferlessSurfaceFrame2->getToken());
+        EXPECT_EQ(false, bufferlessSurfaceFrame2->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState());
         EXPECT_EQ(12, bufferlessSurfaceFrame2->getActuals().endTime);
     }
@@ -341,7 +353,7 @@
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
         sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
         layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
         const auto droppedSurfaceFrame = layer->mCurrentState.bufferSurfaceFrameTX;
 
@@ -349,7 +361,7 @@
         auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
         sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
         layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         acquireFence2->signalForTest(12);
 
         ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
@@ -376,7 +388,7 @@
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
         sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
         layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
         const auto droppedSurfaceFrame1 = layer->mCurrentState.bufferSurfaceFrameTX;
@@ -386,7 +398,8 @@
         sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
         auto dropStartTime1 = systemTime();
         layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0});
+                         {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0},
+                         nullptr /* releaseBufferCallback */);
         auto dropEndTime1 = systemTime();
         EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
@@ -397,7 +410,7 @@
         sp<GraphicBuffer> buffer3{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
         auto dropStartTime2 = systemTime();
         layer->setBuffer(buffer3, fence3, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 2, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 2, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         auto dropEndTime2 = systemTime();
         acquireFence3->signalForTest(12);
 
@@ -410,20 +423,86 @@
         layer->updateTexImage(computeVisisbleRegions, 15, 0);
 
         EXPECT_EQ(1, droppedSurfaceFrame1->getToken());
+        EXPECT_EQ(true, droppedSurfaceFrame1->getIsBuffer());
         EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame1->getPresentState());
         EXPECT_EQ(0u, droppedSurfaceFrame1->getActuals().endTime);
         auto dropTime1 = droppedSurfaceFrame1->getDropTime();
         EXPECT_TRUE(dropTime1 > dropStartTime1 && dropTime1 < dropEndTime1);
 
         EXPECT_EQ(FrameTimelineInfo::INVALID_VSYNC_ID, droppedSurfaceFrame2->getToken());
+        EXPECT_EQ(true, droppedSurfaceFrame2->getIsBuffer());
         EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame2->getPresentState());
         EXPECT_EQ(0u, droppedSurfaceFrame2->getActuals().endTime);
         auto dropTime2 = droppedSurfaceFrame2->getDropTime();
         EXPECT_TRUE(dropTime2 > dropStartTime2 && dropTime2 < dropEndTime2);
 
         EXPECT_EQ(2, presentedSurfaceFrame->getToken());
+        EXPECT_EQ(true, presentedSurfaceFrame->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, presentedSurfaceFrame->getPresentState());
     }
+
+    void MultipleCommitsBeforeLatch() {
+        sp<BufferStateLayer> layer = createBufferStateLayer();
+        uint32_t surfaceFramesPendingClassification = 0;
+        std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> bufferlessSurfaceFrames;
+        for (int i = 0; i < 10; i += 2) {
+            sp<Fence> fence1(new Fence());
+            sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+            layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
+                             {/*vsyncId*/ 1, /*inputEventId*/ 0},
+                             nullptr /* releaseBufferCallback */);
+            layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 2,
+                                                                  /*inputEventId*/ 0},
+                                                                 10);
+            ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+            EXPECT_EQ(1u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+            auto& bufferlessSurfaceFrame =
+                    layer->mCurrentState.bufferlessSurfaceFramesTX.at(/*vsyncId*/ 2);
+            bufferlessSurfaceFrames.push_back(bufferlessSurfaceFrame);
+
+            commitTransaction(layer.get());
+            surfaceFramesPendingClassification += 2;
+            EXPECT_EQ(surfaceFramesPendingClassification,
+                      layer->mPendingJankClassifications.size());
+        }
+
+        auto presentedBufferSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
+        bool computeVisisbleRegions;
+        layer->updateTexImage(computeVisisbleRegions, 15, 0);
+        // BufferlessSurfaceFrames are immediately set to presented and added to the DisplayFrame.
+        // Since we don't have access to DisplayFrame here, trigger an onPresent directly.
+        for (auto& surfaceFrame : bufferlessSurfaceFrames) {
+            surfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11),
+                                    /*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0);
+        }
+        presentedBufferSurfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11),
+                                               /*displayDeadlineDelta*/ 0,
+                                               /*displayPresentDelta*/ 0);
+
+        // There should be 10 bufferlessSurfaceFrames and 1 bufferSurfaceFrame
+        ASSERT_EQ(10u, surfaceFramesPendingClassification);
+        ASSERT_EQ(surfaceFramesPendingClassification, layer->mPendingJankClassifications.size());
+
+        // For the frames upto 8, the bufferSurfaceFrame should have been dropped while the
+        // bufferlessSurfaceFrame presented
+        for (uint32_t i = 0; i < 8; i += 2) {
+            auto& bufferSurfaceFrame = layer->mPendingJankClassifications[i];
+            auto& bufferlessSurfaceFrame = layer->mPendingJankClassifications[i + 1];
+            EXPECT_EQ(bufferSurfaceFrame->getPresentState(), PresentState::Dropped);
+            EXPECT_EQ(bufferlessSurfaceFrame->getPresentState(), PresentState::Presented);
+        }
+        {
+            auto& bufferSurfaceFrame = layer->mPendingJankClassifications[8u];
+            auto& bufferlessSurfaceFrame = layer->mPendingJankClassifications[9u];
+            EXPECT_EQ(bufferSurfaceFrame->getPresentState(), PresentState::Presented);
+            EXPECT_EQ(bufferlessSurfaceFrame->getPresentState(), PresentState::Presented);
+        }
+
+        layer->releasePendingBuffer(25);
+
+        // There shouldn't be any pending classifications. Everything should have been cleared.
+        EXPECT_EQ(0u, layer->mPendingJankClassifications.size());
+    }
 };
 
 TEST_F(TransactionSurfaceFrameTest, PresentedBufferlessSurfaceFrame) {
@@ -469,4 +548,8 @@
     BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer();
 }
 
+TEST_F(TransactionSurfaceFrameTest, MultipleCommitsBeforeLatch) {
+    MultipleCommitsBeforeLatch();
+}
+
 } // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
index 106da81..17648d5 100644
--- a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
@@ -101,24 +101,6 @@
     CHECK_REFRESH(1, kLate, kLate);
 }
 
-TEST_F(VsyncModulatorTest, EarlyStartWithEarly) {
-    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
-
-    CHECK_COMMIT(kEarly, kEarly);
-    CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
-
-    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::Early));
-
-    CHECK_COMMIT(kEarly, kEarly);
-    CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly);
-
-    EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd));
-
-    CHECK_COMMIT(kEarly, kEarly);
-    CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly);
-    CHECK_REFRESH(1, kLate, kLate);
-}
-
 TEST_F(VsyncModulatorTest, EarlyStartWithMoreTransactions) {
     EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart));
 
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index 7efd730..ddaa5a1 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -85,6 +85,7 @@
 
     void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
         ASSERT_NE(nullptr, mOutBuffer);
+        ASSERT_NE(nullptr, mPixels);
         ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
         TransactionUtils::expectBufferColor(mOutBuffer, mPixels, rect, color, tolerance);
     }
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index 537e49b..2de2e0e 100644
--- a/services/vibratorservice/VibratorHalController.cpp
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -209,6 +209,18 @@
     return apply(getSupportedPrimitivesFn, "getSupportedPrimitives");
 }
 
+HalResult<float> HalController::getResonantFrequency() {
+    hal_fn<float> getResonantFrequencyFn = [](std::shared_ptr<HalWrapper> hal) {
+        return hal->getResonantFrequency();
+    };
+    return apply(getResonantFrequencyFn, "getResonantFrequency");
+}
+
+HalResult<float> HalController::getQFactor() {
+    hal_fn<float> getQFactorFn = [](std::shared_ptr<HalWrapper> hal) { return hal->getQFactor(); };
+    return apply(getQFactorFn, "getQFactor");
+}
+
 HalResult<milliseconds> HalController::performEffect(
         Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
     hal_fn<milliseconds> performEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 6faab38..3d20fa1 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -205,6 +205,17 @@
                                  mSupportedPrimitives);
 }
 
+HalResult<float> AidlHalWrapper::getResonantFrequency() {
+    std::lock_guard<std::mutex> lock(mResonantFrequencyMutex);
+    return loadCached<float>(std::bind(&AidlHalWrapper::getResonantFrequencyInternal, this),
+                             mResonantFrequency);
+}
+
+HalResult<float> AidlHalWrapper::getQFactor() {
+    std::lock_guard<std::mutex> lock(mQFactorMutex);
+    return loadCached<float>(std::bind(&AidlHalWrapper::getQFactorInternal, this), mQFactor);
+}
+
 HalResult<milliseconds> AidlHalWrapper::performEffect(
         Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
     HalResult<Capabilities> capabilities = getCapabilities();
@@ -283,6 +294,18 @@
     return HalResult<std::vector<CompositePrimitive>>::fromStatus(result, supportedPrimitives);
 }
 
+HalResult<float> AidlHalWrapper::getResonantFrequencyInternal() {
+    float f0 = 0;
+    auto result = getHal()->getResonantFrequency(&f0);
+    return HalResult<float>::fromStatus(result, f0);
+}
+
+HalResult<float> AidlHalWrapper::getQFactorInternal() {
+    float qFactor = 0;
+    auto result = getHal()->getQFactor(&qFactor);
+    return HalResult<float>::fromStatus(result, qFactor);
+}
+
 sp<Aidl::IVibrator> AidlHalWrapper::getHal() {
     std::lock_guard<std::mutex> lock(mHandleMutex);
     return mHandle;
@@ -366,6 +389,18 @@
 }
 
 template <typename I>
+HalResult<float> HidlHalWrapper<I>::getResonantFrequency() {
+    ALOGV("Skipped getResonantFrequency because Vibrator HAL AIDL is not available");
+    return HalResult<float>::unsupported();
+}
+
+template <typename I>
+HalResult<float> HidlHalWrapper<I>::getQFactor() {
+    ALOGV("Skipped getQFactor because Vibrator HAL AIDL is not available");
+    return HalResult<float>::unsupported();
+}
+
+template <typename I>
 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");
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index 16d571d..14ec7b2 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -73,6 +73,9 @@
     HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
             final override;
 
+    HalResult<float> getResonantFrequency() final override;
+    HalResult<float> getQFactor() final override;
+
     HalResult<std::chrono::milliseconds> performEffect(
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
             const std::function<void()>& completionCallback) final override;
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index e22ad34..039a2d9 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -185,6 +185,9 @@
     virtual HalResult<std::vector<hardware::vibrator::CompositePrimitive>>
     getSupportedPrimitives() = 0;
 
+    virtual HalResult<float> getResonantFrequency() = 0;
+    virtual HalResult<float> getQFactor() = 0;
+
     virtual HalResult<std::chrono::milliseconds> performEffect(
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
             const std::function<void()>& completionCallback) = 0;
@@ -232,6 +235,9 @@
     HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
             override final;
 
+    HalResult<float> getResonantFrequency() override final;
+    HalResult<float> getQFactor() override final;
+
     HalResult<std::chrono::milliseconds> performEffect(
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
             const std::function<void()>& completionCallback) override final;
@@ -246,6 +252,8 @@
     std::mutex mCapabilitiesMutex;
     std::mutex mSupportedEffectsMutex;
     std::mutex mSupportedPrimitivesMutex;
+    std::mutex mResonantFrequencyMutex;
+    std::mutex mQFactorMutex;
     sp<hardware::vibrator::IVibrator> mHandle GUARDED_BY(mHandleMutex);
     std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
     std::optional<std::vector<hardware::vibrator::Effect>> mSupportedEffects
@@ -254,6 +262,8 @@
             GUARDED_BY(mSupportedPrimitivesMutex);
     std::vector<std::optional<std::chrono::milliseconds>> mPrimitiveDurations
             GUARDED_BY(mSupportedPrimitivesMutex);
+    std::optional<float> mResonantFrequency GUARDED_BY(mResonantFrequencyMutex);
+    std::optional<float> mQFactor GUARDED_BY(mQFactorMutex);
 
     // Loads and caches from IVibrator.
     HalResult<std::chrono::milliseconds> getPrimitiveDuration(
@@ -263,6 +273,10 @@
     HalResult<Capabilities> getCapabilitiesInternal();
     HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
     HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal();
+
+    HalResult<float> getResonantFrequencyInternal();
+    HalResult<float> getQFactorInternal();
+
     sp<hardware::vibrator::IVibrator> getHal();
 };
 
@@ -293,6 +307,9 @@
     HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
             override final;
 
+    HalResult<float> getResonantFrequency() override final;
+    HalResult<float> getQFactor() override final;
+
     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 c4b39ed..0c39247 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -67,6 +67,10 @@
     MOCK_METHOD(vibrator::HalResult<std::vector<Effect>>, getSupportedEffects, (), (override));
     MOCK_METHOD(vibrator::HalResult<std::vector<CompositePrimitive>>, getSupportedPrimitives, (),
                 (override));
+
+    MOCK_METHOD(vibrator::HalResult<float>, getResonantFrequency, (), (override));
+    MOCK_METHOD(vibrator::HalResult<float>, getQFactor, (), (override));
+
     MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect,
                 (Effect effect, EffectStrength strength,
                  const std::function<void()>& completionCallback),
@@ -106,6 +110,8 @@
                             vibrator::HalResult<vibrator::Capabilities> capabilitiesResult,
                             vibrator::HalResult<std::vector<Effect>> effectsResult,
                             vibrator::HalResult<std::vector<CompositePrimitive>> primitivesResult,
+                            vibrator::HalResult<float> resonantFrequencyResult,
+                            vibrator::HalResult<float> qFactorResult,
                             vibrator::HalResult<milliseconds> durationResult) {
         EXPECT_CALL(*mMockHal.get(), ping())
                 .Times(Exactly(cardinality))
@@ -138,6 +144,12 @@
         EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives())
                 .Times(Exactly(cardinality))
                 .WillRepeatedly(Return(primitivesResult));
+        EXPECT_CALL(*mMockHal.get(), getResonantFrequency())
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(resonantFrequencyResult));
+        EXPECT_CALL(*mMockHal.get(), getQFactor())
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(qFactorResult));
         EXPECT_CALL(*mMockHal.get(), performEffect(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _))
                 .Times(Exactly(cardinality))
                 .WillRepeatedly(Return(durationResult));
@@ -147,7 +159,7 @@
 
         if (cardinality > 1) {
             // One reconnection call after each failure.
-            EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(12 * cardinality));
+            EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(14 * cardinality));
         }
     }
 };
@@ -164,23 +176,21 @@
 }
 
 TEST_F(VibratorHalControllerTest, TestApiCallsAreForwardedToHal) {
-    std::vector<Effect> effects;
-    effects.push_back(Effect::CLICK);
-    effects.push_back(Effect::TICK);
-    std::vector<CompositePrimitive> primitives;
-    primitives.push_back(CompositePrimitive::CLICK);
-    primitives.push_back(CompositePrimitive::THUD);
-    std::vector<CompositeEffect> compositeEffects;
-    compositeEffects.push_back(
-            vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
-    compositeEffects.push_back(
-            vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+    std::vector<Effect> effects = {Effect::CLICK, Effect::TICK};
+    std::vector<CompositePrimitive> primitives = {CompositePrimitive::CLICK,
+                                                  CompositePrimitive::THUD};
+    constexpr float F0 = 123.f;
+    constexpr float Q_FACTOR = 12.f;
+    const std::vector<CompositeEffect> compositeEffects =
+            {vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f),
+             vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f)};
 
     setHalExpectations(/* cardinality= */ 1, compositeEffects, vibrator::HalResult<void>::ok(),
                        vibrator::HalResult<vibrator::Capabilities>::ok(
                                vibrator::Capabilities::ON_CALLBACK),
                        vibrator::HalResult<std::vector<Effect>>::ok(effects),
                        vibrator::HalResult<std::vector<CompositePrimitive>>::ok(primitives),
+                       vibrator::HalResult<float>::ok(F0), vibrator::HalResult<float>::ok(Q_FACTOR),
                        vibrator::HalResult<milliseconds>::ok(100ms));
 
     ASSERT_TRUE(mController->ping().isOk());
@@ -203,6 +213,14 @@
     ASSERT_TRUE(getSupportedPrimitivesResult.isOk());
     ASSERT_EQ(primitives, getSupportedPrimitivesResult.value());
 
+    auto getResonantFrequencyResult = mController->getResonantFrequency();
+    ASSERT_TRUE(getResonantFrequencyResult.isOk());
+    ASSERT_EQ(F0, getResonantFrequencyResult.value());
+
+    auto getQFactorResult = mController->getQFactor();
+    ASSERT_TRUE(getQFactorResult.isOk());
+    ASSERT_EQ(Q_FACTOR, getQFactorResult.value());
+
     auto performEffectResult =
             mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {});
     ASSERT_TRUE(performEffectResult.isOk());
@@ -222,6 +240,8 @@
                        vibrator::HalResult<vibrator::Capabilities>::unsupported(),
                        vibrator::HalResult<std::vector<Effect>>::unsupported(),
                        vibrator::HalResult<std::vector<CompositePrimitive>>::unsupported(),
+                       vibrator::HalResult<float>::unsupported(),
+                       vibrator::HalResult<float>::unsupported(),
                        vibrator::HalResult<milliseconds>::unsupported());
 
     ASSERT_EQ(0, mConnectCounter);
@@ -237,6 +257,8 @@
     ASSERT_TRUE(mController->getCapabilities().isUnsupported());
     ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
     ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
+    ASSERT_TRUE(mController->getResonantFrequency().isUnsupported());
+    ASSERT_TRUE(mController->getQFactor().isUnsupported());
     ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
                         .isUnsupported());
     ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
@@ -251,6 +273,8 @@
                        vibrator::HalResult<vibrator::Capabilities>::failed("message"),
                        vibrator::HalResult<std::vector<Effect>>::failed("message"),
                        vibrator::HalResult<std::vector<CompositePrimitive>>::failed("message"),
+                       vibrator::HalResult<float>::failed("message"),
+                       vibrator::HalResult<float>::failed("message"),
                        vibrator::HalResult<milliseconds>::failed("message"));
 
     ASSERT_EQ(0, mConnectCounter);
@@ -265,6 +289,8 @@
     ASSERT_TRUE(mController->getCapabilities().isFailed());
     ASSERT_TRUE(mController->getSupportedEffects().isFailed());
     ASSERT_TRUE(mController->getSupportedPrimitives().isFailed());
+    ASSERT_TRUE(mController->getResonantFrequency().isFailed());
+    ASSERT_TRUE(mController->getQFactor().isFailed());
     ASSERT_TRUE(
             mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}).isFailed());
     ASSERT_TRUE(
@@ -327,13 +353,15 @@
     ASSERT_TRUE(mController->getCapabilities().isUnsupported());
     ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
     ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
+    ASSERT_TRUE(mController->getResonantFrequency().isUnsupported());
+    ASSERT_TRUE(mController->getQFactor().isUnsupported());
     ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
                         .isUnsupported());
     ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
                         .isUnsupported());
 
     // One connection attempt per api call.
-    ASSERT_EQ(13, mConnectCounter);
+    ASSERT_EQ(15, mConnectCounter);
 }
 
 TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) {
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index 8b5caa5..5d77595 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -413,6 +413,82 @@
     ASSERT_EQ(supportedPrimitives, result.value());
 }
 
+TEST_F(VibratorHalWrapperAidlTest, TestGetResonantFrequencyDoesNotCacheFailedResult) {
+    constexpr float F0 = 123.f;
+    EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
+            .Times(Exactly(3))
+            .WillOnce(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
+
+    ASSERT_TRUE(mWrapper->getResonantFrequency().isUnsupported());
+    ASSERT_TRUE(mWrapper->getResonantFrequency().isFailed());
+
+    auto result = mWrapper->getResonantFrequency();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(F0, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetResonantFrequencyCachesResult) {
+    constexpr float F0 = 123.f;
+    EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
+
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 10; i++) {
+        threads.push_back(std::thread([&]() {
+            auto result = mWrapper->getResonantFrequency();
+            ASSERT_TRUE(result.isOk());
+            ASSERT_EQ(F0, result.value());
+        }));
+    }
+    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+    auto result = mWrapper->getResonantFrequency();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(F0, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetQFactorDoesNotCacheFailedResult) {
+    constexpr float Q_FACTOR = 123.f;
+    EXPECT_CALL(*mMockHal.get(), getQFactor(_))
+            .Times(Exactly(3))
+            .WillOnce(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
+
+    ASSERT_TRUE(mWrapper->getQFactor().isUnsupported());
+    ASSERT_TRUE(mWrapper->getQFactor().isFailed());
+
+    auto result = mWrapper->getQFactor();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(Q_FACTOR, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetQFactorCachesResult) {
+    constexpr float Q_FACTOR = 123.f;
+    EXPECT_CALL(*mMockHal.get(), getQFactor(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
+
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 10; i++) {
+        threads.push_back(std::thread([&]() {
+            auto result = mWrapper->getQFactor();
+            ASSERT_TRUE(result.isOk());
+            ASSERT_EQ(Q_FACTOR, result.value());
+        }));
+    }
+    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+    auto result = mWrapper->getQFactor();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(Q_FACTOR, result.value());
+}
+
 TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) {
     {
         InSequence seq;
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index a513239..438e5dd 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -842,6 +842,8 @@
   bool ret = true;
   switch (device->properties.apiVersion ^
           VK_VERSION_PATCH(device->properties.apiVersion)) {
+    case VK_API_VERSION_1_2:
+      FALLTHROUGH_INTENDED;
     case VK_API_VERSION_1_1:
       ret &=
           visitor->Visit("subgroupProperties", &device->subgroup_properties) &&
@@ -896,6 +898,8 @@
 inline bool Iterate(Visitor* visitor, VkJsonInstance* instance) {
   bool ret = true;
   switch (instance->api_version ^ VK_VERSION_PATCH(instance->api_version)) {
+    case VK_API_VERSION_1_2:
+      FALLTHROUGH_INTENDED;
     case VK_API_VERSION_1_1:
       ret &= visitor->Visit("deviceGroups", &instance->device_groups);
       FALLTHROUGH_INTENDED;
diff --git a/vulkan/vkjson/vkjson_info.cc b/vulkan/vkjson/vkjson_info.cc
deleted file mode 100644
index 3c4b08b..0000000
--- a/vulkan/vkjson/vkjson_info.cc
+++ /dev/null
@@ -1,184 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-//
-// Copyright (c) 2015-2016 The Khronos Group Inc.
-// Copyright (c) 2015-2016 Valve Corporation
-// Copyright (c) 2015-2016 LunarG, Inc.
-// Copyright (c) 2015-2016 Google, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-///////////////////////////////////////////////////////////////////////////////
-
-#ifndef VK_PROTOTYPES
-#define VK_PROTOTYPES
-#endif
-
-#include "vkjson.h"
-
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <iostream>
-#include <vector>
-
-const uint32_t unsignedNegOne = (uint32_t)(-1);
-
-struct Options {
-  bool instance = false;
-  uint32_t device_index = unsignedNegOne;
-  std::string device_name;
-  std::string output_file;
-};
-
-bool ParseOptions(int argc, char* argv[], Options* options) {
-  for (int i = 1; i < argc; ++i) {
-    std::string arg(argv[i]);
-    if (arg == "--instance" || arg == "-i") {
-      options->instance = true;
-    } else if (arg == "--first" || arg == "-f") {
-      options->device_index = 0;
-    } else {
-      ++i;
-      if (i >= argc) {
-        std::cerr << "Missing parameter after: " << arg << std::endl;
-        return false;
-      }
-      std::string arg2(argv[i]);
-      if (arg == "--device-index" || arg == "-d") {
-        int result = sscanf(arg2.c_str(), "%u", &options->device_index);
-        if (result != 1) {
-          options->device_index = static_cast<uint32_t>(-1);
-          std::cerr << "Unable to parse index: " << arg2 << std::endl;
-          return false;
-        }
-      } else if (arg == "--device-name" || arg == "-n") {
-        options->device_name = arg2;
-      } else if (arg == "--output" || arg == "-o") {
-        options->output_file = arg2;
-      } else {
-        std::cerr << "Unknown argument: " << arg << std::endl;
-        return false;
-      }
-    }
-  }
-  if (options->instance && (options->device_index != unsignedNegOne ||
-                            !options->device_name.empty())) {
-    std::cerr << "Specifying a specific device is incompatible with dumping "
-                 "the whole instance." << std::endl;
-    return false;
-  }
-  if (options->device_index != unsignedNegOne && !options->device_name.empty()) {
-    std::cerr << "Must specify only one of device index and device name."
-              << std::endl;
-    return false;
-  }
-  if (options->instance && options->output_file.empty()) {
-    std::cerr << "Must specify an output file when dumping the whole instance."
-              << std::endl;
-    return false;
-  }
-  if (!options->output_file.empty() && !options->instance &&
-      options->device_index == unsignedNegOne && options->device_name.empty()) {
-    std::cerr << "Must specify instance, device index, or device name when "
-                 "specifying "
-                 "output file." << std::endl;
-    return false;
-  }
-  return true;
-}
-
-bool Dump(const VkJsonInstance& instance, const Options& options) {
-  const VkJsonDevice* out_device = nullptr;
-  if (options.device_index != unsignedNegOne) {
-    if (static_cast<uint32_t>(options.device_index) >=
-        instance.devices.size()) {
-      std::cerr << "Error: device " << options.device_index
-                << " requested but only " << instance.devices.size()
-                << " devices found." << std::endl;
-      return false;
-    }
-    out_device = &instance.devices[options.device_index];
-  } else if (!options.device_name.empty()) {
-    for (const auto& device : instance.devices) {
-      if (device.properties.deviceName == options.device_name) {
-        out_device = &device;
-      }
-    }
-    if (!out_device) {
-      std::cerr << "Error: device '" << options.device_name
-                << "' requested but not found." << std::endl;
-      return false;
-    }
-  }
-
-  std::string output_file;
-  if (options.output_file.empty()) {
-    assert(out_device);
-#if defined(ANDROID)
-    output_file.assign("/sdcard/Android/" + std::string(out_device->properties.deviceName));
-#else
-    output_file.assign(out_device->properties.deviceName);
-#endif
-    output_file.append(".json");
-  } else {
-    output_file = options.output_file;
-  }
-  FILE* file = nullptr;
-  if (output_file == "-") {
-    file = stdout;
-  } else {
-    file = fopen(output_file.c_str(), "w");
-    if (!file) {
-      std::cerr << "Unable to open file " << output_file << "." << std::endl;
-      return false;
-    }
-  }
-
-  std::string json = out_device ? VkJsonDeviceToJson(*out_device)
-                                : VkJsonInstanceToJson(instance);
-  fwrite(json.data(), 1, json.size(), file);
-  fputc('\n', file);
-
-  if (output_file != "-") {
-    fclose(file);
-    std::cout << "Wrote file " << output_file;
-    if (out_device)
-      std::cout << " for device " << out_device->properties.deviceName;
-    std::cout << "." << std::endl;
-  }
-  return true;
-}
-
-int main(int argc, char* argv[]) {
-#if defined(ANDROID)
-  int vulkanSupport = InitVulkan();
-  if (vulkanSupport == 0)
-    return 1;
-#endif
-  Options options;
-  if (!ParseOptions(argc, argv, &options))
-    return 1;
-
-  VkJsonInstance instance = VkJsonGetInstance();
-  if (options.instance || options.device_index != unsignedNegOne ||
-      !options.device_name.empty()) {
-    Dump(instance, options);
-  } else {
-    for (uint32_t i = 0, n = static_cast<uint32_t>(instance.devices.size()); i < n; i++) {
-      options.device_index = i;
-      Dump(instance, options);
-    }
-  }
-
-  return 0;
-}
diff --git a/vulkan/vkjson/vkjson_unittest.cc b/vulkan/vkjson/vkjson_unittest.cc
deleted file mode 100644
index de765cd..0000000
--- a/vulkan/vkjson/vkjson_unittest.cc
+++ /dev/null
@@ -1,101 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-//
-// Copyright (c) 2015-2016 The Khronos Group Inc.
-// Copyright (c) 2015-2016 Valve Corporation
-// Copyright (c) 2015-2016 LunarG, Inc.
-// Copyright (c) 2015-2016 Google, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-///////////////////////////////////////////////////////////////////////////////
-
-#include "vkjson.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <iostream>
-
-#define EXPECT(X) if (!(X)) \
-  ReportFailure(__FILE__, __LINE__, #X);
-
-#define ASSERT(X) if (!(X)) { \
-  ReportFailure(__FILE__, __LINE__, #X); \
-  return 2; \
-}
-
-int g_failures;
-
-void ReportFailure(const char* file, int line, const char* assertion) {
-  std::cout << file << ":" << line << ": \"" << assertion << "\" failed."
-            << std::endl;
-  ++g_failures;
-}
-
-int main(int argc, char* argv[]) {
-  std::string errors;
-  bool result = false;
-
-  VkJsonInstance instance;
-  instance.devices.resize(1);
-  VkJsonDevice& device = instance.devices[0];
-
-  const char name[] = "Test device";
-  memcpy(device.properties.deviceName, name, sizeof(name));
-  device.properties.limits.maxImageDimension1D = 3;
-  device.properties.limits.maxSamplerLodBias = 3.5f;
-  device.properties.limits.bufferImageGranularity = 0x1ffffffffull;
-  device.properties.limits.maxViewportDimensions[0] = 1;
-  device.properties.limits.maxViewportDimensions[1] = 2;
-  VkFormatProperties format_props = {
-      VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT,
-      VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT,
-      VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT};
-  device.formats.insert(std::make_pair(VK_FORMAT_R8_UNORM, format_props));
-  device.formats.insert(std::make_pair(VK_FORMAT_R8G8_UNORM, format_props));
-
-  std::string json = VkJsonInstanceToJson(instance);
-  std::cout << json << std::endl;
-
-  VkJsonInstance instance2;
-  result = VkJsonInstanceFromJson(json, &instance2, &errors);
-  EXPECT(result);
-  if (!result)
-    std::cout << "Error: " << errors << std::endl;
-  const VkJsonDevice& device2 = instance2.devices.at(0);
-
-  EXPECT(!memcmp(&device.properties, &device2.properties,
-                 sizeof(device.properties)));
-  for (auto& kv : device.formats) {
-    auto it = device2.formats.find(kv.first);
-    EXPECT(it != device2.formats.end());
-    EXPECT(!memcmp(&kv.second, &it->second, sizeof(kv.second)));
-  }
-
-  VkImageFormatProperties props = {};
-  json = VkJsonImageFormatPropertiesToJson(props);
-  VkImageFormatProperties props2 = {};
-  result = VkJsonImageFormatPropertiesFromJson(json, &props2, &errors);
-  EXPECT(result);
-  if (!result)
-    std::cout << "Error: " << errors << std::endl;
-
-  EXPECT(!memcmp(&props, &props2, sizeof(props)));
-
-  if (g_failures) {
-    std::cout << g_failures << " failures." << std::endl;
-    return 1;
-  } else {
-    std::cout << "Success." << std::endl;
-    return 0;
-  }
-}