Merge "Fix broken FrameLifecycle due to Blast adapter" into sc-dev
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index 3a3df08..01f7d30 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -19,7 +19,7 @@
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 #include <gui/SurfaceComposerClient.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
 
 namespace android {
 
@@ -227,15 +227,15 @@
         return false;
     }
 
-    DisplayConfig config;
-    status_t err = mSurfaceComposerClient->getActiveDisplayConfig(dpy, &config);
+    ui::DisplayMode mode;
+    status_t err = mSurfaceComposerClient->getActiveDisplayMode(dpy, &mode);
     if (err != NO_ERROR) {
-        fprintf(stderr, "SurfaceComposer::getActiveDisplayConfig failed: %#x\n", err);
+        fprintf(stderr, "SurfaceComposer::getActiveDisplayMode failed: %#x\n", err);
         return false;
     }
 
-    float scaleX = static_cast<float>(config.resolution.getWidth()) / w;
-    float scaleY = static_cast<float>(config.resolution.getHeight()) / h;
+    float scaleX = static_cast<float>(mode.resolution.getWidth()) / w;
+    float scaleY = static_cast<float>(mode.resolution.getHeight()) / h;
     *scale = scaleX < scaleY ? scaleX : scaleY;
 
     return true;
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index 2362c9e..a70dffd 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -31,18 +31,8 @@
 #include <stddef.h>
 #include <jni.h>
 
-#ifndef __ANDROID__
-    // Value copied from 'bionic/libc/include/android/api-level.h' which is not available on
-    // non Android systems. It is set to 10000 which is same as __ANDROID_API_FUTURE__ value.
-    #ifndef __ANDROID_API__
-        #define __ANDROID_API__ 10000
-    #endif
-
-    // Value copied from 'bionic/libc/include/android/versioning.h' which is not available on
-    // non Android systems
-    #ifndef __INTRODUCED_IN
-        #define __INTRODUCED_IN(api_level)
-    #endif
+#if !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(__api_level) /* nothing */
 #endif
 
 #ifdef __cplusplus
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index 819a6a4..cac67d4 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -51,18 +51,8 @@
 #include <android/rect.h>
 #include <stdint.h>
 
-#ifndef __ANDROID__
-    // Value copied from 'bionic/libc/include/android/api-level.h' which is not available on
-    // non Android systems. It is set to 10000 which is same as __ANDROID_API_FUTURE__ value.
-    #ifndef __ANDROID_API__
-        #define __ANDROID_API__ 10000
-    #endif
-
-    // Value copied from 'bionic/libc/include/android/versioning.h' which is not available on
-    // non Android systems
-    #ifndef __INTRODUCED_IN
-        #define __INTRODUCED_IN(api_level)
-    #endif
+#if !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(__api_level) /* nothing */
 #endif
 
 #ifdef __cplusplus
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 8744ef7..11b714f 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -30,6 +30,7 @@
  */
 
 #include <string>
+#include <unordered_map>
 
 #include <android-base/chrono_utils.h>
 
@@ -155,6 +156,7 @@
         struct Finished {
             uint32_t empty1;
             uint32_t handled; // actually a bool, but we must maintain 8-byte alignment
+            nsecs_t consumeTime; // The time when the event was consumed by the receiving end
 
             inline size_t size() const { return sizeof(Finished); }
         } finished;
@@ -362,7 +364,8 @@
 
     /* Receives the finished signal from the consumer in reply to the original dispatch signal.
      * If a signal was received, returns the message sequence number,
-     * and whether the consumer handled the message.
+     * 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.
      *
@@ -371,7 +374,8 @@
      * Returns DEAD_OBJECT if the channel's peer has been closed.
      * Other errors probably indicate that the channel is broken.
      */
-    status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled);
+    status_t receiveFinishedSignal(
+            const std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)>& callback);
 
 private:
     std::shared_ptr<InputChannel> mChannel;
@@ -577,6 +581,13 @@
     };
     std::vector<SeqChain> mSeqChains;
 
+    // The time at which each event with the sequence number 'seq' was consumed.
+    // This data is provided in 'finishInputEvent' so that the receiving end can measure the latency
+    // This collection is populated when the event is received, and the entries are erased when the
+    // events are finished. It should not grow infinitely because if an event is not ack'd, ANR
+    // will be raised for that connection, and no further events will be posted to that channel.
+    std::unordered_map<uint32_t /*seq*/, nsecs_t /*consumeTime*/> mConsumeTimes;
+
     status_t consumeBatch(InputEventFactoryInterface* factory,
             nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
     status_t consumeSamples(InputEventFactoryInterface* factory,
@@ -589,6 +600,8 @@
     ssize_t findBatch(int32_t deviceId, int32_t source) const;
     ssize_t findTouchState(int32_t deviceId, int32_t source) const;
 
+    nsecs_t getConsumeTime(uint32_t seq) const;
+    void popConsumeTime(uint32_t seq);
     status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled);
 
     static void rewriteMessage(TouchState& state, InputMessage& msg);
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index dc8d74c..889e15a 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -101,4 +101,11 @@
      * This does nothing if this observer was not already registered.
      */
     void unregisterPackageChangeObserver(in IPackageChangeObserver observer);
+
+    /**
+     * Returns true if the package has the SHA 256 version of the signing certificate.
+     * @see PackageManager#hasSigningCertificate(String, byte[], int), where type
+     * has been set to {@link PackageManager#CERT_INPUT_SHA256}.
+     */
+    boolean hasSha256SigningCertificate(in @utf8InCpp String packageName, in byte[] certificate);
 }
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index bb70588..a44c261 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -41,8 +41,6 @@
  android platform host build, you must use libbinder_ndk_host_user.
 #endif
 
-#if __ANDROID_API__ >= 29
-
 typedef uint32_t binder_flags_t;
 enum {
     /**
@@ -567,10 +565,6 @@
  */
 void AIBinder_DeathRecipient_delete(AIBinder_DeathRecipient* recipient) __INTRODUCED_IN(29);
 
-#endif  //__ANDROID_API__ >= 29
-
-#if __ANDROID_API__ >= 30
-
 /**
  * Gets the extension registered with AIBinder_setExtension.
  *
@@ -640,10 +634,6 @@
  */
 binder_status_t AIBinder_setExtension(AIBinder* binder, AIBinder* ext) __INTRODUCED_IN(30);
 
-#endif  //__ANDROID_API__ >= 30
-
-#if __ANDROID_API__ >= 31
-
 /**
  * Retrieve the class descriptor for the class.
  *
@@ -728,8 +718,6 @@
  */
 bool AIBinder_Weak_lt(const AIBinder_Weak* lhs, const AIBinder_Weak* rhs);
 
-#endif  //__ANDROID_API__ >= 31
-
 __END_DECLS
 
 /** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
index cd1ff1f..6880d86 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
@@ -31,7 +31,6 @@
 #include <jni.h>
 
 __BEGIN_DECLS
-#if __ANDROID_API__ >= 29
 
 /**
  * Converts an android.os.IBinder object into an AIBinder* object.
@@ -67,7 +66,6 @@
 __attribute__((warn_unused_result)) jobject AIBinder_toJavaBinder(JNIEnv* env, AIBinder* binder)
         __INTRODUCED_IN(29);
 
-#endif  //__ANDROID_API__ >= 29
 __END_DECLS
 
 /** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index ab67017..527b151 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -36,7 +36,6 @@
 typedef struct AIBinder AIBinder;
 
 __BEGIN_DECLS
-#if __ANDROID_API__ >= 29
 
 /**
  * This object represents a package of data that can be sent between processes. When transacting, an
@@ -1119,7 +1118,6 @@
 
 // @END-PRIMITIVE-READ-WRITE
 
-#endif  //__ANDROID_API__ >= 29
 /**
  * Reset the parcel to the initial status.
  *
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h b/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h
index 65e1704..384d4f7 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h
@@ -31,7 +31,6 @@
 #include <jni.h>
 
 __BEGIN_DECLS
-#if __ANDROID_API__ >= 30
 
 /**
  * Converts an android.os.Parcel object into an AParcel* object.
@@ -50,7 +49,6 @@
 __attribute__((warn_unused_result)) AParcel* AParcel_fromJavaParcel(JNIEnv* env, jobject parcel)
         __INTRODUCED_IN(30);
 
-#endif  //__ANDROID_API__ >= 30
 __END_DECLS
 
 /** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index 3a55f94..05b25e7 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -31,7 +31,6 @@
 #include <sys/cdefs.h>
 
 __BEGIN_DECLS
-#if __ANDROID_API__ >= 29
 
 enum {
     STATUS_OK = 0,
@@ -275,7 +274,6 @@
  */
 void AStatus_delete(AStatus* status) __INTRODUCED_IN(29);
 
-#endif  //__ANDROID_API__ >= 29
 __END_DECLS
 
 /** @} */
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 42c1e0a..d53a88f 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -16,12 +16,17 @@
 
 //! Trait definitions for binder objects
 
-use crate::error::{status_t, Result};
+use crate::error::{status_t, Result, StatusCode};
 use crate::parcel::Parcel;
-use crate::proxy::{DeathRecipient, SpIBinder};
+use crate::proxy::{DeathRecipient, SpIBinder, WpIBinder};
 use crate::sys;
 
+use std::borrow::Borrow;
+use std::cmp::Ordering;
 use std::ffi::{c_void, CStr, CString};
+use std::fmt;
+use std::marker::PhantomData;
+use std::ops::Deref;
 use std::os::raw::c_char;
 use std::os::unix::io::AsRawFd;
 use std::ptr;
@@ -44,7 +49,7 @@
 /// interfaces) must implement this trait.
 ///
 /// This is equivalent `IInterface` in C++.
-pub trait Interface {
+pub trait Interface: Send {
     /// Convert this binder object into a generic [`SpIBinder`] reference.
     fn as_binder(&self) -> SpIBinder {
         panic!("This object was not a Binder object and cannot be converted into an SpIBinder.")
@@ -230,6 +235,132 @@
     }
 }
 
+/// Strong reference to a binder object
+pub struct Strong<I: FromIBinder + ?Sized>(Box<I>);
+
+impl<I: FromIBinder + ?Sized> Strong<I> {
+    /// Create a new strong reference to the provided binder object
+    pub fn new(binder: Box<I>) -> Self {
+        Self(binder)
+    }
+
+    /// Construct a new weak reference to this binder
+    pub fn downgrade(this: &Strong<I>) -> Weak<I> {
+        Weak::new(this)
+    }
+}
+
+impl<I: FromIBinder + ?Sized> Clone for Strong<I> {
+    fn clone(&self) -> Self {
+        // Since we hold a strong reference, we should always be able to create
+        // a new strong reference to the same interface type, so try_from()
+        // should never fail here.
+        FromIBinder::try_from(self.0.as_binder()).unwrap()
+    }
+}
+
+impl<I: FromIBinder + ?Sized> Borrow<I> for Strong<I> {
+    fn borrow(&self) -> &I {
+        &self.0
+    }
+}
+
+impl<I: FromIBinder + ?Sized> AsRef<I> for Strong<I> {
+    fn as_ref(&self) -> &I {
+        &self.0
+    }
+}
+
+impl<I: FromIBinder + ?Sized> Deref for Strong<I> {
+    type Target = I;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<I: FromIBinder + fmt::Debug + ?Sized> fmt::Debug for Strong<I> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Debug::fmt(&**self, f)
+    }
+}
+
+impl<I: FromIBinder + ?Sized> Ord for Strong<I> {
+    fn cmp(&self, other: &Self) -> Ordering {
+        self.0.as_binder().cmp(&other.0.as_binder())
+    }
+}
+
+impl<I: FromIBinder + ?Sized> PartialOrd for Strong<I> {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        self.0.as_binder().partial_cmp(&other.0.as_binder())
+    }
+}
+
+impl<I: FromIBinder + ?Sized> PartialEq for Strong<I> {
+    fn eq(&self, other: &Self) -> bool {
+        self.0.as_binder().eq(&other.0.as_binder())
+    }
+}
+
+impl<I: FromIBinder + ?Sized> Eq for Strong<I> {}
+
+/// Weak reference to a binder object
+#[derive(Debug)]
+pub struct Weak<I: FromIBinder + ?Sized> {
+    weak_binder: WpIBinder,
+    interface_type: PhantomData<I>,
+}
+
+impl<I: FromIBinder + ?Sized> Weak<I> {
+    /// Construct a new weak reference from a strong reference
+    fn new(binder: &Strong<I>) -> Self {
+        let weak_binder = binder.as_binder().downgrade();
+        Weak {
+            weak_binder,
+            interface_type: PhantomData,
+        }
+    }
+
+    /// Upgrade this weak reference to a strong reference if the binder object
+    /// is still alive
+    pub fn upgrade(&self) -> Result<Strong<I>> {
+        self.weak_binder
+            .promote()
+            .ok_or(StatusCode::DEAD_OBJECT)
+            .and_then(FromIBinder::try_from)
+    }
+}
+
+impl<I: FromIBinder + ?Sized> Clone for Weak<I> {
+    fn clone(&self) -> Self {
+        Self {
+            weak_binder: self.weak_binder.clone(),
+            interface_type: PhantomData,
+        }
+    }
+}
+
+impl<I: FromIBinder + ?Sized> Ord for Weak<I> {
+    fn cmp(&self, other: &Self) -> Ordering {
+        self.weak_binder.cmp(&other.weak_binder)
+    }
+}
+
+impl<I: FromIBinder + ?Sized> PartialOrd for Weak<I> {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        self.weak_binder.partial_cmp(&other.weak_binder)
+    }
+}
+
+impl<I: FromIBinder + ?Sized> PartialEq for Weak<I> {
+    fn eq(&self, other: &Self) -> bool {
+        self.weak_binder == other.weak_binder
+    }
+}
+
+impl<I: FromIBinder + ?Sized> Eq for Weak<I> {}
+
 /// Create a function implementing a static getter for an interface class.
 ///
 /// Each binder interface (i.e. local [`Remotable`] service or remote proxy
@@ -354,12 +485,12 @@
 ///     }
 /// }
 /// ```
-pub trait FromIBinder {
+pub trait FromIBinder: Interface {
     /// Try to interpret a generic Binder object as this interface.
     ///
     /// Returns a trait object for the `Self` interface if this object
     /// implements that interface.
-    fn try_from(ibinder: SpIBinder) -> Result<Box<Self>>;
+    fn try_from(ibinder: SpIBinder) -> Result<Strong<Self>>;
 }
 
 /// Trait for transparent Rust wrappers around android C++ native types.
@@ -534,8 +665,9 @@
 
         impl $native {
             /// Create a new binder service.
-            pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T) -> impl $interface {
-                $crate::Binder::new($native(Box::new(inner)))
+            pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T) -> $crate::Strong<dyn $interface> {
+                let binder = $crate::Binder::new($native(Box::new(inner)));
+                $crate::Strong::new(Box::new(binder))
             }
         }
 
@@ -577,7 +709,7 @@
         }
 
         impl $crate::FromIBinder for dyn $interface {
-            fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<Box<dyn $interface>> {
+            fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $interface>> {
                 use $crate::AssociateClass;
 
                 let existing_class = ibinder.get_class();
@@ -590,7 +722,7 @@
                         // associated object as remote, because we can't cast it
                         // into a Rust service object without a matching class
                         // pointer.
-                        return Ok(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?));
+                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
                     }
                 }
 
@@ -600,10 +732,10 @@
                     if let Ok(service) = service {
                         // We were able to associate with our expected class and
                         // the service is local.
-                        return Ok(Box::new(service));
+                        return Ok($crate::Strong::new(Box::new(service)));
                     } else {
                         // Service is remote
-                        return Ok(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?));
+                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
                     }
                 }
 
@@ -633,9 +765,9 @@
             }
         }
 
-        // Convert a &dyn $interface to Box<dyn $interface>
+        /// Convert a &dyn $interface to Strong<dyn $interface>
         impl std::borrow::ToOwned for dyn $interface {
-            type Owned = Box<dyn $interface>;
+            type Owned = $crate::Strong<dyn $interface>;
             fn to_owned(&self) -> Self::Owned {
                 self.as_binder().into_interface()
                     .expect(concat!("Error cloning interface ", stringify!($interface)))
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index edfb56a..43a237a 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -107,7 +107,8 @@
 pub mod parcel;
 
 pub use crate::binder::{
-    FromIBinder, IBinder, Interface, InterfaceClass, Remotable, TransactionCode, TransactionFlags,
+    FromIBinder, IBinder, Interface, InterfaceClass, Remotable, Strong, TransactionCode,
+    TransactionFlags, Weak,
 };
 pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
 pub use native::add_service;
@@ -122,7 +123,8 @@
     pub use super::parcel::ParcelFileDescriptor;
     pub use super::{add_service, get_interface};
     pub use super::{
-        ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode, WpIBinder,
+        ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode, Strong, ThreadState,
+        Weak, WpIBinder,
     };
 
     /// Binder result containing a [`Status`] on error.
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 8d18fb4..f57788b 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-use crate::binder::{AsNative, FromIBinder};
+use crate::binder::{AsNative, FromIBinder, Strong};
 use crate::error::{status_result, status_t, Result, Status, StatusCode};
 use crate::parcel::Parcel;
 use crate::proxy::SpIBinder;
@@ -628,26 +628,26 @@
     }
 }
 
-impl<T: Serialize + ?Sized> Serialize for Box<T> {
+impl<T: Serialize + FromIBinder + ?Sized> Serialize for Strong<T> {
     fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
         Serialize::serialize(&**self, parcel)
     }
 }
 
-impl<T: SerializeOption + ?Sized> SerializeOption for Box<T> {
+impl<T: SerializeOption + FromIBinder + ?Sized> SerializeOption for Strong<T> {
     fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
         SerializeOption::serialize_option(this.map(|b| &**b), parcel)
     }
 }
 
-impl<T: FromIBinder + ?Sized> Deserialize for Box<T> {
+impl<T: FromIBinder + ?Sized> Deserialize for Strong<T> {
     fn deserialize(parcel: &Parcel) -> Result<Self> {
         let ibinder: SpIBinder = parcel.read()?;
         FromIBinder::try_from(ibinder)
     }
 }
 
-impl<T: FromIBinder + ?Sized> DeserializeOption for Box<T> {
+impl<T: FromIBinder + ?Sized> DeserializeOption for Strong<T> {
     fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
         let ibinder: Option<SpIBinder> = parcel.read()?;
         ibinder.map(FromIBinder::try_from).transpose()
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index e9e74c0..132e075 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -17,7 +17,7 @@
 //! Rust API for interacting with a remote binder service.
 
 use crate::binder::{
-    AsNative, FromIBinder, IBinder, Interface, InterfaceClass, TransactionCode, TransactionFlags,
+    AsNative, FromIBinder, IBinder, Interface, InterfaceClass, Strong, TransactionCode, TransactionFlags,
 };
 use crate::error::{status_result, Result, StatusCode};
 use crate::parcel::{
@@ -27,6 +27,7 @@
 use crate::sys;
 
 use std::convert::TryInto;
+use std::cmp::Ordering;
 use std::ffi::{c_void, CString};
 use std::fmt;
 use std::os::unix::io::AsRawFd;
@@ -99,7 +100,7 @@
     ///
     /// If this object does not implement the expected interface, the error
     /// `StatusCode::BAD_TYPE` is returned.
-    pub fn into_interface<I: FromIBinder + ?Sized>(self) -> Result<Box<I>> {
+    pub fn into_interface<I: FromIBinder + Interface + ?Sized>(self) -> Result<Strong<I>> {
         FromIBinder::try_from(self)
     }
 
@@ -148,6 +149,36 @@
     }
 }
 
+impl Ord for SpIBinder {
+    fn cmp(&self, other: &Self) -> Ordering {
+        let less_than = unsafe {
+            // Safety: SpIBinder always holds a valid `AIBinder` pointer, so
+            // this pointer is always safe to pass to `AIBinder_lt` (null is
+            // also safe to pass to this function, but we should never do that).
+            sys::AIBinder_lt(self.0, other.0)
+        };
+        let greater_than = unsafe {
+            // Safety: SpIBinder always holds a valid `AIBinder` pointer, so
+            // this pointer is always safe to pass to `AIBinder_lt` (null is
+            // also safe to pass to this function, but we should never do that).
+            sys::AIBinder_lt(other.0, self.0)
+        };
+        if !less_than && !greater_than {
+            Ordering::Equal
+        } else if less_than {
+            Ordering::Less
+        } else {
+            Ordering::Greater
+        }
+    }
+}
+
+impl PartialOrd for SpIBinder {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
 impl PartialEq for SpIBinder {
     fn eq(&self, other: &Self) -> bool {
         ptr::eq(self.0, other.0)
@@ -326,7 +357,7 @@
             // Safety: `SpIBinder` guarantees that `self` always contains a
             // valid pointer to an `AIBinder`. `recipient` can always be
             // converted into a valid pointer to an
-            // `AIBinder_DeatRecipient`. Any value is safe to pass as the
+            // `AIBinder_DeathRecipient`. Any value is safe to pass as the
             // cookie, although we depend on this value being set by
             // `get_cookie` when the death recipient callback is called.
             sys::AIBinder_linkToDeath(
@@ -342,7 +373,7 @@
             // Safety: `SpIBinder` guarantees that `self` always contains a
             // valid pointer to an `AIBinder`. `recipient` can always be
             // converted into a valid pointer to an
-            // `AIBinder_DeatRecipient`. Any value is safe to pass as the
+            // `AIBinder_DeathRecipient`. Any value is safe to pass as the
             // cookie, although we depend on this value being set by
             // `get_cookie` when the death recipient callback is called.
             sys::AIBinder_unlinkToDeath(
@@ -430,6 +461,62 @@
     }
 }
 
+impl Clone for WpIBinder {
+    fn clone(&self) -> Self {
+        let ptr = unsafe {
+            // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer,
+            // so this pointer is always safe to pass to `AIBinder_Weak_clone`
+            // (although null is also a safe value to pass to this API).
+            //
+            // We get ownership of the returned pointer, so can construct a new
+            // WpIBinder object from it.
+            sys::AIBinder_Weak_clone(self.0)
+        };
+        assert!(!ptr.is_null(), "Unexpected null pointer from AIBinder_Weak_clone");
+        Self(ptr)
+    }
+}
+
+impl Ord for WpIBinder {
+    fn cmp(&self, other: &Self) -> Ordering {
+        let less_than = unsafe {
+            // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer,
+            // so this pointer is always safe to pass to `AIBinder_Weak_lt`
+            // (null is also safe to pass to this function, but we should never
+            // do that).
+            sys::AIBinder_Weak_lt(self.0, other.0)
+        };
+        let greater_than = unsafe {
+            // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer,
+            // so this pointer is always safe to pass to `AIBinder_Weak_lt`
+            // (null is also safe to pass to this function, but we should never
+            // do that).
+            sys::AIBinder_Weak_lt(other.0, self.0)
+        };
+        if !less_than && !greater_than {
+            Ordering::Equal
+        } else if less_than {
+            Ordering::Less
+        } else {
+            Ordering::Greater
+        }
+    }
+}
+
+impl PartialOrd for WpIBinder {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl PartialEq for WpIBinder {
+    fn eq(&self, other: &Self) -> bool {
+        self.cmp(other) == Ordering::Equal
+    }
+}
+
+impl Eq for WpIBinder {}
+
 impl Drop for WpIBinder {
     fn drop(&mut self) {
         unsafe {
@@ -564,7 +651,7 @@
 
 /// Retrieve an existing service for a particular interface, blocking for a few
 /// seconds if it doesn't yet exist.
-pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Box<T>> {
+pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
     let service = get_service(name);
     match service {
         Some(service) => FromIBinder::try_from(service),
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index bb8c492..719229c 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -209,7 +209,7 @@
     use std::thread;
     use std::time::Duration;
 
-    use binder::{Binder, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode};
+    use binder::{Binder, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode, Strong};
 
     use super::{BnTest, ITest, ITestSameDescriptor, RUST_SERVICE_BINARY, TestService};
 
@@ -271,7 +271,7 @@
     fn trivial_client() {
         let service_name = "trivial_client_test";
         let _process = ScopedServiceProcess::new(service_name);
-        let test_client: Box<dyn ITest> =
+        let test_client: Strong<dyn ITest> =
             binder::get_interface(service_name).expect("Did not get manager binder service");
         assert_eq!(test_client.test().unwrap(), "trivial_client_test");
     }
@@ -280,7 +280,7 @@
     fn get_selinux_context() {
         let service_name = "get_selinux_context";
         let _process = ScopedServiceProcess::new(service_name);
-        let test_client: Box<dyn ITest> =
+        let test_client: Strong<dyn ITest> =
             binder::get_interface(service_name).expect("Did not get manager binder service");
         let expected_context = unsafe {
             let mut out_ptr = ptr::null_mut();
@@ -453,7 +453,7 @@
 
             let extension = maybe_extension.expect("Remote binder did not have an extension");
 
-            let extension: Box<dyn ITest> = FromIBinder::try_from(extension)
+            let extension: Strong<dyn ITest> = FromIBinder::try_from(extension)
                 .expect("Extension could not be converted to the expected interface");
 
             assert_eq!(extension.test().unwrap(), extension_name);
@@ -479,7 +479,7 @@
 
         // This should succeed although we will have to treat the service as
         // remote.
-        let _interface: Box<dyn ITestSameDescriptor> = FromIBinder::try_from(service.as_binder())
+        let _interface: Strong<dyn ITestSameDescriptor> = FromIBinder::try_from(service.as_binder())
             .expect("Could not re-interpret service as the ITestSameDescriptor interface");
     }
 
@@ -490,9 +490,60 @@
         let service_ibinder = BnTest::new_binder(TestService { s: service_name.to_string() })
             .as_binder();
 
-        let service: Box<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);
     }
+
+    #[test]
+    fn weak_binder_upgrade() {
+        let service_name = "testing_service";
+        let service = BnTest::new_binder(TestService { s: service_name.to_string() });
+
+        let weak = Strong::downgrade(&service);
+
+        let upgraded = weak.upgrade().expect("Could not upgrade weak binder");
+
+        assert_eq!(service, upgraded);
+    }
+
+    #[test]
+    fn weak_binder_upgrade_dead() {
+        let service_name = "testing_service";
+        let weak = {
+            let service = BnTest::new_binder(TestService { s: service_name.to_string() });
+
+            Strong::downgrade(&service)
+        };
+
+        assert_eq!(weak.upgrade(), Err(StatusCode::DEAD_OBJECT));
+    }
+
+    #[test]
+    fn weak_binder_clone() {
+        let service_name = "testing_service";
+        let service = BnTest::new_binder(TestService { s: service_name.to_string() });
+
+        let weak = Strong::downgrade(&service);
+        let cloned = weak.clone();
+        assert_eq!(weak, cloned);
+
+        let upgraded = weak.upgrade().expect("Could not upgrade weak binder");
+        let clone_upgraded = cloned.upgrade().expect("Could not upgrade weak binder");
+
+        assert_eq!(service, upgraded);
+        assert_eq!(service, clone_upgraded);
+    }
+
+    #[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() });
+
+        assert!(!(service1 < service1));
+        assert!(!(service1 > service1));
+        assert_eq!(service1 < service2, !(service2 < service1));
+    }
 }
diff --git a/libs/binder/rust/tests/ndk_rust_interop.rs b/libs/binder/rust/tests/ndk_rust_interop.rs
index 70a6dc0..ce75ab7 100644
--- a/libs/binder/rust/tests/ndk_rust_interop.rs
+++ b/libs/binder/rust/tests/ndk_rust_interop.rs
@@ -37,7 +37,7 @@
 
     // The Rust class descriptor pointer will not match the NDK one, but the
     // descriptor strings match so this needs to still associate.
-    let service: Box<dyn IBinderRustNdkInteropTest> = match binder::get_interface(service_name) {
+    let service: binder::Strong<dyn IBinderRustNdkInteropTest> = match binder::get_interface(service_name) {
         Err(e) => {
             eprintln!("Could not find Ndk service {}: {:?}", service_name, e);
             return StatusCode::NAME_NOT_FOUND as c_int;
@@ -53,7 +53,7 @@
     }
 
     // Try using the binder service through the wrong interface type
-    let wrong_service: Result<Box<dyn IBinderRustNdkInteropTestOther>, StatusCode> =
+    let wrong_service: Result<binder::Strong<dyn IBinderRustNdkInteropTestOther>, StatusCode> =
         binder::get_interface(service_name);
     match wrong_service {
         Err(e) if e == StatusCode::BAD_TYPE => {}
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index c2ec0fe..1e6fc2b 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -133,8 +133,10 @@
     if (enableTripleBuffering) {
         mProducer->setMaxDequeuedBufferCount(2);
     }
-    mBufferItemConsumer =
-        new BLASTBufferItemConsumer(mConsumer, GraphicBuffer::USAGE_HW_COMPOSER, 1, false);
+    mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
+                                                      GraphicBuffer::USAGE_HW_COMPOSER |
+                                                              GraphicBuffer::USAGE_HW_TEXTURE,
+                                                      1, false);
     static int32_t id = 0;
     auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id);
     id++;
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 2ad484a..9cd3f63 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -36,9 +36,7 @@
 DisplayEventDispatcher::DisplayEventDispatcher(
         const sp<Looper>& looper, ISurfaceComposer::VsyncSource vsyncSource,
         ISurfaceComposer::EventRegistrationFlags eventRegistration)
-      : mLooper(looper),
-        mReceiver(vsyncSource, eventRegistration),
-        mVsyncState(VsyncState::Unregistered) {
+      : mLooper(looper), mReceiver(vsyncSource, eventRegistration), mWaitingForVsync(false) {
     ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
 }
 
@@ -68,37 +66,26 @@
 }
 
 status_t DisplayEventDispatcher::scheduleVsync() {
-    switch (mVsyncState) {
-        case VsyncState::Unregistered: {
-            ALOGV("dispatcher %p ~ Scheduling vsync.", this);
+    if (!mWaitingForVsync) {
+        ALOGV("dispatcher %p ~ Scheduling vsync.", this);
 
-            // Drain all pending events.
-            nsecs_t vsyncTimestamp;
-            PhysicalDisplayId vsyncDisplayId;
-            uint32_t vsyncCount;
-            VsyncEventData vsyncEventData;
-            if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount,
-                                     &vsyncEventData)) {
-                ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "",
-                      this, ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
-            }
+        // Drain all pending events.
+        nsecs_t vsyncTimestamp;
+        PhysicalDisplayId vsyncDisplayId;
+        uint32_t vsyncCount;
+        VsyncEventData vsyncEventData;
+        if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncEventData)) {
+            ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", this,
+                  ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
+        }
 
-            status_t status = mReceiver.setVsyncRate(1);
-            if (status) {
-                ALOGW("Failed to set vsync rate, status=%d", status);
-                return status;
-            }
+        status_t status = mReceiver.requestNextVsync();
+        if (status) {
+            ALOGW("Failed to request next vsync, status=%d", status);
+            return status;
+        }
 
-            mVsyncState = VsyncState::RegisteredAndWaitingForVsync;
-            break;
-        }
-        case VsyncState::Registered: {
-            mVsyncState = VsyncState::RegisteredAndWaitingForVsync;
-            break;
-        }
-        case VsyncState::RegisteredAndWaitingForVsync: {
-            break;
-        }
+        mWaitingForVsync = true;
     }
     return OK;
 }
@@ -136,23 +123,8 @@
               ", displayId=%s, count=%d, vsyncId=%" PRId64,
               this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount,
               vsyncEventData.id);
-        switch (mVsyncState) {
-            case VsyncState::Unregistered:
-                ALOGW("Received unexpected VSYNC event");
-                break;
-            case VsyncState::RegisteredAndWaitingForVsync:
-                mVsyncState = VsyncState::Registered;
-                dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData);
-                break;
-            case VsyncState::Registered:
-                status_t status = mReceiver.setVsyncRate(0);
-                if (status) {
-                    ALOGW("Failed to reset vsync rate, status=%d", status);
-                    return status;
-                }
-                mVsyncState = VsyncState::Unregistered;
-                break;
-        }
+        mWaitingForVsync = false;
+        dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData);
     }
 
     return 1; // keep the callback
@@ -184,9 +156,9 @@
                 case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
                     dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
                     break;
-                case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
-                    dispatchConfigChanged(ev.header.timestamp, ev.header.displayId,
-                                          ev.config.configId, ev.config.vsyncPeriod);
+                case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE:
+                    dispatchModeChanged(ev.header.timestamp, ev.header.displayId,
+                                        ev.modeChange.modeId, ev.modeChange.vsyncPeriod);
                     break;
                 case DisplayEventReceiver::DISPLAY_EVENT_NULL:
                     dispatchNullEvent(ev.header.timestamp, ev.header.displayId);
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index f68f3e1..762746c 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -36,8 +36,8 @@
 
 #include <system/graphics.h>
 
-#include <ui/DisplayConfig.h>
 #include <ui/DisplayInfo.h>
+#include <ui/DisplayMode.h>
 #include <ui/DisplayStatInfo.h>
 #include <ui/DisplayState.h>
 #include <ui/HdrCapabilities.h>
@@ -333,20 +333,19 @@
         return reply.read(*info);
     }
 
-    status_t getDisplayConfigs(const sp<IBinder>& display,
-                               Vector<DisplayConfig>* configs) override {
+    status_t getDisplayModes(const sp<IBinder>& display, Vector<ui::DisplayMode>* modes) override {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         data.writeStrongBinder(display);
-        remote()->transact(BnSurfaceComposer::GET_DISPLAY_CONFIGS, data, &reply);
+        remote()->transact(BnSurfaceComposer::GET_DISPLAY_MODES, data, &reply);
         const status_t result = reply.readInt32();
         if (result == NO_ERROR) {
-            const size_t numConfigs = reply.readUint32();
-            configs->clear();
-            configs->resize(numConfigs);
-            for (size_t c = 0; c < numConfigs; ++c) {
-                memcpy(&(configs->editItemAt(c)), reply.readInplace(sizeof(DisplayConfig)),
-                       sizeof(DisplayConfig));
+            const size_t numModes = reply.readUint32();
+            modes->clear();
+            modes->resize(numModes);
+            for (size_t i = 0; i < numModes; i++) {
+                memcpy(&(modes->editItemAt(i)), reply.readInplace(sizeof(ui::DisplayMode)),
+                       sizeof(ui::DisplayMode));
             }
         }
         return result;
@@ -366,11 +365,11 @@
         return result;
     }
 
-    int getActiveConfig(const sp<IBinder>& display) override {
+    int getActiveDisplayModeId(const sp<IBinder>& display) override {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         data.writeStrongBinder(display);
-        remote()->transact(BnSurfaceComposer::GET_ACTIVE_CONFIG, data, &reply);
+        remote()->transact(BnSurfaceComposer::GET_ACTIVE_DISPLAY_MODE, data, &reply);
         return reply.readInt32();
     }
 
@@ -882,71 +881,70 @@
         return error;
     }
 
-    status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, int32_t defaultConfig,
-                                          bool allowGroupSwitching, float primaryRefreshRateMin,
-                                          float primaryRefreshRateMax,
-                                          float appRequestRefreshRateMin,
-                                          float appRequestRefreshRateMax) override {
+    status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, size_t defaultMode,
+                                        bool allowGroupSwitching, float primaryRefreshRateMin,
+                                        float primaryRefreshRateMax, float appRequestRefreshRateMin,
+                                        float appRequestRefreshRateMax) override {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         if (result != NO_ERROR) {
-            ALOGE("setDesiredDisplayConfigSpecs: failed to writeInterfaceToken: %d", result);
+            ALOGE("setDesiredDisplayModeSpecs: failed to writeInterfaceToken: %d", result);
             return result;
         }
         result = data.writeStrongBinder(displayToken);
         if (result != NO_ERROR) {
-            ALOGE("setDesiredDisplayConfigSpecs: failed to write display token: %d", result);
+            ALOGE("setDesiredDisplayModeSpecs: failed to write display token: %d", result);
             return result;
         }
-        result = data.writeInt32(defaultConfig);
+        result = data.writeInt32(defaultMode);
         if (result != NO_ERROR) {
-            ALOGE("setDesiredDisplayConfigSpecs failed to write defaultConfig: %d", result);
+            ALOGE("setDesiredDisplayModeSpecs failed to write defaultMode: %d", result);
             return result;
         }
         result = data.writeBool(allowGroupSwitching);
         if (result != NO_ERROR) {
-            ALOGE("setDesiredDisplayConfigSpecs failed to write allowGroupSwitching: %d", result);
+            ALOGE("setDesiredDisplayModeSpecs failed to write allowGroupSwitching: %d", result);
             return result;
         }
         result = data.writeFloat(primaryRefreshRateMin);
         if (result != NO_ERROR) {
-            ALOGE("setDesiredDisplayConfigSpecs failed to write primaryRefreshRateMin: %d", result);
+            ALOGE("setDesiredDisplayModeSpecs failed to write primaryRefreshRateMin: %d", result);
             return result;
         }
         result = data.writeFloat(primaryRefreshRateMax);
         if (result != NO_ERROR) {
-            ALOGE("setDesiredDisplayConfigSpecs failed to write primaryRefreshRateMax: %d", result);
+            ALOGE("setDesiredDisplayModeSpecs failed to write primaryRefreshRateMax: %d", result);
             return result;
         }
         result = data.writeFloat(appRequestRefreshRateMin);
         if (result != NO_ERROR) {
-            ALOGE("setDesiredDisplayConfigSpecs failed to write appRequestRefreshRateMin: %d",
+            ALOGE("setDesiredDisplayModeSpecs failed to write appRequestRefreshRateMin: %d",
                   result);
             return result;
         }
         result = data.writeFloat(appRequestRefreshRateMax);
         if (result != NO_ERROR) {
-            ALOGE("setDesiredDisplayConfigSpecs failed to write appRequestRefreshRateMax: %d",
+            ALOGE("setDesiredDisplayModeSpecs failed to write appRequestRefreshRateMax: %d",
                   result);
             return result;
         }
 
-        result = remote()->transact(BnSurfaceComposer::SET_DESIRED_DISPLAY_CONFIG_SPECS, data,
-                                    &reply);
+        result =
+                remote()->transact(BnSurfaceComposer::SET_DESIRED_DISPLAY_MODE_SPECS, data, &reply);
         if (result != NO_ERROR) {
-            ALOGE("setDesiredDisplayConfigSpecs failed to transact: %d", result);
+            ALOGE("setDesiredDisplayModeSpecs failed to transact: %d", result);
             return result;
         }
         return reply.readInt32();
     }
 
-    status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                          int32_t* outDefaultConfig, bool* outAllowGroupSwitching,
-                                          float* outPrimaryRefreshRateMin,
-                                          float* outPrimaryRefreshRateMax,
-                                          float* outAppRequestRefreshRateMin,
-                                          float* outAppRequestRefreshRateMax) override {
-        if (!outDefaultConfig || !outAllowGroupSwitching || !outPrimaryRefreshRateMin ||
+    status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, size_t* outDefaultMode,
+                                        bool* outAllowGroupSwitching,
+                                        float* outPrimaryRefreshRateMin,
+                                        float* outPrimaryRefreshRateMax,
+                                        float* outAppRequestRefreshRateMin,
+                                        float* outAppRequestRefreshRateMax) override {
+        if (!outDefaultMode || !outAllowGroupSwitching || !outPrimaryRefreshRateMin ||
             !outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin ||
             !outAppRequestRefreshRateMax) {
             return BAD_VALUE;
@@ -954,50 +952,55 @@
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         if (result != NO_ERROR) {
-            ALOGE("getDesiredDisplayConfigSpecs failed to writeInterfaceToken: %d", result);
+            ALOGE("getDesiredDisplayModeSpecs failed to writeInterfaceToken: %d", result);
             return result;
         }
         result = data.writeStrongBinder(displayToken);
         if (result != NO_ERROR) {
-            ALOGE("getDesiredDisplayConfigSpecs failed to writeStrongBinder: %d", result);
+            ALOGE("getDesiredDisplayModeSpecs failed to writeStrongBinder: %d", result);
             return result;
         }
-        result = remote()->transact(BnSurfaceComposer::GET_DESIRED_DISPLAY_CONFIG_SPECS, data,
-                                    &reply);
+        result =
+                remote()->transact(BnSurfaceComposer::GET_DESIRED_DISPLAY_MODE_SPECS, data, &reply);
         if (result != NO_ERROR) {
-            ALOGE("getDesiredDisplayConfigSpecs failed to transact: %d", result);
+            ALOGE("getDesiredDisplayModeSpecs failed to transact: %d", result);
             return result;
         }
-        result = reply.readInt32(outDefaultConfig);
+        int32_t defaultMode;
+        result = reply.readInt32(&defaultMode);
         if (result != NO_ERROR) {
-            ALOGE("getDesiredDisplayConfigSpecs failed to read defaultConfig: %d", result);
+            ALOGE("getDesiredDisplayModeSpecs failed to read defaultMode: %d", result);
             return result;
         }
+        if (defaultMode < 0) {
+            ALOGE("%s: defaultMode must be non-negative but it was %d", __func__, defaultMode);
+            return BAD_VALUE;
+        }
+        *outDefaultMode = static_cast<size_t>(defaultMode);
+
         result = reply.readBool(outAllowGroupSwitching);
         if (result != NO_ERROR) {
-            ALOGE("getDesiredDisplayConfigSpecs failed to read allowGroupSwitching: %d", result);
+            ALOGE("getDesiredDisplayModeSpecs failed to read allowGroupSwitching: %d", result);
             return result;
         }
         result = reply.readFloat(outPrimaryRefreshRateMin);
         if (result != NO_ERROR) {
-            ALOGE("getDesiredDisplayConfigSpecs failed to read primaryRefreshRateMin: %d", result);
+            ALOGE("getDesiredDisplayModeSpecs failed to read primaryRefreshRateMin: %d", result);
             return result;
         }
         result = reply.readFloat(outPrimaryRefreshRateMax);
         if (result != NO_ERROR) {
-            ALOGE("getDesiredDisplayConfigSpecs failed to read primaryRefreshRateMax: %d", result);
+            ALOGE("getDesiredDisplayModeSpecs failed to read primaryRefreshRateMax: %d", result);
             return result;
         }
         result = reply.readFloat(outAppRequestRefreshRateMin);
         if (result != NO_ERROR) {
-            ALOGE("getDesiredDisplayConfigSpecs failed to read appRequestRefreshRateMin: %d",
-                  result);
+            ALOGE("getDesiredDisplayModeSpecs failed to read appRequestRefreshRateMin: %d", result);
             return result;
         }
         result = reply.readFloat(outAppRequestRefreshRateMax);
         if (result != NO_ERROR) {
-            ALOGE("getDesiredDisplayConfigSpecs failed to read appRequestRefreshRateMax: %d",
-                  result);
+            ALOGE("getDesiredDisplayModeSpecs failed to read appRequestRefreshRateMax: %d", result);
             return result;
         }
         return reply.readInt32();
@@ -1232,6 +1235,18 @@
         }
         return reply.readInt32();
     }
+
+    status_t getExtraBufferCount(int* extraBuffers) const override {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        status_t err = remote()->transact(BnSurfaceComposer::GET_EXTRA_BUFFER_COUNT, data, &reply);
+        if (err != NO_ERROR) {
+            ALOGE("getExtraBufferCount failed to read data:  %s (%d)", strerror(-err), err);
+            return err;
+        }
+
+        return reply.readInt32(extraBuffers);
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -1430,17 +1445,17 @@
             if (result != NO_ERROR) return result;
             return reply->write(info);
         }
-        case GET_DISPLAY_CONFIGS: {
+        case GET_DISPLAY_MODES: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            Vector<DisplayConfig> configs;
+            Vector<ui::DisplayMode> modes;
             const sp<IBinder> display = data.readStrongBinder();
-            const status_t result = getDisplayConfigs(display, &configs);
+            const status_t result = getDisplayModes(display, &modes);
             reply->writeInt32(result);
             if (result == NO_ERROR) {
-                reply->writeUint32(static_cast<uint32_t>(configs.size()));
-                for (size_t c = 0; c < configs.size(); ++c) {
-                    memcpy(reply->writeInplace(sizeof(DisplayConfig)), &configs[c],
-                           sizeof(DisplayConfig));
+                reply->writeUint32(static_cast<uint32_t>(modes.size()));
+                for (size_t i = 0; i < modes.size(); i++) {
+                    memcpy(reply->writeInplace(sizeof(ui::DisplayMode)), &modes[i],
+                           sizeof(ui::DisplayMode));
                 }
             }
             return NO_ERROR;
@@ -1457,10 +1472,10 @@
             }
             return NO_ERROR;
         }
-        case GET_ACTIVE_CONFIG: {
+        case GET_ACTIVE_DISPLAY_MODE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> display = data.readStrongBinder();
-            int id = getActiveConfig(display);
+            int id = getActiveDisplayModeId(display);
             reply->writeInt32(id);
             return NO_ERROR;
         }
@@ -1844,56 +1859,59 @@
             }
             return removeRegionSamplingListener(listener);
         }
-        case SET_DESIRED_DISPLAY_CONFIG_SPECS: {
+        case SET_DESIRED_DISPLAY_MODE_SPECS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> displayToken = data.readStrongBinder();
-            int32_t defaultConfig;
-            status_t result = data.readInt32(&defaultConfig);
+            int32_t defaultMode;
+            status_t result = data.readInt32(&defaultMode);
             if (result != NO_ERROR) {
-                ALOGE("setDesiredDisplayConfigSpecs: failed to read defaultConfig: %d", result);
+                ALOGE("setDesiredDisplayModeSpecs: failed to read defaultMode: %d", result);
                 return result;
             }
+            if (defaultMode < 0) {
+                ALOGE("%s: defaultMode must be non-negative but it was %d", __func__, defaultMode);
+                return BAD_VALUE;
+            }
             bool allowGroupSwitching;
             result = data.readBool(&allowGroupSwitching);
             if (result != NO_ERROR) {
-                ALOGE("setDesiredDisplayConfigSpecs: failed to read allowGroupSwitching: %d",
-                      result);
+                ALOGE("setDesiredDisplayModeSpecs: failed to read allowGroupSwitching: %d", result);
                 return result;
             }
             float primaryRefreshRateMin;
             result = data.readFloat(&primaryRefreshRateMin);
             if (result != NO_ERROR) {
-                ALOGE("setDesiredDisplayConfigSpecs: failed to read primaryRefreshRateMin: %d",
+                ALOGE("setDesiredDisplayModeSpecs: failed to read primaryRefreshRateMin: %d",
                       result);
                 return result;
             }
             float primaryRefreshRateMax;
             result = data.readFloat(&primaryRefreshRateMax);
             if (result != NO_ERROR) {
-                ALOGE("setDesiredDisplayConfigSpecs: failed to read primaryRefreshRateMax: %d",
+                ALOGE("setDesiredDisplayModeSpecs: failed to read primaryRefreshRateMax: %d",
                       result);
                 return result;
             }
             float appRequestRefreshRateMin;
             result = data.readFloat(&appRequestRefreshRateMin);
             if (result != NO_ERROR) {
-                ALOGE("setDesiredDisplayConfigSpecs: failed to read appRequestRefreshRateMin: %d",
+                ALOGE("setDesiredDisplayModeSpecs: failed to read appRequestRefreshRateMin: %d",
                       result);
                 return result;
             }
             float appRequestRefreshRateMax;
             result = data.readFloat(&appRequestRefreshRateMax);
             if (result != NO_ERROR) {
-                ALOGE("setDesiredDisplayConfigSpecs: failed to read appRequestRefreshRateMax: %d",
+                ALOGE("setDesiredDisplayModeSpecs: failed to read appRequestRefreshRateMax: %d",
                       result);
                 return result;
             }
-            result = setDesiredDisplayConfigSpecs(displayToken, defaultConfig, allowGroupSwitching,
-                                                  primaryRefreshRateMin, primaryRefreshRateMax,
-                                                  appRequestRefreshRateMin,
-                                                  appRequestRefreshRateMax);
+            result = setDesiredDisplayModeSpecs(displayToken, static_cast<size_t>(defaultMode),
+                                                allowGroupSwitching, primaryRefreshRateMin,
+                                                primaryRefreshRateMax, appRequestRefreshRateMin,
+                                                appRequestRefreshRateMax);
             if (result != NO_ERROR) {
-                ALOGE("setDesiredDisplayConfigSpecs: failed to call setDesiredDisplayConfigSpecs: "
+                ALOGE("setDesiredDisplayModeSpecs: failed to call setDesiredDisplayModeSpecs: "
                       "%d",
                       result);
                 return result;
@@ -1901,10 +1919,10 @@
             reply->writeInt32(result);
             return result;
         }
-        case GET_DESIRED_DISPLAY_CONFIG_SPECS: {
+        case GET_DESIRED_DISPLAY_MODE_SPECS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> displayToken = data.readStrongBinder();
-            int32_t defaultConfig;
+            size_t defaultMode;
             bool allowGroupSwitching;
             float primaryRefreshRateMin;
             float primaryRefreshRateMax;
@@ -1912,49 +1930,49 @@
             float appRequestRefreshRateMax;
 
             status_t result =
-                    getDesiredDisplayConfigSpecs(displayToken, &defaultConfig, &allowGroupSwitching,
-                                                 &primaryRefreshRateMin, &primaryRefreshRateMax,
-                                                 &appRequestRefreshRateMin,
-                                                 &appRequestRefreshRateMax);
+                    getDesiredDisplayModeSpecs(displayToken, &defaultMode, &allowGroupSwitching,
+                                               &primaryRefreshRateMin, &primaryRefreshRateMax,
+                                               &appRequestRefreshRateMin,
+                                               &appRequestRefreshRateMax);
             if (result != NO_ERROR) {
-                ALOGE("getDesiredDisplayConfigSpecs: failed to get getDesiredDisplayConfigSpecs: "
+                ALOGE("getDesiredDisplayModeSpecs: failed to get getDesiredDisplayModeSpecs: "
                       "%d",
                       result);
                 return result;
             }
 
-            result = reply->writeInt32(defaultConfig);
+            result = reply->writeInt32(static_cast<int32_t>(defaultMode));
             if (result != NO_ERROR) {
-                ALOGE("getDesiredDisplayConfigSpecs: failed to write defaultConfig: %d", result);
+                ALOGE("getDesiredDisplayModeSpecs: failed to write defaultMode: %d", result);
                 return result;
             }
             result = reply->writeBool(allowGroupSwitching);
             if (result != NO_ERROR) {
-                ALOGE("getDesiredDisplayConfigSpecs: failed to write allowGroupSwitching: %d",
+                ALOGE("getDesiredDisplayModeSpecs: failed to write allowGroupSwitching: %d",
                       result);
                 return result;
             }
             result = reply->writeFloat(primaryRefreshRateMin);
             if (result != NO_ERROR) {
-                ALOGE("getDesiredDisplayConfigSpecs: failed to write primaryRefreshRateMin: %d",
+                ALOGE("getDesiredDisplayModeSpecs: failed to write primaryRefreshRateMin: %d",
                       result);
                 return result;
             }
             result = reply->writeFloat(primaryRefreshRateMax);
             if (result != NO_ERROR) {
-                ALOGE("getDesiredDisplayConfigSpecs: failed to write primaryRefreshRateMax: %d",
+                ALOGE("getDesiredDisplayModeSpecs: failed to write primaryRefreshRateMax: %d",
                       result);
                 return result;
             }
             result = reply->writeFloat(appRequestRefreshRateMin);
             if (result != NO_ERROR) {
-                ALOGE("getDesiredDisplayConfigSpecs: failed to write appRequestRefreshRateMin: %d",
+                ALOGE("getDesiredDisplayModeSpecs: failed to write appRequestRefreshRateMin: %d",
                       result);
                 return result;
             }
             result = reply->writeFloat(appRequestRefreshRateMax);
             if (result != NO_ERROR) {
-                ALOGE("getDesiredDisplayConfigSpecs: failed to write appRequestRefreshRateMax: %d",
+                ALOGE("getDesiredDisplayModeSpecs: failed to write appRequestRefreshRateMax: %d",
                       result);
                 return result;
             }
@@ -2101,6 +2119,16 @@
             SAFE_PARCEL(reply->writeInt32, priority);
             return NO_ERROR;
         }
+        case GET_EXTRA_BUFFER_COUNT: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            int extraBuffers = 0;
+            int err = getExtraBufferCount(&extraBuffers);
+            if (err != NO_ERROR) {
+                return err;
+            }
+            SAFE_PARCEL(reply->writeInt32, extraBuffers);
+            return NO_ERROR;
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index fff3305..f053372 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -167,6 +167,9 @@
         SAFE_PARCEL(output.writeInt32, region.right);
         SAFE_PARCEL(output.writeInt32, region.bottom);
     }
+
+    SAFE_PARCEL(output.write, stretchEffect);
+
     return NO_ERROR;
 }
 
@@ -290,6 +293,9 @@
         SAFE_PARCEL(input.readInt32, &region.bottom);
         blurRegions.push_back(region);
     }
+
+    SAFE_PARCEL(input.read, stretchEffect);
+
     return NO_ERROR;
 }
 
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 59ad8d2..07fc069 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1499,6 +1499,9 @@
     case NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO:
         res = dispatchSetFrameTimelineInfo(args);
         break;
+    case NATIVE_WINDOW_GET_EXTRA_BUFFER_COUNT:
+        res = dispatchGetExtraBufferCount(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -1815,6 +1818,14 @@
     return setFrameTimelineInfo({frameTimelineVsyncId, inputEventId});
 }
 
+int Surface::dispatchGetExtraBufferCount(va_list args) {
+    ATRACE_CALL();
+    auto extraBuffers = static_cast<int*>(va_arg(args, int*));
+
+    ALOGV("Surface::dispatchGetExtraBufferCount");
+    return getExtraBufferCount(extraBuffers);
+}
+
 bool Surface::transformToDisplayInverse() {
     return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
             NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
@@ -2584,4 +2595,8 @@
     return composerService()->setFrameTimelineInfo(mGraphicBufferProducer, frameTimelineInfo);
 }
 
+status_t Surface::getExtraBufferCount(int* extraBuffers) const {
+    return composerService()->getExtraBufferCount(extraBuffers);
+}
+
 }; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 550803d..27fb2a8 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -39,7 +39,7 @@
 #include <gui/LayerState.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
 
 #ifndef NO_INPUT
 #include <input/InputWindow.h>
@@ -1569,6 +1569,23 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setStretchEffect(
+        const sp<SurfaceControl>& sc, float left, float top, float right, float bottom, float vecX,
+        float vecY, float maxAmount) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    s->what |= layer_state_t::eStretchChanged;
+    s->stretchEffect = StretchEffect{.area = {left, top, right, bottom},
+                                     .vectorX = vecX,
+                                     .vectorY = vecY,
+                                     .maxAmount = maxAmount};
+    return *this;
+}
+
 // ---------------------------------------------------------------------------
 
 DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -1802,52 +1819,51 @@
     return ComposerService::getComposerService()->getDisplayInfo(display, info);
 }
 
-status_t SurfaceComposerClient::getDisplayConfigs(const sp<IBinder>& display,
-                                                  Vector<DisplayConfig>* configs) {
-    return ComposerService::getComposerService()->getDisplayConfigs(display, configs);
+status_t SurfaceComposerClient::getDisplayModes(const sp<IBinder>& display,
+                                                Vector<ui::DisplayMode>* modes) {
+    return ComposerService::getComposerService()->getDisplayModes(display, modes);
 }
 
-status_t SurfaceComposerClient::getActiveDisplayConfig(const sp<IBinder>& display,
-                                                       DisplayConfig* config) {
-    Vector<DisplayConfig> configs;
-    status_t result = getDisplayConfigs(display, &configs);
+status_t SurfaceComposerClient::getActiveDisplayMode(const sp<IBinder>& display,
+                                                     ui::DisplayMode* mode) {
+    Vector<ui::DisplayMode> modes;
+    status_t result = getDisplayModes(display, &modes);
     if (result != NO_ERROR) {
         return result;
     }
 
-    int activeId = getActiveConfig(display);
+    int activeId = getActiveDisplayModeId(display);
     if (activeId < 0) {
-        ALOGE("No active configuration found");
+        ALOGE("No active mode found");
         return NAME_NOT_FOUND;
     }
 
-    *config = configs[static_cast<size_t>(activeId)];
+    *mode = modes[static_cast<size_t>(activeId)];
     return NO_ERROR;
 }
 
-int SurfaceComposerClient::getActiveConfig(const sp<IBinder>& display) {
-    return ComposerService::getComposerService()->getActiveConfig(display);
+int SurfaceComposerClient::getActiveDisplayModeId(const sp<IBinder>& display) {
+    return ComposerService::getComposerService()->getActiveDisplayModeId(display);
 }
 
-status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs(
-        const sp<IBinder>& displayToken, int32_t defaultConfig, bool allowGroupSwitching,
+status_t SurfaceComposerClient::setDesiredDisplayModeSpecs(
+        const sp<IBinder>& displayToken, size_t defaultMode, bool allowGroupSwitching,
         float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin,
         float appRequestRefreshRateMax) {
     return ComposerService::getComposerService()
-            ->setDesiredDisplayConfigSpecs(displayToken, defaultConfig, allowGroupSwitching,
-                                           primaryRefreshRateMin, primaryRefreshRateMax,
-                                           appRequestRefreshRateMin, appRequestRefreshRateMax);
+            ->setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching,
+                                         primaryRefreshRateMin, primaryRefreshRateMax,
+                                         appRequestRefreshRateMin, appRequestRefreshRateMax);
 }
 
-status_t SurfaceComposerClient::getDesiredDisplayConfigSpecs(
-        const sp<IBinder>& displayToken, int32_t* outDefaultConfig, bool* outAllowGroupSwitching,
+status_t SurfaceComposerClient::getDesiredDisplayModeSpecs(
+        const sp<IBinder>& displayToken, size_t* outDefaultMode, bool* outAllowGroupSwitching,
         float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax,
         float* outAppRequestRefreshRateMin, float* outAppRequestRefreshRateMax) {
     return ComposerService::getComposerService()
-            ->getDesiredDisplayConfigSpecs(displayToken, outDefaultConfig, outAllowGroupSwitching,
-                                           outPrimaryRefreshRateMin, outPrimaryRefreshRateMax,
-                                           outAppRequestRefreshRateMin,
-                                           outAppRequestRefreshRateMax);
+            ->getDesiredDisplayModeSpecs(displayToken, outDefaultMode, outAllowGroupSwitching,
+                                         outPrimaryRefreshRateMin, outPrimaryRefreshRateMax,
+                                         outAppRequestRefreshRateMin, outAppRequestRefreshRateMax);
 }
 
 status_t SurfaceComposerClient::getDisplayColorModes(const sp<IBinder>& display,
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index 4175a49..d74c2ba 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -52,20 +52,7 @@
 private:
     sp<Looper> mLooper;
     DisplayEventReceiver mReceiver;
-    // The state of vsync event registration and whether the client is expecting
-    // an event or not.
-    enum class VsyncState {
-        // The dispatcher is not registered for vsync events.
-        Unregistered,
-        // The dispatcher is registered to receive vsync events but should not dispatch it to the
-        // client as the client is not expecting a vsync event.
-        Registered,
-
-        // The dispatcher is registered to receive vsync events and supposed to dispatch it to
-        // the client.
-        RegisteredAndWaitingForVsync,
-    };
-    VsyncState mVsyncState;
+    bool mWaitingForVsync;
 
     std::vector<FrameRateOverride> mFrameRateOverrides;
 
@@ -73,8 +60,8 @@
                                VsyncEventData vsyncEventData) = 0;
     virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
                                  bool connected) = 0;
-    virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
-                                       int32_t configId, nsecs_t vsyncPeriod) = 0;
+    virtual void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
+                                     nsecs_t vsyncPeriod) = 0;
     // AChoreographer-specific hook for processing null-events so that looper
     // can be properly poked.
     virtual void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) = 0;
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 3191fc9..7179a20 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -52,7 +52,7 @@
     enum {
         DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
         DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
-        DISPLAY_EVENT_CONFIG_CHANGED = fourcc('c', 'o', 'n', 'f'),
+        DISPLAY_EVENT_MODE_CHANGE = fourcc('m', 'o', 'd', 'e'),
         DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'),
         DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'),
         DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'),
@@ -82,8 +82,8 @@
             bool connected;
         };
 
-        struct Config {
-            int32_t configId;
+        struct ModeChange {
+            int32_t modeId;
             nsecs_t vsyncPeriod __attribute__((aligned(8)));
         };
 
@@ -96,7 +96,7 @@
         union {
             VSync vsync;
             Hotplug hotplug;
-            Config config;
+            ModeChange modeChange;
             FrameRateOverride frameRateOverride;
         };
     };
@@ -106,7 +106,7 @@
      * DisplayEventReceiver creates and registers an event connection with
      * SurfaceFlinger. VSync events are disabled by default. Call setVSyncRate
      * or requestNextVsync to receive them.
-     * To receive ConfigChanged and/or FrameRateOverrides events specify this in
+     * To receive ModeChanged and/or FrameRateOverrides events specify this in
      * the constructor. Other events start being delivered immediately.
      */
     explicit DisplayEventReceiver(
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 81ff6b0..292838e 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -54,7 +54,6 @@
 struct client_cache_t;
 struct ComposerState;
 struct DisplayCaptureArgs;
-struct DisplayConfig;
 struct DisplayInfo;
 struct DisplayStatInfo;
 struct DisplayState;
@@ -73,6 +72,7 @@
 
 namespace ui {
 
+struct DisplayMode;
 struct DisplayState;
 
 } // namespace ui
@@ -112,7 +112,7 @@
     };
 
     enum class EventRegistration {
-        configChanged = 1 << 0,
+        modeChanged = 1 << 0,
         frameRateOverride = 1 << 1,
     };
 
@@ -207,15 +207,15 @@
     virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo*) = 0;
 
     /**
-     * Get configurations supported by given physical display.
+     * Get modes supported by given physical display.
      */
-    virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>*) = 0;
+    virtual status_t getDisplayModes(const sp<IBinder>& display, Vector<ui::DisplayMode>*) = 0;
 
     /**
-     * Get the index into configurations returned by getDisplayConfigs,
-     * corresponding to the active configuration.
+     * Get the index into modes returned by getDisplayModes,
+     * corresponding to the active mode.
      */
-    virtual int getActiveConfig(const sp<IBinder>& display) = 0;
+    virtual int getActiveDisplayModeId(const sp<IBinder>& display) = 0;
 
     virtual status_t getDisplayColorModes(const sp<IBinder>& display,
             Vector<ui::ColorMode>* outColorModes) = 0;
@@ -386,34 +386,31 @@
     /* Sets the refresh rate boundaries for the display.
      *
      * The primary refresh rate range represents display manager's general guidance on the display
-     * configs we'll consider when switching refresh rates. Unless we get an explicit signal from an
+     * modes we'll consider when switching refresh rates. Unless we get an explicit signal from an
      * app, we should stay within this range.
      *
-     * The app request refresh rate range allows us to consider more display configs when switching
+     * The app request refresh rate range allows us to consider more display modes when switching
      * refresh rates. Although we should generally stay within the primary range, specific
      * considerations, such as layer frame rate settings specified via the setFrameRate() api, may
      * cause us to go outside the primary range. We never go outside the app request range. The app
      * request range will be greater than or equal to the primary refresh rate range, never smaller.
      *
-     * defaultConfig is used to narrow the list of display configs SurfaceFlinger will consider
-     * switching between. Only configs with a config group and resolution matching defaultConfig
-     * will be considered for switching. The defaultConfig index corresponds to the list of configs
-     * returned from getDisplayConfigs().
+     * defaultMode is used to narrow the list of display modes SurfaceFlinger will consider
+     * switching between. Only modes with a mode group and resolution matching defaultMode
+     * will be considered for switching. The defaultMode index corresponds to the list of modes
+     * returned from getDisplayModes().
      */
-    virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                  int32_t defaultConfig, bool allowGroupSwitching,
-                                                  float primaryRefreshRateMin,
-                                                  float primaryRefreshRateMax,
-                                                  float appRequestRefreshRateMin,
-                                                  float appRequestRefreshRateMax) = 0;
+    virtual status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, size_t defaultMode,
+                                                bool allowGroupSwitching,
+                                                float primaryRefreshRateMin,
+                                                float primaryRefreshRateMax,
+                                                float appRequestRefreshRateMin,
+                                                float appRequestRefreshRateMax) = 0;
 
-    virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                  int32_t* outDefaultConfig,
-                                                  bool* outAllowGroupSwitching,
-                                                  float* outPrimaryRefreshRateMin,
-                                                  float* outPrimaryRefreshRateMax,
-                                                  float* outAppRequestRefreshRateMin,
-                                                  float* outAppRequestRefreshRateMax) = 0;
+    virtual status_t getDesiredDisplayModeSpecs(
+            const sp<IBinder>& displayToken, size_t* outDefaultMode, bool* outAllowGroupSwitching,
+            float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax,
+            float* outAppRequestRefreshRateMin, float* outAppRequestRefreshRateMax) = 0;
     /*
      * Gets whether brightness operations are supported on a display.
      *
@@ -508,6 +505,24 @@
      * Gets priority of the RenderEngine in SurfaceFlinger.
      */
     virtual int getGPUContextPriority() = 0;
+
+    /**
+     * Gets the extra buffers a client would need to allocate if it passes
+     * the Choreographer#getVsyncId with its buffers.
+     *
+     * When Choreographer#getVsyncId is passed to SurfaceFlinger, it is used
+     * as an indication of when to latch the buffer. SurfaceFlinger will make
+     * sure that it will give the app at least the time configured as the
+     * 'appDuration' before trying to latch the buffer.
+     *
+     * The total buffers needed for a given configuration is basically the
+     * numbers of vsyncs a single buffer is used across the stack. For the default
+     * configuration a buffer is held ~1 vsync by the app, ~1 vsync by SurfaceFlinger
+     * and 1 vsync by the display. The extra buffers are calculated as the
+     * number of additional buffers on top of the 3 buffers already allocated
+     * by the app.
+     */
+    virtual status_t getExtraBufferCount(int* extraBuffers) const = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -527,8 +542,8 @@
         SET_TRANSACTION_STATE,
         AUTHENTICATE_SURFACE,
         GET_SUPPORTED_FRAME_TIMESTAMPS,
-        GET_DISPLAY_CONFIGS,
-        GET_ACTIVE_CONFIG,
+        GET_DISPLAY_MODES,
+        GET_ACTIVE_DISPLAY_MODE,
         GET_DISPLAY_STATE,
         CAPTURE_DISPLAY,
         CAPTURE_LAYERS,
@@ -554,8 +569,8 @@
         GET_PHYSICAL_DISPLAY_IDS,
         ADD_REGION_SAMPLING_LISTENER,
         REMOVE_REGION_SAMPLING_LISTENER,
-        SET_DESIRED_DISPLAY_CONFIG_SPECS,
-        GET_DESIRED_DISPLAY_CONFIG_SPECS,
+        SET_DESIRED_DISPLAY_MODE_SPECS,
+        GET_DESIRED_DISPLAY_MODE_SPECS,
         GET_DISPLAY_BRIGHTNESS_SUPPORT,
         SET_DISPLAY_BRIGHTNESS,
         CAPTURE_DISPLAY_BY_ID,
@@ -570,6 +585,7 @@
         SET_FRAME_TIMELINE_INFO,
         ADD_TRANSACTION_TRACE_LISTENER,
         GET_GPU_CONTEXT_PRIORITY,
+        GET_EXTRA_BUFFER_COUNT,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 2f9a0c0..b273805 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -56,6 +56,7 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Rotation.h>
+#include <ui/StretchEffect.h>
 #include <ui/Transform.h>
 #include <utils/Errors.h>
 
@@ -135,6 +136,7 @@
         eFrameTimelineInfoChanged = 0x800'00000000,
         eBlurRegionsChanged = 0x1000'00000000,
         eAutoRefreshChanged = 0x2000'00000000,
+        eStretchChanged = 0x4000'00000000,
     };
 
     layer_state_t();
@@ -244,6 +246,9 @@
     // can and not wait for a frame to become available. This is only relevant
     // in shared buffer mode.
     bool autoRefresh;
+
+    // Stretch effect to be applied to this layer
+    StretchEffect stretchEffect;
 };
 
 struct ComposerState {
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index b6b5c7c..5881221 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -189,6 +189,7 @@
 
     virtual status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
     virtual status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
+    virtual status_t getExtraBufferCount(int* extraBuffers) const;
 
 protected:
     virtual ~Surface();
@@ -275,6 +276,7 @@
     int dispatchAddQueryInterceptor(va_list args);
     int dispatchGetLastQueuedBuffer(va_list args);
     int dispatchSetFrameTimelineInfo(va_list args);
+    int dispatchGetExtraBufferCount(va_list args);
     bool transformToDisplayInverse();
 
 protected:
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index e7abfe6..e89f3a7 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -110,30 +110,29 @@
     // Get immutable information about given physical display.
     static status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo*);
 
-    // Get configurations supported by given physical display.
-    static status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>*);
+    // Get modes supported by given physical display.
+    static status_t getDisplayModes(const sp<IBinder>& display, Vector<ui::DisplayMode>*);
 
-    // Get the ID of the active DisplayConfig, as getDisplayConfigs index.
-    static int getActiveConfig(const sp<IBinder>& display);
+    // Get the ID of the active DisplayMode, as getDisplayModes index.
+    static int getActiveDisplayModeId(const sp<IBinder>& display);
 
-    // Shorthand for getDisplayConfigs element at getActiveConfig index.
-    static status_t getActiveDisplayConfig(const sp<IBinder>& display, DisplayConfig*);
+    // Shorthand for getDisplayModes element at getActiveDisplayModeId index.
+    static status_t getActiveDisplayMode(const sp<IBinder>& display, ui::DisplayMode*);
 
     // Sets the refresh rate boundaries for the display.
-    static status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                 int32_t defaultConfig, bool allowGroupSwitching,
-                                                 float primaryRefreshRateMin,
-                                                 float primaryRefreshRateMax,
-                                                 float appRequestRefreshRateMin,
-                                                 float appRequestRefreshRateMax);
+    static status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, size_t defaultMode,
+                                               bool allowGroupSwitching,
+                                               float primaryRefreshRateMin,
+                                               float primaryRefreshRateMax,
+                                               float appRequestRefreshRateMin,
+                                               float appRequestRefreshRateMax);
     // Gets the refresh rate boundaries for the display.
-    static status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                 int32_t* outDefaultConfig,
-                                                 bool* outAllowGroupSwitching,
-                                                 float* outPrimaryRefreshRateMin,
-                                                 float* outPrimaryRefreshRateMax,
-                                                 float* outAppRequestRefreshRateMin,
-                                                 float* outAppRequestRefreshRateMax);
+    static status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+                                               size_t* outDefaultMode, bool* outAllowGroupSwitching,
+                                               float* outPrimaryRefreshRateMin,
+                                               float* outPrimaryRefreshRateMax,
+                                               float* outAppRequestRefreshRateMin,
+                                               float* outAppRequestRefreshRateMax);
 
     // Gets the list of supported color modes for the given display
     static status_t getDisplayColorModes(const sp<IBinder>& display,
@@ -551,6 +550,10 @@
         // transactions from blocking each other.
         Transaction& setApplyToken(const sp<IBinder>& token);
 
+        Transaction& setStretchEffect(const sp<SurfaceControl>& sc, float left, float top,
+                                      float right, float bottom, float vecX, float vecY,
+                                      float maxAmount);
+
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
 
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 70e656d..5447b76 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -28,7 +28,7 @@
 #include <gui/SurfaceComposerClient.h>
 #include <gui/SyncScreenCaptureListener.h>
 #include <private/gui/ComposerService.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Transform.h>
@@ -107,9 +107,9 @@
         t.apply();
         t.clear();
 
-        DisplayConfig config;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &config));
-        const ui::Size& resolution = config.resolution;
+        ui::DisplayMode mode;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(mDisplayToken, &mode));
+        const ui::Size& resolution = mode.resolution;
         mDisplayWidth = resolution.getWidth();
         mDisplayHeight = resolution.getHeight();
 
diff --git a/libs/gui/tests/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp
index c5093e2..7dbba31 100644
--- a/libs/gui/tests/DisplayEventStructLayout_test.cpp
+++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp
@@ -25,7 +25,7 @@
 TEST(DisplayEventStructLayoutTest, TestEventAlignment) {
     CHECK_OFFSET(DisplayEventReceiver::Event, vsync, 24);
     CHECK_OFFSET(DisplayEventReceiver::Event, hotplug, 24);
-    CHECK_OFFSET(DisplayEventReceiver::Event, config, 24);
+    CHECK_OFFSET(DisplayEventReceiver::Event, modeChange, 24);
 
     CHECK_OFFSET(DisplayEventReceiver::Event::Header, type, 0);
     CHECK_OFFSET(DisplayEventReceiver::Event::Header, displayId, 8);
@@ -38,8 +38,8 @@
 
     CHECK_OFFSET(DisplayEventReceiver::Event::Hotplug, connected, 0);
 
-    CHECK_OFFSET(DisplayEventReceiver::Event::Config, configId, 0);
-    CHECK_OFFSET(DisplayEventReceiver::Event::Config, vsyncPeriod, 8);
+    CHECK_OFFSET(DisplayEventReceiver::Event::ModeChange, modeId, 0);
+    CHECK_OFFSET(DisplayEventReceiver::Event::ModeChange, vsyncPeriod, 8);
 
     CHECK_OFFSET(DisplayEventReceiver::Event::FrameRateOverride, uid, 0);
     CHECK_OFFSET(DisplayEventReceiver::Event::FrameRateOverride, frameRateHz, 8);
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 31cbbdc..d65a40b 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -41,7 +41,7 @@
 #include <input/InputTransport.h>
 #include <input/InputWindow.h>
 
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
@@ -272,13 +272,13 @@
         const auto display = mComposerClient->getInternalDisplayToken();
         ASSERT_NE(display, nullptr);
 
-        DisplayConfig config;
-        ASSERT_EQ(NO_ERROR, mComposerClient->getActiveDisplayConfig(display, &config));
+        ui::DisplayMode mode;
+        ASSERT_EQ(NO_ERROR, mComposerClient->getActiveDisplayMode(display, &mode));
 
         // After a new buffer is queued, SurfaceFlinger is notified and will
         // latch the new buffer on next vsync.  Let's heuristically wait for 3
         // vsyncs.
-        mBufferPostDelay = static_cast<int32_t>(1e6 / config.refreshRate) * 3;
+        mBufferPostDelay = static_cast<int32_t>(1e6 / mode.refreshRate) * 3;
     }
 
     void TearDown() {
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 3f7a5b1..43909ac 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -737,7 +737,7 @@
     status_t getDisplayInfo(const sp<IBinder>& /*display*/, DisplayInfo*) override {
         return NO_ERROR;
     }
-    status_t getDisplayConfigs(const sp<IBinder>& /*display*/, Vector<DisplayConfig>*) override {
+    status_t getDisplayModes(const sp<IBinder>& /*display*/, Vector<ui::DisplayMode>*) override {
         return NO_ERROR;
     }
     status_t getDisplayState(const sp<IBinder>& /*display*/, ui::DisplayState*) override {
@@ -745,7 +745,7 @@
     }
     status_t getDisplayStats(const sp<IBinder>& /*display*/,
             DisplayStatInfo* /*stats*/) override { return NO_ERROR; }
-    int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; }
+    int getActiveDisplayModeId(const sp<IBinder>& /*display*/) override { return 0; }
     status_t getDisplayColorModes(const sp<IBinder>& /*display*/,
             Vector<ColorMode>* /*outColorModes*/) override {
         return NO_ERROR;
@@ -843,21 +843,21 @@
             const sp<IRegionSamplingListener>& /*listener*/) override {
         return NO_ERROR;
     }
-    status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
-                                          int32_t /*defaultConfig*/, bool /*allowGroupSwitching*/,
-                                          float /*primaryRefreshRateMin*/,
-                                          float /*primaryRefreshRateMax*/,
-                                          float /*appRequestRefreshRateMin*/,
-                                          float /*appRequestRefreshRateMax*/) {
+    status_t setDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/, size_t /*defaultMode*/,
+                                        bool /*allowGroupSwitching*/,
+                                        float /*primaryRefreshRateMin*/,
+                                        float /*primaryRefreshRateMax*/,
+                                        float /*appRequestRefreshRateMin*/,
+                                        float /*appRequestRefreshRateMax*/) {
         return NO_ERROR;
     }
-    status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
-                                          int32_t* /*outDefaultConfig*/,
-                                          bool* /*outAllowGroupSwitching*/,
-                                          float* /*outPrimaryRefreshRateMin*/,
-                                          float* /*outPrimaryRefreshRateMax*/,
-                                          float* /*outAppRequestRefreshRateMin*/,
-                                          float* /*outAppRequestRefreshRateMax*/) override {
+    status_t getDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/,
+                                        size_t* /*outDefaultMode*/,
+                                        bool* /*outAllowGroupSwitching*/,
+                                        float* /*outPrimaryRefreshRateMin*/,
+                                        float* /*outPrimaryRefreshRateMax*/,
+                                        float* /*outAppRequestRefreshRateMin*/,
+                                        float* /*outAppRequestRefreshRateMax*/) override {
         return NO_ERROR;
     };
     status_t notifyPowerBoost(int32_t /*boostId*/) override { return NO_ERROR; }
@@ -889,6 +889,8 @@
 
     int getGPUContextPriority() override { return 0; };
 
+    status_t getExtraBufferCount(int* /*extraBuffers*/) const override { return NO_ERROR; }
+
 protected:
     IBinder* onAsBinder() override { return nullptr; }
 
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index acea473..6218fdc 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -234,6 +234,7 @@
         }
         case InputMessage::Type::FINISHED: {
             msg->body.finished.handled = body.finished.handled;
+            msg->body.finished.consumeTime = body.finished.consumeTime;
             break;
         }
         case InputMessage::Type::FOCUS: {
@@ -597,7 +598,8 @@
     return mChannel->sendMessage(&msg);
 }
 
-status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
+status_t InputPublisher::receiveFinishedSignal(
+        const std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)>& callback) {
     if (DEBUG_TRANSPORT_ACTIONS) {
         ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str());
     }
@@ -605,8 +607,6 @@
     InputMessage msg;
     status_t result = mChannel->receiveMessage(&msg);
     if (result) {
-        *outSeq = 0;
-        *outHandled = false;
         return result;
     }
     if (msg.header.type != InputMessage::Type::FINISHED) {
@@ -614,8 +614,7 @@
                 mChannel->getName().c_str(), msg.header.type);
         return UNKNOWN_ERROR;
     }
-    *outSeq = msg.header.seq;
-    *outHandled = msg.body.finished.handled == 1;
+    callback(msg.header.seq, msg.body.finished.handled == 1, msg.body.finished.consumeTime);
     return OK;
 }
 
@@ -651,6 +650,9 @@
         } else {
             // Receive a fresh message.
             status_t result = mChannel->receiveMessage(&mMsg);
+            if (result == OK) {
+                mConsumeTimes.emplace(mMsg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC));
+            }
             if (result) {
                 // Consume the next batched event unless batches are being held for later.
                 if (consumeBatches || result != WOULD_BLOCK) {
@@ -1147,12 +1149,33 @@
     return sendUnchainedFinishedSignal(seq, handled);
 }
 
+nsecs_t InputConsumer::getConsumeTime(uint32_t seq) const {
+    auto it = mConsumeTimes.find(seq);
+    // Consume time will be missing if either 'finishInputEvent' is called twice, or if it was
+    // called for the wrong (synthetic?) input event. Either way, it is a bug that should be fixed.
+    LOG_ALWAYS_FATAL_IF(it == mConsumeTimes.end(), "Could not find consume time for seq=%" PRIu32,
+                        seq);
+    return it->second;
+}
+
+void InputConsumer::popConsumeTime(uint32_t seq) {
+    mConsumeTimes.erase(seq);
+}
+
 status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
     InputMessage msg;
     msg.header.type = InputMessage::Type::FINISHED;
     msg.header.seq = seq;
     msg.body.finished.handled = handled ? 1 : 0;
-    return mChannel->sendMessage(&msg);
+    msg.body.finished.consumeTime = getConsumeTime(seq);
+    status_t result = mChannel->sendMessage(&msg);
+    if (result == OK) {
+        // Remove the consume time if the socket write succeeded. We will not need to ack this
+        // message anymore. If the socket write did not succeed, we will try again and will still
+        // need consume time.
+        popConsumeTime(seq);
+    }
+    return result;
 }
 
 bool InputConsumer::hasDeferredEvent() const {
@@ -1304,8 +1327,9 @@
                     break;
                 }
                 case InputMessage::Type::FINISHED: {
-                    out += android::base::StringPrintf("handled=%s",
-                                                       toString(msg.body.finished.handled));
+                    out += android::base::StringPrintf("handled=%s, consumeTime=%" PRId64,
+                                                       toString(msg.body.finished.handled),
+                                                       msg.body.finished.consumeTime);
                     break;
                 }
                 case InputMessage::Type::FOCUS: {
@@ -1335,6 +1359,14 @@
     if (mSeqChains.empty()) {
         out += "    <empty>\n";
     }
+    out += "mConsumeTimes:\n";
+    for (const auto& [seq, consumeTime] : mConsumeTimes) {
+        out += android::base::StringPrintf("    seq = %" PRIu32 " consumeTime = %" PRId64, seq,
+                                           consumeTime);
+    }
+    if (mConsumeTimes.empty()) {
+        out += "    <empty>\n";
+    }
     return out;
 }
 
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 9da7b69..e7e566d 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -82,6 +82,7 @@
     constexpr int32_t repeatCount = 1;
     constexpr nsecs_t downTime = 3;
     constexpr nsecs_t eventTime = 4;
+    const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     status = mPublisher->publishKeyEvent(seq, eventId, deviceId, source, displayId, hmac, action,
                                          flags, keyCode, scanCode, metaState, repeatCount, downTime,
@@ -122,13 +123,22 @@
 
     uint32_t finishedSeq = 0;
     bool handled = false;
-    status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
+    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)
+            << "finished signal's consume time should be greater than publish time";
 }
 
 void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() {
@@ -160,6 +170,7 @@
     constexpr nsecs_t downTime = 3;
     constexpr size_t pointerCount = 3;
     constexpr nsecs_t eventTime = 4;
+    const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
     PointerProperties pointerProperties[pointerCount];
     PointerCoords pointerCoords[pointerCount];
     for (size_t i = 0; i < pointerCount; i++) {
@@ -262,13 +273,22 @@
 
     uint32_t finishedSeq = 0;
     bool handled = true;
-    status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
+    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)
+            << "finished signal's consume time should be greater than publish time";
 }
 
 void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() {
@@ -278,6 +298,7 @@
     int32_t eventId = InputEvent::nextId();
     constexpr bool hasFocus = true;
     constexpr bool inTouchMode = true;
+    const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     status = mPublisher->publishFocusEvent(seq, eventId, hasFocus, inTouchMode);
     ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK";
@@ -302,12 +323,21 @@
 
     uint32_t finishedSeq = 0;
     bool handled = false;
-    status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
+    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)
+            << "finished signal's consume time should be greater than publish time";
 }
 
 void InputPublisherAndConsumerTest::PublishAndConsumeCaptureEvent() {
@@ -316,6 +346,7 @@
     constexpr uint32_t seq = 42;
     int32_t eventId = InputEvent::nextId();
     constexpr bool captureEnabled = true;
+    const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     status = mPublisher->publishCaptureEvent(seq, eventId, captureEnabled);
     ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK";
@@ -339,12 +370,21 @@
 
     uint32_t finishedSeq = 0;
     bool handled = false;
-    status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
+    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)
+            << "finished signal's consume time should be greater than publish time";
 }
 
 TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 4107d61..a886585 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -83,6 +83,7 @@
   CHECK_OFFSET(InputMessage::Body::Capture, pointerCaptureEnabled, 4);
 
   CHECK_OFFSET(InputMessage::Body::Finished, handled, 4);
+  CHECK_OFFSET(InputMessage::Body::Finished, consumeTime, 8);
 }
 
 void TestHeaderSize() {
@@ -100,7 +101,7 @@
     static_assert(sizeof(InputMessage::Body::Motion) ==
                   offsetof(InputMessage::Body::Motion, pointers) +
                           sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
-    static_assert(sizeof(InputMessage::Body::Finished) == 8);
+    static_assert(sizeof(InputMessage::Body::Finished) == 16);
     static_assert(sizeof(InputMessage::Body::Focus) == 8);
     static_assert(sizeof(InputMessage::Body::Capture) == 8);
 }
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 6b085a3..dc2dd29 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -138,8 +138,8 @@
     void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
                        VsyncEventData vsyncEventData) override;
     void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
-    void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId,
-                               nsecs_t vsyncPeriod) override;
+    void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
+                             nsecs_t vsyncPeriod) override;
     void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override;
     void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
                                     std::vector<FrameRateOverride> overrides) override;
@@ -382,8 +382,8 @@
             this, to_string(displayId).c_str(), toString(connected));
 }
 
-void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) {
-    LOG_ALWAYS_FATAL("dispatchConfigChanged was called but was never registered");
+void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) {
+    LOG_ALWAYS_FATAL("dispatchModeChanged was called but was never registered");
 }
 
 void Choreographer::dispatchFrameRateOverrides(nsecs_t, PhysicalDisplayId,
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index 277635c..c595aa6 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -16,8 +16,8 @@
 
 #include <apex/display.h>
 #include <gui/SurfaceComposerClient.h>
-#include <ui/DisplayConfig.h>
 #include <ui/DisplayInfo.h>
+#include <ui/DisplayMode.h>
 #include <ui/GraphicTypes.h>
 #include <ui/PixelFormat.h>
 
@@ -134,8 +134,8 @@
         return NO_INIT;
     }
 
-    std::vector<DisplayConfigImpl> configsPerDisplay[size];
-    int numConfigs = 0;
+    std::vector<DisplayConfigImpl> modesPerDisplay[size];
+    int numModes = 0;
     for (int i = 0; i < size; ++i) {
         const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]);
 
@@ -145,23 +145,23 @@
             return status;
         }
 
-        Vector<DisplayConfig> configs;
-        if (const status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs);
+        Vector<ui::DisplayMode> modes;
+        if (const status_t status = SurfaceComposerClient::getDisplayModes(token, &modes);
             status != OK) {
             return status;
         }
-        if (configs.empty()) {
+        if (modes.empty()) {
             return NO_INIT;
         }
 
-        numConfigs += configs.size();
-        configsPerDisplay[i].reserve(configs.size());
-        for (int j = 0; j < configs.size(); ++j) {
-            const DisplayConfig& config = configs[j];
-            configsPerDisplay[i].emplace_back(
-                    DisplayConfigImpl{config.resolution.getWidth(), config.resolution.getHeight(),
-                                      info.density, config.refreshRate, config.sfVsyncOffset,
-                                      config.appVsyncOffset});
+        numModes += modes.size();
+        modesPerDisplay[i].reserve(modes.size());
+        for (int j = 0; j < modes.size(); ++j) {
+            const ui::DisplayMode& mode = modes[j];
+            modesPerDisplay[i].emplace_back(
+                    DisplayConfigImpl{mode.resolution.getWidth(), mode.resolution.getHeight(),
+                                      info.density, mode.refreshRate, mode.sfVsyncOffset,
+                                      mode.appVsyncOffset});
         }
     }
 
@@ -192,7 +192,7 @@
     // contiguous block of DisplayConfigImpls specific to that display.
     DisplayImpl** const impls = reinterpret_cast<DisplayImpl**>(
             malloc((sizeof(DisplayImpl) + sizeof(DisplayImpl*)) * size +
-                   sizeof(DisplayConfigImpl) * numConfigs));
+                   sizeof(DisplayConfigImpl) * numModes));
     DisplayImpl* const displayData = reinterpret_cast<DisplayImpl*>(impls + size);
     DisplayConfigImpl* configData = reinterpret_cast<DisplayConfigImpl*>(displayData + size);
 
@@ -200,7 +200,7 @@
         const PhysicalDisplayId id = ids[i];
         const ADisplayType type = (internalId == id) ? ADisplayType::DISPLAY_TYPE_INTERNAL
                                                      : ADisplayType::DISPLAY_TYPE_EXTERNAL;
-        const std::vector<DisplayConfigImpl>& configs = configsPerDisplay[i];
+        const std::vector<DisplayConfigImpl>& configs = modesPerDisplay[i];
         memcpy(configData, configs.data(), sizeof(DisplayConfigImpl) * configs.size());
 
         displayData[i] = DisplayImpl{id,
@@ -257,7 +257,7 @@
     CHECK_NOT_NULL(display);
 
     sp<IBinder> token = getToken(display);
-    const int index = SurfaceComposerClient::getActiveConfig(token);
+    const int index = SurfaceComposerClient::getActiveDisplayModeId(token);
     if (index < 0) {
         return index;
     }
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index ffe4412..7aa2cf4 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -256,6 +256,7 @@
     NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER          = 46,    /* private */
     NATIVE_WINDOW_SET_QUERY_INTERCEPTOR           = 47,    /* private */
     NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO         = 48,    /* private */
+    NATIVE_WINDOW_GET_EXTRA_BUFFER_COUNT          = 49,    /* private */
     // clang-format on
 };
 
@@ -1030,6 +1031,11 @@
                            frameTimelineVsyncId, inputEventId);
 }
 
+static inline int native_window_get_extra_buffer_count(
+    struct ANativeWindow* window, int* extraBuffers) {
+    return window->perform(window, NATIVE_WINDOW_GET_EXTRA_BUFFER_COUNT, extraBuffers);
+}
+
 // ------------------------------------------------------------------------------------------------
 // Candidates for APEX visibility
 // These functions are planned to be made stable for APEX modules, but have not
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index d69c7ae..00540b8 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -31,10 +31,6 @@
         "libui",
         "libutils",
     ],
-    include_dirs: [
-        "external/skia/src/gpu",
-    ],
-    whole_static_libs: ["libskia"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
 }
@@ -106,6 +102,10 @@
         ":librenderengine_threaded_sources",
         ":librenderengine_skia_sources",
     ],
+    include_dirs: [
+        "external/skia/src/gpu",
+    ],
+    whole_static_libs: ["libskia"],
     lto: {
         thin: true,
     },
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 3a727c9..7661233 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -28,6 +28,7 @@
 #include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
+#include <ui/StretchEffect.h>
 #include <ui/Transform.h>
 
 namespace android {
@@ -155,6 +156,8 @@
 
     std::vector<BlurRegion> blurRegions;
 
+    StretchEffect stretchEffect;
+
     // Name associated with the layer for debugging purposes.
     std::string name;
 };
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index dd26b17..b4a2d79 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -286,6 +286,7 @@
     }
 
     if (args.supportsBackgroundBlur) {
+        ALOGD("Background Blurs Enabled");
         mBlurFilter = new BlurFilter();
     }
     mCapture = std::make_unique<SkiaCapture>();
@@ -491,6 +492,9 @@
                                                               const LayerSettings* layer,
                                                               const DisplaySettings& display,
                                                               bool undoPremultipliedAlpha) {
+    if (layer->stretchEffect.hasEffect()) {
+        // TODO: Implement
+    }
     if (mUseColorManagement &&
         needsLinearEffect(layer->colorTransform, layer->sourceDataspace, display.outputDataspace)) {
         LinearEffect effect = LinearEffect{.inputDataspace = layer->sourceDataspace,
@@ -513,6 +517,45 @@
     return shader;
 }
 
+void SkiaGLRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) {
+    if (mCapture->isCaptureRunning()) {
+        // Record display settings when capture is running.
+        std::stringstream displaySettings;
+        PrintTo(display, &displaySettings);
+        // Store the DisplaySettings in additional information.
+        canvas->drawAnnotation(SkRect::MakeEmpty(), "DisplaySettings",
+                               SkData::MakeWithCString(displaySettings.str().c_str()));
+    }
+
+    // Before doing any drawing, let's make sure that we'll start at the origin of the display.
+    // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual
+    // displays might have different scaling when compared to the physical screen.
+
+    canvas->clipRect(getSkRect(display.physicalDisplay));
+    canvas->translate(display.physicalDisplay.left, display.physicalDisplay.top);
+
+    const auto clipWidth = display.clip.width();
+    const auto clipHeight = display.clip.height();
+    auto rotatedClipWidth = clipWidth;
+    auto rotatedClipHeight = clipHeight;
+    // Scale is contingent on the rotation result.
+    if (display.orientation & ui::Transform::ROT_90) {
+        std::swap(rotatedClipWidth, rotatedClipHeight);
+    }
+    const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) /
+            static_cast<SkScalar>(rotatedClipWidth);
+    const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) /
+            static_cast<SkScalar>(rotatedClipHeight);
+    canvas->scale(scaleX, scaleY);
+
+    // Canvas rotation is done by centering the clip window at the origin, rotating, translating
+    // back so that the top left corner of the clip is at (0, 0).
+    canvas->translate(rotatedClipWidth / 2, rotatedClipHeight / 2);
+    canvas->rotate(toDegrees(display.orientation));
+    canvas->translate(-clipWidth / 2, -clipHeight / 2);
+    canvas->translate(-display.clip.left, -display.clip.top);
+}
+
 status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
                                         const std::vector<const LayerSettings*>& layers,
                                         const sp<GraphicBuffer>& buffer,
@@ -567,57 +610,49 @@
         }
     }
 
-    sk_sp<SkSurface> surface =
+    sk_sp<SkSurface> dstSurface =
             surfaceTextureRef->getTexture()->getOrCreateSurface(mUseColorManagement
                                                                         ? display.outputDataspace
                                                                         : ui::Dataspace::UNKNOWN,
                                                                 grContext.get());
 
-    SkCanvas* canvas = mCapture->tryCapture(surface.get());
-    if (canvas == nullptr) {
+    SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
+    if (dstCanvas == nullptr) {
         ALOGE("Cannot acquire canvas from Skia.");
         return BAD_VALUE;
     }
+
+    // Find if any layers have requested blur, we'll use that info to decide when to render to an
+    // offscreen buffer and when to render to the native buffer.
+    sk_sp<SkSurface> activeSurface(dstSurface);
+    SkCanvas* canvas = dstCanvas;
+    const LayerSettings* blurCompositionLayer = nullptr;
+    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
+                requiresCompositionLayer = true;
+            }
+            for (auto region : layer->blurRegions) {
+                if (region.blurRadius < BlurFilter::kMaxCrossFadeRadius) {
+                    requiresCompositionLayer = true;
+                }
+            }
+            if (requiresCompositionLayer) {
+                activeSurface = dstSurface->makeSurface(dstSurface->imageInfo());
+                canvas = activeSurface->getCanvas();
+                blurCompositionLayer = layer;
+                break;
+            }
+        }
+    }
+
+    canvas->save();
     // Clear the entire canvas with a transparent black to prevent ghost images.
     canvas->clear(SK_ColorTRANSPARENT);
-    canvas->save();
-
-    if (mCapture->isCaptureRunning()) {
-        // Record display settings when capture is running.
-        std::stringstream displaySettings;
-        PrintTo(display, &displaySettings);
-        // Store the DisplaySettings in additional information.
-        canvas->drawAnnotation(SkRect::MakeEmpty(), "DisplaySettings",
-                               SkData::MakeWithCString(displaySettings.str().c_str()));
-    }
-
-    // Before doing any drawing, let's make sure that we'll start at the origin of the display.
-    // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual
-    // displays might have different scaling when compared to the physical screen.
-
-    canvas->clipRect(getSkRect(display.physicalDisplay));
-    canvas->translate(display.physicalDisplay.left, display.physicalDisplay.top);
-
-    const auto clipWidth = display.clip.width();
-    const auto clipHeight = display.clip.height();
-    auto rotatedClipWidth = clipWidth;
-    auto rotatedClipHeight = clipHeight;
-    // Scale is contingent on the rotation result.
-    if (display.orientation & ui::Transform::ROT_90) {
-        std::swap(rotatedClipWidth, rotatedClipHeight);
-    }
-    const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) /
-            static_cast<SkScalar>(rotatedClipWidth);
-    const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) /
-            static_cast<SkScalar>(rotatedClipHeight);
-    canvas->scale(scaleX, scaleY);
-
-    // Canvas rotation is done by centering the clip window at the origin, rotating, translating
-    // back so that the top left corner of the clip is at (0, 0).
-    canvas->translate(rotatedClipWidth / 2, rotatedClipHeight / 2);
-    canvas->rotate(toDegrees(display.orientation));
-    canvas->translate(-clipWidth / 2, -clipHeight / 2);
-    canvas->translate(-display.clip.left, -display.clip.top);
+    initCanvas(canvas, display);
 
     // TODO: clearRegion was required for SurfaceView when a buffer is not yet available but the
     // view is still on-screen. The clear region could be re-specified as a black color layer,
@@ -644,8 +679,36 @@
 
     for (const auto& layer : layers) {
         ATRACE_NAME("DrawLayer");
-        canvas->save();
 
+        sk_sp<SkImage> blurInput;
+        if (blurCompositionLayer == layer) {
+            LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface);
+            LOG_ALWAYS_FATAL_IF(canvas == dstCanvas);
+
+            // save a snapshot of the activeSurface to use as input to the blur shaders
+            blurInput = activeSurface->makeImageSnapshot();
+
+            // TODO we could skip this step if we know the blur will cover the entire image
+            //  blit the offscreen framebuffer into the destination AHB
+            SkPaint paint;
+            paint.setBlendMode(SkBlendMode::kSrc);
+            activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint);
+
+            // assign dstCanvas to canvas and ensure that the canvas state is up to date
+            canvas = dstCanvas;
+            canvas->save();
+            initCanvas(canvas, display);
+
+            LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getSaveCount() !=
+                                dstSurface->getCanvas()->getSaveCount());
+            LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getTotalMatrix() !=
+                                dstSurface->getCanvas()->getTotalMatrix());
+
+            // assign dstSurface to activeSurface
+            activeSurface = dstSurface;
+        }
+
+        canvas->save();
         if (mCapture->isCaptureRunning()) {
             // Record the name of the layer if the capture is running.
             std::stringstream layerSettings;
@@ -654,34 +717,42 @@
             canvas->drawAnnotation(SkRect::MakeEmpty(), layer->name.c_str(),
                                    SkData::MakeWithCString(layerSettings.str().c_str()));
         }
-
         // Layers have a local transform that should be applied to them
         canvas->concat(getSkM44(layer->geometry.positionTransform).asM33());
 
-        SkPaint paint;
-        const auto& bounds = layer->geometry.boundaries;
-        const auto dest = getSkRect(bounds);
-        const auto layerRect = canvas->getTotalMatrix().mapRect(dest);
-        std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
-        if (mBlurFilter) {
+        const auto bounds = getSkRect(layer->geometry.boundaries);
+        if (mBlurFilter && layerHasBlur(layer)) {
+            std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
+
+            // if multiple layers have blur, then we need to take a snapshot now because
+            // only the lowest layer will have blurImage populated earlier
+            if (!blurInput) {
+                blurInput = activeSurface->makeImageSnapshot();
+            }
+            // rect to be blurred in the coordinate space of blurInput
+            const auto blurRect = canvas->getTotalMatrix().mapRect(bounds);
+
             if (layer->backgroundBlurRadius > 0) {
                 ATRACE_NAME("BackgroundBlur");
-                auto blurredSurface = mBlurFilter->generate(canvas, surface,
-                                                            layer->backgroundBlurRadius, layerRect);
-                cachedBlurs[layer->backgroundBlurRadius] = blurredSurface;
+                auto blurredImage =
+                        mBlurFilter->generate(grContext.get(), layer->backgroundBlurRadius,
+                                              blurInput, blurRect);
 
-                drawBlurRegion(canvas, getBlurRegion(layer), layerRect, blurredSurface);
+                cachedBlurs[layer->backgroundBlurRadius] = blurredImage;
+
+                mBlurFilter->drawBlurRegion(canvas, getBlurRegion(layer), blurRect, blurredImage,
+                                            blurInput);
             }
-            if (layer->blurRegions.size() > 0) {
-                for (auto region : layer->blurRegions) {
-                    if (cachedBlurs[region.blurRadius]) {
-                        continue;
-                    }
+            for (auto region : layer->blurRegions) {
+                if (cachedBlurs[region.blurRadius] != nullptr) {
                     ATRACE_NAME("BlurRegion");
-                    auto blurredSurface =
-                            mBlurFilter->generate(canvas, surface, region.blurRadius, layerRect);
-                    cachedBlurs[region.blurRadius] = blurredSurface;
+                    cachedBlurs[region.blurRadius] =
+                            mBlurFilter->generate(grContext.get(), region.blurRadius, blurInput,
+                                                  blurRect);
                 }
+
+                mBlurFilter->drawBlurRegion(canvas, region, blurRect,
+                                            cachedBlurs[region.blurRadius], blurInput);
             }
         }
 
@@ -695,6 +766,7 @@
                            : layer->sourceDataspace)
                 : ui::Dataspace::UNKNOWN;
 
+        SkPaint paint;
         if (layer->source.buffer.buffer) {
             ATRACE_NAME("DrawImage");
             const auto& item = layer->source.buffer;
@@ -721,7 +793,7 @@
             // textureTansform was intended to be passed directly into a shader, so when
             // building the total matrix with the textureTransform we need to first
             // normalize it, then apply the textureTransform, then scale back up.
-            texMatrix.preScale(1.0f / bounds.getWidth(), 1.0f / bounds.getHeight());
+            texMatrix.preScale(1.0f / bounds.width(), 1.0f / bounds.height());
             texMatrix.postScale(image->width(), image->height());
 
             SkMatrix matrix;
@@ -791,14 +863,10 @@
 
         paint.setColorFilter(filter);
 
-        for (const auto effectRegion : layer->blurRegions) {
-            drawBlurRegion(canvas, effectRegion, layerRect, cachedBlurs[effectRegion.blurRadius]);
-        }
-
         if (layer->shadow.length > 0) {
             const auto rect = layer->geometry.roundedCornersRadius > 0
                     ? getSkRect(layer->geometry.roundedCornersCrop)
-                    : dest;
+                    : bounds;
             drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
         } else {
             // Shadows are assumed to live only on their own layer - it's not valid
@@ -809,7 +877,7 @@
             if (layer->geometry.roundedCornersRadius > 0) {
                 canvas->clipRRect(getRoundedRect(layer), true);
             }
-            canvas->drawRect(dest, paint);
+            canvas->drawRect(bounds, paint);
         }
         canvas->restore();
     }
@@ -817,7 +885,8 @@
     mCapture->endCapture();
     {
         ATRACE_NAME("flush surface");
-        surface->flush();
+        LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
+        activeSurface->flush();
     }
 
     if (drawFence != nullptr) {
@@ -874,6 +943,10 @@
                       .bottom = static_cast<int>(rect.fBottom)};
 }
 
+inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer) {
+    return layer->backgroundBlurRadius > 0 || layer->blurRegions.size();
+}
+
 inline SkColor SkiaGLRenderEngine::getSkColor(const vec4& color) {
     return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255);
 }
@@ -913,53 +986,6 @@
                               flags);
 }
 
-void SkiaGLRenderEngine::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion,
-                                        const SkRect& layerRect, sk_sp<SkImage> blurredImage) {
-    ATRACE_CALL();
-
-    SkPaint paint;
-    paint.setAlpha(static_cast<int>(effectRegion.alpha * 255));
-    const auto matrix = getBlurShaderTransform(canvas, layerRect);
-    SkSamplingOptions linearSampling(SkFilterMode::kLinear, SkMipmapMode::kNone);
-    paint.setShader(blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling,
-                                             &matrix));
-
-    auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right,
-                                 effectRegion.bottom);
-
-    if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 ||
-        effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) {
-        const SkVector radii[4] =
-                {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL),
-                 SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR),
-                 SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL),
-                 SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)};
-        SkRRect roundedRect;
-        roundedRect.setRectRadii(rect, radii);
-        canvas->drawRRect(roundedRect, paint);
-    } else {
-        canvas->drawRect(rect, paint);
-    }
-}
-
-SkMatrix SkiaGLRenderEngine::getBlurShaderTransform(const SkCanvas* canvas,
-                                                    const SkRect& layerRect) {
-    // 1. Apply the blur shader matrix, which scales up the blured surface to its real size
-    auto matrix = mBlurFilter->getShaderMatrix();
-    // 2. Since the blurred surface has the size of the layer, we align it with the
-    // top left corner of the layer position.
-    matrix.postConcat(SkMatrix::Translate(layerRect.fLeft, layerRect.fTop));
-    // 3. Finally, apply the inverse canvas matrix. The snapshot made in the BlurFilter is in the
-    // original surface orientation. The inverse matrix has to be applied to align the blur
-    // surface with the current orientation/position of the canvas.
-    SkMatrix drawInverse;
-    if (canvas->getTotalMatrix().invert(&drawInverse)) {
-        matrix.postConcat(drawInverse);
-    }
-
-    return matrix;
-}
-
 EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
                                                 EGLContext shareContext,
                                                 std::optional<ContextPriority> contextPriority,
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 810fc2a..8ef7359 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -81,17 +81,16 @@
     inline SkRect getSkRect(const Rect& layer);
     inline SkRRect getRoundedRect(const LayerSettings* layer);
     inline BlurRegion getBlurRegion(const LayerSettings* layer);
+    inline bool layerHasBlur(const LayerSettings* layer);
     inline SkColor getSkColor(const vec4& color);
     inline SkM44 getSkM44(const mat4& matrix);
     inline SkPoint3 getSkPoint3(const vec3& vector);
 
     base::unique_fd flush();
     bool waitFence(base::unique_fd fenceFd);
+    void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
     void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius,
                     const ShadowSettings& shadowSettings);
-    void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& layerRect,
-                        sk_sp<SkImage> blurredImage);
-    SkMatrix getBlurShaderTransform(const SkCanvas* canvas, const SkRect& layerRect);
     // If mUseColorManagement is correct and layer needsLinearEffect, it returns a linear runtime
     // shader. Otherwise it returns the input shader.
     sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, const LayerSettings* layer,
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 87b1a7c..9d20e32 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -34,17 +34,14 @@
 BlurFilter::BlurFilter() {
     SkString blurString(R"(
         in shader input;
-        uniform float in_inverseScale;
         uniform float2 in_blurOffset;
 
         half4 main(float2 xy) {
-            float2 scaled_xy = float2(xy.x * in_inverseScale, xy.y * in_inverseScale);
-
-            half4 c = sample(input, scaled_xy);
-            c += sample(input, scaled_xy + float2( in_blurOffset.x,  in_blurOffset.y));
-            c += sample(input, scaled_xy + float2( in_blurOffset.x, -in_blurOffset.y));
-            c += sample(input, scaled_xy + float2(-in_blurOffset.x,  in_blurOffset.y));
-            c += sample(input, scaled_xy + float2(-in_blurOffset.x, -in_blurOffset.y));
+            half4 c = sample(input, xy);
+            c += sample(input, xy + float2( in_blurOffset.x,  in_blurOffset.y));
+            c += sample(input, xy + float2( in_blurOffset.x, -in_blurOffset.y));
+            c += sample(input, xy + float2(-in_blurOffset.x,  in_blurOffset.y));
+            c += sample(input, xy + float2(-in_blurOffset.x, -in_blurOffset.y));
 
             return half4(c.rgb * 0.2, 1.0);
         }
@@ -55,10 +52,26 @@
         LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
     }
     mBlurEffect = std::move(blurEffect);
+
+    SkString mixString(R"(
+        in shader blurredInput;
+        in shader originalInput;
+        uniform float mixFactor;
+
+        half4 main(float2 xy) {
+            return half4(mix(sample(originalInput), sample(blurredInput), mixFactor));
+        }
+    )");
+
+    auto [mixEffect, mixError] = SkRuntimeEffect::Make(mixString);
+    if (!mixEffect) {
+        LOG_ALWAYS_FATAL("RuntimeShader error: %s", mixError.c_str());
+    }
+    mMixEffect = std::move(mixEffect);
 }
 
-sk_sp<SkImage> BlurFilter::generate(SkCanvas* canvas, const sk_sp<SkSurface> input,
-                                    const uint32_t blurRadius, SkRect rect) const {
+sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
+                                    const sk_sp<SkImage> input, const SkRect& blurRect) const {
     // Kawase is an approximation of Gaussian, but it behaves differently from it.
     // A radius transformation is required for approximating them, and also to introduce
     // non-integer steps, necessary to smoothly interpolate large radii.
@@ -66,40 +79,110 @@
     float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
     float radiusByPasses = tmpRadius / (float)numberOfPasses;
 
-    SkImageInfo scaledInfo = SkImageInfo::MakeN32Premul((float)rect.width() * kInputScale,
-                                                        (float)rect.height() * kInputScale);
+    // create blur surface with the bit depth and colorspace of the original surface
+    SkImageInfo scaledInfo = input->imageInfo().makeWH(blurRect.width() * kInputScale,
+                                                       blurRect.height() * kInputScale);
 
     const float stepX = radiusByPasses;
     const float stepY = radiusByPasses;
 
-    // start by drawing and downscaling and doing the first blur pass
+    // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
+    // case you might expect Translate(blurRect.fLeft, blurRect.fTop) X Scale(kInverseInputScale)
+    // but instead we must do the inverse.
+    SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
+    blurMatrix.postScale(kInputScale, kInputScale);
+
+    // start by downscaling and doing the first blur pass
     SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
     SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
     blurBuilder.child("input") =
-            input->makeImageSnapshot(rect.round())
-                    ->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
-    blurBuilder.uniform("in_inverseScale") = kInverseInputScale;
-    blurBuilder.uniform("in_blurOffset") =
-            SkV2{stepX * kInverseInputScale, stepY * kInverseInputScale};
+            input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
+    blurBuilder.uniform("in_blurOffset") = SkV2{stepX * kInputScale, stepY * kInputScale};
 
-    sk_sp<SkImage> tmpBlur(
-            blurBuilder.makeImage(canvas->recordingContext(), nullptr, scaledInfo, false));
+    sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
 
     // And now we'll build our chain of scaled blur stages
-    blurBuilder.uniform("in_inverseScale") = 1.0f;
     for (auto i = 1; i < numberOfPasses; i++) {
         const float stepScale = (float)i * kInputScale;
         blurBuilder.child("input") =
                 tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
         blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};
-        tmpBlur = blurBuilder.makeImage(canvas->recordingContext(), nullptr, scaledInfo, false);
+        tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
     }
 
     return tmpBlur;
 }
 
-SkMatrix BlurFilter::getShaderMatrix() const {
-    return SkMatrix::Scale(kInverseInputScale, kInverseInputScale);
+static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRect, float scale) {
+    // 1. Apply the blur shader matrix, which scales up the blured surface to its real size
+    auto matrix = SkMatrix::Scale(scale, scale);
+    // 2. Since the blurred surface has the size of the layer, we align it with the
+    // top left corner of the layer position.
+    matrix.postConcat(SkMatrix::Translate(blurRect.fLeft, blurRect.fTop));
+    // 3. Finally, apply the inverse canvas matrix. The snapshot made in the BlurFilter is in the
+    // original surface orientation. The inverse matrix has to be applied to align the blur
+    // surface with the current orientation/position of the canvas.
+    SkMatrix drawInverse;
+    if (canvas != nullptr && canvas->getTotalMatrix().invert(&drawInverse)) {
+        matrix.postConcat(drawInverse);
+    }
+    return matrix;
+}
+
+void BlurFilter::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion,
+                                const SkRect& blurRect, sk_sp<SkImage> blurredImage,
+                                sk_sp<SkImage> input) {
+    ATRACE_CALL();
+
+    SkPaint paint;
+    paint.setAlphaf(effectRegion.alpha);
+    if (effectRegion.alpha == 1.0f) {
+        paint.setBlendMode(SkBlendMode::kSrc);
+    }
+
+    const auto blurMatrix = getShaderTransform(canvas, blurRect, kInverseInputScale);
+    SkSamplingOptions linearSampling(SkFilterMode::kLinear, SkMipmapMode::kNone);
+    const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+                                                     linearSampling, &blurMatrix);
+
+    if (effectRegion.blurRadius < kMaxCrossFadeRadius) {
+        // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
+        // case you might expect the matrix to simply be the canvas matrix.
+        SkMatrix inputMatrix;
+        if (!canvas->getTotalMatrix().invert(&inputMatrix)) {
+            ALOGE("matrix was unable to be inverted");
+        }
+
+        SkRuntimeShaderBuilder blurBuilder(mMixEffect);
+        blurBuilder.child("blurredInput") = blurShader;
+        blurBuilder.child("originalInput") =
+                input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling,
+                                  inputMatrix);
+        blurBuilder.uniform("mixFactor") = effectRegion.blurRadius / kMaxCrossFadeRadius;
+
+        paint.setShader(blurBuilder.makeShader(nullptr, true));
+    } else {
+        paint.setShader(blurShader);
+    }
+
+    // TODO we should AA at least the drawRoundRect which would mean no SRC blending
+    // TODO this round rect calculation doesn't match the one used to draw in RenderEngine
+    auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right,
+                                 effectRegion.bottom);
+
+    if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 ||
+        effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) {
+        const SkVector radii[4] =
+                {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL),
+                 SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR),
+                 SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL),
+                 SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)};
+        SkRRect roundedRect;
+        roundedRect.setRectRadii(rect, radii);
+        canvas->drawRRect(roundedRect, paint);
+    } else {
+        canvas->drawRect(rect, paint);
+    }
 }
 
 } // namespace skia
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 08641c9..731ba11 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -20,6 +20,7 @@
 #include <SkImage.h>
 #include <SkRuntimeEffect.h>
 #include <SkSurface.h>
+#include <ui/BlurRegion.h>
 
 using namespace std;
 
@@ -48,13 +49,15 @@
     virtual ~BlurFilter(){};
 
     // Execute blur, saving it to a texture
-    sk_sp<SkImage> generate(SkCanvas* canvas, const sk_sp<SkSurface> input, const uint32_t radius,
-                            SkRect rect) const;
-    // Returns a matrix that should be applied to the blur shader
-    SkMatrix getShaderMatrix() const;
+    sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+                            const sk_sp<SkImage> blurInput, const SkRect& blurRect) const;
+
+    void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& blurRect,
+                        sk_sp<SkImage> blurredImage, sk_sp<SkImage> input);
 
 private:
     sk_sp<SkRuntimeEffect> mBlurEffect;
+    sk_sp<SkRuntimeEffect> mMixEffect;
 };
 
 } // namespace skia
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 58afe6e..886c9da 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -950,15 +950,18 @@
     blurLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     blurLayer.geometry.boundaries = fullscreenRect().toFloatRect();
     blurLayer.backgroundBlurRadius = blurRadius;
+    SourceVariant::fillColor(blurLayer, 0.0f, 0.0f, 1.0f, this);
     blurLayer.alpha = 0;
     layers.push_back(&blurLayer);
 
     invokeDraw(settings, layers);
 
-    expectBufferColor(Rect(center - 1, center - 5, center, center + 5), 150, 150, 0, 255,
-                      50 /* tolerance */);
-    expectBufferColor(Rect(center, center - 5, center + 1, center + 5), 150, 150, 0, 255,
-                      50 /* tolerance */);
+    // solid color
+    expectBufferColor(Rect(0, 0, 1, 1), 255, 0, 0, 255, 0 /* tolerance */);
+
+    // blurred color (downsampling should result in the center color being close to 128)
+    expectBufferColor(Rect(center - 1, center - 5, center + 1, center + 5), 128, 128, 0, 255,
+                      10 /* tolerance */);
 }
 
 template <typename SourceVariant>
diff --git a/libs/ui/include/ui/DisplayConfig.h b/libs/ui/include/ui/DisplayMode.h
similarity index 81%
rename from libs/ui/include/ui/DisplayConfig.h
rename to libs/ui/include/ui/DisplayMode.h
index d6fbaab..145d7ef 100644
--- a/libs/ui/include/ui/DisplayConfig.h
+++ b/libs/ui/include/ui/DisplayMode.h
@@ -21,10 +21,10 @@
 #include <ui/Size.h>
 #include <utils/Timers.h>
 
-namespace android {
+namespace android::ui {
 
-// Configuration supported by physical display.
-struct DisplayConfig {
+// Mode supported by physical display.
+struct DisplayMode {
     ui::Size resolution;
     float xDpi = 0;
     float yDpi = 0;
@@ -33,9 +33,9 @@
     nsecs_t appVsyncOffset = 0;
     nsecs_t sfVsyncOffset = 0;
     nsecs_t presentationDeadline = 0;
-    int configGroup = -1;
+    int group = -1;
 };
 
-static_assert(std::is_trivially_copyable_v<DisplayConfig>);
+static_assert(std::is_trivially_copyable_v<DisplayMode>);
 
-} // namespace android
+} // namespace android::ui
diff --git a/libs/ui/include/ui/StretchEffect.h b/libs/ui/include/ui/StretchEffect.h
new file mode 100644
index 0000000..1d2460c
--- /dev/null
+++ b/libs/ui/include/ui/StretchEffect.h
@@ -0,0 +1,57 @@
+/*
+ * 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 <utils/Flattenable.h>
+#include "FloatRect.h"
+
+#include <type_traits>
+
+namespace android {
+
+struct StretchEffect : public LightFlattenablePod<StretchEffect> {
+    FloatRect area = {0, 0, 0, 0};
+    float vectorX = 0;
+    float vectorY = 0;
+    float maxAmount = 0;
+
+    bool operator==(const StretchEffect& other) const {
+        return area == other.area && vectorX == other.vectorX && vectorY == other.vectorY &&
+                maxAmount == other.maxAmount;
+    }
+
+    static bool isZero(float value) {
+        constexpr float NON_ZERO_EPSILON = 0.001f;
+        return fabsf(value) <= NON_ZERO_EPSILON;
+    }
+
+    bool isNoOp() const { return isZero(vectorX) && isZero(vectorY); }
+
+    bool hasEffect() const { return !isNoOp(); }
+
+    void sanitize() {
+        // If the area is empty, or the max amount is zero, then reset back to defaults
+        if (area.bottom >= area.top || area.left >= area.right || isZero(maxAmount)) {
+            *this = StretchEffect{};
+        }
+    }
+};
+
+static_assert(std::is_trivially_copyable<StretchEffect>::value,
+              "StretchEffect must be trivially copyable to be flattenable");
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/DisplayConfig.h b/libs/ui/include_vndk/ui/DisplayConfig.h
deleted file mode 120000
index 1450319..0000000
--- a/libs/ui/include_vndk/ui/DisplayConfig.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/ui/DisplayConfig.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/DisplayMode.h b/libs/ui/include_vndk/ui/DisplayMode.h
new file mode 120000
index 0000000..c87754a
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DisplayMode.h
@@ -0,0 +1 @@
+../../include/ui/DisplayMode.h
\ No newline at end of file
diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp
index dfb9c92..fd4522e 100644
--- a/opengl/tests/lib/WindowSurface.cpp
+++ b/opengl/tests/lib/WindowSurface.cpp
@@ -21,7 +21,7 @@
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
 #include <ui/DisplayState.h>
 
 using namespace android;
@@ -42,10 +42,10 @@
         return;
     }
 
-    DisplayConfig displayConfig;
-    err = SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
+    ui::DisplayMode displayMode;
+    err = SurfaceComposerClient::getActiveDisplayMode(displayToken, &displayMode);
     if (err != NO_ERROR) {
-        fprintf(stderr, "ERROR: unable to get active display config\n");
+        fprintf(stderr, "ERROR: unable to get active display mode\n");
         return;
     }
 
@@ -56,7 +56,7 @@
         return;
     }
 
-    const ui::Size& resolution = displayConfig.resolution;
+    const ui::Size& resolution = displayMode.resolution;
     auto width = resolution.getWidth();
     auto height = resolution.getHeight();
 
diff --git a/services/automotive/display/AutomotiveDisplayProxyService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp
index 321bb83..d6fc695 100644
--- a/services/automotive/display/AutomotiveDisplayProxyService.cpp
+++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp
@@ -41,10 +41,10 @@
         }
 
         // Get the resolution from stored display state.
-        DisplayConfig displayConfig = {};
-        auto err = SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
+        ui::DisplayMode displayMode = {};
+        auto err = SurfaceComposerClient::getActiveDisplayMode(displayToken, &displayMode);
         if (err != NO_ERROR) {
-            ALOGE("Failed to get display configuration of %lX.  "
+            ALOGE("Failed to get display mode of %lX.  "
                   "This display will be ignored.", (unsigned long)id);
             return nullptr;
         }
@@ -57,8 +57,8 @@
             return nullptr;
         }
 
-        auto displayWidth  = displayConfig.resolution.getWidth();
-        auto displayHeight = displayConfig.resolution.getHeight();
+        auto displayWidth  = displayMode.resolution.getWidth();
+        auto displayHeight = displayMode.resolution.getHeight();
         if ((displayState.orientation != ui::ROTATION_0) &&
             (displayState.orientation != ui::ROTATION_180)) {
             std::swap(displayWidth, displayHeight);
@@ -161,10 +161,10 @@
     if (displayToken == nullptr) {
         ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
     } else {
-        DisplayConfig displayConfig = {};
-        auto err = SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
+        ui::DisplayMode displayMode = {};
+        auto err = SurfaceComposerClient::getActiveDisplayMode(displayToken, &displayMode);
         if (err != NO_ERROR) {
-            ALOGW("Failed to get display configuration of %lX.  "
+            ALOGW("Failed to get display mode of %lX.  "
                   "This display will be ignored.", (unsigned long)id);
         }
 
@@ -175,7 +175,7 @@
                   "This display will be ignored.", (unsigned long)id);
         }
 
-        activeConfig.setToExternal((uint8_t*)&displayConfig, sizeof(DisplayConfig));
+        activeConfig.setToExternal((uint8_t*)&displayMode, sizeof(ui::DisplayMode));
         activeState.setToExternal((uint8_t*)&displayState, sizeof(DisplayState));
     }
 
diff --git a/services/automotive/display/include/AutomotiveDisplayProxyService.h b/services/automotive/display/include/AutomotiveDisplayProxyService.h
index e2fc0d2..4482b9b 100644
--- a/services/automotive/display/include/AutomotiveDisplayProxyService.h
+++ b/services/automotive/display/include/AutomotiveDisplayProxyService.h
@@ -20,7 +20,7 @@
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
 #include <ui/DisplayState.h>
 #include <tuple>
 #include <vector>
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 499f42e..45a007b 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -272,6 +272,7 @@
     std::string obscuringPackage;
     bool enabled;
     int32_t pid;
+    nsecs_t consumeTime; // time when the event was consumed by InputConsumer
 };
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 5a577d4..e4237b5 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1247,9 +1247,10 @@
         nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
         DropReason& dropReason) {
     const bool haveWindowWithPointerCapture = mWindowTokenWithPointerCapture != nullptr;
-    if (entry->pointerCaptureEnabled == haveWindowWithPointerCapture) {
-        LOG_ALWAYS_FATAL_IF(mFocusedWindowRequestedPointerCapture,
-                            "The Pointer Capture state has already been dispatched to the window.");
+    if (entry->pointerCaptureEnabled && haveWindowWithPointerCapture) {
+        LOG_ALWAYS_FATAL("Pointer Capture has already been enabled for the window.");
+    }
+    if (!entry->pointerCaptureEnabled && !haveWindowWithPointerCapture) {
         // Pointer capture was already forcefully disabled because of focus change.
         dropReason = DropReason::NOT_DROPPED;
         return;
@@ -3112,7 +3113,7 @@
 
 void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
                                                 const sp<Connection>& connection, uint32_t seq,
-                                                bool handled) {
+                                                bool handled, nsecs_t consumeTime) {
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
           connection->getInputChannelName().c_str(), seq, toString(handled));
@@ -3124,7 +3125,7 @@
     }
 
     // Notify other system components and prepare to start the next dispatch cycle.
-    onDispatchCycleFinishedLocked(currentTime, connection, seq, handled);
+    onDispatchCycleFinishedLocked(currentTime, connection, seq, handled, consumeTime);
 }
 
 void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
@@ -3195,13 +3196,15 @@
             bool gotOne = false;
             status_t status;
             for (;;) {
-                uint32_t seq;
-                bool handled;
-                status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
+                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) {
                     break;
                 }
-                d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
                 gotOne = true;
             }
             if (gotOne) {
@@ -5153,13 +5156,14 @@
 
 void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
                                                     const sp<Connection>& connection, uint32_t seq,
-                                                    bool handled) {
+                                                    bool handled, nsecs_t consumeTime) {
     std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
             &InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
     commandEntry->connection = connection;
     commandEntry->eventTime = currentTime;
     commandEntry->seq = seq;
     commandEntry->handled = handled;
+    commandEntry->consumeTime = consumeTime;
     postCommandLocked(std::move(commandEntry));
 }
 
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index c93d74e..83094c2 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -528,7 +528,7 @@
     void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
             REQUIRES(mLock);
     void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
-                                   uint32_t seq, bool handled) REQUIRES(mLock);
+                                   uint32_t seq, bool handled, nsecs_t consumeTime) REQUIRES(mLock);
     void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
                                         bool notify) REQUIRES(mLock);
     void drainDispatchQueue(std::deque<DispatchEntry*>& queue);
@@ -578,7 +578,8 @@
 
     // Interesting events that we might like to log or tell the framework about.
     void onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp<Connection>& connection,
-                                       uint32_t seq, bool handled) REQUIRES(mLock);
+                                       uint32_t seq, bool handled, nsecs_t consumeTime)
+            REQUIRES(mLock);
     void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection)
             REQUIRES(mLock);
     void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock);
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 8fc6f4a..574f651 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -520,7 +520,8 @@
 }
 
 void InputDevice::notifyReset(nsecs_t when) {
-    mContext->notifyDeviceReset(when, mId);
+    NotifyDeviceResetArgs args(mContext->getNextId(), when, mId);
+    mContext->getListener()->notifyDeviceReset(&args);
 }
 
 std::optional<int32_t> InputDevice::getAssociatedDisplayId() {
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index c044393..14fb77b 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -339,7 +339,8 @@
     updateGlobalMetaStateLocked();
 
     // Enqueue configuration changed.
-    mContext.notifyConfigurationChanged(when);
+    NotifyConfigurationChangedArgs args(mContext.getNextId(), when);
+    mQueuedListener->notifyConfigurationChanged(&args);
 }
 
 void InputReader::refreshConfigurationLocked(uint32_t changes) {
@@ -366,7 +367,9 @@
     }
 
     if (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE) {
-        mContext.notifyPointerCaptureChanged(now, mConfig.pointerCapture);
+        const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
+                                                   mConfig.pointerCapture);
+        mQueuedListener->notifyPointerCaptureChanged(&args);
     }
 }
 
@@ -885,69 +888,16 @@
     return mReader->mPolicy.get();
 }
 
+InputListenerInterface* InputReader::ContextImpl::getListener() {
+    return mReader->mQueuedListener.get();
+}
+
 EventHubInterface* InputReader::ContextImpl::getEventHub() {
     return mReader->mEventHub.get();
 }
 
-void InputReader::ContextImpl::notifyConfigurationChanged(nsecs_t when) {
-    NotifyConfigurationChangedArgs args(mIdGenerator.nextId(), when);
-    mReader->mQueuedListener->notifyConfigurationChanged(&args);
-}
-
-void InputReader::ContextImpl::notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source,
-                                         int32_t displayId, uint32_t policyFlags, int32_t action,
-                                         int32_t flags, int32_t keyCode, int32_t scanCode,
-                                         int32_t metaState, nsecs_t downTime) {
-    NotifyKeyArgs args(mIdGenerator.nextId(), eventTime, deviceId, source, displayId, policyFlags,
-                       action, flags, keyCode, scanCode, metaState, downTime);
-    mReader->mQueuedListener->notifyKey(&args);
-}
-void InputReader::ContextImpl::notifyMotion(
-        nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
-        uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags,
-        int32_t metaState, int32_t buttonState, MotionClassification classification,
-        int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties,
-        const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
-        float xCursorPosition, float yCursorPosition, nsecs_t downTime,
-        const std::vector<TouchVideoFrame>& videoFrames) {
-    NotifyMotionArgs args(mIdGenerator.nextId(), eventTime, deviceId, source, displayId,
-                          policyFlags, action, actionButton, flags, metaState, buttonState,
-                          classification, edgeFlags, pointerCount, pointerProperties, pointerCoords,
-                          xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime,
-                          videoFrames);
-    mReader->mQueuedListener->notifyMotion(&args);
-}
-
-void InputReader::ContextImpl::notifySensor(nsecs_t when, int32_t deviceId,
-                                            InputDeviceSensorType sensorType,
-                                            InputDeviceSensorAccuracy accuracy,
-                                            bool accuracyChanged, nsecs_t timestamp,
-                                            std::vector<float> values) {
-    NotifySensorArgs args(mIdGenerator.nextId(), when, deviceId, AINPUT_SOURCE_SENSOR, sensorType,
-                          accuracy, accuracyChanged, timestamp, std::move(values));
-    mReader->mQueuedListener->notifySensor(&args);
-}
-
-void InputReader::ContextImpl::notifyVibratorState(nsecs_t when, int32_t deviceId, bool isOn) {
-    NotifyVibratorStateArgs args(mIdGenerator.nextId(), when, deviceId, isOn);
-    mReader->mQueuedListener->notifyVibratorState(&args);
-}
-
-void InputReader::ContextImpl::notifySwitch(nsecs_t eventTime, uint32_t switchValues,
-                                            uint32_t switchMask) {
-    NotifySwitchArgs args(mIdGenerator.nextId(), eventTime, 0 /*policyFlags*/, switchValues,
-                          switchMask);
-    mReader->mQueuedListener->notifySwitch(&args);
-}
-
-void InputReader::ContextImpl::notifyDeviceReset(nsecs_t when, int32_t deviceId) {
-    NotifyDeviceResetArgs args(mIdGenerator.nextId(), when, deviceId);
-    mReader->mQueuedListener->notifyDeviceReset(&args);
-}
-
-void InputReader::ContextImpl::notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) {
-    const NotifyPointerCaptureChangedArgs args(mIdGenerator.nextId(), when, hasCapture);
-    mReader->mQueuedListener->notifyPointerCaptureChanged(&args);
+int32_t InputReader::ContextImpl::getNextId() {
+    return mIdGenerator.nextId();
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 5f78149..81e3e9a 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -131,31 +131,11 @@
         void dispatchExternalStylusState(const StylusState& outState)
                 NO_THREAD_SAFETY_ANALYSIS override;
         InputReaderPolicyInterface* getPolicy() NO_THREAD_SAFETY_ANALYSIS override;
+        InputListenerInterface* getListener() NO_THREAD_SAFETY_ANALYSIS override;
         EventHubInterface* getEventHub() NO_THREAD_SAFETY_ANALYSIS override;
+        int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override;
         void updateLedMetaState(int32_t metaState) NO_THREAD_SAFETY_ANALYSIS override;
         int32_t getLedMetaState() NO_THREAD_SAFETY_ANALYSIS override;
-
-        // Send events to InputListener interface
-        void notifyConfigurationChanged(nsecs_t when) override;
-        void notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
-                       uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
-                       int32_t scanCode, int32_t metaState, nsecs_t downTime) override;
-        void notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
-                          uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags,
-                          int32_t metaState, int32_t buttonState,
-                          MotionClassification classification, int32_t edgeFlags,
-                          uint32_t pointerCount, const PointerProperties* pointerProperties,
-                          const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
-                          float xCursorPosition, float yCursorPosition, nsecs_t downTime,
-                          const std::vector<TouchVideoFrame>& videoFrames) override;
-        void notifySwitch(nsecs_t eventTime, uint32_t switchValues, uint32_t switchMask) override;
-        void notifySensor(nsecs_t when, int32_t deviceId, InputDeviceSensorType sensorType,
-                          InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
-                          nsecs_t timestamp, std::vector<float> values) override;
-        void notifyVibratorState(nsecs_t when, int32_t deviceId, bool isOn) override;
-        void notifyDeviceReset(nsecs_t when, int32_t deviceId) override;
-        void notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) override;
-
     } mContext;
 
     friend class ContextImpl;
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index e6ea523..dc807f7 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -55,34 +55,13 @@
     virtual void dispatchExternalStylusState(const StylusState& outState) = 0;
 
     virtual InputReaderPolicyInterface* getPolicy() = 0;
+    virtual InputListenerInterface* getListener() = 0;
     virtual EventHubInterface* getEventHub() = 0;
 
+    virtual int32_t getNextId() = 0;
+
     virtual void updateLedMetaState(int32_t metaState) = 0;
     virtual int32_t getLedMetaState() = 0;
-
-    // Send events to InputListener interface
-
-    virtual void notifyConfigurationChanged(nsecs_t when) = 0;
-    virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
-                           uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
-                           int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0;
-    virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t source,
-                              int32_t displayId, uint32_t policyFlags, int32_t action,
-                              int32_t actionButton, int32_t flags, int32_t metaState,
-                              int32_t buttonState, MotionClassification classification,
-                              int32_t edgeFlags, uint32_t pointerCount,
-                              const PointerProperties* pointerProperties,
-                              const PointerCoords* pointerCoords, float xPrecision,
-                              float yPrecision, float xCursorPosition, float yCursorPosition,
-                              nsecs_t downTime,
-                              const std::vector<TouchVideoFrame>& videoFrames) = 0;
-    virtual void notifySwitch(nsecs_t eventTime, uint32_t switchValues, uint32_t switchMask) = 0;
-    virtual void notifySensor(nsecs_t when, int32_t deviceId, InputDeviceSensorType sensorType,
-                              InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
-                              nsecs_t timestamp, std::vector<float> values) = 0;
-    virtual void notifyVibratorState(nsecs_t when, int32_t deviceId, bool isOn) = 0;
-    virtual void notifyDeviceReset(nsecs_t when, int32_t deviceId) = 0;
-    virtual void notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 7f7b33c..254b64b 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -175,7 +175,8 @@
         }
         bumpGeneration();
         if (changes) {
-            getContext()->notifyDeviceReset(when, getDeviceId());
+            NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
+            getListener()->notifyDeviceReset(&args);
         }
     }
 
@@ -382,35 +383,40 @@
             while (!released.isEmpty()) {
                 int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
                 buttonState &= ~actionButton;
-                getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                           AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
-                                           metaState, buttonState, MotionClassification::NONE,
-                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                           &pointerCoords, mXPrecision, mYPrecision,
-                                           xCursorPosition, yCursorPosition, downTime,
-                                           /* videoFrames */ {});
+                NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, getDeviceId(),
+                                             mSource, displayId, policyFlags,
+                                             AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
+                                             metaState, buttonState, MotionClassification::NONE,
+                                             AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                             &pointerCoords, mXPrecision, mYPrecision,
+                                             xCursorPosition, yCursorPosition, downTime,
+                                             /* videoFrames */ {});
+                getListener()->notifyMotion(&releaseArgs);
             }
         }
 
-        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                   motionEventAction, 0, 0, metaState, currentButtonState,
-                                   MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                   &pointerProperties, &pointerCoords, mXPrecision, mYPrecision,
-                                   xCursorPosition, yCursorPosition, downTime,
-                                   /* videoFrames */ {});
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, motionEventAction, 0, 0, metaState, currentButtonState,
+                              MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                              &pointerProperties, &pointerCoords, mXPrecision, mYPrecision,
+                              xCursorPosition, yCursorPosition, downTime,
+                              /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
 
         if (buttonsPressed) {
             BitSet32 pressed(buttonsPressed);
             while (!pressed.isEmpty()) {
                 int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
                 buttonState |= actionButton;
-                getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+                NotifyMotionArgs pressArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
+                                           displayId, policyFlags,
                                            AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
                                            metaState, buttonState, MotionClassification::NONE,
                                            AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                            &pointerCoords, mXPrecision, mYPrecision,
                                            xCursorPosition, yCursorPosition, downTime,
                                            /* videoFrames */ {});
+                getListener()->notifyMotion(&pressArgs);
             }
         }
 
@@ -418,12 +424,13 @@
 
         // Send hover move after UP to tell the application that the mouse is hovering now.
         if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
-            getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                       AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                                       currentButtonState, MotionClassification::NONE,
+            NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
+                                       displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
+                                       0, metaState, currentButtonState, MotionClassification::NONE,
                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                        &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
                                        yCursorPosition, downTime, /* videoFrames */ {});
+            getListener()->notifyMotion(&hoverArgs);
         }
 
         // Send scroll events.
@@ -431,12 +438,13 @@
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-            getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                       AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
-                                       currentButtonState, MotionClassification::NONE,
-                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                       &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
-                                       yCursorPosition, downTime, /* videoFrames */ {});
+            NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
+                                        displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
+                                        metaState, currentButtonState, MotionClassification::NONE,
+                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                        &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
+                                        yCursorPosition, downTime, /* videoFrames */ {});
+            getListener()->notifyMotion(&scrollArgs);
         }
     }
 
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 44af998..1cc5979 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -19,6 +19,7 @@
 
 #include "EventHub.h"
 #include "InputDevice.h"
+#include "InputListener.h"
 #include "InputReaderContext.h"
 #include "StylusState.h"
 #include "VibrationElement.h"
@@ -47,6 +48,7 @@
     inline const std::string getDeviceName() { return mDeviceContext.getName(); }
     inline InputReaderContext* getContext() { return mDeviceContext.getContext(); }
     inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); }
+    inline InputListenerInterface* getListener() { return getContext()->getListener(); }
 
     virtual uint32_t getSources() = 0;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 28f29e0..37aa140 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -337,12 +337,13 @@
     // TODO: Use the input device configuration to control this behavior more finely.
     uint32_t policyFlags = 0;
 
-    getContext()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE,
-                               policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
-                               MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                               &pointerProperties, &pointerCoords, 0, 0,
-                               AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                               AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
+    NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_JOYSTICK,
+                          ADISPLAY_ID_NONE, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
+                          buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                          &pointerProperties, &pointerCoords, 0, 0,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
+    getListener()->notifyMotion(&args);
 }
 
 void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis,
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 03d7405..8b9f235 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -350,9 +350,10 @@
         policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
     }
 
-    getContext()->notifyKey(when, getDeviceId(), mSource, getDisplayId(), policyFlags,
-                            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
-                            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
+    NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, getDisplayId(),
+                       policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
+                       AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
+    getListener()->notifyKey(&args);
 }
 
 ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 3f8a364..594ff42 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -121,12 +121,13 @@
         int32_t metaState = getContext()->getGlobalMetaState();
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
 
-        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                   AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
-                                   /* buttonState */ 0, MotionClassification::NONE,
-                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                   &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                   AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
+        NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
+                                    displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
+                                    metaState, /* buttonState */ 0, MotionClassification::NONE,
+                                    AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                    &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                    AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
+        getListener()->notifyMotion(&scrollArgs);
     }
 
     mRotaryEncoderScrollAccumulator.finishSync();
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index 68c1e40..7ac2dec 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -405,10 +405,13 @@
             // Convert to Android unit
             convertFromLinuxToAndroid(values, sensorType);
             // Notify dispatcher for sensor event
-            getContext()->notifySensor(when, getDeviceId(), sensorType, sensor.sensorInfo.accuracy,
-                                       sensor.accuracy !=
-                                               sensor.sensorInfo.accuracy /* accuracyChanged */,
-                                       timestamp /* hwTimestamp */, std::move(values));
+            NotifySensorArgs args(getContext()->getNextId(), when, getDeviceId(),
+                                  AINPUT_SOURCE_SENSOR, sensorType, sensor.sensorInfo.accuracy,
+                                  sensor.accuracy !=
+                                          sensor.sensorInfo.accuracy /* accuracyChanged */,
+                                  timestamp /* hwTimestamp */, values);
+
+            getListener()->notifySensor(&args);
             sensor.lastSampleTimeNs = timestamp;
             sensor.accuracy = sensor.sensorInfo.accuracy;
         }
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index 07de244..4f73681 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -56,7 +56,10 @@
 void SwitchInputMapper::sync(nsecs_t when) {
     if (mUpdatedSwitchMask) {
         uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
-        getContext()->notifySwitch(when, updatedSwitchValues, mUpdatedSwitchMask);
+        NotifySwitchArgs args(getContext()->getNextId(), when, 0 /*policyFlags*/,
+                              updatedSwitchValues, mUpdatedSwitchMask);
+        getListener()->notifySwitch(&args);
+
         mUpdatedSwitchMask = 0;
     }
 }
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index ff6341f..a86443d 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -66,8 +66,9 @@
          (currentButtonState & buttonState)) ||
         (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
          !(currentButtonState & buttonState))) {
-        context->notifyKey(when, deviceId, source, displayId, policyFlags, action, 0 /*flags*/,
-                           keyCode, 0 /*scanCode*/, context->getGlobalMetaState(), when);
+        NotifyKeyArgs args(context->getNextId(), when, deviceId, source, displayId, policyFlags,
+                           action, 0, keyCode, 0, context->getGlobalMetaState(), when);
+        context->getListener()->notifyKey(&args);
     }
 }
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 1a17bef..d1df37b 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -397,7 +397,8 @@
     if (changes && resetNeeded) {
         // Send reset, unless this is the first time the device has been configured,
         // in which case the reader will call reset itself after all mappers are ready.
-        getContext()->notifyDeviceReset(when, getDeviceId());
+        NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
+        getListener()->notifyDeviceReset(&args);
     }
 }
 
@@ -1845,9 +1846,10 @@
     int32_t metaState = getContext()->getGlobalMetaState();
     policyFlags |= POLICY_FLAG_VIRTUAL;
 
-    getContext()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, mViewport.displayId,
-                            policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode,
-                            metaState, downTime);
+    NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
+                       mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode,
+                       scanCode, metaState, downTime);
+    getListener()->notifyKey(&args);
 }
 
 void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) {
@@ -2526,11 +2528,12 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
 
         const int32_t displayId = mPointerController->getDisplayId();
-        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                   AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, buttonState,
-                                   MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                   &pointerProperties, &pointerCoords, 0, 0, x, y,
-                                   mPointerGesture.downTime, /* videoFrames */ {});
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+                              buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+                              1, &pointerProperties, &pointerCoords, 0, 0, x, y,
+                              mPointerGesture.downTime, /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
     }
 
     // Update state.
@@ -3445,28 +3448,28 @@
         mPointerSimple.down = false;
 
         // Send up.
-        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                   AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
-                                   mLastRawState.buttonState, MotionClassification::NONE,
-                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
-                                   &mPointerSimple.lastCoords, mOrientedXPrecision,
-                                   mOrientedYPrecision, xCursorPosition, yCursorPosition,
-                                   mPointerSimple.downTime,
-                                   /* videoFrames */ {});
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
+                              mLastRawState.buttonState, MotionClassification::NONE,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+                              &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
+                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
+                              /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
     }
 
     if (mPointerSimple.hovering && !hovering) {
         mPointerSimple.hovering = false;
 
         // Send hover exit.
-        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                   AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
-                                   mLastRawState.buttonState, MotionClassification::NONE,
-                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
-                                   &mPointerSimple.lastCoords, mOrientedXPrecision,
-                                   mOrientedYPrecision, xCursorPosition, yCursorPosition,
-                                   mPointerSimple.downTime,
-                                   /* videoFrames */ {});
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
+                              mLastRawState.buttonState, MotionClassification::NONE,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+                              &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
+                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
+                              /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
     }
 
     if (down) {
@@ -3475,24 +3478,25 @@
             mPointerSimple.downTime = when;
 
             // Send down.
-            getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                       AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState,
-                                       mCurrentRawState.buttonState, MotionClassification::NONE,
-                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                       &mPointerSimple.currentProperties,
-                                       &mPointerSimple.currentCoords, mOrientedXPrecision,
-                                       mOrientedYPrecision, xCursorPosition, yCursorPosition,
-                                       mPointerSimple.downTime, /* videoFrames */ {});
+            NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource,
+                                  displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+                                  metaState, mCurrentRawState.buttonState,
+                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
+            getListener()->notifyMotion(&args);
         }
 
         // Send move.
-        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                   AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
-                                   mCurrentRawState.buttonState, MotionClassification::NONE,
-                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                   &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
-                                   mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                   yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
+                              mCurrentRawState.buttonState, MotionClassification::NONE,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
+                              &mPointerSimple.currentCoords, mOrientedXPrecision,
+                              mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                              mPointerSimple.downTime, /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
     }
 
     if (hovering) {
@@ -3500,24 +3504,25 @@
             mPointerSimple.hovering = true;
 
             // Send hover enter.
-            getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                       AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState,
-                                       mCurrentRawState.buttonState, MotionClassification::NONE,
-                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                       &mPointerSimple.currentProperties,
-                                       &mPointerSimple.currentCoords, mOrientedXPrecision,
-                                       mOrientedYPrecision, xCursorPosition, yCursorPosition,
-                                       mPointerSimple.downTime, /* videoFrames */ {});
+            NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource,
+                                  displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
+                                  metaState, mCurrentRawState.buttonState,
+                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
+            getListener()->notifyMotion(&args);
         }
 
         // Send hover move.
-        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                   AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                                   mCurrentRawState.buttonState, MotionClassification::NONE,
-                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                   &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
-                                   mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                   yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+                              mCurrentRawState.buttonState, MotionClassification::NONE,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
+                              &mPointerSimple.currentCoords, mOrientedXPrecision,
+                              mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                              mPointerSimple.downTime, /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
     }
 
     if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
@@ -3532,14 +3537,14 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                   AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
-                                   mCurrentRawState.buttonState, MotionClassification::NONE,
-                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                   &mPointerSimple.currentProperties, &pointerCoords,
-                                   mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                   yCursorPosition, mPointerSimple.downTime,
-                                   /* videoFrames */ {});
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+                              mCurrentRawState.buttonState, MotionClassification::NONE,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
+                              &pointerCoords, mOrientedXPrecision, mOrientedYPrecision,
+                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
+                              /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
     }
 
     // Save state.
@@ -3610,11 +3615,12 @@
     std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
     std::for_each(frames.begin(), frames.end(),
                   [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
-    getContext()->notifyMotion(when, deviceId, source, displayId, policyFlags, action, actionButton,
-                               flags, metaState, buttonState, MotionClassification::NONE, edgeFlags,
-                               pointerCount, pointerProperties, pointerCoords, xPrecision,
-                               yPrecision, xCursorPosition, yCursorPosition, downTime,
-                               std::move(frames));
+    NotifyMotionArgs args(getContext()->getNextId(), when, deviceId, source, displayId, policyFlags,
+                          action, actionButton, flags, metaState, buttonState,
+                          MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
+                          pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
+                          downTime, std::move(frames));
+    getListener()->notifyMotion(&args);
 }
 
 bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties,
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 2e4ab45..3df6f36 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -53,7 +53,8 @@
     mIndex = -1;
 
     // Request InputReader to notify InputManagerService for vibration started.
-    getContext()->notifyVibratorState(systemTime(), getDeviceId(), true);
+    NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), true);
+    getListener()->notifyVibratorState(&args);
     nextStep();
 }
 
@@ -131,7 +132,8 @@
     getDeviceContext().cancelVibrate();
 
     // Request InputReader to notify InputManagerService for vibration complete.
-    getContext()->notifyVibratorState(systemTime(), getDeviceId(), false);
+    NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), false);
+    getListener()->notifyVibratorState(&args);
 }
 
 void VibratorInputMapper::dump(std::string& dump) {
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index e063dfc..01945db 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -4217,4 +4217,27 @@
     mWindow->assertNoEvents();
 }
 
+TEST_F(InputDispatcherPointerCaptureTests, OutOfOrderRequests) {
+    requestAndVerifyPointerCapture(mWindow, true);
+
+    // The first window loses focus.
+    setFocusedWindow(mSecondWindow);
+    mFakePolicy->waitForSetPointerCapture(false);
+    mWindow->consumeCaptureEvent(false);
+
+    // Request Pointer Capture from the second window before the notification from InputReader
+    // arrives.
+    mDispatcher->requestPointerCapture(mSecondWindow->getToken(), true);
+    mFakePolicy->waitForSetPointerCapture(true);
+
+    // InputReader notifies Pointer Capture was disabled (because of the focus change).
+    notifyPointerCaptureChanged(false);
+
+    // InputReader notifies Pointer Capture was enabled (because of mSecondWindow's request).
+    notifyPointerCaptureChanged(true);
+
+    mSecondWindow->consumeFocusEvent(true);
+    mSecondWindow->consumeCaptureEvent(true);
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index bea9932..409c62a 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -409,7 +409,7 @@
 
     KeyedVector<int32_t, Device*> mDevices;
     std::vector<std::string> mExcludedDevices;
-    List<RawEvent> mEvents GUARDED_BY(mLock);
+    std::vector<RawEvent> mEvents GUARDED_BY(mLock);
     std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames;
     std::vector<int32_t> mVibrators = {0, 1};
 
@@ -722,16 +722,15 @@
         mExcludedDevices = devices;
     }
 
-    size_t getEvents(int, RawEvent* buffer, size_t) override {
-        std::scoped_lock<std::mutex> lock(mLock);
-        if (mEvents.empty()) {
-            return 0;
-        }
+    size_t getEvents(int, RawEvent* buffer, size_t bufferSize) override {
+        std::scoped_lock lock(mLock);
 
-        *buffer = *mEvents.begin();
-        mEvents.erase(mEvents.begin());
+        const size_t filledSize = std::min(mEvents.size(), bufferSize);
+        std::copy(mEvents.begin(), mEvents.begin() + filledSize, buffer);
+
+        mEvents.erase(mEvents.begin(), mEvents.begin() + filledSize);
         mEventsCondition.notify_all();
-        return 1;
+        return filledSize;
     }
 
     std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 825626a..8d1258a 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -173,7 +173,7 @@
         "SurfaceFlingerDefaultFactory.cpp",
         "SurfaceInterceptor.cpp",
         "SurfaceTracing.cpp",
-        "TransactionCompletedThread.cpp",
+        "TransactionCallbackInvoker.cpp",
     ],
 }
 
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 076c707..5f08d80 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -191,7 +191,7 @@
                 JankData(surfaceFrame->getToken(), surfaceFrame->getJankType().value()));
     }
 
-    mFlinger->getTransactionCompletedThread().finalizePendingCallbackHandles(
+    mFlinger->getTransactionCallbackInvoker().finalizePendingCallbackHandles(
             mDrawingState.callbackHandles, jankData);
 
     mDrawingState.callbackHandles = {};
@@ -464,14 +464,14 @@
 
             // Notify the transaction completed thread that there is a pending latched callback
             // handle
-            mFlinger->getTransactionCompletedThread().registerPendingCallbackHandle(handle);
+            mFlinger->getTransactionCallbackInvoker().registerPendingCallbackHandle(handle);
 
             // Store so latched time and release fence can be set
             mCurrentState.callbackHandles.push_back(handle);
 
         } else { // If this layer will NOT need to be relatched and presented this frame
             // Notify the transaction completed thread this handle is done
-            mFlinger->getTransactionCompletedThread().registerUnpresentedCallbackHandle(handle);
+            mFlinger->getTransactionCallbackInvoker().registerUnpresentedCallbackHandle(handle);
         }
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index c445d5b..8402149 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -34,6 +34,7 @@
 #include <gui/BufferQueue.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/GraphicTypes.h>
+#include <ui/StretchEffect.h>
 
 #include "DisplayHardware/Hal.h"
 
@@ -123,6 +124,8 @@
     // List of regions that require blur
     std::vector<BlurRegion> blurRegions;
 
+    StretchEffect stretchEffect;
+
     /*
      * Geometry state
      */
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 4f99495..3907ac5 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -989,9 +989,16 @@
                    });
 
     const nsecs_t renderEngineStart = systemTime();
+    // Only use the framebuffer cache when rendering to an internal display
+    // TODO(b/173560331): This is only to help mitigate memory leaks from virtual displays because
+    // right now we don't have a concrete eviction policy for output buffers: GLESRenderEngine
+    // bounds its framebuffer cache but Skia RenderEngine has no current policy. The best fix is
+    // probably to encapsulate the output buffer into a structure that dispatches resource cleanup
+    // over to RenderEngine, in which case this flag can be removed from the drawLayers interface.
+    const bool useFramebufferCache = outputState.layerStackInternal;
     status_t status =
             renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buf,
-                                    /*useFramebufferCache=*/true, std::move(fd), &readyFence);
+                                    useFramebufferCache, std::move(fd), &readyFence);
 
     if (status != NO_ERROR && mClientCompositionRequestCache) {
         // If rendering was not successful, remove the request from the cache.
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 376bac8..c4ae3a7 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -3003,7 +3003,7 @@
             .WillRepeatedly(Return());
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, true, _, _))
+    EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, false, _, _))
             .WillRepeatedly(Return(NO_ERROR));
 
     verify().execute().expectAFenceWasReturned();
@@ -3030,6 +3030,36 @@
                     }));
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
+            .WillRepeatedly(Return(NO_ERROR));
+
+    verify().execute().expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest,
+       buildsAndRendersRequestListAndCachesFramebufferForInternalLayers) {
+    LayerFE::LayerSettings r1;
+    LayerFE::LayerSettings r2;
+
+    r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
+    r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
+    const constexpr uint32_t kInternalLayerStack = 1234;
+    mOutput.setLayerStackFilter(kInternalLayerStack, true);
+
+    EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+            .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1}));
+    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+            .WillRepeatedly(
+                    Invoke([&](const Region&,
+                               std::vector<LayerFE::LayerSettings>& clientCompositionLayers) {
+                        clientCompositionLayers.emplace_back(r2);
+                    }));
+
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
     EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
             .WillRepeatedly(Return(NO_ERROR));
 
@@ -3054,7 +3084,7 @@
             .WillRepeatedly(Return());
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
             .Times(2)
             .WillOnce(Return(NO_ERROR));
 
@@ -3083,7 +3113,7 @@
             .WillRepeatedly(Return());
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
 
@@ -3115,7 +3145,7 @@
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_))
             .WillOnce(Return(mOutputBuffer))
             .WillOnce(Return(otherOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
             .WillRepeatedly(Return(NO_ERROR));
 
     verify().execute().expectAFenceWasReturned();
@@ -3145,9 +3175,9 @@
             .WillRepeatedly(Return());
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
             .WillOnce(Return(NO_ERROR));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r3)), _, true, _, _))
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r3)), _, false, _, _))
             .WillOnce(Return(NO_ERROR));
 
     verify().execute().expectAFenceWasReturned();
@@ -3197,7 +3227,7 @@
     struct ExpectDisplaySettingsState
           : public CallOrderStateMachineHelper<TestType, ExpectDisplaySettingsState> {
         auto thenExpectDisplaySettingsUsed(renderengine::DisplaySettings settings) {
-            EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, true, _, _))
+            EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, false, _, _))
                     .WillOnce(Return(NO_ERROR));
             return nextState<ExecuteState>();
         }
@@ -3296,7 +3326,7 @@
         EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
                 .WillRepeatedly(Return());
         EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _))
+        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _))
                 .WillRepeatedly(Return(NO_ERROR));
     }
 
@@ -3349,7 +3379,7 @@
     EXPECT_CALL(*mRenderSurface, setProtected(true));
     // Must happen after setting the protected content state.
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
 
     mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
@@ -3419,7 +3449,7 @@
     InSequence seq;
 
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
 
     mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
@@ -3434,7 +3464,7 @@
         EXPECT_CALL(mLayer.outputLayer, writeStateToHWC(false));
         EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
                 .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
-        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
         EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
         EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
                 .WillRepeatedly(Return(&mLayer.outputLayer));
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index a785968..c751f22 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -265,7 +265,8 @@
     StringAppendF(&result, "+ %s\n", getDebugName().c_str());
     StringAppendF(&result, "   powerMode=%s (%d)\n", to_string(mPowerMode).c_str(),
                   static_cast<int32_t>(mPowerMode));
-    StringAppendF(&result, "   activeMode=%s\n", to_string(*getActiveMode()).c_str());
+    StringAppendF(&result, "   activeMode=%s\n",
+                  mSupportedModes.size() ? to_string(*getActiveMode()).c_str() : "none");
 
     result.append("   supportedModes=\n");
 
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 31d1245..1f0f3c3 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -86,8 +86,8 @@
             return *this;
         }
 
-        Builder& setConfigGroup(int32_t configGroup) {
-            mDisplayMode->mConfigGroup = configGroup;
+        Builder& setGroup(int32_t group) {
+            mDisplayMode->mGroup = group;
             return *this;
         }
 
@@ -119,7 +119,10 @@
     nsecs_t getVsyncPeriod() const { return mFps.getPeriodNsecs(); }
     float getDpiX() const { return mDpiX; }
     float getDpiY() const { return mDpiY; }
-    int32_t getConfigGroup() const { return mConfigGroup; }
+
+    // Switches between modes in the same group are seamless, i.e.
+    // without visual interruptions such as a black screen.
+    int32_t getGroup() const { return mGroup; }
 
 private:
     explicit DisplayMode(hal::HWConfigId id) : mHwcId(id) {}
@@ -132,15 +135,15 @@
     Fps mFps;
     float mDpiX = -1;
     float mDpiY = -1;
-    int32_t mConfigGroup = -1;
+    int32_t mGroup = -1;
 };
 
 inline std::string to_string(const DisplayMode& mode) {
     return base::StringPrintf("{id=%zu, hwcId=%d, width=%d, height=%d, refreshRate=%s, "
-                              "dpiX=%.2f, dpiY=%.2f, configGroup=%d}",
+                              "dpiX=%.2f, dpiY=%.2f, group=%d}",
                               mode.getId().value(), mode.getHwcId(), mode.getWidth(),
                               mode.getHeight(), to_string(mode.getFps()).c_str(), mode.getDpiX(),
-                              mode.getDpiY(), mode.getConfigGroup());
+                              mode.getDpiY(), mode.getGroup());
 }
 
 } // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index df14003..27b2b87 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -488,6 +488,7 @@
     compositionState->alpha = alpha;
     compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
     compositionState->blurRegions = drawingState.blurRegions;
+    compositionState->stretchEffect = drawingState.stretchEffect;
 }
 
 void Layer::prepareGeometryCompositionState() {
@@ -556,8 +557,8 @@
             isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
 
     // Force client composition for special cases known only to the front-end.
-    if (isHdrY410() || usesRoundedCorners || drawShadows() ||
-        getDrawingState().blurRegions.size() > 0) {
+    if (isHdrY410() || usesRoundedCorners || drawShadows() || drawingState.blurRegions.size() > 0 ||
+        drawingState.stretchEffect.hasEffect()) {
         compositionState->forceClientComposition = true;
     }
 }
@@ -656,6 +657,7 @@
         layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
         layerSettings.blurRegions = getBlurRegions();
     }
+    layerSettings.stretchEffect = getDrawingState().stretchEffect;
     // Record the name of the layer for debugging further down the stack.
     layerSettings.name = getName();
     return layerSettings;
@@ -867,9 +869,7 @@
 void Layer::popPendingState(State* stateToCommit) {
     ATRACE_CALL();
 
-    if (stateToCommit != nullptr) {
-        mergeSurfaceFrames(*stateToCommit, mPendingStates[0]);
-    }
+    mergeSurfaceFrames(*stateToCommit, mPendingStates[0]);
     *stateToCommit = mPendingStates[0];
     mPendingStates.pop_front();
     ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
@@ -1423,6 +1423,19 @@
     return true;
 }
 
+bool Layer::setStretchEffect(const StretchEffect& effect) {
+    StretchEffect temp = effect;
+    temp.sanitize();
+    if (mCurrentState.stretchEffect == temp) {
+        return false;
+    }
+    mCurrentState.sequence++;
+    mCurrentState.stretchEffect = temp;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
 void Layer::updateTreeHasFrameRateVote() {
     const auto traverseTree = [&](const LayerVector::Visitor& visitor) {
         auto parent = getParent();
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 4c2fd44..a1fdc3c 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -32,6 +32,7 @@
 #include <ui/GraphicBuffer.h>
 #include <ui/PixelFormat.h>
 #include <ui/Region.h>
+#include <ui/StretchEffect.h>
 #include <ui/Transform.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
@@ -54,7 +55,7 @@
 #include "Scheduler/Seamlessness.h"
 #include "SurfaceFlinger.h"
 #include "SurfaceTracing.h"
-#include "TransactionCompletedThread.h"
+#include "TransactionCallbackInvoker.h"
 
 using namespace android::surfaceflinger;
 
@@ -323,6 +324,9 @@
         // An arbitrary threshold for the number of BufferlessSurfaceFrames in the state. Used to
         // trigger a warning if the number of SurfaceFrames crosses the threshold.
         static constexpr uint32_t kStateSurfaceFramesThreshold = 25;
+
+        // Stretch effect to apply to this layer
+        StretchEffect stretchEffect;
     };
 
     /*
@@ -938,6 +942,8 @@
 
     bool backpressureEnabled() { return mDrawingState.flags & layer_state_t::eEnableBackpressure; }
 
+    bool setStretchEffect(const StretchEffect& effect);
+
 protected:
     class SyncPoint {
     public:
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index f90c130..ba43e70 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -90,9 +90,9 @@
             return StringPrintf("VSync{displayId=%s, count=%u, expectedVSyncTimestamp=%" PRId64 "}",
                                 to_string(event.header.displayId).c_str(), event.vsync.count,
                                 event.vsync.expectedVSyncTimestamp);
-        case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
-            return StringPrintf("ConfigChanged{displayId=%s, configId=%u}",
-                                to_string(event.header.displayId).c_str(), event.config.configId);
+        case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE:
+            return StringPrintf("ModeChanged{displayId=%s, modeId=%u}",
+                                to_string(event.header.displayId).c_str(), event.modeChange.modeId);
         default:
             return "Event{}";
     }
@@ -118,12 +118,12 @@
     return event;
 }
 
-DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId, DisplayModeId configId,
-                                              nsecs_t vsyncPeriod) {
+DisplayEventReceiver::Event makeModeChanged(PhysicalDisplayId displayId, DisplayModeId modeId,
+                                            nsecs_t vsyncPeriod) {
     DisplayEventReceiver::Event event;
-    event.header = {DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, displayId, systemTime()};
-    event.config.configId = configId.value();
-    event.config.vsyncPeriod = vsyncPeriod;
+    event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, displayId, systemTime()};
+    event.modeChange.modeId = modeId.value();
+    event.modeChange.vsyncPeriod = vsyncPeriod;
     return event;
 }
 
@@ -303,10 +303,6 @@
     std::lock_guard<std::mutex> lock(mMutex);
 
     const auto request = rate == 0 ? VSyncRequest::None : static_cast<VSyncRequest>(rate);
-    if (request != VSyncRequest::None && connection->resyncCallback) {
-        connection->resyncCallback();
-    }
-
     if (connection->vsyncRequest != request) {
         connection->vsyncRequest = request;
         mCondition.notify_all();
@@ -373,11 +369,11 @@
     mCondition.notify_all();
 }
 
-void EventThread::onConfigChanged(PhysicalDisplayId displayId, DisplayModeId configId,
-                                  nsecs_t vsyncPeriod) {
+void EventThread::onModeChanged(PhysicalDisplayId displayId, DisplayModeId modeId,
+                                nsecs_t vsyncPeriod) {
     std::lock_guard<std::mutex> lock(mMutex);
 
-    mPendingEvents.push_back(makeConfigChanged(displayId, configId, vsyncPeriod));
+    mPendingEvents.push_back(makeModeChanged(displayId, modeId, vsyncPeriod));
     mCondition.notify_all();
 }
 
@@ -518,9 +514,9 @@
         case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
             return true;
 
-        case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: {
+        case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: {
             return connection->mEventRegistration.test(
-                    ISurfaceComposer::EventRegistration::configChanged);
+                    ISurfaceComposer::EventRegistration::modeChanged);
         }
 
         case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 15efe21..3540604 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -123,9 +123,9 @@
 
     virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
 
-    // called when SF changes the active config and apps needs to be notified about the change
-    virtual void onConfigChanged(PhysicalDisplayId displayId, DisplayModeId configId,
-                                 nsecs_t vsyncPeriod) = 0;
+    // called when SF changes the active mode and apps needs to be notified about the change
+    virtual void onModeChanged(PhysicalDisplayId displayId, DisplayModeId modeId,
+                               nsecs_t vsyncPeriod) = 0;
 
     // called when SF updates the Frame Rate Override list
     virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
@@ -173,8 +173,8 @@
 
     void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
 
-    void onConfigChanged(PhysicalDisplayId displayId, DisplayModeId configId,
-                         nsecs_t vsyncPeriod) override;
+    void onModeChanged(PhysicalDisplayId displayId, DisplayModeId modeId,
+                       nsecs_t vsyncPeriod) override;
 
     void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
                                      std::vector<FrameRateOverride> overrides) override;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 7ef531d..ea92ad8 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -102,7 +102,7 @@
     LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
 
     const auto& info = it->second;
-    info->setLastPresentTime(presentTime, now, updateType, mConfigChangePending);
+    info->setLastPresentTime(presentTime, now, updateType, mModeChangePending);
 
     // Activate layer if inactive.
     if (const auto end = activeLayers().end(); it >= end) {
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index bae9b5a..05ecc70 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -51,8 +51,8 @@
     // Sets the display size. Client is responsible for synchronization.
     void setDisplayArea(uint32_t displayArea) { mDisplayArea = displayArea; }
 
-    // Sets whether a config change is pending to be applied
-    void setConfigChangePending(bool pending) { mConfigChangePending = pending; }
+    // Sets whether a mode change is pending to be applied
+    void setModeChangePending(bool pending) { mModeChangePending = pending; }
 
     // Represents which layer activity is recorded
     enum class LayerUpdateType {
@@ -109,8 +109,8 @@
     // Whether to use priority sent from WindowManager to determine the relevancy of the layer.
     const bool mUseFrameRatePriority;
 
-    // Whether a config change is in progress or not
-    std::atomic<bool> mConfigChangePending = false;
+    // Whether a mode change is in progress or not
+    std::atomic<bool> mModeChangePending = false;
 };
 
 } // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 4324855..4b4cdae 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -44,7 +44,7 @@
         mRefreshRateHistory(name) {}
 
 void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
-                                   bool pendingConfigChange) {
+                                   bool pendingModeChange) {
     lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
 
     mLastUpdatedTime = std::max(lastPresentTime, now);
@@ -56,7 +56,7 @@
         case LayerUpdateType::Buffer:
             FrameTimeData frameTime = {.presentTime = lastPresentTime,
                                        .queueTime = mLastUpdatedTime,
-                                       .pendingConfigChange = pendingConfigChange};
+                                       .pendingModeChange = pendingModeChange};
             mFrameTimes.push_back(frameTime);
             if (mFrameTimes.size() > HISTORY_SIZE) {
                 mFrameTimes.pop_front();
@@ -124,11 +124,11 @@
 }
 
 std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const {
-    // Ignore frames captured during a config change
-    const bool isDuringConfigChange =
+    // Ignore frames captured during a mode change
+    const bool isDuringModeChange =
             std::any_of(mFrameTimes.begin(), mFrameTimes.end(),
-                        [](auto frame) { return frame.pendingConfigChange; });
-    if (isDuringConfigChange) {
+                        [](const auto& frame) { return frame.pendingModeChange; });
+    if (isDuringModeChange) {
         return std::nullopt;
     }
 
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index e32ba09..40c0214 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -80,7 +80,7 @@
     // the layer was last updated. If the present time is farther in the future than the
     // updated time, the updated time is the present time.
     void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
-                            bool pendingConfigChange);
+                            bool pendingModeChange);
 
     // Sets an explicit layer vote. This usually comes directly from the application via
     // ANativeWindow_setFrameRate API
@@ -124,7 +124,7 @@
     struct FrameTimeData {
         nsecs_t presentTime; // desiredPresentTime, if provided
         nsecs_t queueTime;  // buffer queue time
-        bool pendingConfigChange;
+        bool pendingModeChange;
     };
 
     // Holds information about the calculated and reported refresh rate
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 81ffe0f..e7a44a7 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -40,13 +40,13 @@
                               to_string(layer.desiredRefreshRate).c_str());
 }
 
-std::vector<Fps> constructKnownFrameRates(const DisplayModes& configs) {
+std::vector<Fps> constructKnownFrameRates(const DisplayModes& modes) {
     std::vector<Fps> knownFrameRates = {Fps(24.0f), Fps(30.0f), Fps(45.0f), Fps(60.0f), Fps(72.0f)};
-    knownFrameRates.reserve(knownFrameRates.size() + configs.size());
+    knownFrameRates.reserve(knownFrameRates.size() + modes.size());
 
     // Add all supported refresh rates to the set
-    for (const auto& config : configs) {
-        const auto refreshRate = Fps::fromPeriodNsecs(config->getVsyncPeriod());
+    for (const auto& mode : modes) {
+        const auto refreshRate = Fps::fromPeriodNsecs(mode->getVsyncPeriod());
         knownFrameRates.emplace_back(refreshRate);
     }
 
@@ -65,8 +65,8 @@
 
 std::string RefreshRate::toString() const {
     return base::StringPrintf("{id=%zu, hwcId=%d, fps=%.2f, width=%d, height=%d group=%d}",
-                              getConfigId().value(), hwcConfig->getHwcId(), getFps().getValue(),
-                              hwcConfig->getWidth(), hwcConfig->getHeight(), getConfigGroup());
+                              getModeId().value(), mode->getHwcId(), getFps().getValue(),
+                              mode->getWidth(), mode->getHeight(), getModeGroup());
 }
 
 std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) {
@@ -89,9 +89,9 @@
 }
 
 std::string RefreshRateConfigs::Policy::toString() const {
-    return base::StringPrintf("default config ID: %zu, allowGroupSwitching = %d"
+    return base::StringPrintf("default mode ID: %zu, allowGroupSwitching = %d"
                               ", primary range: %s, app request range: %s",
-                              defaultConfig.value(), allowGroupSwitching,
+                              defaultMode.value(), allowGroupSwitching,
                               primaryRange.toString().c_str(), appRequestRange.toString().c_str());
 }
 
@@ -291,7 +291,7 @@
         scores.emplace_back(RefreshRateScore{refreshRate, 0.0f});
     }
 
-    const auto& defaultConfig = mRefreshRates.at(policy->defaultConfig);
+    const auto& defaultMode = mRefreshRates.at(policy->defaultMode);
 
     for (const auto& layer : layers) {
         ALOGV("Calculating score for %s (%s, weight %.2f)", layer.name.c_str(),
@@ -303,11 +303,11 @@
         auto weight = layer.weight;
 
         for (auto i = 0u; i < scores.size(); i++) {
-            const bool isSeamlessSwitch = scores[i].refreshRate->getConfigGroup() ==
-                    mCurrentRefreshRate->getConfigGroup();
+            const bool isSeamlessSwitch =
+                    scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup();
 
             if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
-                ALOGV("%s ignores %s to avoid non-seamless switch. Current config = %s",
+                ALOGV("%s ignores %s to avoid non-seamless switch. Current mode = %s",
                       formatLayerInfo(layer, weight).c_str(),
                       scores[i].refreshRate->toString().c_str(),
                       mCurrentRefreshRate->toString().c_str());
@@ -317,26 +317,25 @@
             if (layer.seamlessness == Seamlessness::SeamedAndSeamless && !isSeamlessSwitch &&
                 !layer.focused) {
                 ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
-                      " Current config = %s",
+                      " Current mode = %s",
                       formatLayerInfo(layer, weight).c_str(),
                       scores[i].refreshRate->toString().c_str(),
                       mCurrentRefreshRate->toString().c_str());
                 continue;
             }
 
-            // Layers with default seamlessness vote for the current config group if
+            // Layers with default seamlessness vote for the current mode group if
             // there are layers with seamlessness=SeamedAndSeamless and for the default
-            // config group otherwise. In second case, if the current config group is different
+            // mode group otherwise. In second case, if the current mode group is different
             // from the default, this means a layer with seamlessness=SeamedAndSeamless has just
             // disappeared.
             const bool isInPolicyForDefault = seamedLayers > 0
-                    ? scores[i].refreshRate->getConfigGroup() ==
-                            mCurrentRefreshRate->getConfigGroup()
-                    : scores[i].refreshRate->getConfigGroup() == defaultConfig->getConfigGroup();
+                    ? scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup()
+                    : scores[i].refreshRate->getModeGroup() == defaultMode->getModeGroup();
 
             if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault &&
                 !layer.focused) {
-                ALOGV("%s ignores %s. Current config = %s", formatLayerInfo(layer, weight).c_str(),
+                ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
                       scores[i].refreshRate->toString().c_str(),
                       mCurrentRefreshRate->toString().c_str());
                 continue;
@@ -548,12 +547,12 @@
 
 const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
     for (auto refreshRate : mPrimaryRefreshRates) {
-        if (mCurrentRefreshRate->getConfigGroup() == refreshRate->getConfigGroup()) {
+        if (mCurrentRefreshRate->getModeGroup() == refreshRate->getModeGroup()) {
             return *refreshRate;
         }
     }
-    ALOGE("Can't find min refresh rate by policy with the same config group"
-          " as the current config %s",
+    ALOGE("Can't find min refresh rate by policy with the same mode group"
+          " as the current mode %s",
           mCurrentRefreshRate->toString().c_str());
     // Defaulting to the lowest refresh rate
     return *mPrimaryRefreshRates.front();
@@ -567,12 +566,12 @@
 const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked() const {
     for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); it++) {
         const auto& refreshRate = (**it);
-        if (mCurrentRefreshRate->getConfigGroup() == refreshRate.getConfigGroup()) {
+        if (mCurrentRefreshRate->getModeGroup() == refreshRate.getModeGroup()) {
             return refreshRate;
         }
     }
-    ALOGE("Can't find max refresh rate by policy with the same config group"
-          " as the current config %s",
+    ALOGE("Can't find max refresh rate by policy with the same mode group"
+          " as the current mode %s",
           mCurrentRefreshRate->toString().c_str());
     // Defaulting to the highest refresh rate
     return *mPrimaryRefreshRates.back();
@@ -593,50 +592,50 @@
                   mCurrentRefreshRate) != mAppRequestRefreshRates.end()) {
         return *mCurrentRefreshRate;
     }
-    return *mRefreshRates.at(getCurrentPolicyLocked()->defaultConfig);
+    return *mRefreshRates.at(getCurrentPolicyLocked()->defaultMode);
 }
 
-void RefreshRateConfigs::setCurrentConfigId(DisplayModeId configId) {
+void RefreshRateConfigs::setCurrentModeId(DisplayModeId modeId) {
     std::lock_guard lock(mLock);
-    mCurrentRefreshRate = mRefreshRates.at(configId).get();
+    mCurrentRefreshRate = mRefreshRates.at(modeId).get();
 }
 
-RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& configs, DisplayModeId currentConfigId,
+RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId,
                                        bool enableFrameRateOverride)
-      : mKnownFrameRates(constructKnownFrameRates(configs)),
+      : mKnownFrameRates(constructKnownFrameRates(modes)),
         mEnableFrameRateOverride(enableFrameRateOverride) {
-    updateDisplayConfigs(configs, currentConfigId);
+    updateDisplayModes(modes, currentModeId);
 }
 
-void RefreshRateConfigs::updateDisplayConfigs(const DisplayModes& configs,
-                                              DisplayModeId currentConfigId) {
+void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes,
+                                            DisplayModeId currentModeId) {
     std::lock_guard lock(mLock);
-    LOG_ALWAYS_FATAL_IF(configs.empty());
-    LOG_ALWAYS_FATAL_IF(currentConfigId.value() >= configs.size());
+    LOG_ALWAYS_FATAL_IF(modes.empty());
+    LOG_ALWAYS_FATAL_IF(currentModeId.value() >= modes.size());
 
     mRefreshRates.clear();
-    for (const auto& config : configs) {
-        const auto configId = config->getId();
-        const auto fps = Fps::fromPeriodNsecs(config->getVsyncPeriod());
-        mRefreshRates.emplace(configId,
-                              std::make_unique<RefreshRate>(configId, config, fps,
+    for (const auto& mode : modes) {
+        const auto modeId = mode->getId();
+        const auto fps = Fps::fromPeriodNsecs(mode->getVsyncPeriod());
+        mRefreshRates.emplace(modeId,
+                              std::make_unique<RefreshRate>(modeId, mode, fps,
                                                             RefreshRate::ConstructorTag(0)));
-        if (configId == currentConfigId) {
-            mCurrentRefreshRate = mRefreshRates.at(configId).get();
+        if (modeId == currentModeId) {
+            mCurrentRefreshRate = mRefreshRates.at(modeId).get();
         }
     }
 
-    std::vector<const RefreshRate*> sortedConfigs;
-    getSortedRefreshRateListLocked([](const RefreshRate&) { return true; }, &sortedConfigs);
-    mDisplayManagerPolicy.defaultConfig = currentConfigId;
-    mMinSupportedRefreshRate = sortedConfigs.front();
-    mMaxSupportedRefreshRate = sortedConfigs.back();
+    std::vector<const RefreshRate*> sortedModes;
+    getSortedRefreshRateListLocked([](const RefreshRate&) { return true; }, &sortedModes);
+    mDisplayManagerPolicy.defaultMode = currentModeId;
+    mMinSupportedRefreshRate = sortedModes.front();
+    mMaxSupportedRefreshRate = sortedModes.back();
 
     mSupportsFrameRateOverride = false;
     if (mEnableFrameRateOverride) {
-        for (const auto& config1 : sortedConfigs) {
-            for (const auto& config2 : sortedConfigs) {
-                if (getFrameRateDivider(config1->getFps(), config2->getFps()) >= 2) {
+        for (const auto& mode1 : sortedModes) {
+            for (const auto& mode2 : sortedModes) {
+                if (getFrameRateDivider(mode1->getFps(), mode2->getFps()) >= 2) {
                     mSupportsFrameRateOverride = true;
                     break;
                 }
@@ -648,15 +647,15 @@
 }
 
 bool RefreshRateConfigs::isPolicyValidLocked(const Policy& policy) const {
-    // defaultConfig must be a valid config, and within the given refresh rate range.
-    auto iter = mRefreshRates.find(policy.defaultConfig);
+    // defaultMode must be a valid mode, and within the given refresh rate range.
+    auto iter = mRefreshRates.find(policy.defaultMode);
     if (iter == mRefreshRates.end()) {
-        ALOGE("Default config is not found.");
+        ALOGE("Default mode is not found.");
         return false;
     }
     const RefreshRate& refreshRate = *iter->second;
     if (!refreshRate.inPolicy(policy.primaryRange.min, policy.primaryRange.max)) {
-        ALOGE("Default config is not in the primary range.");
+        ALOGE("Default mode is not in the primary range.");
         return false;
     }
     return policy.appRequestRange.min.lessThanOrEqualWithMargin(policy.primaryRange.min) &&
@@ -706,10 +705,10 @@
     return mDisplayManagerPolicy;
 }
 
-bool RefreshRateConfigs::isConfigAllowed(DisplayModeId config) const {
+bool RefreshRateConfigs::isModeAllowed(DisplayModeId modeId) const {
     std::lock_guard lock(mLock);
     for (const RefreshRate* refreshRate : mAppRequestRefreshRates) {
-        if (refreshRate->configId == config) {
+        if (refreshRate->modeId == modeId) {
             return true;
         }
     }
@@ -723,60 +722,59 @@
     outRefreshRates->reserve(mRefreshRates.size());
     for (const auto& [type, refreshRate] : mRefreshRates) {
         if (shouldAddRefreshRate(*refreshRate)) {
-            ALOGV("getSortedRefreshRateListLocked: config %zu added to list policy",
-                  refreshRate->configId.value());
+            ALOGV("getSortedRefreshRateListLocked: mode %zu added to list policy",
+                  refreshRate->modeId.value());
             outRefreshRates->push_back(refreshRate.get());
         }
     }
 
     std::sort(outRefreshRates->begin(), outRefreshRates->end(),
               [](const auto refreshRate1, const auto refreshRate2) {
-                  if (refreshRate1->hwcConfig->getVsyncPeriod() !=
-                      refreshRate2->hwcConfig->getVsyncPeriod()) {
-                      return refreshRate1->hwcConfig->getVsyncPeriod() >
-                              refreshRate2->hwcConfig->getVsyncPeriod();
+                  if (refreshRate1->mode->getVsyncPeriod() !=
+                      refreshRate2->mode->getVsyncPeriod()) {
+                      return refreshRate1->mode->getVsyncPeriod() >
+                              refreshRate2->mode->getVsyncPeriod();
                   } else {
-                      return refreshRate1->hwcConfig->getConfigGroup() >
-                              refreshRate2->hwcConfig->getConfigGroup();
+                      return refreshRate1->mode->getGroup() > refreshRate2->mode->getGroup();
                   }
               });
 }
 
 void RefreshRateConfigs::constructAvailableRefreshRates() {
-    // Filter configs based on current policy and sort based on vsync period
+    // Filter modes based on current policy and sort based on vsync period
     const Policy* policy = getCurrentPolicyLocked();
-    const auto& defaultConfig = mRefreshRates.at(policy->defaultConfig)->hwcConfig;
+    const auto& defaultMode = mRefreshRates.at(policy->defaultMode)->mode;
     ALOGV("constructAvailableRefreshRates: %s ", policy->toString().c_str());
 
-    auto filterRefreshRates = [&](Fps min, Fps max, const char* listName,
-                                  std::vector<const RefreshRate*>*
-                                          outRefreshRates) REQUIRES(mLock) {
-        getSortedRefreshRateListLocked(
-                [&](const RefreshRate& refreshRate) REQUIRES(mLock) {
-                    const auto& hwcConfig = refreshRate.hwcConfig;
+    auto filterRefreshRates =
+            [&](Fps min, Fps max, const char* listName,
+                std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock) {
+                getSortedRefreshRateListLocked(
+                        [&](const RefreshRate& refreshRate) REQUIRES(mLock) {
+                            const auto& mode = refreshRate.mode;
 
-                    return hwcConfig->getHeight() == defaultConfig->getHeight() &&
-                            hwcConfig->getWidth() == defaultConfig->getWidth() &&
-                            hwcConfig->getDpiX() == defaultConfig->getDpiX() &&
-                            hwcConfig->getDpiY() == defaultConfig->getDpiY() &&
-                            (policy->allowGroupSwitching ||
-                             hwcConfig->getConfigGroup() == defaultConfig->getConfigGroup()) &&
-                            refreshRate.inPolicy(min, max);
-                },
-                outRefreshRates);
+                            return mode->getHeight() == defaultMode->getHeight() &&
+                                    mode->getWidth() == defaultMode->getWidth() &&
+                                    mode->getDpiX() == defaultMode->getDpiX() &&
+                                    mode->getDpiY() == defaultMode->getDpiY() &&
+                                    (policy->allowGroupSwitching ||
+                                     mode->getGroup() == defaultMode->getGroup()) &&
+                                    refreshRate.inPolicy(min, max);
+                        },
+                        outRefreshRates);
 
-        LOG_ALWAYS_FATAL_IF(outRefreshRates->empty(),
-                            "No matching configs for %s range: min=%s max=%s", listName,
-                            to_string(min).c_str(), to_string(max).c_str());
-        auto stringifyRefreshRates = [&]() -> std::string {
-            std::string str;
-            for (auto refreshRate : *outRefreshRates) {
-                base::StringAppendF(&str, "%s ", refreshRate->getName().c_str());
-            }
-            return str;
-        };
-        ALOGV("%s refresh rates: %s", listName, stringifyRefreshRates().c_str());
-    };
+                LOG_ALWAYS_FATAL_IF(outRefreshRates->empty(),
+                                    "No matching modes for %s range: min=%s max=%s", listName,
+                                    to_string(min).c_str(), to_string(max).c_str());
+                auto stringifyRefreshRates = [&]() -> std::string {
+                    std::string str;
+                    for (auto refreshRate : *outRefreshRates) {
+                        base::StringAppendF(&str, "%s ", refreshRate->getName().c_str());
+                    }
+                    return str;
+                };
+                ALOGV("%s refresh rates: %s", listName, stringifyRefreshRates().c_str());
+            };
 
     filterRefreshRates(policy->primaryRange.min, policy->primaryRange.max, "primary",
                        &mPrimaryRefreshRates);
@@ -845,20 +843,20 @@
 
 void RefreshRateConfigs::dump(std::string& result) const {
     std::lock_guard lock(mLock);
-    base::StringAppendF(&result, "DesiredDisplayConfigSpecs (DisplayManager): %s\n\n",
+    base::StringAppendF(&result, "DesiredDisplayModeSpecs (DisplayManager): %s\n\n",
                         mDisplayManagerPolicy.toString().c_str());
     scheduler::RefreshRateConfigs::Policy currentPolicy = *getCurrentPolicyLocked();
     if (mOverridePolicy && currentPolicy != mDisplayManagerPolicy) {
-        base::StringAppendF(&result, "DesiredDisplayConfigSpecs (Override): %s\n\n",
+        base::StringAppendF(&result, "DesiredDisplayModeSpecs (Override): %s\n\n",
                             currentPolicy.toString().c_str());
     }
 
-    auto config = mCurrentRefreshRate->hwcConfig;
-    base::StringAppendF(&result, "Current config: %s\n", mCurrentRefreshRate->toString().c_str());
+    auto mode = mCurrentRefreshRate->mode;
+    base::StringAppendF(&result, "Current mode: %s\n", mCurrentRefreshRate->toString().c_str());
 
     result.append("Refresh rates:\n");
     for (const auto& [id, refreshRate] : mRefreshRates) {
-        config = refreshRate->hwcConfig;
+        mode = refreshRate->mode;
         base::StringAppendF(&result, "\t%s\n", refreshRate->toString().c_str());
     }
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 0c7dc05..2bc22b4 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -64,12 +64,12 @@
         };
 
     public:
-        RefreshRate(DisplayModeId configId, DisplayModePtr config, Fps fps, ConstructorTag)
-              : configId(configId), hwcConfig(config), fps(std::move(fps)) {}
+        RefreshRate(DisplayModeId modeId, DisplayModePtr mode, Fps fps, ConstructorTag)
+              : modeId(modeId), mode(mode), fps(std::move(fps)) {}
 
-        DisplayModeId getConfigId() const { return configId; }
-        nsecs_t getVsyncPeriod() const { return hwcConfig->getVsyncPeriod(); }
-        int32_t getConfigGroup() const { return hwcConfig->getConfigGroup(); }
+        DisplayModeId getModeId() const { return modeId; }
+        nsecs_t getVsyncPeriod() const { return mode->getVsyncPeriod(); }
+        int32_t getModeGroup() const { return mode->getGroup(); }
         std::string getName() const { return to_string(fps); }
         Fps getFps() const { return fps; }
 
@@ -81,7 +81,7 @@
         }
 
         bool operator!=(const RefreshRate& other) const {
-            return configId != other.configId || hwcConfig != other.hwcConfig;
+            return modeId != other.modeId || mode != other.mode;
         }
 
         bool operator<(const RefreshRate& other) const {
@@ -99,11 +99,8 @@
         friend RefreshRateConfigs;
         friend class RefreshRateConfigsTest;
 
-        // This config ID corresponds to the position of the config in the vector that is stored
-        // on the device.
-        const DisplayModeId configId;
-        // The config itself
-        DisplayModePtr hwcConfig;
+        const DisplayModeId modeId;
+        DisplayModePtr mode;
         // Refresh rate in frames per second
         const Fps fps{0.0f};
     };
@@ -131,16 +128,16 @@
         static constexpr int kAllowGroupSwitchingDefault = false;
 
     public:
-        // The default config, used to ensure we only initiate display config switches within the
-        // same config group as defaultConfigId's group.
-        DisplayModeId defaultConfig;
-        // Whether or not we switch config groups to get the best frame rate.
+        // The default mode, used to ensure we only initiate display mode switches within the
+        // same mode group as defaultMode's group.
+        DisplayModeId defaultMode;
+        // Whether or not we switch mode groups to get the best frame rate.
         bool allowGroupSwitching = kAllowGroupSwitchingDefault;
         // The primary refresh rate range represents display manager's general guidance on the
-        // display configs we'll consider when switching refresh rates. Unless we get an explicit
+        // display modes we'll consider when switching refresh rates. Unless we get an explicit
         // signal from an app, we should stay within this range.
         FpsRange primaryRange;
-        // The app request refresh rate range allows us to consider more display configs when
+        // The app request refresh rate range allows us to consider more display modes when
         // switching refresh rates. Although we should generally stay within the primary range,
         // specific considerations, such as layer frame rate settings specified via the
         // setFrameRate() api, may cause us to go outside the primary range. We never go outside the
@@ -150,25 +147,25 @@
 
         Policy() = default;
 
-        Policy(DisplayModeId defaultConfig, const FpsRange& range)
-              : Policy(defaultConfig, kAllowGroupSwitchingDefault, range, range) {}
+        Policy(DisplayModeId defaultMode, const FpsRange& range)
+              : Policy(defaultMode, kAllowGroupSwitchingDefault, range, range) {}
 
-        Policy(DisplayModeId defaultConfig, bool allowGroupSwitching, const FpsRange& range)
-              : Policy(defaultConfig, allowGroupSwitching, range, range) {}
+        Policy(DisplayModeId defaultMode, bool allowGroupSwitching, const FpsRange& range)
+              : Policy(defaultMode, allowGroupSwitching, range, range) {}
 
-        Policy(DisplayModeId defaultConfig, const FpsRange& primaryRange,
+        Policy(DisplayModeId defaultMode, const FpsRange& primaryRange,
                const FpsRange& appRequestRange)
-              : Policy(defaultConfig, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {}
+              : Policy(defaultMode, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {}
 
-        Policy(DisplayModeId defaultConfig, bool allowGroupSwitching, const FpsRange& primaryRange,
+        Policy(DisplayModeId defaultMode, bool allowGroupSwitching, const FpsRange& primaryRange,
                const FpsRange& appRequestRange)
-              : defaultConfig(defaultConfig),
+              : defaultMode(defaultMode),
                 allowGroupSwitching(allowGroupSwitching),
                 primaryRange(primaryRange),
                 appRequestRange(appRequestRange) {}
 
         bool operator==(const Policy& other) const {
-            return defaultConfig == other.defaultConfig && primaryRange == other.primaryRange &&
+            return defaultMode == other.defaultMode && primaryRange == other.primaryRange &&
                     appRequestRange == other.appRequestRange &&
                     allowGroupSwitching == other.allowGroupSwitching;
         }
@@ -200,8 +197,8 @@
     // Gets the display manager policy, regardless of whether an override policy is active.
     Policy getDisplayManagerPolicy() const EXCLUDES(mLock);
 
-    // Returns true if config is allowed by the current policy.
-    bool isConfigAllowed(DisplayModeId config) const EXCLUDES(mLock);
+    // Returns true if mode is allowed by the current policy.
+    bool isModeAllowed(DisplayModeId) const EXCLUDES(mLock);
 
     // Describes the different options the layer voted for refresh rate
     enum class LayerVoteType {
@@ -270,7 +267,7 @@
         return {mMinSupportedRefreshRate->getFps(), mMaxSupportedRefreshRate->getFps()};
     }
 
-    std::optional<Fps> onKernelTimerChanged(std::optional<DisplayModeId> desiredActiveConfigId,
+    std::optional<Fps> onKernelTimerChanged(std::optional<DisplayModeId> desiredActiveModeId,
                                             bool timerExpired) const EXCLUDES(mLock);
 
     // Returns the highest refresh rate according to the current policy. May change at runtime. Only
@@ -286,14 +283,14 @@
 
     // Returns the refresh rate that corresponds to a DisplayModeId. This may change at
     // runtime.
-    // TODO(b/159590486) An invalid config id may be given here if the dipslay configs have changed.
-    RefreshRate getRefreshRateFromConfigId(DisplayModeId configId) const EXCLUDES(mLock) {
+    // TODO(b/159590486) An invalid mode id may be given here if the dipslay modes have changed.
+    RefreshRate getRefreshRateFromModeId(DisplayModeId modeId) const EXCLUDES(mLock) {
         std::lock_guard lock(mLock);
-        return *mRefreshRates.at(configId);
+        return *mRefreshRates.at(modeId);
     };
 
-    // Stores the current configId the device operates at
-    void setCurrentConfigId(DisplayModeId configId) EXCLUDES(mLock);
+    // Stores the current modeId the device operates at
+    void setCurrentModeId(DisplayModeId) EXCLUDES(mLock);
 
     // Returns a string that represents the layer vote type
     static std::string layerVoteTypeString(LayerVoteType vote);
@@ -301,14 +298,13 @@
     // Returns a known frame rate that is the closest to frameRate
     Fps findClosestKnownFrameRate(Fps frameRate) const;
 
-    RefreshRateConfigs(const DisplayModes& configs, DisplayModeId currentConfigId,
+    RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId,
                        bool enableFrameRateOverride = false);
 
-    void updateDisplayConfigs(const DisplayModes& configs, DisplayModeId currentConfig)
-            EXCLUDES(mLock);
+    void updateDisplayModes(const DisplayModes& mode, DisplayModeId currentModeId) EXCLUDES(mLock);
 
-    // Returns whether switching configs (refresh rate or resolution) is possible.
-    // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the configs only
+    // Returns whether switching modes (refresh rate or resolution) is possible.
+    // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only
     // differ in resolution.
     bool canSwitch() const EXCLUDES(mLock) {
         std::lock_guard lock(mLock);
@@ -387,7 +383,7 @@
     float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
                                     bool isSeamlessSwitch) const REQUIRES(mLock);
 
-    // The list of refresh rates, indexed by display config ID. This may change after this
+    // The list of refresh rates, indexed by display modes ID. This may change after this
     // object is initialized.
     AllRefreshRatesMapType mRefreshRates GUARDED_BY(mLock);
 
@@ -399,7 +395,7 @@
     // vsyncPeriod (the first element is the lowest refresh rate).
     std::vector<const RefreshRate*> mAppRequestRefreshRates GUARDED_BY(mLock);
 
-    // The current config. This will change at runtime. This is set by SurfaceFlinger on
+    // The current display mode. This will change at runtime. This is set by SurfaceFlinger on
     // the main thread, and read by the Scheduler (and other objects) on other threads.
     const RefreshRate* mCurrentRefreshRate GUARDED_BY(mLock);
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index d861209..9813270 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -348,55 +348,53 @@
     thread->onFrameRateOverridesChanged(displayId, std::move(overrides));
 }
 
-void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
-                                              DisplayModeId configId, nsecs_t vsyncPeriod) {
+void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+                                            DisplayModeId modeId, nsecs_t vsyncPeriod) {
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
-        // Cache the last reported config for primary display.
-        mFeatures.cachedConfigChangedParams = {handle, displayId, configId, vsyncPeriod};
+        // Cache the last reported modes for primary display.
+        mFeatures.cachedModeChangedParams = {handle, displayId, modeId, vsyncPeriod};
     }
-    onNonPrimaryDisplayConfigChanged(handle, displayId, configId, vsyncPeriod);
+    onNonPrimaryDisplayModeChanged(handle, displayId, modeId, vsyncPeriod);
 }
 
-void Scheduler::dispatchCachedReportedConfig() {
+void Scheduler::dispatchCachedReportedMode() {
     // Check optional fields first.
-    if (!mFeatures.configId.has_value()) {
-        ALOGW("No config ID found, not dispatching cached config.");
+    if (!mFeatures.modeId.has_value()) {
+        ALOGW("No mode ID found, not dispatching cached mode.");
         return;
     }
-    if (!mFeatures.cachedConfigChangedParams.has_value()) {
-        ALOGW("No config changed params found, not dispatching cached config.");
+    if (!mFeatures.cachedModeChangedParams.has_value()) {
+        ALOGW("No mode changed params found, not dispatching cached mode.");
         return;
     }
 
-    const auto configId = *mFeatures.configId;
-    const auto vsyncPeriod =
-            mRefreshRateConfigs.getRefreshRateFromConfigId(configId).getVsyncPeriod();
+    const auto modeId = *mFeatures.modeId;
+    const auto vsyncPeriod = mRefreshRateConfigs.getRefreshRateFromModeId(modeId).getVsyncPeriod();
 
-    // If there is no change from cached config, there is no need to dispatch an event
-    if (configId == mFeatures.cachedConfigChangedParams->configId &&
-        vsyncPeriod == mFeatures.cachedConfigChangedParams->vsyncPeriod) {
+    // If there is no change from cached mode, there is no need to dispatch an event
+    if (modeId == mFeatures.cachedModeChangedParams->modeId &&
+        vsyncPeriod == mFeatures.cachedModeChangedParams->vsyncPeriod) {
         return;
     }
 
-    mFeatures.cachedConfigChangedParams->configId = configId;
-    mFeatures.cachedConfigChangedParams->vsyncPeriod = vsyncPeriod;
-    onNonPrimaryDisplayConfigChanged(mFeatures.cachedConfigChangedParams->handle,
-                                     mFeatures.cachedConfigChangedParams->displayId,
-                                     mFeatures.cachedConfigChangedParams->configId,
-                                     mFeatures.cachedConfigChangedParams->vsyncPeriod);
+    mFeatures.cachedModeChangedParams->modeId = modeId;
+    mFeatures.cachedModeChangedParams->vsyncPeriod = vsyncPeriod;
+    onNonPrimaryDisplayModeChanged(mFeatures.cachedModeChangedParams->handle,
+                                   mFeatures.cachedModeChangedParams->displayId,
+                                   mFeatures.cachedModeChangedParams->modeId,
+                                   mFeatures.cachedModeChangedParams->vsyncPeriod);
 }
 
-void Scheduler::onNonPrimaryDisplayConfigChanged(ConnectionHandle handle,
-                                                 PhysicalDisplayId displayId,
-                                                 DisplayModeId configId, nsecs_t vsyncPeriod) {
+void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+                                               DisplayModeId modeId, nsecs_t vsyncPeriod) {
     android::EventThread* thread;
     {
         std::lock_guard<std::mutex> lock(mConnectionsLock);
         RETURN_IF_INVALID_HANDLE(handle);
         thread = mConnections[handle].thread.get();
     }
-    thread->onConfigChanged(displayId, configId, vsyncPeriod);
+    thread->onModeChanged(displayId, modeId, vsyncPeriod);
 }
 
 size_t Scheduler::getEventThreadConnectionCount(ConnectionHandle handle) {
@@ -590,9 +588,9 @@
     }
 }
 
-void Scheduler::setConfigChangePending(bool pending) {
+void Scheduler::setModeChangePending(bool pending) {
     if (mLayerHistory) {
-        mLayerHistory->setConfigChangePending(pending);
+        mLayerHistory->setModeChangePending(pending);
     }
 }
 
@@ -603,7 +601,7 @@
 
     scheduler::LayerHistory::Summary summary = mLayerHistory->summarize(systemTime());
     scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
-    DisplayModeId newConfigId;
+    DisplayModeId newModeId;
     bool frameRateChanged;
     bool frameRateOverridesChanged;
     {
@@ -613,28 +611,28 @@
         }
         mFeatures.contentRequirements = summary;
 
-        newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
-        auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+        newModeId = calculateRefreshRateModeId(&consideredSignals);
+        auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromModeId(newModeId);
         frameRateOverridesChanged =
                 updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
 
-        if (mFeatures.configId == newConfigId) {
-            // We don't need to change the config, but we might need to send an event
-            // about a config change, since it was suppressed due to a previous idleConsidered
+        if (mFeatures.modeId == newModeId) {
+            // We don't need to change the display mode, but we might need to send an event
+            // about a mode change, since it was suppressed due to a previous idleConsidered
             if (!consideredSignals.idle) {
-                dispatchCachedReportedConfig();
+                dispatchCachedReportedMode();
             }
             frameRateChanged = false;
         } else {
-            mFeatures.configId = newConfigId;
+            mFeatures.modeId = newModeId;
             frameRateChanged = true;
         }
     }
     if (frameRateChanged) {
-        auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+        auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromModeId(newModeId);
         mSchedulerCallback.changeRefreshRate(newRefreshRate,
-                                             consideredSignals.idle ? ConfigEvent::None
-                                                                    : ConfigEvent::Changed);
+                                             consideredSignals.idle ? ModeEvent::None
+                                                                    : ModeEvent::Changed);
     }
     if (frameRateOverridesChanged) {
         mSchedulerCallback.triggerOnFrameRateOverridesChanged();
@@ -783,7 +781,7 @@
 
 template <class T>
 bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
-    DisplayModeId newConfigId;
+    DisplayModeId newModeId;
     bool refreshRateChanged = false;
     bool frameRateOverridesChanged;
     scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
@@ -793,29 +791,27 @@
             return false;
         }
         *currentState = newState;
-        newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
-        const RefreshRate& newRefreshRate =
-                mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+        newModeId = calculateRefreshRateModeId(&consideredSignals);
+        const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromModeId(newModeId);
         frameRateOverridesChanged =
                 updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
-        if (mFeatures.configId == newConfigId) {
-            // We don't need to change the config, but we might need to send an event
-            // about a config change, since it was suppressed due to a previous idleConsidered
+        if (mFeatures.modeId == newModeId) {
+            // We don't need to change the display mode, but we might need to send an event
+            // about a mode change, since it was suppressed due to a previous idleConsidered
             if (!consideredSignals.idle) {
-                dispatchCachedReportedConfig();
+                dispatchCachedReportedMode();
             }
         } else {
-            mFeatures.configId = newConfigId;
+            mFeatures.modeId = newModeId;
             refreshRateChanged = true;
         }
     }
     if (refreshRateChanged) {
-        const RefreshRate& newRefreshRate =
-                mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+        const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromModeId(newModeId);
 
         mSchedulerCallback.changeRefreshRate(newRefreshRate,
-                                             consideredSignals.idle ? ConfigEvent::None
-                                                                    : ConfigEvent::Changed);
+                                             consideredSignals.idle ? ModeEvent::None
+                                                                    : ModeEvent::Changed);
     }
     if (frameRateOverridesChanged) {
         mSchedulerCallback.triggerOnFrameRateOverridesChanged();
@@ -823,7 +819,7 @@
     return consideredSignals.touch;
 }
 
-DisplayModeId Scheduler::calculateRefreshRateConfigIndexType(
+DisplayModeId Scheduler::calculateRefreshRateModeId(
         scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals) {
     ATRACE_CALL();
     if (consideredSignals) *consideredSignals = {};
@@ -833,7 +829,7 @@
     if (mDisplayPowerTimer &&
         (!mFeatures.isDisplayPowerStateNormal ||
          mFeatures.displayPowerTimer == TimerState::Reset)) {
-        return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId();
+        return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getModeId();
     }
 
     const bool touchActive = mTouchTimer && mFeatures.touch == TouchState::Active;
@@ -842,16 +838,16 @@
     return mRefreshRateConfigs
             .getBestRefreshRate(mFeatures.contentRequirements, {.touch = touchActive, .idle = idle},
                                 consideredSignals)
-            .getConfigId();
+            .getModeId();
 }
 
-std::optional<DisplayModeId> Scheduler::getPreferredConfigId() {
+std::optional<DisplayModeId> Scheduler::getPreferredModeId() {
     std::lock_guard<std::mutex> lock(mFeatureStateLock);
-    // Make sure that the default config ID is first updated, before returned.
-    if (mFeatures.configId.has_value()) {
-        mFeatures.configId = calculateRefreshRateConfigIndexType();
+    // Make sure that the default mode ID is first updated, before returned.
+    if (mFeatures.modeId.has_value()) {
+        mFeatures.modeId = calculateRefreshRateModeId();
     }
-    return mFeatures.configId;
+    return mFeatures.modeId;
 }
 
 void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 340ca8e..5237516 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -70,7 +70,7 @@
 class Scheduler {
 public:
     using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
-    using ConfigEvent = scheduler::RefreshRateConfigEvent;
+    using ModeEvent = scheduler::RefreshRateConfigEvent;
 
     Scheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&);
     ~Scheduler();
@@ -87,10 +87,10 @@
     sp<EventThreadConnection> getEventConnection(ConnectionHandle);
 
     void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
-    void onPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId, DisplayModeId configId,
-                                       nsecs_t vsyncPeriod) EXCLUDES(mFeatureStateLock);
-    void onNonPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId,
-                                          DisplayModeId configId, nsecs_t vsyncPeriod);
+    void onPrimaryDisplayModeChanged(ConnectionHandle, PhysicalDisplayId, DisplayModeId,
+                                     nsecs_t vsyncPeriod) EXCLUDES(mFeatureStateLock);
+    void onNonPrimaryDisplayModeChanged(ConnectionHandle, PhysicalDisplayId, DisplayModeId,
+                                        nsecs_t vsyncPeriod);
     void onScreenAcquired(ConnectionHandle);
     void onScreenReleased(ConnectionHandle);
 
@@ -128,7 +128,7 @@
     // Layers are registered on creation, and unregistered when the weak reference expires.
     void registerLayer(Layer*);
     void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType);
-    void setConfigChangePending(bool pending);
+    void setModeChangePending(bool pending);
 
     // Detects content using layer history, and selects a matching refresh rate.
     void chooseRefreshRateForContent();
@@ -153,7 +153,7 @@
     void dumpVsync(std::string&) const;
 
     // Get the appropriate refresh for current conditions.
-    std::optional<DisplayModeId> getPreferredConfigId();
+    std::optional<DisplayModeId> getPreferredModeId();
 
     // Notifies the scheduler about a refresh rate timeline change.
     void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline);
@@ -227,11 +227,11 @@
     // This function checks whether individual features that are affecting the refresh rate
     // selection were initialized, prioritizes them, and calculates the DisplayModeId
     // for the suggested refresh rate.
-    DisplayModeId calculateRefreshRateConfigIndexType(
+    DisplayModeId calculateRefreshRateModeId(
             scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr)
             REQUIRES(mFeatureStateLock);
 
-    void dispatchCachedReportedConfig() REQUIRES(mFeatureStateLock);
+    void dispatchCachedReportedMode() REQUIRES(mFeatureStateLock);
     bool updateFrameRateOverrides(scheduler::RefreshRateConfigs::GlobalSignals consideredSignals,
                                   Fps displayRefreshRate) REQUIRES(mFeatureStateLock)
             EXCLUDES(mFrameRateOverridesMutex);
@@ -283,20 +283,20 @@
         TouchState touch = TouchState::Inactive;
         TimerState displayPowerTimer = TimerState::Expired;
 
-        std::optional<DisplayModeId> configId;
+        std::optional<DisplayModeId> modeId;
         LayerHistory::Summary contentRequirements;
 
         bool isDisplayPowerStateNormal = true;
 
-        // Used to cache the last parameters of onPrimaryDisplayConfigChanged
-        struct ConfigChangedParams {
+        // Used to cache the last parameters of onPrimaryDisplayModeChanged
+        struct ModeChangedParams {
             ConnectionHandle handle;
             PhysicalDisplayId displayId;
-            DisplayModeId configId;
+            DisplayModeId modeId;
             nsecs_t vsyncPeriod;
         };
 
-        std::optional<ConfigChangedParams> cachedConfigChangedParams;
+        std::optional<ModeChangedParams> cachedModeChangedParams;
     } mFeatures GUARDED_BY(mFeatureStateLock);
 
     const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 689a302..a36502c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -67,9 +67,9 @@
 #include <sys/types.h>
 #include <ui/ColorSpace.h>
 #include <ui/DebugUtils.h>
-#include <ui/DisplayConfig.h>
 #include <ui/DisplayId.h>
 #include <ui/DisplayInfo.h>
+#include <ui/DisplayMode.h>
 #include <ui/DisplayStatInfo.h>
 #include <ui/DisplayState.h>
 #include <ui/GraphicBufferAllocator.h>
@@ -887,9 +887,9 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken,
-                                           Vector<DisplayConfig>* configs) {
-    if (!displayToken || !configs) {
+status_t SurfaceFlinger::getDisplayModes(const sp<IBinder>& displayToken,
+                                         Vector<ui::DisplayMode>* modes) {
+    if (!displayToken || !modes) {
         return BAD_VALUE;
     }
 
@@ -900,16 +900,16 @@
         return NAME_NOT_FOUND;
     }
 
-    configs->clear();
+    modes->clear();
 
-    for (const auto& mode : display->getSupportedModes()) {
-        DisplayConfig config;
+    for (const auto& supportedMode : display->getSupportedModes()) {
+        ui::DisplayMode mode;
 
-        auto width = mode->getWidth();
-        auto height = mode->getHeight();
+        auto width = supportedMode->getWidth();
+        auto height = supportedMode->getHeight();
 
-        auto xDpi = mode->getDpiX();
-        auto yDpi = mode->getDpiY();
+        auto xDpi = supportedMode->getDpiX();
+        auto yDpi = supportedMode->getDpiY();
 
         if (display->isPrimary() &&
             (internalDisplayOrientation == ui::ROTATION_90 ||
@@ -918,24 +918,24 @@
             std::swap(xDpi, yDpi);
         }
 
-        config.resolution = ui::Size(width, height);
+        mode.resolution = ui::Size(width, height);
 
         if (mEmulatedDisplayDensity) {
-            config.xDpi = mEmulatedDisplayDensity;
-            config.yDpi = mEmulatedDisplayDensity;
+            mode.xDpi = mEmulatedDisplayDensity;
+            mode.yDpi = mEmulatedDisplayDensity;
         } else {
-            config.xDpi = xDpi;
-            config.yDpi = yDpi;
+            mode.xDpi = xDpi;
+            mode.yDpi = yDpi;
         }
 
-        const nsecs_t period = mode->getVsyncPeriod();
-        config.refreshRate = Fps::fromPeriodNsecs(period).getValue();
+        const nsecs_t period = supportedMode->getVsyncPeriod();
+        mode.refreshRate = Fps::fromPeriodNsecs(period).getValue();
 
         const auto vsyncConfigSet =
-                mVsyncConfiguration->getConfigsForRefreshRate(Fps(config.refreshRate));
-        config.appVsyncOffset = vsyncConfigSet.late.appOffset;
-        config.sfVsyncOffset = vsyncConfigSet.late.sfOffset;
-        config.configGroup = mode->getConfigGroup();
+                mVsyncConfiguration->getConfigsForRefreshRate(Fps(mode.refreshRate));
+        mode.appVsyncOffset = vsyncConfigSet.late.appOffset;
+        mode.sfVsyncOffset = vsyncConfigSet.late.sfOffset;
+        mode.group = supportedMode->getGroup();
 
         // This is how far in advance a buffer must be queued for
         // presentation at a given time.  If you want a buffer to appear
@@ -949,9 +949,9 @@
         //
         // We add an additional 1ms to allow for processing time and
         // differences between the ideal and actual refresh rate.
-        config.presentationDeadline = period - config.sfVsyncOffset + 1000000;
+        mode.presentationDeadline = period - mode.sfVsyncOffset + 1000000;
 
-        configs->push_back(config);
+        modes->push_back(mode);
     }
 
     return NO_ERROR;
@@ -966,15 +966,15 @@
     return NO_ERROR;
 }
 
-int SurfaceFlinger::getActiveConfig(const sp<IBinder>& displayToken) {
-    int activeConfig;
+int SurfaceFlinger::getActiveDisplayModeId(const sp<IBinder>& displayToken) {
+    int activeMode;
     bool isPrimary;
 
     {
         Mutex::Autolock lock(mStateLock);
 
         if (const auto display = getDisplayDeviceLocked(displayToken)) {
-            activeConfig = display->getActiveMode()->getId().value();
+            activeMode = display->getActiveMode()->getId().value();
             isPrimary = display->isPrimary();
         } else {
             ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
@@ -983,36 +983,35 @@
     }
 
     if (isPrimary) {
-        if (const auto config = getDesiredActiveConfig()) {
-            return config->configId.value();
+        if (const auto mode = getDesiredActiveMode()) {
+            return mode->modeId.value();
         }
     }
 
-    return activeConfig;
+    return activeMode;
 }
 
-void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) {
+void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) {
     ATRACE_CALL();
-    auto refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(info.configId);
-    ALOGV("setDesiredActiveConfig(%s)", refreshRate.getName().c_str());
+    auto refreshRate = mRefreshRateConfigs->getRefreshRateFromModeId(info.modeId);
+    ALOGV("%s(%s)", __func__, refreshRate.getName().c_str());
 
-    std::lock_guard<std::mutex> lock(mActiveConfigLock);
-    if (mDesiredActiveConfigChanged) {
-        // If a config change is pending, just cache the latest request in
-        // mDesiredActiveConfig
-        const Scheduler::ConfigEvent prevConfig = mDesiredActiveConfig.event;
-        mDesiredActiveConfig = info;
-        mDesiredActiveConfig.event = mDesiredActiveConfig.event | prevConfig;
+    std::lock_guard<std::mutex> lock(mActiveModeLock);
+    if (mDesiredActiveModeChanged) {
+        // If a mode change is pending, just cache the latest request in mDesiredActiveMode
+        const Scheduler::ModeEvent prevConfig = mDesiredActiveMode.event;
+        mDesiredActiveMode = info;
+        mDesiredActiveMode.event = mDesiredActiveMode.event | prevConfig;
     } else {
-        // Check if we are already at the desired config
+        // Check if we are already at the desired mode
         const auto display = getDefaultDisplayDeviceLocked();
-        if (!display || display->getActiveMode()->getId() == refreshRate.getConfigId()) {
+        if (!display || display->getActiveMode()->getId() == refreshRate.getModeId()) {
             return;
         }
 
-        // Initiate a config change.
-        mDesiredActiveConfigChanged = true;
-        mDesiredActiveConfig = info;
+        // Initiate a mode change.
+        mDesiredActiveModeChanged = true;
+        mDesiredActiveMode = info;
 
         // This will trigger HWC refresh without resetting the idle timer.
         repaintEverythingForHWC();
@@ -1024,7 +1023,7 @@
         modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
 
         updatePhaseConfiguration(refreshRate.getFps());
-        mScheduler->setConfigChangePending(true);
+        mScheduler->setModeChangePending(true);
     }
 
     if (mRefreshRateOverlay) {
@@ -1032,7 +1031,7 @@
     }
 }
 
-status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int modeId) {
+status_t SurfaceFlinger::setActiveMode(const sp<IBinder>& displayToken, int modeId) {
     ATRACE_CALL();
 
     if (!displayToken) {
@@ -1042,13 +1041,13 @@
     auto future = schedule([=]() -> status_t {
         const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken));
         if (!display) {
-            ALOGE("Attempt to set allowed display configs for invalid display token %p",
+            ALOGE("Attempt to set allowed display modes for invalid display token %p",
                   displayToken.get());
             return NAME_NOT_FOUND;
         }
 
         if (display->isVirtual()) {
-            ALOGW("Attempt to set allowed display configs for virtual display");
+            ALOGW("Attempt to set allowed display modes for virtual display");
             return INVALID_OPERATION;
         }
 
@@ -1067,13 +1066,13 @@
                                                            {fps, fps}};
         constexpr bool kOverridePolicy = false;
 
-        return setDesiredDisplayConfigSpecsInternal(display, policy, kOverridePolicy);
+        return setDesiredDisplayModeSpecsInternal(display, policy, kOverridePolicy);
     });
 
     return future.get();
 }
 
-void SurfaceFlinger::setActiveConfigInternal() {
+void SurfaceFlinger::setActiveModeInternal() {
     ATRACE_CALL();
 
     const auto display = getDefaultDisplayDeviceLocked();
@@ -1081,21 +1080,21 @@
         return;
     }
 
-    const auto upcomingConfig = display->getMode(mUpcomingActiveConfig.configId);
-    if (!upcomingConfig) {
-        ALOGW("Upcoming active config is no longer supported. ConfigId = %zu",
-              mUpcomingActiveConfig.configId.value());
+    const auto upcomingMode = display->getMode(mUpcomingActiveMode.modeId);
+    if (!upcomingMode) {
+        ALOGW("Upcoming active mode is no longer supported. Mode ID = %zu",
+              mUpcomingActiveMode.modeId.value());
         // TODO(b/159590486) Handle the error better. Some parts of SurfaceFlinger may
-        // have been already updated with the upcoming active config.
+        // have been already updated with the upcoming active mode.
         return;
     }
     const Fps oldRefreshRate = display->getActiveMode()->getFps();
 
-    std::lock_guard<std::mutex> lock(mActiveConfigLock);
-    mRefreshRateConfigs->setCurrentConfigId(mUpcomingActiveConfig.configId);
-    display->setActiveMode(mUpcomingActiveConfig.configId);
+    std::lock_guard<std::mutex> lock(mActiveModeLock);
+    mRefreshRateConfigs->setCurrentModeId(mUpcomingActiveMode.modeId);
+    display->setActiveMode(mUpcomingActiveMode.modeId);
 
-    const Fps refreshRate = upcomingConfig->getFps();
+    const Fps refreshRate = upcomingMode->getFps();
 
     mRefreshRateStats->setRefreshRate(refreshRate);
 
@@ -1105,71 +1104,71 @@
     updatePhaseConfiguration(refreshRate);
     ATRACE_INT("ActiveConfigFPS", refreshRate.getValue());
 
-    if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
+    if (mUpcomingActiveMode.event != Scheduler::ModeEvent::None) {
         const nsecs_t vsyncPeriod = refreshRate.getPeriodNsecs();
         const auto physicalId = display->getPhysicalId();
-        mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, physicalId,
-                                                  mUpcomingActiveConfig.configId, vsyncPeriod);
+        mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, physicalId,
+                                                mUpcomingActiveMode.modeId, vsyncPeriod);
     }
 }
 
-void SurfaceFlinger::clearDesiredActiveConfigState() {
-    std::lock_guard<std::mutex> lock(mActiveConfigLock);
-    mDesiredActiveConfig.event = Scheduler::ConfigEvent::None;
-    mDesiredActiveConfigChanged = false;
-    mScheduler->setConfigChangePending(false);
+void SurfaceFlinger::clearDesiredActiveModeState() {
+    std::lock_guard<std::mutex> lock(mActiveModeLock);
+    mDesiredActiveMode.event = Scheduler::ModeEvent::None;
+    mDesiredActiveModeChanged = false;
+    mScheduler->setModeChangePending(false);
 }
 
-void SurfaceFlinger::desiredActiveConfigChangeDone() {
-    const auto modeId = getDesiredActiveConfig()->configId;
+void SurfaceFlinger::desiredActiveModeChangeDone() {
+    const auto modeId = getDesiredActiveMode()->modeId;
 
-    clearDesiredActiveConfigState();
+    clearDesiredActiveModeState();
 
     const auto refreshRate = getDefaultDisplayDeviceLocked()->getMode(modeId)->getFps();
     mScheduler->resyncToHardwareVsync(true, refreshRate.getPeriodNsecs());
     updatePhaseConfiguration(refreshRate);
 }
 
-void SurfaceFlinger::performSetActiveConfig() {
+void SurfaceFlinger::performSetActiveMode() {
     ATRACE_CALL();
     ALOGV("%s", __FUNCTION__);
     // Store the local variable to release the lock.
-    const auto desiredActiveConfig = getDesiredActiveConfig();
-    if (!desiredActiveConfig) {
-        // No desired active config pending to be applied
+    const auto desiredActiveMode = getDesiredActiveMode();
+    if (!desiredActiveMode) {
+        // No desired active mode pending to be applied
         return;
     }
 
     const auto display = getDefaultDisplayDeviceLocked();
-    const auto desiredConfig = display->getMode(desiredActiveConfig->configId);
-    if (!desiredConfig) {
-        ALOGW("Desired display config is no longer supported. Config ID = %zu",
-              desiredActiveConfig->configId.value());
-        clearDesiredActiveConfigState();
+    const auto desiredMode = display->getMode(desiredActiveMode->modeId);
+    if (!desiredMode) {
+        ALOGW("Desired display mode is no longer supported. Mode ID = %zu",
+              desiredActiveMode->modeId.value());
+        clearDesiredActiveModeState();
         return;
     }
-    const auto refreshRate = desiredConfig->getFps();
-    ALOGV("performSetActiveConfig changing active config to %zu(%s)",
-          desiredConfig->getId().value(), to_string(refreshRate).c_str());
+    const auto refreshRate = desiredMode->getFps();
+    ALOGV("%s changing active mode to %zu(%s)", __FUNCTION__, desiredMode->getId().value(),
+          to_string(refreshRate).c_str());
 
-    if (!display || display->getActiveMode()->getId() == desiredActiveConfig->configId) {
+    if (!display || display->getActiveMode()->getId() == desiredActiveMode->modeId) {
         // display is not valid or we are already in the requested mode
         // on both cases there is nothing left to do
-        desiredActiveConfigChangeDone();
+        desiredActiveModeChangeDone();
         return;
     }
 
-    // Desired active config was set, it is different than the config currently in use, however
-    // allowed configs might have changed by the time we process the refresh.
-    // Make sure the desired config is still allowed
-    if (!isDisplayConfigAllowed(desiredActiveConfig->configId)) {
-        desiredActiveConfigChangeDone();
+    // Desired active mode was set, it is different than the mode currently in use, however
+    // allowed modes might have changed by the time we process the refresh.
+    // Make sure the desired mode is still allowed
+    if (!isDisplayModeAllowed(desiredActiveMode->modeId)) {
+        desiredActiveModeChangeDone();
         return;
     }
 
-    mUpcomingActiveConfig = *desiredActiveConfig;
+    mUpcomingActiveMode = *desiredActiveMode;
 
-    ATRACE_INT("ActiveConfigFPS_HWC", refreshRate.getValue());
+    ATRACE_INT("ActiveModeFPS_HWC", refreshRate.getValue());
 
     // TODO(b/142753666) use constrains
     hal::VsyncPeriodChangeConstraints constraints;
@@ -1178,7 +1177,7 @@
 
     hal::VsyncPeriodChangeTimeline outTimeline;
     const auto status =
-            display->initiateModeChange(mUpcomingActiveConfig.configId, constraints, &outTimeline);
+            display->initiateModeChange(mUpcomingActiveMode.modeId, constraints, &outTimeline);
     if (status != NO_ERROR) {
         // initiateModeChange may fail if a hotplug event is just about
         // to be sent. We just log the error in this case.
@@ -1188,7 +1187,7 @@
 
     mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
     // Scheduler will submit an empty frame to HWC if needed.
-    mSetActiveConfigPending = true;
+    mSetActiveModePending = true;
 }
 
 status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken,
@@ -1627,12 +1626,12 @@
     *compositorTiming = getBE().mCompositorTiming;
 }
 
-bool SurfaceFlinger::isDisplayConfigAllowed(DisplayModeId configId) const {
-    return mRefreshRateConfigs->isConfigAllowed(configId);
+bool SurfaceFlinger::isDisplayModeAllowed(DisplayModeId modeId) const {
+    return mRefreshRateConfigs->isModeAllowed(modeId);
 }
 
 void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate,
-                                             Scheduler::ConfigEvent event) {
+                                             Scheduler::ModeEvent event) {
     const auto display = getDefaultDisplayDeviceLocked();
     if (!display || mBootStage != BootStage::FINISHED) {
         return;
@@ -1640,13 +1639,13 @@
     ATRACE_CALL();
 
     // Don't do any updating if the current fps is the same as the new one.
-    if (!isDisplayConfigAllowed(refreshRate.getConfigId())) {
-        ALOGV("Skipping config %zu as it is not part of allowed configs",
-              refreshRate.getConfigId().value());
+    if (!isDisplayModeAllowed(refreshRate.getModeId())) {
+        ALOGV("Skipping mode %zu as it is not part of allowed modes",
+              refreshRate.getModeId().value());
         return;
     }
 
-    setDesiredActiveConfig({refreshRate.getConfigId(), event});
+    setDesiredActiveMode({refreshRate.getModeId(), event});
 }
 
 void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId,
@@ -1829,18 +1828,18 @@
         mGpuFrameMissedCount++;
     }
 
-    // If we are in the middle of a config change and the fence hasn't
+    // If we are in the middle of a mode change and the fence hasn't
     // fired yet just wait for the next invalidate
-    if (mSetActiveConfigPending) {
+    if (mSetActiveModePending) {
         if (framePending) {
             mEventQueue->invalidate();
             return;
         }
 
         // We received the present fence from the HWC, so we assume it successfully updated
-        // the config, hence we update SF.
-        mSetActiveConfigPending = false;
-        ON_MAIN_THREAD(setActiveConfigInternal());
+        // the mode, hence we update SF.
+        mSetActiveModePending = false;
+        ON_MAIN_THREAD(setActiveModeInternal());
     }
 
     if (framePending) {
@@ -1920,7 +1919,7 @@
         mScheduler->chooseRefreshRateForContent();
     }
 
-    ON_MAIN_THREAD(performSetActiveConfig());
+    ON_MAIN_THREAD(performSetActiveMode());
 
     updateCursorAsync();
     updateInputFlinger();
@@ -2195,8 +2194,8 @@
         }
     });
 
-    mTransactionCompletedThread.addPresentFence(mPreviousPresentFences[0]);
-    mTransactionCompletedThread.sendCallbacks();
+    mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0]);
+    mTransactionCallbackInvoker.sendCallbacks();
 
     if (display && display->isPrimary() && display->getPowerMode() == hal::PowerMode::ON &&
         presentFenceTime->isValid()) {
@@ -2370,7 +2369,7 @@
                                 .setVsyncPeriod(hwcMode.vsyncPeriod)
                                 .setDpiX(hwcMode.dpiX)
                                 .setDpiY(hwcMode.dpiY)
-                                .setConfigGroup(hwcMode.configGroup)
+                                .setGroup(hwcMode.configGroup)
                                 .build());
     }
     return modes;
@@ -2391,7 +2390,7 @@
         if (event.connection == hal::Connection::CONNECTED) {
             auto supportedModes = loadSupportedDisplayModes(displayId);
             const auto activeModeHwcId = getHwComposer().getActiveMode(displayId);
-            LOG_ALWAYS_FATAL_IF(!activeModeHwcId, "HWC returned no active config");
+            LOG_ALWAYS_FATAL_IF(!activeModeHwcId, "HWC returned no active mode");
 
             const auto activeMode = *std::find_if(supportedModes.begin(), supportedModes.end(),
                                                   [activeModeHwcId](const DisplayModePtr& mode) {
@@ -2675,9 +2674,8 @@
 
             // TODO(b/175678251) Call a listener instead.
             if (currentState.physical->hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
-                mRefreshRateConfigs
-                        ->updateDisplayConfigs(currentState.physical->supportedModes,
-                                               currentState.physical->activeMode->getId());
+                mRefreshRateConfigs->updateDisplayModes(currentState.physical->supportedModes,
+                                                        currentState.physical->activeMode->getId());
                 mVsyncConfiguration->reset();
                 updatePhaseConfiguration(mRefreshRateConfigs->getCurrentRefreshRate().getFps());
                 if (mRefreshRateOverlay) {
@@ -2935,8 +2933,7 @@
     mCompositionEngine->updateCursorAsync(refreshArgs);
 }
 
-void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate,
-                                       Scheduler::ConfigEvent event) {
+void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate, Scheduler::ModeEvent event) {
     // If this is called from the main thread mStateLock must be locked before
     // Currently the only way to call this function from the main thread is from
     // Scheduler::chooseRefreshRateForContent
@@ -2996,16 +2993,16 @@
     mRegionSamplingThread =
             new RegionSamplingThread(*this, *mScheduler,
                                      RegionSamplingThread::EnvironmentTimingTunables());
-    // Dispatch a config change request for the primary display on scheduler
+    // Dispatch a mode change request for the primary display on scheduler
     // initialization, so that the EventThreads always contain a reference to a
     // prior configuration.
     //
     // This is a bit hacky, but this avoids a back-pointer into the main SF
     // classes from EventThread, and there should be no run-time binder cost
     // anyway since there are no connected apps at this point.
-    mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, displayId,
-                                              displayState.physical->activeMode->getId(),
-                                              vsyncPeriod);
+    mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, displayId,
+                                            displayState.physical->activeMode->getId(),
+                                            vsyncPeriod);
     static auto ignorePresentFences =
             base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false);
     mScheduler->setIgnorePresentFences(
@@ -3555,8 +3552,8 @@
     // that listeners with SurfaceControls will start registration during setClientStateLocked
     // below.
     for (const auto& listener : listenerCallbacks) {
-        mTransactionCompletedThread.startRegistration(listener);
-        mTransactionCompletedThread.endRegistration(listener);
+        mTransactionCallbackInvoker.startRegistration(listener);
+        mTransactionCallbackInvoker.endRegistration(listener);
     }
 
     std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> listenerCallbacksWithSurfaces;
@@ -3575,12 +3572,12 @@
     }
 
     for (const auto& listenerCallback : listenerCallbacksWithSurfaces) {
-        mTransactionCompletedThread.endRegistration(listenerCallback);
+        mTransactionCallbackInvoker.endRegistration(listenerCallback);
     }
 
     // If the state doesn't require a traversal and there are callbacks, send them now
     if (!(clientStateFlags & eTraversalNeeded) && hasListenerCallbacks) {
-        mTransactionCompletedThread.sendCallbacks();
+        mTransactionCallbackInvoker.sendCallbacks();
     }
     transactionFlags |= clientStateFlags;
 
@@ -3695,7 +3692,7 @@
     for (auto& listener : s.listeners) {
         // note that startRegistration will not re-register if the listener has
         // already be registered for a prior surface control
-        mTransactionCompletedThread.startRegistration(listener);
+        mTransactionCallbackInvoker.startRegistration(listener);
         listenerCallbacks.insert(listener);
     }
 
@@ -3708,7 +3705,7 @@
     }
     if (layer == nullptr) {
         for (auto& [listener, callbackIds] : s.listeners) {
-            mTransactionCompletedThread.registerUnpresentedCallbackHandle(
+            mTransactionCallbackInvoker.registerUnpresentedCallbackHandle(
                     new CallbackHandle(listener, callbackIds, s.surface));
         }
         return 0;
@@ -3953,6 +3950,11 @@
     if (what & layer_state_t::eAutoRefreshChanged) {
         layer->setAutoRefresh(s.autoRefresh);
     }
+    if (what & layer_state_t::eStretchChanged) {
+        if (layer->setStretchEffect(s.stretchEffect)) {
+            flags |= eTraversalNeeded;
+        }
+    }
     // This has to happen after we reparent children because when we reparent to null we remove
     // child layers from current state and remove its relative z. If the children are reparented in
     // the same transaction, then we have to make sure we reparent the children first so we do not
@@ -4543,8 +4545,8 @@
 
     mRefreshRateConfigs->dump(result);
 
-    StringAppendF(&result, "(config override by backdoor: %s)\n\n",
-                  mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
+    StringAppendF(&result, "(mode override by backdoor: %s)\n\n",
+                  mDebugDisplayModeSetByBackdoor ? "yes" : "no");
 
     mScheduler->dump(mAppConnectionHandle, result);
     mScheduler->dumpVsync(result);
@@ -4963,8 +4965,8 @@
         case ENABLE_VSYNC_INJECTIONS:
         case GET_ANIMATION_FRAME_STATS:
         case GET_HDR_CAPABILITIES:
-        case SET_DESIRED_DISPLAY_CONFIG_SPECS:
-        case GET_DESIRED_DISPLAY_CONFIG_SPECS:
+        case SET_DESIRED_DISPLAY_MODE_SPECS:
+        case GET_DESIRED_DISPLAY_MODE_SPECS:
         case SET_ACTIVE_COLOR_MODE:
         case GET_AUTO_LOW_LATENCY_MODE_SUPPORT:
         case SET_AUTO_LOW_LATENCY_MODE:
@@ -5006,13 +5008,13 @@
         // information, so it is OK to pass them.
         case AUTHENTICATE_SURFACE:
         case GET_ACTIVE_COLOR_MODE:
-        case GET_ACTIVE_CONFIG:
+        case GET_ACTIVE_DISPLAY_MODE:
         case GET_PHYSICAL_DISPLAY_IDS:
         case GET_PHYSICAL_DISPLAY_TOKEN:
         case GET_DISPLAY_COLOR_MODES:
         case GET_DISPLAY_NATIVE_PRIMARIES:
         case GET_DISPLAY_INFO:
-        case GET_DISPLAY_CONFIGS:
+        case GET_DISPLAY_MODES:
         case GET_DISPLAY_STATE:
         case GET_DISPLAY_STATS:
         case GET_SUPPORTED_FRAME_TIMESTAMPS:
@@ -5033,8 +5035,9 @@
         case CAPTURE_DISPLAY:
         case SET_DISPLAY_BRIGHTNESS:
         case SET_FRAME_TIMELINE_INFO:
-        // This is not sensitive information, so should not require permission control.
-        case GET_GPU_CONTEXT_PRIORITY: {
+        case GET_GPU_CONTEXT_PRIORITY:
+        case GET_EXTRA_BUFFER_COUNT: {
+            // This is not sensitive information, so should not require permission control.
             return OK;
         }
         case ADD_REGION_SAMPLING_LISTENER:
@@ -5386,7 +5389,7 @@
             }
             case 1035: {
                 const int modeId = data.readInt32();
-                mDebugDisplayConfigSetByBackdoor = false;
+                mDebugDisplayModeSetByBackdoor = false;
 
                 const auto displayId = getInternalDisplayId();
                 if (!displayId) {
@@ -5394,12 +5397,12 @@
                     return NO_ERROR;
                 }
 
-                status_t result = setActiveConfig(getPhysicalDisplayToken(*displayId), modeId);
+                status_t result = setActiveMode(getPhysicalDisplayToken(*displayId), modeId);
                 if (result != NO_ERROR) {
                     return result;
                 }
 
-                mDebugDisplayConfigSetByBackdoor = true;
+                mDebugDisplayModeSetByBackdoor = true;
 
                 return NO_ERROR;
             }
@@ -5478,14 +5481,13 @@
     // Update the overlay on the main thread to avoid race conditions with
     // mRefreshRateConfigs->getCurrentRefreshRate()
     static_cast<void>(schedule([=] {
-        const auto desiredActiveConfig = getDesiredActiveConfig();
-        const std::optional<DisplayModeId> desiredConfigId = desiredActiveConfig
-                ? std::make_optional(desiredActiveConfig->configId)
-                : std::nullopt;
+        const auto desiredActiveMode = getDesiredActiveMode();
+        const std::optional<DisplayModeId> desiredModeId =
+                desiredActiveMode ? std::make_optional(desiredActiveMode->modeId) : std::nullopt;
 
         const bool timerExpired = mKernelIdleTimerEnabled && expired;
         const auto newRefreshRate =
-                mRefreshRateConfigs->onKernelTimerChanged(desiredConfigId, timerExpired);
+                mRefreshRateConfigs->onKernelTimerChanged(desiredModeId, timerExpired);
         if (newRefreshRate) {
             if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) {
                 mRefreshRateOverlay->changeRefreshRate(*newRefreshRate);
@@ -6072,7 +6074,7 @@
     }
 }
 
-status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal(
+status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal(
         const sp<DisplayDevice>& display,
         const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) {
     Mutex::Autolock lock(mStateLock);
@@ -6082,10 +6084,10 @@
     LOG_ALWAYS_FATAL_IF(!policy && !overridePolicy, "Can only clear the override policy");
 
     if (!display->isPrimary()) {
-        // TODO(b/144711714): For non-primary displays we should be able to set an active config
+        // TODO(b/144711714): For non-primary displays we should be able to set an active mode
         // as well. For now, just call directly to initiateModeChange but ideally
-        // it should go thru setDesiredActiveConfig, similar to primary display.
-        ALOGV("setAllowedDisplayConfigsInternal for non-primary display");
+        // it should go thru setDesiredActiveMode, similar to primary display.
+        ALOGV("%s for non-primary display", __func__);
         const auto displayId = display->getPhysicalId();
 
         hal::VsyncPeriodChangeConstraints constraints;
@@ -6093,23 +6095,22 @@
         constraints.seamlessRequired = false;
 
         hal::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
-        if (display->initiateModeChange(policy->defaultConfig, constraints, &timeline) !=
-            NO_ERROR) {
+        if (display->initiateModeChange(policy->defaultMode, constraints, &timeline) != NO_ERROR) {
             return BAD_VALUE;
         }
         if (timeline.refreshRequired) {
             repaintEverythingForHWC();
         }
 
-        display->setActiveMode(policy->defaultConfig);
-        const nsecs_t vsyncPeriod = display->getMode(policy->defaultConfig)->getVsyncPeriod();
-        mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle, displayId,
-                                                     policy->defaultConfig, vsyncPeriod);
+        display->setActiveMode(policy->defaultMode);
+        const nsecs_t vsyncPeriod = display->getMode(policy->defaultMode)->getVsyncPeriod();
+        mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, displayId,
+                                                   policy->defaultMode, vsyncPeriod);
         return NO_ERROR;
     }
 
-    if (mDebugDisplayConfigSetByBackdoor) {
-        // ignore this request as config is overridden by backdoor
+    if (mDebugDisplayModeSetByBackdoor) {
+        // ignore this request as mode is overridden by backdoor
         return NO_ERROR;
     }
 
@@ -6124,42 +6125,43 @@
     }
     scheduler::RefreshRateConfigs::Policy currentPolicy = mRefreshRateConfigs->getCurrentPolicy();
 
-    ALOGV("Setting desired display config specs: %s", currentPolicy.toString().c_str());
+    ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
 
     // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
     // be depending in this callback.
-    const auto activeConfig = display->getActiveMode();
-    const nsecs_t vsyncPeriod = activeConfig->getVsyncPeriod();
+    const auto activeMode = display->getActiveMode();
+    const nsecs_t vsyncPeriod = activeMode->getVsyncPeriod();
     const auto physicalId = display->getPhysicalId();
-    mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, physicalId,
-                                              activeConfig->getId(), vsyncPeriod);
+    mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, physicalId, activeMode->getId(),
+                                            vsyncPeriod);
     toggleKernelIdleTimer();
 
-    auto configId = mScheduler->getPreferredConfigId();
-    auto preferredRefreshRate = configId
-            ? mRefreshRateConfigs->getRefreshRateFromConfigId(*configId)
-            // NOTE: Choose the default config ID, if Scheduler doesn't have one in mind.
-            : mRefreshRateConfigs->getRefreshRateFromConfigId(currentPolicy.defaultConfig);
-    ALOGV("trying to switch to Scheduler preferred config %zu (%s)",
-          preferredRefreshRate.getConfigId().value(), preferredRefreshRate.getName().c_str());
+    auto modeId = mScheduler->getPreferredModeId();
+    auto preferredRefreshRate = modeId
+            ? mRefreshRateConfigs->getRefreshRateFromModeId(*modeId)
+            // NOTE: Choose the default mode ID, if Scheduler doesn't have one in mind.
+            : mRefreshRateConfigs->getRefreshRateFromModeId(currentPolicy.defaultMode);
+    ALOGV("trying to switch to Scheduler preferred mode %zu (%s)",
+          preferredRefreshRate.getModeId().value(), preferredRefreshRate.getName().c_str());
 
-    if (isDisplayConfigAllowed(preferredRefreshRate.getConfigId())) {
-        ALOGV("switching to Scheduler preferred config %zu",
-              preferredRefreshRate.getConfigId().value());
-        setDesiredActiveConfig(
-                {preferredRefreshRate.getConfigId(), Scheduler::ConfigEvent::Changed});
+    if (isDisplayModeAllowed(preferredRefreshRate.getModeId())) {
+        ALOGV("switching to Scheduler preferred display mode %zu",
+              preferredRefreshRate.getModeId().value());
+        setDesiredActiveMode({preferredRefreshRate.getModeId(), Scheduler::ModeEvent::Changed});
     } else {
-        LOG_ALWAYS_FATAL("Desired config not allowed: %zu",
-                         preferredRefreshRate.getConfigId().value());
+        LOG_ALWAYS_FATAL("Desired display mode not allowed: %zu",
+                         preferredRefreshRate.getModeId().value());
     }
 
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(
-        const sp<IBinder>& displayToken, int32_t defaultConfig, bool allowGroupSwitching,
-        float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin,
-        float appRequestRefreshRateMax) {
+status_t SurfaceFlinger::setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+                                                    size_t defaultMode, bool allowGroupSwitching,
+                                                    float primaryRefreshRateMin,
+                                                    float primaryRefreshRateMax,
+                                                    float appRequestRefreshRateMin,
+                                                    float appRequestRefreshRateMax) {
     ATRACE_CALL();
 
     if (!displayToken) {
@@ -6169,34 +6171,34 @@
     auto future = schedule([=]() -> status_t {
         const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken));
         if (!display) {
-            ALOGE("Attempt to set desired display configs for invalid display token %p",
+            ALOGE("Attempt to set desired display modes for invalid display token %p",
                   displayToken.get());
             return NAME_NOT_FOUND;
         } else if (display->isVirtual()) {
-            ALOGW("Attempt to set desired display configs for virtual display");
+            ALOGW("Attempt to set desired display modes for virtual display");
             return INVALID_OPERATION;
         } else {
             using Policy = scheduler::RefreshRateConfigs::Policy;
-            const Policy policy{DisplayModeId(defaultConfig),
+            const Policy policy{DisplayModeId(defaultMode),
                                 allowGroupSwitching,
                                 {Fps(primaryRefreshRateMin), Fps(primaryRefreshRateMax)},
                                 {Fps(appRequestRefreshRateMin), Fps(appRequestRefreshRateMax)}};
             constexpr bool kOverridePolicy = false;
 
-            return setDesiredDisplayConfigSpecsInternal(display, policy, kOverridePolicy);
+            return setDesiredDisplayModeSpecsInternal(display, policy, kOverridePolicy);
         }
     });
 
     return future.get();
 }
 
-status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(
-        const sp<IBinder>& displayToken, int32_t* outDefaultConfig, bool* outAllowGroupSwitching,
+status_t SurfaceFlinger::getDesiredDisplayModeSpecs(
+        const sp<IBinder>& displayToken, size_t* outDefaultMode, bool* outAllowGroupSwitching,
         float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax,
         float* outAppRequestRefreshRateMin, float* outAppRequestRefreshRateMax) {
     ATRACE_CALL();
 
-    if (!displayToken || !outDefaultConfig || !outPrimaryRefreshRateMin ||
+    if (!displayToken || !outDefaultMode || !outPrimaryRefreshRateMin ||
         !outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) {
         return BAD_VALUE;
     }
@@ -6210,7 +6212,7 @@
     if (display->isPrimary()) {
         scheduler::RefreshRateConfigs::Policy policy =
                 mRefreshRateConfigs->getDisplayManagerPolicy();
-        *outDefaultConfig = policy.defaultConfig.value();
+        *outDefaultMode = policy.defaultMode.value();
         *outAllowGroupSwitching = policy.allowGroupSwitching;
         *outPrimaryRefreshRateMin = policy.primaryRange.min.getValue();
         *outPrimaryRefreshRateMax = policy.primaryRange.max.getValue();
@@ -6221,7 +6223,7 @@
         return INVALID_OPERATION;
     } else {
         const auto activeMode = display->getActiveMode();
-        *outDefaultConfig = activeMode->getId().value();
+        *outDefaultMode = activeMode->getId().value();
         *outAllowGroupSwitching = false;
         auto vsyncPeriod = activeMode->getVsyncPeriod();
         *outPrimaryRefreshRateMin = Fps::fromPeriodNsecs(vsyncPeriod).getValue();
@@ -6351,16 +6353,15 @@
             const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
 
             // This is a little racy, but not in a way that hurts anything. As we grab the
-            // defaultConfig from the display manager policy, we could be setting a new display
-            // manager policy, leaving us using a stale defaultConfig. The defaultConfig doesn't
+            // defaultMode from the display manager policy, we could be setting a new display
+            // manager policy, leaving us using a stale defaultMode. The defaultMode doesn't
             // matter for the override policy though, since we set allowGroupSwitching to
             // true, so it's not a problem.
             scheduler::RefreshRateConfigs::Policy overridePolicy;
-            overridePolicy.defaultConfig =
-                    mRefreshRateConfigs->getDisplayManagerPolicy().defaultConfig;
+            overridePolicy.defaultMode = mRefreshRateConfigs->getDisplayManagerPolicy().defaultMode;
             overridePolicy.allowGroupSwitching = true;
             constexpr bool kOverridePolicy = true;
-            result = setDesiredDisplayConfigSpecsInternal(display, overridePolicy, kOverridePolicy);
+            result = setDesiredDisplayModeSpecsInternal(display, overridePolicy, kOverridePolicy);
         }
 
         if (result == NO_ERROR) {
@@ -6400,7 +6401,7 @@
         if (mFrameRateFlexibilityTokenCount == 0) {
             const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
             constexpr bool kOverridePolicy = true;
-            status_t result = setDesiredDisplayConfigSpecsInternal(display, {}, kOverridePolicy);
+            status_t result = setDesiredDisplayModeSpecsInternal(display, {}, kOverridePolicy);
             LOG_ALWAYS_FATAL_IF(result < 0, "Failed releasing frame rate flexibility token");
         }
     }));
@@ -6461,6 +6462,25 @@
     return getRenderEngine().getContextPriority();
 }
 
+int SurfaceFlinger::calculateExtraBufferCount(Fps maxSupportedRefreshRate,
+                                              std::chrono::nanoseconds presentLatency) {
+    auto pipelineDepth = presentLatency.count() / maxSupportedRefreshRate.getPeriodNsecs();
+    if (presentLatency.count() % maxSupportedRefreshRate.getPeriodNsecs()) {
+        pipelineDepth++;
+    }
+    return std::max(0ll, pipelineDepth - 2);
+}
+
+status_t SurfaceFlinger::getExtraBufferCount(int* extraBuffers) const {
+    const auto maxSupportedRefreshRate = mRefreshRateConfigs->getSupportedRefreshRateRange().max;
+    const auto vsyncConfig =
+            mVsyncConfiguration->getConfigsForRefreshRate(maxSupportedRefreshRate).late;
+    const auto presentLatency = vsyncConfig.appWorkDuration + vsyncConfig.sfWorkDuration;
+
+    *extraBuffers = calculateExtraBufferCount(maxSupportedRefreshRate, presentLatency);
+    return NO_ERROR;
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 400345f..a62d0b9 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -64,7 +64,7 @@
 #include "SurfaceFlingerFactory.h"
 #include "SurfaceTracing.h"
 #include "TracedOrdinal.h"
-#include "TransactionCompletedThread.h"
+#include "TransactionCallbackInvoker.h"
 
 #include <atomic>
 #include <cstdint>
@@ -319,8 +319,8 @@
 
     void removeFromOffscreenLayers(Layer* layer);
 
-    TransactionCompletedThread& getTransactionCompletedThread() {
-        return mTransactionCompletedThread;
+    TransactionCallbackInvoker& getTransactionCallbackInvoker() {
+        return mTransactionCallbackInvoker;
     }
 
     // Converts from a binder handle to a Layer
@@ -414,12 +414,12 @@
         void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
     };
 
-    struct ActiveConfigInfo {
-        DisplayModeId configId;
-        Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None;
+    struct ActiveModeInfo {
+        DisplayModeId modeId;
+        Scheduler::ModeEvent event = Scheduler::ModeEvent::None;
 
-        bool operator!=(const ActiveConfigInfo& other) const {
-            return configId != other.configId || event != other.event;
+        bool operator!=(const ActiveModeInfo& other) const {
+            return modeId != other.modeId || event != other.event;
         }
     };
 
@@ -554,8 +554,8 @@
     status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
     status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*) override;
     status_t getDisplayInfo(const sp<IBinder>& displayToken, DisplayInfo*) override;
-    status_t getDisplayConfigs(const sp<IBinder>& displayToken, Vector<DisplayConfig>*) override;
-    int getActiveConfig(const sp<IBinder>& displayToken) override;
+    status_t getDisplayModes(const sp<IBinder>& displayToken, Vector<ui::DisplayMode>*) override;
+    int getActiveDisplayModeId(const sp<IBinder>& displayToken) override;
     status_t getDisplayColorModes(const sp<IBinder>& displayToken, Vector<ui::ColorMode>*) override;
     status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken,
                                        ui::DisplayPrimaries&) override;
@@ -594,17 +594,16 @@
     status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
                                        const sp<IRegionSamplingListener>& listener) override;
     status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
-    status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, int32_t displayModeId,
-                                          bool allowGroupSwitching, float primaryRefreshRateMin,
-                                          float primaryRefreshRateMax,
-                                          float appRequestRefreshRateMin,
-                                          float appRequestRefreshRateMax) override;
-    status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                          int32_t* outDefaultConfig, bool* outAllowGroupSwitching,
-                                          float* outPrimaryRefreshRateMin,
-                                          float* outPrimaryRefreshRateMax,
-                                          float* outAppRequestRefreshRateMin,
-                                          float* outAppRequestRefreshRateMax) override;
+    status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, size_t displayModeId,
+                                        bool allowGroupSwitching, float primaryRefreshRateMin,
+                                        float primaryRefreshRateMax, float appRequestRefreshRateMin,
+                                        float appRequestRefreshRateMax) override;
+    status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, size_t* outDefaultMode,
+                                        bool* outAllowGroupSwitching,
+                                        float* outPrimaryRefreshRateMin,
+                                        float* outPrimaryRefreshRateMax,
+                                        float* outAppRequestRefreshRateMin,
+                                        float* outAppRequestRefreshRateMax) override;
     status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
                                          bool* outSupport) const override;
     status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) override;
@@ -623,6 +622,8 @@
 
     int getGPUContextPriority() override;
 
+    status_t getExtraBufferCount(int* extraBuffers) const override;
+
     // Implements IBinder::DeathRecipient.
     void binderDied(const wp<IBinder>& who) override;
 
@@ -649,7 +650,7 @@
     // Toggles hardware VSYNC by calling into HWC.
     void setVsyncEnabled(bool) override;
     // Initiates a refresh rate change to be applied on invalidate.
-    void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override;
+    void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override;
     // Forces full composition on all displays without resetting the scheduler idle timer.
     void repaintEverythingForHWC() override;
     // Called when kernel idle timer has expired. Used to update the refresh rate overlay.
@@ -677,24 +678,24 @@
 
     // Called on the main thread in response to initializeDisplays()
     void onInitializeDisplays() REQUIRES(mStateLock);
-    // Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
-    void setDesiredActiveConfig(const ActiveConfigInfo& info) REQUIRES(mStateLock);
-    status_t setActiveConfig(const sp<IBinder>& displayToken, int id);
-    // Once HWC has returned the present fence, this sets the active config and a new refresh
+    // Sets the desired active mode bit. It obtains the lock, and sets mDesiredActiveMode.
+    void setDesiredActiveMode(const ActiveModeInfo& info) REQUIRES(mStateLock);
+    status_t setActiveMode(const sp<IBinder>& displayToken, int id);
+    // Once HWC has returned the present fence, this sets the active mode and a new refresh
     // rate in SF.
-    void setActiveConfigInternal() REQUIRES(mStateLock);
-    // Calls to setActiveConfig on the main thread if there is a pending config
+    void setActiveModeInternal() REQUIRES(mStateLock);
+    // Calls to setActiveMode on the main thread if there is a pending mode change
     // that needs to be applied.
-    void performSetActiveConfig() REQUIRES(mStateLock);
-    void clearDesiredActiveConfigState() REQUIRES(mStateLock) EXCLUDES(mActiveConfigLock);
-    // Called when active config is no longer is progress
-    void desiredActiveConfigChangeDone() REQUIRES(mStateLock);
+    void performSetActiveMode() REQUIRES(mStateLock);
+    void clearDesiredActiveModeState() REQUIRES(mStateLock) EXCLUDES(mActiveModeLock);
+    // Called when active mode is no longer is progress
+    void desiredActiveModeChangeDone() REQUIRES(mStateLock);
     // Called on the main thread in response to setPowerMode()
     void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode)
             REQUIRES(mStateLock);
 
-    // Sets the desired display configs.
-    status_t setDesiredDisplayConfigSpecsInternal(
+    // Sets the desired display mode specs.
+    status_t setDesiredDisplayModeSpecsInternal(
             const sp<DisplayDevice>& display,
             const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy)
             EXCLUDES(mStateLock);
@@ -929,9 +930,9 @@
 
     // Sets the refresh rate by switching active configs, if they are available for
     // the desired refresh rate.
-    void changeRefreshRateLocked(const RefreshRate&, Scheduler::ConfigEvent) REQUIRES(mStateLock);
+    void changeRefreshRateLocked(const RefreshRate&, Scheduler::ModeEvent) REQUIRES(mStateLock);
 
-    bool isDisplayConfigAllowed(DisplayModeId configId) const REQUIRES(mStateLock);
+    bool isDisplayModeAllowed(DisplayModeId) const REQUIRES(mStateLock);
 
     // Gets the fence for the previous frame.
     // Must be called on the main thread.
@@ -1047,12 +1048,15 @@
      * Misc
      */
 
-    std::optional<ActiveConfigInfo> getDesiredActiveConfig() EXCLUDES(mActiveConfigLock) {
-        std::lock_guard<std::mutex> lock(mActiveConfigLock);
-        if (mDesiredActiveConfigChanged) return mDesiredActiveConfig;
+    std::optional<ActiveModeInfo> getDesiredActiveMode() EXCLUDES(mActiveModeLock) {
+        std::lock_guard<std::mutex> lock(mActiveModeLock);
+        if (mDesiredActiveModeChanged) return mDesiredActiveMode;
         return std::nullopt;
     }
 
+    static int calculateExtraBufferCount(Fps maxSupportedRefreshRate,
+                                         std::chrono::nanoseconds presentLatency);
+
     sp<StartPropertySetThread> mStartPropertySetThread;
     surfaceflinger::Factory& mFactory;
 
@@ -1161,7 +1165,7 @@
     std::atomic<uint32_t> mHwcFrameMissedCount = 0;
     std::atomic<uint32_t> mGpuFrameMissedCount = 0;
 
-    TransactionCompletedThread mTransactionCompletedThread;
+    TransactionCallbackInvoker mTransactionCallbackInvoker;
 
     // Restrict layers to use two buffers in their bufferqueues.
     bool mLayerTripleBufferingDisabled = false;
@@ -1246,18 +1250,18 @@
     nsecs_t mScheduledPresentTime = 0;
     hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
 
-    std::mutex mActiveConfigLock;
-    // This bit is set once we start setting the config. We read from this bit during the
-    // process. If at the end, this bit is different than mDesiredActiveConfig, we restart
+    std::mutex mActiveModeLock;
+    // This bit is set once we start setting the mode. We read from this bit during the
+    // process. If at the end, this bit is different than mDesiredActiveMode, we restart
     // the process.
-    ActiveConfigInfo mUpcomingActiveConfig; // Always read and written on the main thread.
-    // This bit can be set at any point in time when the system wants the new config.
-    ActiveConfigInfo mDesiredActiveConfig GUARDED_BY(mActiveConfigLock);
+    ActiveModeInfo mUpcomingActiveMode; // Always read and written on the main thread.
+    // This bit can be set at any point in time when the system wants the new mode.
+    ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock);
 
     // below flags are set by main thread only
-    TracedOrdinal<bool> mDesiredActiveConfigChanged
-            GUARDED_BY(mActiveConfigLock) = {"DesiredActiveConfigChanged", false};
-    bool mSetActiveConfigPending = false;
+    TracedOrdinal<bool> mDesiredActiveModeChanged
+            GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false};
+    bool mSetActiveModePending = false;
 
     bool mLumaSampling = true;
     sp<RegionSamplingThread> mRegionSamplingThread;
@@ -1281,8 +1285,8 @@
     void enableRefreshRateOverlay(bool enable);
     std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay GUARDED_BY(mStateLock);
 
-    // Flag used to set override allowed display configs from backdoor
-    bool mDebugDisplayConfigSetByBackdoor = false;
+    // Flag used to set override desired display mode from backdoor
+    bool mDebugDisplayModeSetByBackdoor = false;
 
     // A set of layers that have no parent so they are not drawn on screen.
     // Should only be accessed by the main thread.
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
new file mode 100644
index 0000000..a78510e
--- /dev/null
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2018 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/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "TransactionCallbackInvoker"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "TransactionCallbackInvoker.h"
+
+#include <cinttypes>
+
+#include <binder/IInterface.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+// Returns 0 if they are equal
+//         <0 if the first id that doesn't match is lower in c2 or all ids match but c2 is shorter
+//         >0 if the first id that doesn't match is greater in c2 or all ids match but c2 is longer
+//
+// See CallbackIdsHash for a explaniation of why this works
+static int compareCallbackIds(const std::vector<CallbackId>& c1,
+                              const std::vector<CallbackId>& c2) {
+    if (c1.empty()) {
+        return !c2.empty();
+    }
+    return c1.front() - c2.front();
+}
+
+TransactionCallbackInvoker::~TransactionCallbackInvoker() {
+    {
+        std::lock_guard lock(mMutex);
+        for (const auto& [listener, transactionStats] : mCompletedTransactions) {
+            listener->unlinkToDeath(mDeathRecipient);
+        }
+    }
+}
+
+status_t TransactionCallbackInvoker::startRegistration(const ListenerCallbacks& listenerCallbacks) {
+    std::lock_guard lock(mMutex);
+
+    auto [itr, inserted] = mRegisteringTransactions.insert(listenerCallbacks);
+    auto& [listener, callbackIds] = listenerCallbacks;
+
+    if (inserted) {
+        if (mCompletedTransactions.count(listener) == 0) {
+            status_t err = listener->linkToDeath(mDeathRecipient);
+            if (err != NO_ERROR) {
+                ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
+                return err;
+            }
+        }
+        auto& transactionStatsDeque = mCompletedTransactions[listener];
+        transactionStatsDeque.emplace_back(callbackIds);
+    }
+
+    return NO_ERROR;
+}
+
+status_t TransactionCallbackInvoker::endRegistration(const ListenerCallbacks& listenerCallbacks) {
+    std::lock_guard lock(mMutex);
+
+    auto itr = mRegisteringTransactions.find(listenerCallbacks);
+    if (itr == mRegisteringTransactions.end()) {
+        ALOGE("cannot end a registration that does not exist");
+        return BAD_VALUE;
+    }
+
+    mRegisteringTransactions.erase(itr);
+
+    return NO_ERROR;
+}
+
+bool TransactionCallbackInvoker::isRegisteringTransaction(
+        const sp<IBinder>& transactionListener, const std::vector<CallbackId>& callbackIds) {
+    ListenerCallbacks listenerCallbacks(transactionListener, callbackIds);
+
+    auto itr = mRegisteringTransactions.find(listenerCallbacks);
+    return itr != mRegisteringTransactions.end();
+}
+
+status_t TransactionCallbackInvoker::registerPendingCallbackHandle(
+        const sp<CallbackHandle>& handle) {
+    std::lock_guard lock(mMutex);
+
+    // If we can't find the transaction stats something has gone wrong. The client should call
+    // startRegistration before trying to register a pending callback handle.
+    TransactionStats* transactionStats;
+    status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
+    if (err != NO_ERROR) {
+        ALOGE("cannot find transaction stats");
+        return err;
+    }
+
+    mPendingTransactions[handle->listener][handle->callbackIds]++;
+    return NO_ERROR;
+}
+
+status_t TransactionCallbackInvoker::finalizePendingCallbackHandles(
+        const std::deque<sp<CallbackHandle>>& handles, const std::vector<JankData>& jankData) {
+    if (handles.empty()) {
+        return NO_ERROR;
+    }
+    std::lock_guard lock(mMutex);
+
+    for (const auto& handle : handles) {
+        auto listener = mPendingTransactions.find(handle->listener);
+        if (listener != mPendingTransactions.end()) {
+            auto& pendingCallbacks = listener->second;
+            auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
+
+            if (pendingCallback != pendingCallbacks.end()) {
+                auto& pendingCount = pendingCallback->second;
+
+                // Decrease the pending count for this listener
+                if (--pendingCount == 0) {
+                    pendingCallbacks.erase(pendingCallback);
+                }
+            } else {
+                ALOGW("there are more latched callbacks than there were registered callbacks");
+            }
+            if (listener->second.size() == 0) {
+                mPendingTransactions.erase(listener);
+            }
+        } else {
+            ALOGW("cannot find listener in mPendingTransactions");
+        }
+
+        status_t err = addCallbackHandle(handle, jankData);
+        if (err != NO_ERROR) {
+            ALOGE("could not add callback handle");
+            return err;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+status_t TransactionCallbackInvoker::registerUnpresentedCallbackHandle(
+        const sp<CallbackHandle>& handle) {
+    std::lock_guard lock(mMutex);
+
+    return addCallbackHandle(handle, std::vector<JankData>());
+}
+
+status_t TransactionCallbackInvoker::findTransactionStats(
+        const sp<IBinder>& listener, const std::vector<CallbackId>& callbackIds,
+        TransactionStats** outTransactionStats) {
+    auto& transactionStatsDeque = mCompletedTransactions[listener];
+
+    // Search back to front because the most recent transactions are at the back of the deque
+    auto itr = transactionStatsDeque.rbegin();
+    for (; itr != transactionStatsDeque.rend(); itr++) {
+        if (compareCallbackIds(itr->callbackIds, callbackIds) == 0) {
+            *outTransactionStats = &(*itr);
+            return NO_ERROR;
+        }
+    }
+
+    ALOGE("could not find transaction stats");
+    return BAD_VALUE;
+}
+
+status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& handle,
+        const std::vector<JankData>& jankData) {
+    // If we can't find the transaction stats something has gone wrong. The client should call
+    // startRegistration before trying to add a callback handle.
+    TransactionStats* transactionStats;
+    status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    transactionStats->latchTime = handle->latchTime;
+    // If the layer has already been destroyed, don't add the SurfaceControl to the callback.
+    // The client side keeps a sp<> to the SurfaceControl so if the SurfaceControl has been
+    // destroyed the client side is dead and there won't be anyone to send the callback to.
+    sp<IBinder> surfaceControl = handle->surfaceControl.promote();
+    if (surfaceControl) {
+        FrameEventHistoryStats eventStats(handle->frameNumber,
+                                          handle->gpuCompositionDoneFence->getSnapshot().fence,
+                                          handle->compositorTiming, handle->refreshStartTime,
+                                          handle->dequeueReadyTime);
+        transactionStats->surfaceStats.emplace_back(surfaceControl, handle->acquireTime,
+                                                    handle->previousReleaseFence,
+                                                    handle->transformHint, eventStats, jankData);
+    }
+    return NO_ERROR;
+}
+
+void TransactionCallbackInvoker::addPresentFence(const sp<Fence>& presentFence) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mPresentFence = presentFence;
+}
+
+void TransactionCallbackInvoker::sendCallbacks() {
+    std::lock_guard lock(mMutex);
+
+    // For each listener
+    auto completedTransactionsItr = mCompletedTransactions.begin();
+    while (completedTransactionsItr != mCompletedTransactions.end()) {
+        auto& [listener, transactionStatsDeque] = *completedTransactionsItr;
+        ListenerStats listenerStats;
+        listenerStats.listener = listener;
+
+        // For each transaction
+        auto transactionStatsItr = transactionStatsDeque.begin();
+        while (transactionStatsItr != transactionStatsDeque.end()) {
+            auto& transactionStats = *transactionStatsItr;
+
+            // If this transaction is still registering, it is not safe to send a callback
+            // because there could be surface controls that haven't been added to
+            // transaction stats or mPendingTransactions.
+            if (isRegisteringTransaction(listener, transactionStats.callbackIds)) {
+                break;
+            }
+
+            // If we are still waiting on the callback handles for this transaction, stop
+            // here because all transaction callbacks for the same listener must come in order
+            auto pendingTransactions = mPendingTransactions.find(listener);
+            if (pendingTransactions != mPendingTransactions.end() &&
+                pendingTransactions->second.count(transactionStats.callbackIds) != 0) {
+                break;
+            }
+
+            // If the transaction has been latched
+            if (transactionStats.latchTime >= 0) {
+                if (!mPresentFence) {
+                    break;
+                }
+                transactionStats.presentFence = mPresentFence;
+            }
+
+            // Remove the transaction from completed to the callback
+            listenerStats.transactionStats.push_back(std::move(transactionStats));
+            transactionStatsItr = transactionStatsDeque.erase(transactionStatsItr);
+        }
+        // If the listener has completed transactions
+        if (!listenerStats.transactionStats.empty()) {
+            // If the listener is still alive
+            if (listener->isBinderAlive()) {
+                // Send callback.  The listener stored in listenerStats
+                // comes from the cross-process setTransactionState call to
+                // SF.  This MUST be an ITransactionCompletedListener.  We
+                // keep it as an IBinder due to consistency reasons: if we
+                // interface_cast at the IPC boundary when reading a Parcel,
+                // we get pointers that compare unequal in the SF process.
+                interface_cast<ITransactionCompletedListener>(listenerStats.listener)
+                        ->onTransactionCompleted(listenerStats);
+                if (transactionStatsDeque.empty()) {
+                    listener->unlinkToDeath(mDeathRecipient);
+                    completedTransactionsItr =
+                            mCompletedTransactions.erase(completedTransactionsItr);
+                } else {
+                    completedTransactionsItr++;
+                }
+            } else {
+                completedTransactionsItr =
+                        mCompletedTransactions.erase(completedTransactionsItr);
+            }
+        } else {
+            completedTransactionsItr++;
+        }
+    }
+
+    if (mPresentFence) {
+        mPresentFence.clear();
+    }
+}
+
+// -----------------------------------------------------------------------
+
+CallbackHandle::CallbackHandle(const sp<IBinder>& transactionListener,
+                               const std::vector<CallbackId>& ids, const sp<IBinder>& sc)
+      : listener(transactionListener), callbackIds(ids), surfaceControl(sc) {}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCallbackInvoker.h
similarity index 88%
rename from services/surfaceflinger/TransactionCompletedThread.h
rename to services/surfaceflinger/TransactionCallbackInvoker.h
index c4ba7e4..a240c82 100644
--- a/services/surfaceflinger/TransactionCompletedThread.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -52,11 +52,9 @@
     uint64_t frameNumber = 0;
 };
 
-class TransactionCompletedThread {
+class TransactionCallbackInvoker {
 public:
-    ~TransactionCompletedThread();
-
-    void run();
+    ~TransactionCallbackInvoker();
 
     // Adds listener and callbackIds in case there are no SurfaceControls that are supposed
     // to be included in the callback. This functions should be call before attempting to register
@@ -66,13 +64,13 @@
     // It is safe to send a callback if the Transaction doesn't have any Pending callback handles.
     status_t endRegistration(const ListenerCallbacks& listenerCallbacks);
 
-    // Informs the TransactionCompletedThread that there is a Transaction with a CallbackHandle
+    // Informs the TransactionCallbackInvoker that there is a Transaction with a CallbackHandle
     // that needs to be latched and presented this frame. This function should be called once the
-    // layer has received the CallbackHandle so the TransactionCompletedThread knows not to send
+    // layer has received the CallbackHandle so the TransactionCallbackInvoker knows not to send
     // a callback for that Listener/Transaction pair until that CallbackHandle has been latched and
     // presented.
     status_t registerPendingCallbackHandle(const sp<CallbackHandle>& handle);
-    // Notifies the TransactionCompletedThread that a pending CallbackHandle has been presented.
+    // Notifies the TransactionCallbackInvoker that a pending CallbackHandle has been presented.
     status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
                                             const std::vector<JankData>& jankData);
 
@@ -85,7 +83,6 @@
     void sendCallbacks();
 
 private:
-    void threadMain();
 
     bool isRegisteringTransaction(const sp<IBinder>& transactionListener,
                                   const std::vector<CallbackId>& callbackIds) REQUIRES(mMutex);
@@ -97,7 +94,7 @@
     status_t addCallbackHandle(const sp<CallbackHandle>& handle,
                                const std::vector<JankData>& jankData) REQUIRES(mMutex);
 
-    class ThreadDeathRecipient : public IBinder::DeathRecipient {
+    class CallbackDeathRecipient : public IBinder::DeathRecipient {
     public:
         // This function is a no-op. isBinderAlive needs a linked DeathRecipient to work.
         // Death recipients needs a binderDied function.
@@ -106,12 +103,8 @@
         // sendObituary is only called if linkToDeath was called with a DeathRecipient.)
         void binderDied(const wp<IBinder>& /*who*/) override {}
     };
-    sp<ThreadDeathRecipient> mDeathRecipient;
-
-    // Protects the creation and destruction of mThread
-    std::mutex mThreadMutex;
-
-    std::thread mThread GUARDED_BY(mThreadMutex);
+    sp<CallbackDeathRecipient> mDeathRecipient =
+        new CallbackDeathRecipient();
 
     std::mutex mMutex;
     std::condition_variable_any mConditionVariable;
@@ -128,9 +121,6 @@
     std::unordered_map<sp<IBinder>, std::deque<TransactionStats>, IListenerHash>
             mCompletedTransactions GUARDED_BY(mMutex);
 
-    bool mRunning GUARDED_BY(mMutex) = false;
-    bool mKeepRunning GUARDED_BY(mMutex) = true;
-
     sp<Fence> mPresentFence GUARDED_BY(mMutex);
 };
 
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
deleted file mode 100644
index 1797af4..0000000
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ /dev/null
@@ -1,372 +0,0 @@
-/*
- * Copyright 2018 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/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-//#define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "TransactionCompletedThread"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "TransactionCompletedThread.h"
-
-#include <cinttypes>
-
-#include <binder/IInterface.h>
-#include <utils/RefBase.h>
-
-namespace android {
-
-// Returns 0 if they are equal
-//         <0 if the first id that doesn't match is lower in c2 or all ids match but c2 is shorter
-//         >0 if the first id that doesn't match is greater in c2 or all ids match but c2 is longer
-//
-// See CallbackIdsHash for a explaniation of why this works
-static int compareCallbackIds(const std::vector<CallbackId>& c1,
-                              const std::vector<CallbackId>& c2) {
-    if (c1.empty()) {
-        return !c2.empty();
-    }
-    return c1.front() - c2.front();
-}
-
-TransactionCompletedThread::~TransactionCompletedThread() {
-    std::lock_guard lockThread(mThreadMutex);
-
-    {
-        std::lock_guard lock(mMutex);
-        mKeepRunning = false;
-        mConditionVariable.notify_all();
-    }
-
-    if (mThread.joinable()) {
-        mThread.join();
-    }
-
-    {
-        std::lock_guard lock(mMutex);
-        for (const auto& [listener, transactionStats] : mCompletedTransactions) {
-            listener->unlinkToDeath(mDeathRecipient);
-        }
-    }
-}
-
-void TransactionCompletedThread::run() {
-    std::lock_guard lock(mMutex);
-    if (mRunning || !mKeepRunning) {
-        return;
-    }
-    mDeathRecipient = new ThreadDeathRecipient();
-    mRunning = true;
-
-    std::lock_guard lockThread(mThreadMutex);
-    mThread = std::thread(&TransactionCompletedThread::threadMain, this);
-}
-
-status_t TransactionCompletedThread::startRegistration(const ListenerCallbacks& listenerCallbacks) {
-    // begin running if not already running
-    run();
-    std::lock_guard lock(mMutex);
-    if (!mRunning) {
-        ALOGE("cannot add callback because the callback thread isn't running");
-        return BAD_VALUE;
-    }
-
-    auto [itr, inserted] = mRegisteringTransactions.insert(listenerCallbacks);
-    auto& [listener, callbackIds] = listenerCallbacks;
-
-    if (inserted) {
-        if (mCompletedTransactions.count(listener) == 0) {
-            status_t err = listener->linkToDeath(mDeathRecipient);
-            if (err != NO_ERROR) {
-                ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
-                return err;
-            }
-        }
-        auto& transactionStatsDeque = mCompletedTransactions[listener];
-        transactionStatsDeque.emplace_back(callbackIds);
-    }
-
-    return NO_ERROR;
-}
-
-status_t TransactionCompletedThread::endRegistration(const ListenerCallbacks& listenerCallbacks) {
-    std::lock_guard lock(mMutex);
-    if (!mRunning) {
-        ALOGE("cannot add callback because the callback thread isn't running");
-        return BAD_VALUE;
-    }
-
-    auto itr = mRegisteringTransactions.find(listenerCallbacks);
-    if (itr == mRegisteringTransactions.end()) {
-        ALOGE("cannot end a registration that does not exist");
-        return BAD_VALUE;
-    }
-
-    mRegisteringTransactions.erase(itr);
-
-    return NO_ERROR;
-}
-
-bool TransactionCompletedThread::isRegisteringTransaction(
-        const sp<IBinder>& transactionListener, const std::vector<CallbackId>& callbackIds) {
-    ListenerCallbacks listenerCallbacks(transactionListener, callbackIds);
-
-    auto itr = mRegisteringTransactions.find(listenerCallbacks);
-    return itr != mRegisteringTransactions.end();
-}
-
-status_t TransactionCompletedThread::registerPendingCallbackHandle(
-        const sp<CallbackHandle>& handle) {
-    std::lock_guard lock(mMutex);
-    if (!mRunning) {
-        ALOGE("cannot register callback handle because the callback thread isn't running");
-        return BAD_VALUE;
-    }
-
-    // If we can't find the transaction stats something has gone wrong. The client should call
-    // startRegistration before trying to register a pending callback handle.
-    TransactionStats* transactionStats;
-    status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
-    if (err != NO_ERROR) {
-        ALOGE("cannot find transaction stats");
-        return err;
-    }
-
-    mPendingTransactions[handle->listener][handle->callbackIds]++;
-    return NO_ERROR;
-}
-
-status_t TransactionCompletedThread::finalizePendingCallbackHandles(
-        const std::deque<sp<CallbackHandle>>& handles, const std::vector<JankData>& jankData) {
-    if (handles.empty()) {
-        return NO_ERROR;
-    }
-    std::lock_guard lock(mMutex);
-    if (!mRunning) {
-        ALOGE("cannot add presented callback handle because the callback thread isn't running");
-        return BAD_VALUE;
-    }
-
-    for (const auto& handle : handles) {
-        auto listener = mPendingTransactions.find(handle->listener);
-        if (listener != mPendingTransactions.end()) {
-            auto& pendingCallbacks = listener->second;
-            auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
-
-            if (pendingCallback != pendingCallbacks.end()) {
-                auto& pendingCount = pendingCallback->second;
-
-                // Decrease the pending count for this listener
-                if (--pendingCount == 0) {
-                    pendingCallbacks.erase(pendingCallback);
-                }
-            } else {
-                ALOGW("there are more latched callbacks than there were registered callbacks");
-            }
-            if (listener->second.size() == 0) {
-                mPendingTransactions.erase(listener);
-            }
-        } else {
-            ALOGW("cannot find listener in mPendingTransactions");
-        }
-
-        status_t err = addCallbackHandle(handle, jankData);
-        if (err != NO_ERROR) {
-            ALOGE("could not add callback handle");
-            return err;
-        }
-    }
-
-    return NO_ERROR;
-}
-
-status_t TransactionCompletedThread::registerUnpresentedCallbackHandle(
-        const sp<CallbackHandle>& handle) {
-    std::lock_guard lock(mMutex);
-    if (!mRunning) {
-        ALOGE("cannot add unpresented callback handle because the callback thread isn't running");
-        return BAD_VALUE;
-    }
-
-    return addCallbackHandle(handle, std::vector<JankData>());
-}
-
-status_t TransactionCompletedThread::findTransactionStats(
-        const sp<IBinder>& listener, const std::vector<CallbackId>& callbackIds,
-        TransactionStats** outTransactionStats) {
-    auto& transactionStatsDeque = mCompletedTransactions[listener];
-
-    // Search back to front because the most recent transactions are at the back of the deque
-    auto itr = transactionStatsDeque.rbegin();
-    for (; itr != transactionStatsDeque.rend(); itr++) {
-        if (compareCallbackIds(itr->callbackIds, callbackIds) == 0) {
-            *outTransactionStats = &(*itr);
-            return NO_ERROR;
-        }
-    }
-
-    ALOGE("could not find transaction stats");
-    return BAD_VALUE;
-}
-
-status_t TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle,
-        const std::vector<JankData>& jankData) {
-    // If we can't find the transaction stats something has gone wrong. The client should call
-    // startRegistration before trying to add a callback handle.
-    TransactionStats* transactionStats;
-    status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
-    if (err != NO_ERROR) {
-        return err;
-    }
-
-    transactionStats->latchTime = handle->latchTime;
-    // If the layer has already been destroyed, don't add the SurfaceControl to the callback.
-    // The client side keeps a sp<> to the SurfaceControl so if the SurfaceControl has been
-    // destroyed the client side is dead and there won't be anyone to send the callback to.
-    sp<IBinder> surfaceControl = handle->surfaceControl.promote();
-    if (surfaceControl) {
-        FrameEventHistoryStats eventStats(handle->frameNumber,
-                                          handle->gpuCompositionDoneFence->getSnapshot().fence,
-                                          handle->compositorTiming, handle->refreshStartTime,
-                                          handle->dequeueReadyTime);
-        transactionStats->surfaceStats.emplace_back(surfaceControl, handle->acquireTime,
-                                                    handle->previousReleaseFence,
-                                                    handle->transformHint, eventStats, jankData);
-    }
-    return NO_ERROR;
-}
-
-void TransactionCompletedThread::addPresentFence(const sp<Fence>& presentFence) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mPresentFence = presentFence;
-}
-
-void TransactionCompletedThread::sendCallbacks() {
-    std::lock_guard lock(mMutex);
-    if (mRunning) {
-        mConditionVariable.notify_all();
-    }
-}
-
-void TransactionCompletedThread::threadMain() {
-    std::lock_guard lock(mMutex);
-
-    while (mKeepRunning) {
-        mConditionVariable.wait(mMutex);
-        std::vector<ListenerStats> completedListenerStats;
-
-        // For each listener
-        auto completedTransactionsItr = mCompletedTransactions.begin();
-        while (completedTransactionsItr != mCompletedTransactions.end()) {
-            auto& [listener, transactionStatsDeque] = *completedTransactionsItr;
-            ListenerStats listenerStats;
-            listenerStats.listener = listener;
-
-            // For each transaction
-            auto transactionStatsItr = transactionStatsDeque.begin();
-            while (transactionStatsItr != transactionStatsDeque.end()) {
-                auto& transactionStats = *transactionStatsItr;
-
-                // If this transaction is still registering, it is not safe to send a callback
-                // because there could be surface controls that haven't been added to
-                // transaction stats or mPendingTransactions.
-                if (isRegisteringTransaction(listener, transactionStats.callbackIds)) {
-                    break;
-                }
-
-                // If we are still waiting on the callback handles for this transaction, stop
-                // here because all transaction callbacks for the same listener must come in order
-                auto pendingTransactions = mPendingTransactions.find(listener);
-                if (pendingTransactions != mPendingTransactions.end() &&
-                    pendingTransactions->second.count(transactionStats.callbackIds) != 0) {
-                    break;
-                }
-
-                // If the transaction has been latched
-                if (transactionStats.latchTime >= 0) {
-                    if (!mPresentFence) {
-                        break;
-                    }
-                    transactionStats.presentFence = mPresentFence;
-                }
-
-                // Remove the transaction from completed to the callback
-                listenerStats.transactionStats.push_back(std::move(transactionStats));
-                transactionStatsItr = transactionStatsDeque.erase(transactionStatsItr);
-            }
-            // If the listener has completed transactions
-            if (!listenerStats.transactionStats.empty()) {
-                // If the listener is still alive
-                if (listener->isBinderAlive()) {
-                    // Send callback.  The listener stored in listenerStats
-                    // comes from the cross-process setTransactionState call to
-                    // SF.  This MUST be an ITransactionCompletedListener.  We
-                    // keep it as an IBinder due to consistency reasons: if we
-                    // interface_cast at the IPC boundary when reading a Parcel,
-                    // we get pointers that compare unequal in the SF process.
-                    interface_cast<ITransactionCompletedListener>(listenerStats.listener)
-                            ->onTransactionCompleted(listenerStats);
-                    if (transactionStatsDeque.empty()) {
-                        listener->unlinkToDeath(mDeathRecipient);
-                        completedTransactionsItr =
-                                mCompletedTransactions.erase(completedTransactionsItr);
-                    } else {
-                        completedTransactionsItr++;
-                    }
-                } else {
-                    completedTransactionsItr =
-                            mCompletedTransactions.erase(completedTransactionsItr);
-                }
-            } else {
-                completedTransactionsItr++;
-            }
-
-            completedListenerStats.push_back(std::move(listenerStats));
-        }
-
-        if (mPresentFence) {
-            mPresentFence.clear();
-        }
-
-        // If everyone else has dropped their reference to a layer and its listener is dead,
-        // we are about to cause the layer to be deleted. If this happens at the wrong time and
-        // we are holding mMutex, we will cause a deadlock.
-        //
-        // The deadlock happens because this thread is holding on to mMutex and when we delete
-        // the layer, it grabs SF's mStateLock. A different SF binder thread grabs mStateLock,
-        // then call's TransactionCompletedThread::run() which tries to grab mMutex.
-        //
-        // To avoid this deadlock, we need to unlock mMutex when dropping our last reference to
-        // to the layer.
-        mMutex.unlock();
-        completedListenerStats.clear();
-        mMutex.lock();
-    }
-}
-
-// -----------------------------------------------------------------------
-
-CallbackHandle::CallbackHandle(const sp<IBinder>& transactionListener,
-                               const std::vector<CallbackId>& ids, const sp<IBinder>& sc)
-      : listener(transactionListener), callbackIds(ids), surfaceControl(sc) {}
-
-} // 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/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 9302463..53e37d8 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -25,7 +25,7 @@
 #include <gui/SurfaceComposerClient.h>
 #include <private/android_filesystem_config.h>
 #include <private/gui/ComposerService.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
 #include <utils/String8.h>
 #include <functional>
 #include "utils/ScreenshotUtils.h"
@@ -81,14 +81,13 @@
         mDisplay = SurfaceComposerClient::getInternalDisplayToken();
         ASSERT_FALSE(mDisplay == nullptr);
 
-        DisplayConfig config;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplay, &config));
+        ui::DisplayMode mode;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(mDisplay, &mode));
 
         // Background surface
-        mBGSurfaceControl =
-                mComposerClient->createSurface(SURFACE_NAME, config.resolution.getWidth(),
-                                               config.resolution.getHeight(),
-                                               PIXEL_FORMAT_RGBA_8888, 0);
+        mBGSurfaceControl = mComposerClient->createSurface(SURFACE_NAME, mode.resolution.getWidth(),
+                                                           mode.resolution.getHeight(),
+                                                           PIXEL_FORMAT_RGBA_8888, 0);
         ASSERT_TRUE(mBGSurfaceControl != nullptr);
         ASSERT_TRUE(mBGSurfaceControl->isValid());
 
@@ -185,13 +184,13 @@
     const auto display = SurfaceComposerClient::getInternalDisplayToken();
     ASSERT_TRUE(display != nullptr);
 
-    DisplayConfig config;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+    ui::DisplayMode mode;
+    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
 
-    Vector<DisplayConfig> configs;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
+    Vector<ui::DisplayMode> modes;
+    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayModes(display, &modes));
 
-    ASSERT_TRUE(SurfaceComposerClient::getActiveConfig(display) >= 0);
+    ASSERT_TRUE(SurfaceComposerClient::getActiveDisplayModeId(display) >= 0);
 
     ASSERT_NE(static_cast<ui::ColorMode>(BAD_VALUE),
               SurfaceComposerClient::getActiveColorMode(display));
@@ -217,25 +216,23 @@
 
 TEST_F(CredentialsTest, SetDesiredDisplayConfigsTest) {
     const auto display = SurfaceComposerClient::getInternalDisplayToken();
-    int32_t defaultConfig;
+    size_t defaultMode;
     bool allowGroupSwitching;
     float primaryFpsMin;
     float primaryFpsMax;
     float appRequestFpsMin;
     float appRequestFpsMax;
     status_t res =
-            SurfaceComposerClient::getDesiredDisplayConfigSpecs(display, &defaultConfig,
-                                                                &allowGroupSwitching,
-                                                                &primaryFpsMin, &primaryFpsMax,
-                                                                &appRequestFpsMin,
-                                                                &appRequestFpsMax);
+            SurfaceComposerClient::getDesiredDisplayModeSpecs(display, &defaultMode,
+                                                              &allowGroupSwitching, &primaryFpsMin,
+                                                              &primaryFpsMax, &appRequestFpsMin,
+                                                              &appRequestFpsMax);
     ASSERT_EQ(res, NO_ERROR);
     std::function<status_t()> condition = [=]() {
-        return SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, defaultConfig,
-                                                                   allowGroupSwitching,
-                                                                   primaryFpsMin, primaryFpsMax,
-                                                                   appRequestFpsMin,
-                                                                   appRequestFpsMax);
+        return SurfaceComposerClient::setDesiredDisplayModeSpecs(display, defaultMode,
+                                                                 allowGroupSwitching, primaryFpsMin,
+                                                                 primaryFpsMax, appRequestFpsMin,
+                                                                 appRequestFpsMax);
     };
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
 }
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 55b3173..9f025a6 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -22,7 +22,7 @@
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
 #include <private/gui/ComposerService.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
 #include <utils/Errors.h>
 #include <utils/Vector.h>
 
@@ -38,7 +38,7 @@
  */
 class RefreshRateRangeTest : public ::testing::Test {
 private:
-    int32_t initialDefaultConfig;
+    size_t initialDefaultMode;
     bool initialAllowGroupSwitching;
     float initialPrimaryMin;
     float initialPrimaryMax;
@@ -49,25 +49,24 @@
     void SetUp() override {
         mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
         status_t res =
-                SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken,
-                                                                    &initialDefaultConfig,
-                                                                    &initialAllowGroupSwitching,
-                                                                    &initialPrimaryMin,
-                                                                    &initialPrimaryMax,
-                                                                    &initialAppRequestMin,
-                                                                    &initialAppRequestMax);
+                SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken,
+                                                                  &initialDefaultMode,
+                                                                  &initialAllowGroupSwitching,
+                                                                  &initialPrimaryMin,
+                                                                  &initialPrimaryMax,
+                                                                  &initialAppRequestMin,
+                                                                  &initialAppRequestMax);
         ASSERT_EQ(res, NO_ERROR);
     }
 
     void TearDown() override {
         status_t res =
-                SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken,
-                                                                    initialDefaultConfig,
-                                                                    initialAllowGroupSwitching,
-                                                                    initialPrimaryMin,
-                                                                    initialPrimaryMax,
-                                                                    initialAppRequestMin,
-                                                                    initialAppRequestMax);
+                SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, initialDefaultMode,
+                                                                  initialAllowGroupSwitching,
+                                                                  initialPrimaryMin,
+                                                                  initialPrimaryMax,
+                                                                  initialAppRequestMin,
+                                                                  initialAppRequestMax);
         ASSERT_EQ(res, NO_ERROR);
     }
 
@@ -77,60 +76,59 @@
 };
 
 TEST_F(RefreshRateRangeTest, setAllConfigs) {
-    Vector<DisplayConfig> configs;
-    status_t res = SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &configs);
+    Vector<ui::DisplayMode> modes;
+    status_t res = SurfaceComposerClient::getDisplayModes(mDisplayToken, &modes);
     ASSERT_EQ(res, NO_ERROR);
-    ASSERT_GT(configs.size(), 0);
+    ASSERT_GT(modes.size(), 0);
 
-    for (size_t i = 0; i < configs.size(); i++) {
-        res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken,
-                                                                  static_cast<int32_t>(i), false,
-                                                                  configs[i].refreshRate,
-                                                                  configs[i].refreshRate,
-                                                                  configs[i].refreshRate,
-                                                                  configs[i].refreshRate);
+    for (size_t i = 0; i < modes.size(); i++) {
+        res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, i, false,
+                                                                modes[i].refreshRate,
+                                                                modes[i].refreshRate,
+                                                                modes[i].refreshRate,
+                                                                modes[i].refreshRate);
         ASSERT_EQ(res, NO_ERROR);
 
-        int defaultConfig;
+        size_t defaultConfig;
         bool allowGroupSwitching;
         float primaryRefreshRateMin;
         float primaryRefreshRateMax;
         float appRequestRefreshRateMin;
         float appRequestRefreshRateMax;
-        res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfig,
-                                                                  &allowGroupSwitching,
-                                                                  &primaryRefreshRateMin,
-                                                                  &primaryRefreshRateMax,
-                                                                  &appRequestRefreshRateMin,
-                                                                  &appRequestRefreshRateMax);
+        res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &defaultConfig,
+                                                                &allowGroupSwitching,
+                                                                &primaryRefreshRateMin,
+                                                                &primaryRefreshRateMax,
+                                                                &appRequestRefreshRateMin,
+                                                                &appRequestRefreshRateMax);
         ASSERT_EQ(res, NO_ERROR);
         ASSERT_EQ(defaultConfig, i);
         ASSERT_EQ(allowGroupSwitching, false);
-        ASSERT_EQ(primaryRefreshRateMin, configs[i].refreshRate);
-        ASSERT_EQ(primaryRefreshRateMax, configs[i].refreshRate);
-        ASSERT_EQ(appRequestRefreshRateMin, configs[i].refreshRate);
-        ASSERT_EQ(appRequestRefreshRateMax, configs[i].refreshRate);
+        ASSERT_EQ(primaryRefreshRateMin, modes[i].refreshRate);
+        ASSERT_EQ(primaryRefreshRateMax, modes[i].refreshRate);
+        ASSERT_EQ(appRequestRefreshRateMin, modes[i].refreshRate);
+        ASSERT_EQ(appRequestRefreshRateMax, modes[i].refreshRate);
     }
 }
 
 void RefreshRateRangeTest::testSetAllowGroupSwitching(bool allowGroupSwitching) {
-    status_t res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, 0,
-                                                                       allowGroupSwitching, 0.f,
-                                                                       90.f, 0.f, 90.f);
+    status_t res =
+            SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, 0, allowGroupSwitching,
+                                                              0.f, 90.f, 0.f, 90.f);
     ASSERT_EQ(res, NO_ERROR);
-    int defaultConfig;
+    size_t defaultConfig;
     bool newAllowGroupSwitching;
     float primaryRefreshRateMin;
     float primaryRefreshRateMax;
     float appRequestRefreshRateMin;
     float appRequestRefreshRateMax;
 
-    res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfig,
-                                                              &newAllowGroupSwitching,
-                                                              &primaryRefreshRateMin,
-                                                              &primaryRefreshRateMax,
-                                                              &appRequestRefreshRateMin,
-                                                              &appRequestRefreshRateMax);
+    res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &defaultConfig,
+                                                            &newAllowGroupSwitching,
+                                                            &primaryRefreshRateMin,
+                                                            &primaryRefreshRateMax,
+                                                            &appRequestRefreshRateMin,
+                                                            &appRequestRefreshRateMax);
     ASSERT_EQ(res, NO_ERROR);
     ASSERT_EQ(defaultConfig, 0);
     ASSERT_EQ(newAllowGroupSwitching, allowGroupSwitching);
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index 4023c66..a8647c3 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -23,7 +23,7 @@
 #include <gui/LayerState.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
 #include <utils/String8.h>
 
 #include <limits>
@@ -227,10 +227,10 @@
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
         mPrimaryDisplay = mClient->getInternalDisplayToken();
-        DisplayConfig config;
-        mClient->getActiveDisplayConfig(mPrimaryDisplay, &config);
-        mDisplayWidth = config.resolution.getWidth();
-        mDisplayHeight = config.resolution.getHeight();
+        ui::DisplayMode mode;
+        mClient->getActiveDisplayMode(mPrimaryDisplay, &mode);
+        mDisplayWidth = mode.resolution.getWidth();
+        mDisplayHeight = mode.resolution.getHeight();
 
         Transaction setupTransaction;
         setupTransaction.setDisplayLayerStack(mPrimaryDisplay, 0);
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 6758518..eba2c25 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -25,7 +25,7 @@
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
 #include <private/gui/ComposerService.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
 
 #include "BufferGenerator.h"
 #include "utils/ScreenshotUtils.h"
@@ -265,16 +265,16 @@
         mDisplay = mClient->getInternalDisplayToken();
         ASSERT_FALSE(mDisplay == nullptr) << "failed to get display";
 
-        DisplayConfig config;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplay, &config));
-        mDisplayRect = Rect(config.resolution);
+        ui::DisplayMode mode;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(mDisplay, &mode));
+        mDisplayRect = Rect(mode.resolution);
         mDisplayWidth = mDisplayRect.getWidth();
         mDisplayHeight = mDisplayRect.getHeight();
 
         // After a new buffer is queued, SurfaceFlinger is notified and will
         // latch the new buffer on next vsync.  Let's heuristically wait for 3
         // vsyncs.
-        mBufferPostDelay = static_cast<int32_t>(1e6 / config.refreshRate) * 3;
+        mBufferPostDelay = static_cast<int32_t>(1e6 / mode.refreshRate) * 3;
 
         mDisplayLayerStack = 0;
 
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index 6c56d20..ec826ae 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -36,9 +36,9 @@
         const auto display = SurfaceComposerClient::getInternalDisplayToken();
         ASSERT_FALSE(display == nullptr);
 
-        DisplayConfig config;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
-        const ui::Size& resolution = config.resolution;
+        ui::DisplayMode mode;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+        const ui::Size& resolution = mode.resolution;
 
         // Background surface
         mBGSurfaceControl = createLayer(String8("BG Test Surface"), resolution.getWidth(),
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index db0c56f..eaf54e3 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -37,13 +37,13 @@
 
         mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
         SurfaceComposerClient::getDisplayState(mMainDisplay, &mMainDisplayState);
-        SurfaceComposerClient::getActiveDisplayConfig(mMainDisplay, &mMainDisplayConfig);
+        SurfaceComposerClient::getActiveDisplayMode(mMainDisplay, &mMainDisplayMode);
 
         sp<IGraphicBufferConsumer> consumer;
         BufferQueue::createBufferQueue(&mProducer, &consumer);
         consumer->setConsumerName(String8("Virtual disp consumer"));
-        consumer->setDefaultBufferSize(mMainDisplayConfig.resolution.getWidth(),
-                                       mMainDisplayConfig.resolution.getHeight());
+        consumer->setDefaultBufferSize(mMainDisplayMode.resolution.getWidth(),
+                                       mMainDisplayMode.resolution.getHeight());
     }
 
     virtual void TearDown() {
@@ -59,7 +59,7 @@
             t.setDisplaySurface(mVirtualDisplay, mProducer);
             t.setDisplayLayerStack(mVirtualDisplay, layerStack);
             t.setDisplayProjection(mVirtualDisplay, mMainDisplayState.orientation,
-                                   Rect(layerStackSize), Rect(mMainDisplayConfig.resolution));
+                                   Rect(layerStackSize), Rect(mMainDisplayMode.resolution));
         });
     }
 
@@ -81,7 +81,7 @@
     }
 
     ui::DisplayState mMainDisplayState;
-    DisplayConfig mMainDisplayConfig;
+    ui::DisplayMode mMainDisplayMode;
     sp<IBinder> mMainDisplay;
     sp<IBinder> mVirtualDisplay;
     sp<IGraphicBufferProducer> mProducer;
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 51ce1d3..4598f9d 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -33,9 +33,9 @@
         const auto display = SurfaceComposerClient::getInternalDisplayToken();
         ASSERT_FALSE(display == nullptr);
 
-        DisplayConfig config;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
-        const ui::Size& resolution = config.resolution;
+        ui::DisplayMode mode;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+        const ui::Size& resolution = mode.resolution;
 
         // Background surface
         mBGSurfaceControl = createLayer(String8("BG Test Surface"), resolution.getWidth(),
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index fa88ca5..a20d5c6 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -27,7 +27,7 @@
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 #include <private/gui/ComposerService.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
 
 #include <fstream>
 #include <random>
@@ -267,9 +267,9 @@
     const auto display = SurfaceComposerClient::getInternalDisplayToken();
     ASSERT_FALSE(display == nullptr);
 
-    DisplayConfig config;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
-    const ui::Size& resolution = config.resolution;
+    ui::DisplayMode mode;
+    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+    const ui::Size& resolution = mode.resolution;
 
     // Background surface
     mBGSurfaceControl =
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index 33823d7..89f6086 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -40,9 +40,9 @@
                 ui::DisplayState displayState;
                 SurfaceComposerClient::getDisplayState(displayToken, &displayState);
 
-                DisplayConfig displayConfig;
-                SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
-                const ui::Size& resolution = displayConfig.resolution;
+                ui::DisplayMode displayMode;
+                SurfaceComposerClient::getActiveDisplayMode(displayToken, &displayMode);
+                const ui::Size& resolution = displayMode.resolution;
 
                 sp<IBinder> vDisplay;
                 sp<IGraphicBufferProducer> producer;
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index efa15f1..56e1ae9 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -43,7 +43,7 @@
 #include <hwbinder/ProcessState.h>
 #include <log/log.h>
 #include <private/gui/ComposerService.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
 #include <utils/Looper.h>
 
 #include <gmock/gmock.h>
@@ -243,9 +243,8 @@
         mComposerClient = new SurfaceComposerClient;
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
 
-        mReceiver.reset(
-                new DisplayEventReceiver(ISurfaceComposer::eVsyncSourceApp,
-                                         ISurfaceComposer::EventRegistration::configChanged));
+        mReceiver.reset(new DisplayEventReceiver(ISurfaceComposer::eVsyncSourceApp,
+                                                 ISurfaceComposer::EventRegistration::modeChanged));
         mLooper = new Looper(false);
         mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this);
     }
@@ -305,7 +304,7 @@
         return false;
     }
 
-    bool waitForConfigChangedEvent(Display display, int32_t configId) {
+    bool waitForModeChangedEvent(Display display, int32_t modeId) {
         PhysicalDisplayId displayId(display);
         int waitCount = 20;
         while (waitCount--) {
@@ -313,12 +312,12 @@
                 auto event = mReceivedDisplayEvents.front();
                 mReceivedDisplayEvents.pop_front();
 
-                ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED,
-                         "event config: displayId %s, configId %d",
-                         to_string(event.header.displayId).c_str(), event.config.configId);
+                ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE,
+                         "event mode: displayId %s, modeId %d",
+                         to_string(event.header.displayId).c_str(), event.modeChange.modeId);
 
-                if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED &&
-                    event.header.displayId == displayId && event.config.configId == configId) {
+                if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE &&
+                    event.header.displayId == displayId && event.modeChange.modeId == modeId) {
                     return true;
                 }
             }
@@ -348,11 +347,11 @@
             const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
             EXPECT_FALSE(display == nullptr);
 
-            DisplayConfig config;
-            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
-            const ui::Size& resolution = config.resolution;
+            ui::DisplayMode mode;
+            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+            const ui::Size& resolution = mode.resolution;
             EXPECT_EQ(ui::Size(200, 400), resolution);
-            EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
+            EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
 
             auto surfaceControl =
                     mComposerClient->createSurface(String8("Display Test Surface Foo"),
@@ -380,8 +379,8 @@
             const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
             EXPECT_TRUE(display == nullptr);
 
-            DisplayConfig config;
-            EXPECT_NE(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+            ui::DisplayMode mode;
+            EXPECT_NE(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
         }
     }
 
@@ -409,14 +408,14 @@
         const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
         EXPECT_FALSE(display == nullptr);
 
-        DisplayConfig config;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
-        EXPECT_EQ(ui::Size(200, 400), config.resolution);
-        EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
+        ui::DisplayMode mode;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+        EXPECT_EQ(ui::Size(200, 400), mode.resolution);
+        EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
 
         mFakeComposerClient->clearFrames();
         {
-            const ui::Size& resolution = config.resolution;
+            const ui::Size& resolution = mode.resolution;
             auto surfaceControl =
                     mComposerClient->createSurface(String8("Display Test Surface Foo"),
                                                    resolution.getWidth(), resolution.getHeight(),
@@ -433,11 +432,11 @@
             }
         }
 
-        Vector<DisplayConfig> configs;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
-        EXPECT_EQ(configs.size(), 2);
+        Vector<ui::DisplayMode> modes;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayModes(display, &modes));
+        EXPECT_EQ(modes.size(), 2);
 
-        // change active config
+        // change active mode
 
         if (mIs2_4Client) {
             EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 2, _, _))
@@ -447,28 +446,28 @@
                     .WillOnce(Return(V2_1::Error::NONE));
         }
 
-        for (int i = 0; i < configs.size(); i++) {
-            const auto& config = configs[i];
-            if (config.resolution.getWidth() == 800) {
+        for (int i = 0; i < modes.size(); i++) {
+            const auto& mode = modes[i];
+            if (mode.resolution.getWidth() == 800) {
                 EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, false,
-                                                                              config.refreshRate,
-                                                                              config.refreshRate,
-                                                                              config.refreshRate,
-                                                                              config.refreshRate));
+                          SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
+                                                                            mode.refreshRate,
+                                                                            mode.refreshRate,
+                                                                            mode.refreshRate,
+                                                                            mode.refreshRate));
                 waitForDisplayTransaction(EXTERNAL_DISPLAY);
-                EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+                EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
                 break;
             }
         }
 
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
-        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
-        EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+        EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
+        EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
 
         mFakeComposerClient->clearFrames();
         {
-            const ui::Size& resolution = config.resolution;
+            const ui::Size& resolution = mode.resolution;
             auto surfaceControl =
                     mComposerClient->createSurface(String8("Display Test Surface Foo"),
                                                    resolution.getWidth(), resolution.getHeight(),
@@ -516,14 +515,14 @@
         const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
         EXPECT_FALSE(display == nullptr);
 
-        DisplayConfig config;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
-        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
-        EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
+        ui::DisplayMode mode;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+        EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
+        EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
 
         mFakeComposerClient->clearFrames();
         {
-            const ui::Size& resolution = config.resolution;
+            const ui::Size& resolution = mode.resolution;
             auto surfaceControl =
                     mComposerClient->createSurface(String8("Display Test Surface Foo"),
                                                    resolution.getWidth(), resolution.getHeight(),
@@ -540,11 +539,11 @@
             }
         }
 
-        Vector<DisplayConfig> configs;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
-        EXPECT_EQ(configs.size(), 2);
+        Vector<ui::DisplayMode> modes;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayModes(display, &modes));
+        EXPECT_EQ(modes.size(), 2);
 
-        // change active config
+        // change active mode
         if (mIs2_4Client) {
             EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _))
                     .WillOnce(Return(V2_4::Error::NONE));
@@ -553,28 +552,28 @@
                     .WillOnce(Return(V2_1::Error::NONE));
         }
 
-        for (int i = 0; i < configs.size(); i++) {
-            const auto& config = configs[i];
-            if (config.refreshRate == 1e9f / 11'111'111) {
+        for (int i = 0; i < modes.size(); i++) {
+            const auto& mode = modes[i];
+            if (mode.refreshRate == 1e9f / 11'111'111) {
                 EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, false,
-                                                                              config.refreshRate,
-                                                                              config.refreshRate,
-                                                                              config.refreshRate,
-                                                                              config.refreshRate));
+                          SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
+                                                                            mode.refreshRate,
+                                                                            mode.refreshRate,
+                                                                            mode.refreshRate,
+                                                                            mode.refreshRate));
                 waitForDisplayTransaction(EXTERNAL_DISPLAY);
-                EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+                EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
                 break;
             }
         }
 
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
-        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
-        EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+        EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
+        EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
 
         mFakeComposerClient->clearFrames();
         {
-            const ui::Size& resolution = config.resolution;
+            const ui::Size& resolution = mode.resolution;
             auto surfaceControl =
                     mComposerClient->createSurface(String8("Display Test Surface Foo"),
                                                    resolution.getWidth(), resolution.getHeight(),
@@ -632,14 +631,14 @@
         const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
         EXPECT_FALSE(display == nullptr);
 
-        DisplayConfig config;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
-        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
-        EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
+        ui::DisplayMode mode;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+        EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
+        EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
 
         mFakeComposerClient->clearFrames();
         {
-            const ui::Size& resolution = config.resolution;
+            const ui::Size& resolution = mode.resolution;
             auto surfaceControl =
                     mComposerClient->createSurface(String8("Display Test Surface Foo"),
                                                    resolution.getWidth(), resolution.getHeight(),
@@ -656,11 +655,11 @@
             }
         }
 
-        Vector<DisplayConfig> configs;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
-        EXPECT_EQ(configs.size(), 4);
+        Vector<ui::DisplayMode> modes;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayModes(display, &modes));
+        EXPECT_EQ(modes.size(), 4);
 
-        // change active config to 800x1600@90Hz
+        // change active mode to 800x1600@90Hz
         if (mIs2_4Client) {
             EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _))
                     .WillOnce(Return(V2_4::Error::NONE));
@@ -669,29 +668,28 @@
                     .WillOnce(Return(V2_1::Error::NONE));
         }
 
-        for (size_t i = 0; i < configs.size(); i++) {
-            const auto& config = configs[i];
-            if (config.resolution.getWidth() == 800 && config.refreshRate == 1e9f / 11'111'111) {
+        for (size_t i = 0; i < modes.size(); i++) {
+            const auto& mode = modes[i];
+            if (mode.resolution.getWidth() == 800 && mode.refreshRate == 1e9f / 11'111'111) {
                 EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::
-                                  setDesiredDisplayConfigSpecs(display, i, false,
-                                                               configs[i].refreshRate,
-                                                               configs[i].refreshRate,
-                                                               configs[i].refreshRate,
-                                                               configs[i].refreshRate));
+                          SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
+                                                                            modes[i].refreshRate,
+                                                                            modes[i].refreshRate,
+                                                                            modes[i].refreshRate,
+                                                                            modes[i].refreshRate));
                 waitForDisplayTransaction(EXTERNAL_DISPLAY);
-                EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+                EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
                 break;
             }
         }
 
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
-        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
-        EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+        EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
+        EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
 
         mFakeComposerClient->clearFrames();
         {
-            const ui::Size& resolution = config.resolution;
+            const ui::Size& resolution = mode.resolution;
             auto surfaceControl =
                     mComposerClient->createSurface(String8("Display Test Surface Foo"),
                                                    resolution.getWidth(), resolution.getHeight(),
@@ -708,7 +706,7 @@
             }
         }
 
-        // change active config to 1600x3200@120Hz
+        // change active mode to 1600x3200@120Hz
         if (mIs2_4Client) {
             EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 4, _, _))
                     .WillOnce(Return(V2_4::Error::NONE));
@@ -717,28 +715,28 @@
                     .WillOnce(Return(V2_1::Error::NONE));
         }
 
-        for (int i = 0; i < configs.size(); i++) {
-            const auto& config = configs[i];
-            if (config.refreshRate == 1e9f / 8'333'333) {
+        for (int i = 0; i < modes.size(); i++) {
+            const auto& mode = modes[i];
+            if (mode.refreshRate == 1e9f / 8'333'333) {
                 EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, false,
-                                                                              config.refreshRate,
-                                                                              config.refreshRate,
-                                                                              config.refreshRate,
-                                                                              config.refreshRate));
+                          SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
+                                                                            mode.refreshRate,
+                                                                            mode.refreshRate,
+                                                                            mode.refreshRate,
+                                                                            mode.refreshRate));
                 waitForDisplayTransaction(EXTERNAL_DISPLAY);
-                EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+                EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
                 break;
             }
         }
 
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
-        EXPECT_EQ(ui::Size(1600, 3200), config.resolution);
-        EXPECT_EQ(1e9f / 8'333'333, config.refreshRate);
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+        EXPECT_EQ(ui::Size(1600, 3200), mode.resolution);
+        EXPECT_EQ(1e9f / 8'333'333, mode.refreshRate);
 
         mFakeComposerClient->clearFrames();
         {
-            const ui::Size& resolution = config.resolution;
+            const ui::Size& resolution = mode.resolution;
             auto surfaceControl =
                     mComposerClient->createSurface(String8("Display Test Surface Foo"),
                                                    resolution.getWidth(), resolution.getHeight(),
@@ -755,7 +753,7 @@
             }
         }
 
-        // change active config to 1600x3200@90Hz
+        // change active mode to 1600x3200@90Hz
         if (mIs2_4Client) {
             EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 5, _, _))
                     .WillOnce(Return(V2_4::Error::NONE));
@@ -764,28 +762,28 @@
                     .WillOnce(Return(V2_1::Error::NONE));
         }
 
-        for (int i = 0; i < configs.size(); i++) {
-            const auto& config = configs[i];
-            if (config.resolution.getWidth() == 1600 && config.refreshRate == 1e9f / 11'111'111) {
+        for (int i = 0; i < modes.size(); i++) {
+            const auto& mode = modes[i];
+            if (mode.resolution.getWidth() == 1600 && mode.refreshRate == 1e9f / 11'111'111) {
                 EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, false,
-                                                                              config.refreshRate,
-                                                                              config.refreshRate,
-                                                                              config.refreshRate,
-                                                                              config.refreshRate));
+                          SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
+                                                                            mode.refreshRate,
+                                                                            mode.refreshRate,
+                                                                            mode.refreshRate,
+                                                                            mode.refreshRate));
                 waitForDisplayTransaction(EXTERNAL_DISPLAY);
-                EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+                EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
                 break;
             }
         }
 
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
-        EXPECT_EQ(ui::Size(1600, 3200), config.resolution);
-        EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+        EXPECT_EQ(ui::Size(1600, 3200), mode.resolution);
+        EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
 
         mFakeComposerClient->clearFrames();
         {
-            const ui::Size& resolution = config.resolution;
+            const ui::Size& resolution = mode.resolution;
             auto surfaceControl =
                     mComposerClient->createSurface(String8("Display Test Surface Foo"),
                                                    resolution.getWidth(), resolution.getHeight(),
@@ -822,8 +820,8 @@
             const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
             EXPECT_TRUE(display == nullptr);
 
-            DisplayConfig config;
-            auto result = SurfaceComposerClient::getActiveDisplayConfig(display, &config);
+            ui::DisplayMode mode;
+            auto result = SurfaceComposerClient::getActiveDisplayMode(display, &mode);
             EXPECT_NE(NO_ERROR, result);
         }
 
@@ -848,11 +846,11 @@
             const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
             EXPECT_FALSE(display == nullptr);
 
-            DisplayConfig config;
-            auto result = SurfaceComposerClient::getActiveDisplayConfig(display, &config);
+            ui::DisplayMode mode;
+            auto result = SurfaceComposerClient::getActiveDisplayMode(display, &mode);
             EXPECT_EQ(NO_ERROR, result);
-            ASSERT_EQ(ui::Size(400, 200), config.resolution);
-            EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
+            ASSERT_EQ(ui::Size(400, 200), mode.resolution);
+            EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
         }
     }
 
@@ -881,14 +879,14 @@
 
         // Verify that the active mode and the supported moded are updated
         {
-            DisplayConfig config;
-            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
-            EXPECT_EQ(ui::Size(800, 1600), config.resolution);
-            EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
+            ui::DisplayMode mode;
+            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+            EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
+            EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
 
-            Vector<DisplayConfig> configs;
-            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
-            EXPECT_EQ(configs.size(), 1);
+            Vector<ui::DisplayMode> modes;
+            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayModes(display, &modes));
+            EXPECT_EQ(modes.size(), 1);
         }
 
         // Send another hotplug connected event
@@ -919,27 +917,27 @@
 
         // Verify that the active mode and the supported moded are updated
         {
-            DisplayConfig config;
-            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
-            EXPECT_EQ(ui::Size(800, 1600), config.resolution);
-            EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
+            ui::DisplayMode mode;
+            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+            EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
+            EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
         }
 
-        Vector<DisplayConfig> configs;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
-        EXPECT_EQ(configs.size(), 3);
+        Vector<ui::DisplayMode> modes;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayModes(display, &modes));
+        EXPECT_EQ(modes.size(), 3);
 
-        EXPECT_EQ(ui::Size(800, 1600), configs[0].resolution);
-        EXPECT_EQ(1e9f / 16'666'666, configs[0].refreshRate);
+        EXPECT_EQ(ui::Size(800, 1600), modes[0].resolution);
+        EXPECT_EQ(1e9f / 16'666'666, modes[0].refreshRate);
 
-        EXPECT_EQ(ui::Size(800, 1600), configs[1].resolution);
-        EXPECT_EQ(1e9f / 11'111'111, configs[1].refreshRate);
+        EXPECT_EQ(ui::Size(800, 1600), modes[1].resolution);
+        EXPECT_EQ(1e9f / 11'111'111, modes[1].refreshRate);
 
-        EXPECT_EQ(ui::Size(800, 1600), configs[2].resolution);
-        EXPECT_EQ(1e9f / 8'333'333, configs[2].refreshRate);
+        EXPECT_EQ(ui::Size(800, 1600), modes[2].resolution);
+        EXPECT_EQ(1e9f / 8'333'333, modes[2].refreshRate);
 
         // Verify that we are able to switch to any of the modes
-        for (int i = configs.size() - 1; i >= 0; i--) {
+        for (int i = modes.size() - 1; i >= 0; i--) {
             const auto hwcId = i + 1;
             // Set up HWC expectations for the mode change
             if (mIs2_4Client) {
@@ -952,22 +950,22 @@
             }
 
             EXPECT_EQ(NO_ERROR,
-                      SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, false,
-                                                                          configs[i].refreshRate,
-                                                                          configs[i].refreshRate,
-                                                                          configs[i].refreshRate,
-                                                                          configs[i].refreshRate));
+                      SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
+                                                                        modes[i].refreshRate,
+                                                                        modes[i].refreshRate,
+                                                                        modes[i].refreshRate,
+                                                                        modes[i].refreshRate));
             // We need to refresh twice - once to apply the pending mode change request,
             // and once to process the change.
             waitForDisplayTransaction(hwcDisplayId);
             waitForDisplayTransaction(hwcDisplayId);
-            EXPECT_TRUE(waitForConfigChangedEvent(hwcDisplayId, i))
+            EXPECT_TRUE(waitForModeChangedEvent(hwcDisplayId, i))
                     << "Failure while switching to mode " << i;
 
-            DisplayConfig config;
-            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
-            EXPECT_EQ(ui::Size(800, 1600), config.resolution);
-            EXPECT_EQ(configs[i].refreshRate, config.refreshRate);
+            ui::DisplayMode mode;
+            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+            EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
+            EXPECT_EQ(modes[i].refreshRate, mode.refreshRate);
         }
     }
 
@@ -1168,10 +1166,10 @@
         const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
         ASSERT_FALSE(display == nullptr);
 
-        DisplayConfig config;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        ui::DisplayMode mode;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
 
-        const ui::Size& resolution = config.resolution;
+        const ui::Size& resolution = mode.resolution;
         mDisplayWidth = resolution.getWidth();
         mDisplayHeight = resolution.getHeight();
 
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 45bc29c..7cc0032 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -141,10 +141,10 @@
 
     createThread(std::move(vsyncSource));
     mConnection = createConnection(mConnectionEventCallRecorder,
-                                   ISurfaceComposer::EventRegistration::configChanged |
+                                   ISurfaceComposer::EventRegistration::modeChanged |
                                            ISurfaceComposer::EventRegistration::frameRateOverride);
     mThrottledConnection = createConnection(mThrottledConnectionEventCallRecorder,
-                                            ISurfaceComposer::EventRegistration::configChanged,
+                                            ISurfaceComposer::EventRegistration::modeChanged,
                                             mThrottledConnectionUid);
 
     // A display must be connected for VSYNC events to be delivered.
@@ -257,10 +257,10 @@
     auto args = mConnectionEventCallRecorder.waitForCall();
     ASSERT_TRUE(args.has_value());
     const auto& event = std::get<0>(args.value());
-    EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, event.header.type);
+    EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, event.header.type);
     EXPECT_EQ(expectedDisplayId, event.header.displayId);
-    EXPECT_EQ(expectedConfigId, event.config.configId);
-    EXPECT_EQ(expectedVsyncPeriod, event.config.vsyncPeriod);
+    EXPECT_EQ(expectedConfigId, event.modeChange.modeId);
+    EXPECT_EQ(expectedVsyncPeriod, event.modeChange.vsyncPeriod);
 }
 
 void EventThreadTest::expectUidFrameRateMappingEventReceivedByConnection(
@@ -540,17 +540,17 @@
 }
 
 TEST_F(EventThreadTest, postConfigChangedPrimary) {
-    mThread->onConfigChanged(INTERNAL_DISPLAY_ID, DisplayModeId(7), 16666666);
+    mThread->onModeChanged(INTERNAL_DISPLAY_ID, DisplayModeId(7), 16666666);
     expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, 16666666);
 }
 
 TEST_F(EventThreadTest, postConfigChangedExternal) {
-    mThread->onConfigChanged(EXTERNAL_DISPLAY_ID, DisplayModeId(5), 16666666);
+    mThread->onModeChanged(EXTERNAL_DISPLAY_ID, DisplayModeId(5), 16666666);
     expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, 16666666);
 }
 
 TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
-    mThread->onConfigChanged(DISPLAY_ID_64BIT, DisplayModeId(7), 16666666);
+    mThread->onModeChanged(DISPLAY_ID_64BIT, DisplayModeId(7), 16666666);
     expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, 16666666);
 }
 
@@ -559,7 +559,7 @@
     sp<MockEventThreadConnection> suppressConnection =
             createConnection(suppressConnectionEventRecorder);
 
-    mThread->onConfigChanged(INTERNAL_DISPLAY_ID, DisplayModeId(9), 16666666);
+    mThread->onModeChanged(INTERNAL_DISPLAY_ID, DisplayModeId(9), 16666666);
     expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, 16666666);
 
     auto args = suppressConnectionEventRecorder.waitForCall();
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 04cafbc..fec590e 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -113,12 +113,12 @@
     RefreshRateConfigs mConfigs{{DisplayMode::Builder(0)
                                          .setId(DisplayModeId(0))
                                          .setVsyncPeriod(int32_t(LO_FPS_PERIOD))
-                                         .setConfigGroup(0)
+                                         .setGroup(0)
                                          .build(),
                                  DisplayMode::Builder(1)
                                          .setId(DisplayModeId(1))
                                          .setVsyncPeriod(int32_t(HI_FPS_PERIOD))
-                                         .setConfigGroup(0)
+                                         .setGroup(0)
                                          .build()},
                                 DisplayModeId(0)};
 
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
index d5c9b57..be76e8f 100644
--- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -53,7 +53,7 @@
     for (int i = 1; i <= kNumFrames; i++) {
         frameTimes.push_back(FrameTimeData{.presentTime = kPeriod * i,
                                            .queueTime = 0,
-                                           .pendingConfigChange = false});
+                                           .pendingModeChange = false});
     }
     setFrameTimes(frameTimes);
     const auto averageFrameTime = calculateAverageFrameTime();
@@ -71,7 +71,7 @@
     for (int i = 1; i <= kNumFrames; i++) {
         frameTimes.push_back(FrameTimeData{.presentTime = 0,
                                            .queueTime = kPeriod * i,
-                                           .pendingConfigChange = false});
+                                           .pendingModeChange = false});
     }
     setFrameTimes(frameTimes);
     setLastRefreshRate(Fps(20.0f)); // Set to some valid value
@@ -89,7 +89,7 @@
     for (int i = 1; i <= kNumFrames; i++) {
         frameTimesWithoutConfigChange.push_back(FrameTimeData{.presentTime = period * i,
                                                               .queueTime = period * i,
-                                                              .pendingConfigChange = false});
+                                                              .pendingModeChange = false});
     }
 
     setFrameTimes(frameTimesWithoutConfigChange);
@@ -98,7 +98,7 @@
     {
         // Config change in the first record
         auto frameTimes = frameTimesWithoutConfigChange;
-        frameTimes[0].pendingConfigChange = true;
+        frameTimes[0].pendingModeChange = true;
         setFrameTimes(frameTimes);
         ASSERT_FALSE(calculateAverageFrameTime().has_value());
     }
@@ -106,7 +106,7 @@
     {
         // Config change in the last record
         auto frameTimes = frameTimesWithoutConfigChange;
-        frameTimes[frameTimes.size() - 1].pendingConfigChange = true;
+        frameTimes[frameTimes.size() - 1].pendingModeChange = true;
         setFrameTimes(frameTimes);
         ASSERT_FALSE(calculateAverageFrameTime().has_value());
     }
@@ -114,7 +114,7 @@
     {
         // Config change in the middle
         auto frameTimes = frameTimesWithoutConfigChange;
-        frameTimes[frameTimes.size() / 2].pendingConfigChange = true;
+        frameTimes[frameTimes.size() / 2].pendingModeChange = true;
         setFrameTimes(frameTimes);
         ASSERT_FALSE(calculateAverageFrameTime().has_value());
     }
@@ -131,12 +131,12 @@
     for (int i = 1; i <= kNumIterations; i++) {
         frameTimes.push_back(FrameTimeData{.presentTime = kExpectedPeriod * i,
                                            .queueTime = 0,
-                                           .pendingConfigChange = false});
+                                           .pendingModeChange = false});
 
         // A duplicate frame
         frameTimes.push_back(FrameTimeData{.presentTime = kExpectedPeriod * i + kSmallPeriod,
                                            .queueTime = 0,
-                                           .pendingConfigChange = false});
+                                           .pendingModeChange = false});
     }
     setFrameTimes(frameTimes);
     const auto averageFrameTime = calculateAverageFrameTime();
@@ -156,7 +156,7 @@
 
     auto record = [&](nsecs_t time) {
         frameTimes.push_back(
-                FrameTimeData{.presentTime = time, .queueTime = 0, .pendingConfigChange = false});
+                FrameTimeData{.presentTime = time, .queueTime = 0, .pendingModeChange = false});
     };
 
     auto time = kExpectedPeriod; // Start with non-zero time.
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 738ded1..376995f 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -81,24 +81,25 @@
     static inline const DisplayModeId HWC_CONFIG_ID_50 = DisplayModeId(6);
 
     // Test configs
-    DisplayModePtr mConfig60 = createConfig(HWC_CONFIG_ID_60, 0, Fps(60.0f).getPeriodNsecs());
-    DisplayModePtr mConfig90 = createConfig(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs());
+    DisplayModePtr mConfig60 = createDisplayMode(HWC_CONFIG_ID_60, 0, Fps(60.0f).getPeriodNsecs());
+    DisplayModePtr mConfig90 = createDisplayMode(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs());
     DisplayModePtr mConfig90DifferentGroup =
-            createConfig(HWC_CONFIG_ID_90, 1, Fps(90.0f).getPeriodNsecs());
+            createDisplayMode(HWC_CONFIG_ID_90, 1, Fps(90.0f).getPeriodNsecs());
     DisplayModePtr mConfig90DifferentResolution =
-            createConfig(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs(), ui::Size(111, 222));
-    DisplayModePtr mConfig72 = createConfig(HWC_CONFIG_ID_72, 0, Fps(72.0f).getPeriodNsecs());
+            createDisplayMode(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs(), ui::Size(111, 222));
+    DisplayModePtr mConfig72 = createDisplayMode(HWC_CONFIG_ID_72, 0, Fps(72.0f).getPeriodNsecs());
     DisplayModePtr mConfig72DifferentGroup =
-            createConfig(HWC_CONFIG_ID_72, 1, Fps(72.0f).getPeriodNsecs());
-    DisplayModePtr mConfig120 = createConfig(HWC_CONFIG_ID_120, 0, Fps(120.0f).getPeriodNsecs());
+            createDisplayMode(HWC_CONFIG_ID_72, 1, Fps(72.0f).getPeriodNsecs());
+    DisplayModePtr mConfig120 =
+            createDisplayMode(HWC_CONFIG_ID_120, 0, Fps(120.0f).getPeriodNsecs());
     DisplayModePtr mConfig120DifferentGroup =
-            createConfig(HWC_CONFIG_ID_120, 1, Fps(120.0f).getPeriodNsecs());
-    DisplayModePtr mConfig30 = createConfig(HWC_CONFIG_ID_30, 0, Fps(30.0f).getPeriodNsecs());
+            createDisplayMode(HWC_CONFIG_ID_120, 1, Fps(120.0f).getPeriodNsecs());
+    DisplayModePtr mConfig30 = createDisplayMode(HWC_CONFIG_ID_30, 0, Fps(30.0f).getPeriodNsecs());
     DisplayModePtr mConfig30DifferentGroup =
-            createConfig(HWC_CONFIG_ID_30, 1, Fps(30.0f).getPeriodNsecs());
+            createDisplayMode(HWC_CONFIG_ID_30, 1, Fps(30.0f).getPeriodNsecs());
     DisplayModePtr mConfig25DifferentGroup =
-            createConfig(HWC_CONFIG_ID_25, 1, Fps(25.0f).getPeriodNsecs());
-    DisplayModePtr mConfig50 = createConfig(HWC_CONFIG_ID_50, 0, Fps(50.0f).getPeriodNsecs());
+            createDisplayMode(HWC_CONFIG_ID_25, 1, Fps(25.0f).getPeriodNsecs());
+    DisplayModePtr mConfig50 = createDisplayMode(HWC_CONFIG_ID_50, 0, Fps(50.0f).getPeriodNsecs());
 
     // Test device configurations
     // The positions of the configs in the arrays below MUST match their IDs. For example,
@@ -128,8 +129,8 @@
     RefreshRate mExpected60Config = {HWC_CONFIG_ID_60, mConfig60, Fps(60),
                                      RefreshRate::ConstructorTag(0)};
     RefreshRate mExpectedAlmost60Config = {HWC_CONFIG_ID_60,
-                                           createConfig(HWC_CONFIG_ID_60, 0, 16666665), Fps(60),
-                                           RefreshRate::ConstructorTag(0)};
+                                           createDisplayMode(HWC_CONFIG_ID_60, 0, 16666665),
+                                           Fps(60), RefreshRate::ConstructorTag(0)};
     RefreshRate mExpected90Config = {HWC_CONFIG_ID_90, mConfig90, Fps(90),
                                      RefreshRate::ConstructorTag(0)};
     RefreshRate mExpected90DifferentGroupConfig = {HWC_CONFIG_ID_90, mConfig90DifferentGroup,
@@ -144,8 +145,8 @@
     RefreshRate mExpected120Config = {HWC_CONFIG_ID_120, mConfig120, Fps(120),
                                       RefreshRate::ConstructorTag(0)};
 private:
-    DisplayModePtr createConfig(DisplayModeId configId, int32_t configGroup, int64_t vsyncPeriod,
-                                ui::Size resolution = ui::Size());
+    DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod,
+                                     ui::Size resolution = ui::Size());
 };
 
 using Builder = DisplayMode::Builder;
@@ -162,12 +163,12 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-DisplayModePtr RefreshRateConfigsTest::createConfig(DisplayModeId configId, int32_t configGroup,
-                                                    int64_t vsyncPeriod, ui::Size resolution) {
-    return DisplayMode::Builder(hal::HWConfigId(configId.value()))
-            .setId(configId)
+DisplayModePtr RefreshRateConfigsTest::createDisplayMode(DisplayModeId modeId, int32_t group,
+                                                         int64_t vsyncPeriod, ui::Size resolution) {
+    return DisplayMode::Builder(hal::HWConfigId(modeId.value()))
+            .setId(modeId)
             .setVsyncPeriod(int32_t(vsyncPeriod))
-            .setConfigGroup(configGroup)
+            .setGroup(group)
             .setHeight(resolution.height)
             .setWidth(resolution.width)
             .build();
@@ -226,7 +227,7 @@
 
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(60), Fps(90)}}),
               0);
-    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
 
     const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs);
     const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
@@ -252,7 +253,7 @@
 
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(60), Fps(90)}}),
               0);
-    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
 
     const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs);
     const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
@@ -288,20 +289,20 @@
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
     {
         auto current = refreshRateConfigs->getCurrentRefreshRate();
-        EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_60);
+        EXPECT_EQ(current.getModeId(), HWC_CONFIG_ID_60);
     }
 
-    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
     {
         auto current = refreshRateConfigs->getCurrentRefreshRate();
-        EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_90);
+        EXPECT_EQ(current.getModeId(), HWC_CONFIG_ID_90);
     }
 
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(90), Fps(90)}}),
               0);
     {
         auto current = refreshRateConfigs->getCurrentRefreshRate();
-        EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_90);
+        EXPECT_EQ(current.getModeId(), HWC_CONFIG_ID_90);
     }
 }
 
@@ -1196,30 +1197,30 @@
 
     ASSERT_EQ(HWC_CONFIG_ID_60,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getConfigId());
+                      .getModeId());
 
     RefreshRateConfigs::Policy policy;
-    policy.defaultConfig = refreshRateConfigs->getCurrentPolicy().defaultConfig;
+    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
     ASSERT_EQ(HWC_CONFIG_ID_90,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getConfigId());
+                      .getModeId());
 
     // Verify that we won't change the group if seamless switch is required.
     layer.seamlessness = Seamlessness::OnlySeamless;
     ASSERT_EQ(HWC_CONFIG_ID_60,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getConfigId());
+                      .getModeId());
 
     // Verify that we won't do a seamless switch if we request the same mode as the default
-    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
     layer.desiredRefreshRate = Fps(60.0f);
     layer.name = "60Hz ExplicitDefault";
     layer.seamlessness = Seamlessness::OnlySeamless;
     ASSERT_EQ(HWC_CONFIG_ID_90,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getConfigId());
+                      .getModeId());
 
     // Verify that if the current config is in another group and there are no layers with
     // seamlessness=SeamedAndSeamless we'll go back to the default group.
@@ -1228,11 +1229,11 @@
     layer.seamlessness = Seamlessness::Default;
     ASSERT_EQ(HWC_CONFIG_ID_60,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getConfigId());
+                      .getModeId());
 
     // If there's a layer with seamlessness=SeamedAndSeamless, another layer with
-    // seamlessness=OnlySeamless can't change the config group.
-    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+    // seamlessness=OnlySeamless can't change the mode group.
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
     layer.seamlessness = Seamlessness::OnlySeamless;
 
     layers.push_back(LayerRequirement{.weight = 0.5f});
@@ -1245,14 +1246,14 @@
 
     ASSERT_EQ(HWC_CONFIG_ID_90,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getConfigId());
+                      .getModeId());
 
     // If there's a layer with seamlessness=SeamedAndSeamless, another layer with
-    // seamlessness=Default can't change the config group.
+    // seamlessness=Default can't change the mode group.
     layers[0].seamlessness = Seamlessness::Default;
     ASSERT_EQ(HWC_CONFIG_ID_90,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getConfigId());
+                      .getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) {
@@ -1262,7 +1263,7 @@
 
     // Allow group switching.
     RefreshRateConfigs::Policy policy;
-    policy.defaultConfig = refreshRateConfigs->getCurrentPolicy().defaultConfig;
+    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
 
@@ -1276,12 +1277,12 @@
 
     ASSERT_EQ(HWC_CONFIG_ID_60,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getConfigId());
+                      .getModeId());
 
-    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_120);
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_120);
     ASSERT_EQ(HWC_CONFIG_ID_120,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getConfigId());
+                      .getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) {
@@ -1291,7 +1292,7 @@
 
     // Allow group switching.
     RefreshRateConfigs::Policy policy;
-    policy.defaultConfig = refreshRateConfigs->getCurrentPolicy().defaultConfig;
+    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
 
@@ -1312,14 +1313,14 @@
 
     ASSERT_EQ(HWC_CONFIG_ID_50,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getConfigId());
+                      .getModeId());
 
     seamedLayer.name = "30Hz ExplicitDefault", seamedLayer.desiredRefreshRate = Fps(30.0f);
-    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_30);
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_30);
 
     ASSERT_EQ(HWC_CONFIG_ID_25,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
-                      .getConfigId());
+                      .getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
@@ -1338,7 +1339,7 @@
         layers[0].desiredRefreshRate = fps;
         layers[0].focused = focused;
         return refreshRateConfigs->getBestRefreshRate(layers, {.touch = touchActive, .idle = false})
-                .getConfigId();
+                .getModeId();
     };
 
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
@@ -1346,7 +1347,7 @@
               0);
     EXPECT_EQ(HWC_CONFIG_ID_60,
               refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false})
-                      .getConfigId());
+                      .getModeId());
     EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, Fps(90.f)));
     EXPECT_EQ(HWC_CONFIG_ID_30, getFrameRate(LayerVoteType::Min, Fps(90.f)));
     EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, Fps(90.f)));
@@ -1398,7 +1399,7 @@
                 refreshRateConfigs
                         ->getBestRefreshRate(layers, {.touch = touchActive, .idle = true},
                                              &consideredSignals)
-                        .getConfigId();
+                        .getModeId();
         // Refresh rate will be chosen by either touch state or idle state
         EXPECT_EQ(!touchActive, consideredSignals.idle);
         return configId;
@@ -1421,10 +1422,10 @@
     // With no layers, idle should still be lower priority than touch boost.
     EXPECT_EQ(HWC_CONFIG_ID_90,
               refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true})
-                      .getConfigId());
+                      .getModeId());
 
     // Idle should be higher precedence than other layer frame rate considerations.
-    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
     EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::NoVote, /*touchActive=*/false));
     EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Min, /*touchActive=*/false));
     EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Max, /*touchActive=*/false));
@@ -1437,7 +1438,7 @@
     // Idle should be applied rather than the current config when there are no layers.
     EXPECT_EQ(HWC_CONFIG_ID_60,
               refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = true})
-                      .getConfigId());
+                      .getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) {
@@ -1628,19 +1629,19 @@
     const auto frameRate = Fps(30.f);
     EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDivider(frameRate));
 
-    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60);
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_60);
     EXPECT_EQ(2, refreshRateConfigs->getRefreshRateDivider(frameRate));
 
-    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_72);
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_72);
     EXPECT_EQ(0, refreshRateConfigs->getRefreshRateDivider(frameRate));
 
-    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
     EXPECT_EQ(3, refreshRateConfigs->getRefreshRateDivider(frameRate));
 
-    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_120);
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_120);
     EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(frameRate));
 
-    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
     EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(Fps(22.5f)));
     EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(Fps(22.6f)));
 }
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index ceccd81..bf07106 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -53,7 +53,7 @@
         mRefreshRateConfigs =
                 std::make_unique<RefreshRateConfigs>(configs, /*currentConfig=*/CONFIG_ID_0);
 
-        const auto currFps = mRefreshRateConfigs->getRefreshRateFromConfigId(CONFIG_ID_0).getFps();
+        const auto currFps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_0).getFps();
         mRefreshRateStats = std::make_unique<RefreshRateStats>(mTimeStats, currFps,
                                                                /*currentPowerMode=*/PowerMode::OFF);
     }
@@ -62,7 +62,7 @@
     std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs;
     std::unique_ptr<RefreshRateStats> mRefreshRateStats;
 
-    DisplayModePtr createConfig(DisplayModeId configId, int32_t configGroup, int64_t vsyncPeriod);
+    DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod);
 };
 
 RefreshRateStatsTest::RefreshRateStatsTest() {
@@ -77,12 +77,12 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-DisplayModePtr RefreshRateStatsTest::createConfig(DisplayModeId configId, int32_t configGroup,
-                                                  int64_t vsyncPeriod) {
-    return DisplayMode::Builder(static_cast<hal::HWConfigId>(configId.value()))
-            .setId(configId)
+DisplayModePtr RefreshRateStatsTest::createDisplayMode(DisplayModeId modeId, int32_t group,
+                                                       int64_t vsyncPeriod) {
+    return DisplayMode::Builder(static_cast<hal::HWConfigId>(modeId.value()))
+            .setId(modeId)
             .setVsyncPeriod(static_cast<int32_t>(vsyncPeriod))
-            .setConfigGroup(configGroup)
+            .setGroup(group)
             .build();
 }
 
@@ -91,7 +91,7 @@
  * Test cases
  */
 TEST_F(RefreshRateStatsTest, oneConfigTest) {
-    init({createConfig(CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90)});
+    init({createDisplayMode(CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90)});
 
     EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
     EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
@@ -110,7 +110,7 @@
     EXPECT_LT(screenOff, times["ScreenOff"]);
     EXPECT_EQ(0u, times.count("90.00fps"));
 
-    const auto config0Fps = mRefreshRateConfigs->getRefreshRateFromConfigId(CONFIG_ID_0).getFps();
+    const auto config0Fps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_0).getFps();
     mRefreshRateStats->setRefreshRate(config0Fps);
     mRefreshRateStats->setPowerMode(PowerMode::ON);
     screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
@@ -138,8 +138,8 @@
 }
 
 TEST_F(RefreshRateStatsTest, twoConfigsTest) {
-    init({createConfig(CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90),
-          createConfig(CONFIG_ID_1, CONFIG_GROUP_0, VSYNC_60)});
+    init({createDisplayMode(CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90),
+          createDisplayMode(CONFIG_ID_1, CONFIG_GROUP_0, VSYNC_60)});
 
     EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
     EXPECT_CALL(mTimeStats, recordRefreshRate(60, _)).Times(AtLeast(1));
@@ -158,8 +158,8 @@
     times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
 
-    const auto config0Fps = mRefreshRateConfigs->getRefreshRateFromConfigId(CONFIG_ID_0).getFps();
-    const auto config1Fps = mRefreshRateConfigs->getRefreshRateFromConfigId(CONFIG_ID_1).getFps();
+    const auto config0Fps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_0).getFps();
+    const auto config1Fps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_1).getFps();
     mRefreshRateStats->setRefreshRate(config0Fps);
     mRefreshRateStats->setPowerMode(PowerMode::ON);
     screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index e688e10..e46a270 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -52,7 +52,7 @@
     SchedulerTest();
 
     const scheduler::RefreshRateConfigs
-            mConfigs{{DisplayMode::Builder(0).setVsyncPeriod(16'666'667).setConfigGroup(0).build()},
+            mConfigs{{DisplayMode::Builder(0).setVsyncPeriod(16'666'667).setGroup(0).build()},
                      DisplayModeId(0)};
 
     mock::SchedulerCallback mSchedulerCallback;
@@ -69,6 +69,8 @@
     Scheduler::ConnectionHandle mConnectionHandle;
     mock::EventThread* mEventThread;
     sp<MockEventThreadConnection> mEventThreadConnection;
+
+    TestableSurfaceFlinger mFlinger;
 };
 
 SchedulerTest::SchedulerTest() {
@@ -166,25 +168,35 @@
     mScheduler.chooseRefreshRateForContent();
 }
 
-TEST_F(SchedulerTest, testDispatchCachedReportedConfig) {
+TEST_F(SchedulerTest, testDispatchCachedReportedMode) {
     // If the optional fields are cleared, the function should return before
-    // onConfigChange is called.
+    // onModeChange is called.
     mScheduler.clearOptionalFieldsInFeatures();
-    EXPECT_NO_FATAL_FAILURE(mScheduler.dispatchCachedReportedConfig());
-    EXPECT_CALL(*mEventThread, onConfigChanged(_, _, _)).Times(0);
+    EXPECT_NO_FATAL_FAILURE(mScheduler.dispatchCachedReportedMode());
+    EXPECT_CALL(*mEventThread, onModeChanged(_, _, _)).Times(0);
 }
 
-TEST_F(SchedulerTest, onNonPrimaryDisplayConfigChanged_invalidParameters) {
-    DisplayModeId configId = DisplayModeId(111);
+TEST_F(SchedulerTest, onNonPrimaryDisplayModeChanged_invalidParameters) {
+    DisplayModeId modeId = DisplayModeId(111);
     nsecs_t vsyncPeriod = 111111;
 
     // If the handle is incorrect, the function should return before
-    // onConfigChange is called.
+    // onModeChange is called.
     Scheduler::ConnectionHandle invalidHandle = {.id = 123};
-    EXPECT_NO_FATAL_FAILURE(mScheduler.onNonPrimaryDisplayConfigChanged(invalidHandle,
-                                                                        PHYSICAL_DISPLAY_ID,
-                                                                        configId, vsyncPeriod));
-    EXPECT_CALL(*mEventThread, onConfigChanged(_, _, _)).Times(0);
+    EXPECT_NO_FATAL_FAILURE(mScheduler.onNonPrimaryDisplayModeChanged(invalidHandle,
+                                                                      PHYSICAL_DISPLAY_ID, modeId,
+                                                                      vsyncPeriod));
+    EXPECT_CALL(*mEventThread, onModeChanged(_, _, _)).Times(0);
+}
+
+TEST_F(SchedulerTest, calculateExtraBufferCount) {
+    EXPECT_EQ(0, mFlinger.calculateExtraBufferCount(Fps(60), 30ms));
+    EXPECT_EQ(1, mFlinger.calculateExtraBufferCount(Fps(90), 30ms));
+    EXPECT_EQ(2, mFlinger.calculateExtraBufferCount(Fps(120), 30ms));
+
+    EXPECT_EQ(1, mFlinger.calculateExtraBufferCount(Fps(60), 40ms));
+
+    EXPECT_EQ(0, mFlinger.calculateExtraBufferCount(Fps(60), 10ms));
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index 2b5cb77..e32c4bf 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -246,7 +246,7 @@
                                             .setVsyncPeriod(DEFAULT_VSYNC_PERIOD)
                                             .setDpiX(DEFAULT_DPI)
                                             .setDpiY(DEFAULT_DPI)
-                                            .setConfigGroup(0)
+                                            .setGroup(0)
                                             .build();
         DisplayModes modes{activeMode};
         state.physical = {.id = *displayId,
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index d3e90e3..3f9dd01 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -80,20 +80,19 @@
         return mFeatures.touch == Scheduler::TouchState::Active;
     }
 
-    void dispatchCachedReportedConfig() {
+    void dispatchCachedReportedMode() {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
-        return Scheduler::dispatchCachedReportedConfig();
+        return Scheduler::dispatchCachedReportedMode();
     }
 
     void clearOptionalFieldsInFeatures() {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
-        mFeatures.cachedConfigChangedParams.reset();
+        mFeatures.cachedModeChangedParams.reset();
     }
 
-    void onNonPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
-                                          DisplayModeId configId, nsecs_t vsyncPeriod) {
-        return Scheduler::onNonPrimaryDisplayConfigChanged(handle, displayId, configId,
-                                                           vsyncPeriod);
+    void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+                                        DisplayModeId modeId, nsecs_t vsyncPeriod) {
+        return Scheduler::onNonPrimaryDisplayModeChanged(handle, displayId, modeId, vsyncPeriod);
     }
 
     ~TestableScheduler() {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index a94b9bd..dee13d6 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -210,26 +210,26 @@
                         std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
                         std::unique_ptr<EventThread> appEventThread,
                         std::unique_ptr<EventThread> sfEventThread,
-                        ISchedulerCallback* callback = nullptr, bool hasMultipleConfigs = false) {
-        DisplayModes configs{DisplayMode::Builder(0)
-                                     .setId(DisplayModeId(0))
-                                     .setVsyncPeriod(16'666'667)
-                                     .setConfigGroup(0)
-                                     .build()};
+                        ISchedulerCallback* callback = nullptr, bool hasMultipleModes = false) {
+        DisplayModes modes{DisplayMode::Builder(0)
+                                   .setId(DisplayModeId(0))
+                                   .setVsyncPeriod(16'666'667)
+                                   .setGroup(0)
+                                   .build()};
 
-        if (hasMultipleConfigs) {
-            configs.emplace_back(DisplayMode::Builder(1)
-                                         .setId(DisplayModeId(1))
-                                         .setVsyncPeriod(11'111'111)
-                                         .setConfigGroup(0)
-                                         .build());
+        if (hasMultipleModes) {
+            modes.emplace_back(DisplayMode::Builder(1)
+                                       .setId(DisplayModeId(1))
+                                       .setVsyncPeriod(11'111'111)
+                                       .setGroup(0)
+                                       .build());
         }
 
-        const auto currConfig = DisplayModeId(0);
+        const auto currMode = DisplayModeId(0);
         mFlinger->mRefreshRateConfigs =
-                std::make_unique<scheduler::RefreshRateConfigs>(configs, currConfig);
+                std::make_unique<scheduler::RefreshRateConfigs>(modes, currMode);
         const auto currFps =
-                mFlinger->mRefreshRateConfigs->getRefreshRateFromConfigId(currConfig).getFps();
+                mFlinger->mRefreshRateConfigs->getRefreshRateFromModeId(currMode).getFps();
         mFlinger->mRefreshRateStats =
                 std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps,
                                                               /*powerMode=*/hal::PowerMode::OFF);
@@ -391,6 +391,11 @@
 
     auto getGPUContextPriority() { return mFlinger->getGPUContextPriority(); }
 
+    auto calculateExtraBufferCount(Fps maxSupportedRefreshRate,
+                                   std::chrono::nanoseconds presentLatency) const {
+        return SurfaceFlinger::calculateExtraBufferCount(maxSupportedRefreshRate, presentLatency);
+    }
+
     /* ------------------------------------------------------------------------
      * Read-only access to private data to assert post-conditions.
      */
@@ -647,7 +652,7 @@
                             .setVsyncPeriod(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)
                             .setDpiX(FakeHwcDisplayInjector::DEFAULT_DPI)
                             .setDpiY(FakeHwcDisplayInjector::DEFAULT_DPI)
-                            .setConfigGroup(0)
+                            .setGroup(0)
                             .build();
 
             DisplayModes modes{activeMode};
@@ -760,7 +765,7 @@
 
 private:
     void setVsyncEnabled(bool) override {}
-    void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override {}
+    void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override {}
     void repaintEverythingForHWC() override {}
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() {}
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 40437bf..485b4ac 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -33,7 +33,7 @@
     MOCK_METHOD0(onScreenReleased, void());
     MOCK_METHOD0(onScreenAcquired, void());
     MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
-    MOCK_METHOD3(onConfigChanged, void(PhysicalDisplayId, DisplayModeId, nsecs_t));
+    MOCK_METHOD3(onModeChanged, void(PhysicalDisplayId, DisplayModeId, nsecs_t));
     MOCK_METHOD2(onFrameRateOverridesChanged,
                  void(PhysicalDisplayId, std::vector<FrameRateOverride>));
     MOCK_CONST_METHOD1(dump, void(std::string&));