Merge "(touch-mode-md 2/n) Add md touch mode support in InputDispatcher"
diff --git a/cmds/bugreportz/readme.md b/cmds/bugreportz/readme.md
index eb0d898..3606827 100644
--- a/cmds/bugreportz/readme.md
+++ b/cmds/bugreportz/readme.md
@@ -1,6 +1,6 @@
 # bugreportz protocol
 
-`bugreportz` is used to generate a zippped bugreport whose path is passed back to `adb`, using
+`bugreportz` is used to generate a zipped bugreport whose path is passed back to `adb`, using
 the simple protocol defined below.
 
 # Version 1.1
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 0b69829..235c923 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -183,6 +183,7 @@
 #define PACKAGE_DEX_USE_LIST "/data/system/package-dex-usage.list"
 #define SYSTEM_TRACE_SNAPSHOT "/data/misc/perfetto-traces/bugreport/systrace.pftrace"
 #define CGROUPFS_DIR "/sys/fs/cgroup"
+#define SDK_EXT_INFO "/apex/com.android.sdkext/bin/derive_sdk"
 
 // TODO(narayan): Since this information has to be kept in sync
 // with tombstoned, we should just put it in a common header.
@@ -766,7 +767,7 @@
 }
 
 void Dumpstate::PrintHeader() const {
-    std::string build, fingerprint, radio, bootloader, network;
+    std::string build, fingerprint, radio, bootloader, network, sdkversion;
     char date[80];
 
     build = android::base::GetProperty("ro.build.display.id", "(unknown)");
@@ -774,6 +775,7 @@
     radio = android::base::GetProperty("gsm.version.baseband", "(unknown)");
     bootloader = android::base::GetProperty("ro.bootloader", "(unknown)");
     network = android::base::GetProperty("gsm.operator.alpha", "(unknown)");
+    sdkversion = android::base::GetProperty("ro.build.version.sdk", "(unknown)");
     strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now_));
 
     printf("========================================================\n");
@@ -791,9 +793,10 @@
     if (module_metadata_version != 0) {
         printf("Module Metadata version: %" PRId64 "\n", module_metadata_version);
     }
-    printf("SDK extension versions [r=%s s=%s]\n",
-           android::base::GetProperty("build.version.extensions.r", "-").c_str(),
-           android::base::GetProperty("build.version.extensions.s", "-").c_str());
+    printf("Android SDK version: %s\n", sdkversion.c_str());
+    printf("SDK extensions: ");
+    RunCommandToFd(STDOUT_FILENO, "", {SDK_EXT_INFO, "--header"},
+                   CommandOptions::WithTimeout(1).Always().DropRoot().Build());
 
     printf("Kernel: ");
     DumpFileToFd(STDOUT_FILENO, "", "/proc/version");
@@ -1026,7 +1029,7 @@
         MYLOGE("Could not open %s to dump incident report.\n", path.c_str());
         return;
     }
-    RunCommandToFd(fd, "", {"incident", "-u"}, CommandOptions::WithTimeout(120).Build());
+    RunCommandToFd(fd, "", {"incident", "-u"}, CommandOptions::WithTimeout(20).Build());
     bool empty = 0 == lseek(fd, 0, SEEK_END);
     if (!empty) {
         // Use a different name from "incident.proto"
@@ -1084,7 +1087,7 @@
         return;
     }
     RunCommandToFd(fd, "", {"cmd", "window", "dump-visible-window-views"},
-                   CommandOptions::WithTimeout(120).Build());
+                   CommandOptions::WithTimeout(10).Build());
     bool empty = 0 == lseek(fd, 0, SEEK_END);
     if (!empty) {
         ds.AddZipEntry("visible_windows.zip", path);
@@ -1890,6 +1893,9 @@
     DumpFile("PSI memory", "/proc/pressure/memory");
     DumpFile("PSI io", "/proc/pressure/io");
 
+    RunCommand("SDK EXTENSIONS", {SDK_EXT_INFO, "--dump"},
+               CommandOptions::WithTimeout(10).Always().DropRoot().Build());
+
     if (dump_pool_) {
         RETURN_IF_USER_DENIED_CONSENT();
         WaitForTask(std::move(dump_traces));
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 3cfe529..a99ccae 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -326,16 +326,41 @@
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "linkToDeath failure");
     }
 
+    auto it = mNameToService.find(name);
+    if (it != mNameToService.end()) {
+        const Service& existing = it->second;
+
+        // We could do better than this because if the other service dies, it
+        // may not have an entry here. However, this case is unlikely. We are
+        // only trying to detect when two different services are accidentally installed.
+
+        if (existing.ctx.uid != ctx.uid) {
+            LOG(WARNING) << "Service '" << name << "' originally registered from UID "
+                         << existing.ctx.uid << " but it is now being registered from UID "
+                         << ctx.uid << ". Multiple instances installed?";
+        }
+
+        if (existing.ctx.sid != ctx.sid) {
+            LOG(WARNING) << "Service '" << name << "' originally registered from SID "
+                         << existing.ctx.sid << " but it is now being registered from SID "
+                         << ctx.sid << ". Multiple instances installed?";
+        }
+
+        LOG(INFO) << "Service '" << name << "' originally registered from PID "
+                  << existing.ctx.debugPid << " but it is being registered again from PID "
+                  << ctx.debugPid
+                  << ". Bad state? Late death notification? Multiple instances installed?";
+    }
+
     // Overwrite the old service if it exists
-    mNameToService[name] = Service {
-        .binder = binder,
-        .allowIsolated = allowIsolated,
-        .dumpPriority = dumpPriority,
-        .debugPid = ctx.debugPid,
+    mNameToService[name] = Service{
+            .binder = binder,
+            .allowIsolated = allowIsolated,
+            .dumpPriority = dumpPriority,
+            .ctx = ctx,
     };
 
-    auto it = mNameToRegistrationCallback.find(name);
-    if (it != mNameToRegistrationCallback.end()) {
+    if (auto it = mNameToRegistrationCallback.find(name); it != mNameToRegistrationCallback.end()) {
         for (const sp<IServiceCallback>& cb : it->second) {
             mNameToService[name].guaranteeClient = true;
             // permission checked in registerForNotifications
@@ -571,7 +596,7 @@
         return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
     }
 
-    if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) {
+    if (serviceIt->second.ctx.debugPid != IPCThreadState::self()->getCallingPid()) {
         LOG(WARNING) << "Only a server can register for client callbacks (for " << name << ")";
         return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
     }
@@ -707,7 +732,7 @@
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
 
-    if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) {
+    if (serviceIt->second.ctx.debugPid != IPCThreadState::self()->getCallingPid()) {
         LOG(WARNING) << "Only a server can unregister itself (for " << name << ")";
         return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
     }
@@ -754,7 +779,7 @@
     for (auto const& [name, service] : mNameToService) {
         ServiceDebugInfo info;
         info.name = name;
-        info.debugPid = service.debugPid;
+        info.debugPid = service.ctx.debugPid;
 
         outReturn->push_back(std::move(info));
     }
@@ -762,4 +787,10 @@
     return Status::ok();
 }
 
+void ServiceManager::clear() {
+    mNameToService.clear();
+    mNameToRegistrationCallback.clear();
+    mNameToClientCallback.clear();
+}
+
 }  // namespace android
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 5e40319..07b79f8 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -58,6 +58,12 @@
     void binderDied(const wp<IBinder>& who) override;
     void handleClientCallbacks();
 
+    /**
+     *  This API is added for debug purposes. It clears members which hold service and callback
+     * information.
+     */
+    void clear();
+
 protected:
     virtual void tryStartService(const std::string& name);
 
@@ -68,7 +74,7 @@
         int32_t dumpPriority;
         bool hasClients = false; // notifications sent on true -> false.
         bool guaranteeClient = false; // forces the client check to true
-        pid_t debugPid = 0; // the process in which this service runs
+        Access::CallingContext ctx;   // process that originally registers this
 
         // the number of clients of the service, including servicemanager itself
         ssize_t getNodeStrongRefCount();
diff --git a/cmds/servicemanager/ServiceManagerFuzzer.cpp b/cmds/servicemanager/ServiceManagerFuzzer.cpp
index 39f8522..b76a6bd 100644
--- a/cmds/servicemanager/ServiceManagerFuzzer.cpp
+++ b/cmds/servicemanager/ServiceManagerFuzzer.cpp
@@ -29,6 +29,7 @@
     auto accessPtr = std::make_unique<Access>();
     auto serviceManager = sp<ServiceManager>::make(std::move(accessPtr));
     fuzzService(serviceManager, FuzzedDataProvider(data, size));
+    serviceManager->clear();
 
     return 0;
 }
diff --git a/data/etc/android.software.credentials.xml b/data/etc/android.software.credentials.xml
new file mode 100644
index 0000000..234d144
--- /dev/null
+++ b/data/etc/android.software.credentials.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<permissions>
+    <feature name="android.software.credentials" />
+</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index 8fdd8d0..0833012 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -52,6 +52,7 @@
     <feature name="android.software.print" />
     <feature name="android.software.companion_device_setup" />
     <feature name="android.software.autofill" />
+    <feature name="android.software.credentials" />
     <feature name="android.software.cant_save_state" />
     <feature name="android.software.secure_lock_screen" />
     <feature name="android.software.window_magnification" />
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index 59d5b10..6af4d91 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -52,6 +52,7 @@
     <feature name="android.software.print" />
     <feature name="android.software.companion_device_setup" />
     <feature name="android.software.autofill" />
+    <feature name="android.software.credentials" />
     <feature name="android.software.cant_save_state" />
     <feature name="android.software.secure_lock_screen" />
     <feature name="android.software.window_magnification" />
diff --git a/include/android/input.h b/include/android/input.h
index 8cd9e95..7080386 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -840,6 +840,12 @@
      * This classification type should be used to accelerate the long press behaviour.
      */
     AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS = 2,
+    /**
+     * Classification constant: touchpad two-finger swipe.
+     *
+     * The current event stream represents the user swiping with two fingers on a touchpad.
+     */
+    AMOTION_EVENT_CLASSIFICATION_TWO_FINGER_SWIPE = 3,
 };
 
 /**
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index 16a13d6..e5b5db2 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -818,6 +818,17 @@
     /** Keyboard backlight Toggle key.
      * Toggles the keyboard backlight on/off. */
     AKEYCODE_KEYBOARD_BACKLIGHT_TOGGLE = 307,
+    /** The primary button on the barrel of a stylus.
+     * This is usually the button closest to the tip of the stylus. */
+    AKEYCODE_STYLUS_BUTTON_PRIMARY = 308,
+    /** The secondary button on the barrel of a stylus.
+     * This is usually the second button from the tip of the stylus. */
+    AKEYCODE_STYLUS_BUTTON_SECONDARY = 309,
+    /** The tertiary button on the barrel of a stylus.
+     * This is usually the third button from the tip of the stylus. */
+    AKEYCODE_STYLUS_BUTTON_TERTIARY = 310,
+    /** A button on the tail end of a stylus. */
+    AKEYCODE_STYLUS_BUTTON_TAIL = 311,
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/android/surface_control_jni.h b/include/android/surface_control_jni.h
new file mode 100644
index 0000000..67e3a9f
--- /dev/null
+++ b/include/android/surface_control_jni.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2022 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.
+ */
+
+/**
+ * @addtogroup NativeActivity Native Activity
+ * @{
+ */
+
+/**
+ * @file surface_control_jni.h
+ */
+
+#ifndef ANDROID_SURFACE_CONTROL_JNI_H
+#define ANDROID_SURFACE_CONTROL_JNI_H
+
+#include <jni.h>
+#include <sys/cdefs.h>
+
+#include <android/surface_control.h>
+
+__BEGIN_DECLS
+
+/**
+ * Return the ASurfaceControl wrapped by a Java SurfaceControl object.
+ *
+ * This method does not acquire any additional reference to the ASurfaceControl
+ * that is returned. To keep the ASurfaceControl alive after the Java
+ * SurfaceControl object is closed, explicitly or by the garbage collector, be
+ * sure to use ASurfaceControl_acquire() to acquire an additional reference.
+ *
+ * Available since API level 34.
+ */
+ASurfaceControl* _Nullable ASurfaceControl_fromSurfaceControl(JNIEnv* _Nonnull env,
+        jobject _Nonnull surfaceControlObj) __INTRODUCED_IN(__ANDROID_API_U__);
+
+/**
+ * Return the ASurfaceTransaction wrapped by a Java Transaction object.
+ *
+ * The returned ASurfaceTransaction is still owned by the Java Transaction object is only
+ * valid while the Java Transaction object is alive. In particular, the returned transaction
+ * must NOT be deleted with ASurfaceTransaction_delete.
+ * May return nullptr on error.
+ *
+ * Available since API level 34.
+ */
+ASurfaceTransaction* _Nullable ASurfaceTransaction_fromTransaction(JNIEnv* _Nonnull env,
+        jobject _Nonnull transactionObj) __INTRODUCED_IN(__ANDROID_API_U__);
+
+__END_DECLS
+
+#endif // ANDROID_SURFACE_CONTROL_JNI_H
+/** @} */
diff --git a/include/input/Input.h b/include/input/Input.h
index 2dd651e..172e5b4 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -289,6 +289,10 @@
      * The current gesture likely represents a user intentionally exerting force on the touchscreen.
      */
     DEEP_PRESS = AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS,
+    /**
+     * The current gesture represents the user swiping with two fingers on a touchpad.
+     */
+    TWO_FINGER_SWIPE = AMOTION_EVENT_CLASSIFICATION_TWO_FINGER_SWIPE,
 };
 
 /**
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 415080d..0026e82 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <android/sensor.h>
+#include <ftl/flags.h>
 #include <input/Input.h>
 #include <input/KeyCharacterMap.h>
 #include <unordered_map>
@@ -105,12 +106,18 @@
 };
 
 enum class InputDeviceLightType : int32_t {
-    MONO = 0,
+    INPUT = 0,
     PLAYER_ID = 1,
-    RGB = 2,
-    MULTI_COLOR = 3,
+    KEYBOARD_BACKLIGHT = 2,
 
-    ftl_last = MULTI_COLOR
+    ftl_last = KEYBOARD_BACKLIGHT
+};
+
+enum class InputDeviceLightCapability : uint32_t {
+    /** Capability to change brightness of the light */
+    BRIGHTNESS = 0x00000001,
+    /** Capability to change color of the light */
+    RGB = 0x00000002,
 };
 
 struct InputDeviceSensorInfo {
@@ -171,14 +178,17 @@
 
 struct InputDeviceLightInfo {
     explicit InputDeviceLightInfo(std::string name, int32_t id, InputDeviceLightType type,
+                                  ftl::Flags<InputDeviceLightCapability> capabilityFlags,
                                   int32_t ordinal)
-          : name(name), id(id), type(type), ordinal(ordinal) {}
+          : name(name), id(id), type(type), capabilityFlags(capabilityFlags), ordinal(ordinal) {}
     // Name string of the light.
     std::string name;
     // Light id
     int32_t id;
     // Type of the light.
     InputDeviceLightType type;
+    // Light capabilities.
+    ftl::Flags<InputDeviceLightCapability> capabilityFlags;
     // Ordinal of the light
     int32_t ordinal;
 };
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index 4251f04..294879e 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -145,16 +145,6 @@
     inline int32_t getActivePointerId() const { return mActivePointerId; }
 
 private:
-    // All axes supported for velocity tracking, mapped to their default strategies.
-    // Although other strategies are available for testing and comparison purposes,
-    // the default strategy is the one that applications will actually use.  Be very careful
-    // when adjusting the default strategy because it can dramatically affect
-    // (often in a bad way) the user experience.
-    static const std::map<int32_t, Strategy> DEFAULT_STRATEGY_BY_AXIS;
-
-    // Axes specifying location on a 2D plane (i.e. X and Y).
-    static const std::set<int32_t> PLANAR_AXES;
-
     nsecs_t mLastEventTime;
     BitSet32 mCurrentPointerIdBits;
     int32_t mActivePointerId;
@@ -170,7 +160,12 @@
 
     void configureStrategy(int32_t axis);
 
-    static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy);
+    // Generates a VelocityTrackerStrategy instance for the given Strategy type.
+    // The `deltaValues` parameter indicates whether or not the created strategy should treat motion
+    // values as deltas (and not as absolute values). This the parameter is applicable only for
+    // strategies that support differential axes.
+    static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy,
+                                                                   bool deltaValues);
 };
 
 
@@ -316,7 +311,7 @@
 
 class ImpulseVelocityTrackerStrategy : public VelocityTrackerStrategy {
 public:
-    ImpulseVelocityTrackerStrategy();
+    ImpulseVelocityTrackerStrategy(bool deltaValues);
     virtual ~ImpulseVelocityTrackerStrategy();
 
     virtual void clearPointers(BitSet32 idBits);
@@ -341,6 +336,11 @@
         inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; }
     };
 
+    // Whether or not the input movement values for the strategy come in the form of delta values.
+    // If the input values are not deltas, the strategy needs to calculate deltas as part of its
+    // velocity calculation.
+    const bool mDeltaValues;
+
     size_t mIndex;
     Movement mMovements[HISTORY_SIZE];
 };
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 5db3eef..05db774 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -380,6 +380,13 @@
     if (Status status = realGetService(name, &out); !status.isOk()) {
         ALOGW("Failed to getService in waitForService for %s: %s", name.c_str(),
               status.toString8().c_str());
+        if (0 == ProcessState::self()->getThreadPoolMaxTotalThreadCount()) {
+            ALOGW("Got service, but may be racey because we could not wait efficiently for it. "
+                  "Threadpool has 0 guaranteed threads. "
+                  "Is the threadpool configured properly? "
+                  "See ProcessState::startThreadPool and "
+                  "ProcessState::setThreadPoolMaxThreadCount.");
+        }
         return nullptr;
     }
     if (out != nullptr) return out;
@@ -410,7 +417,9 @@
             if (waiter->mBinder != nullptr) return waiter->mBinder;
         }
 
-        ALOGW("Waited one second for %s (is service started? are binder threads started and available?)", name.c_str());
+        ALOGW("Waited one second for %s (is service started? Number of threads started in the "
+              "threadpool: %zu. Are binder threads started and available?)",
+              name.c_str(), ProcessState::self()->getThreadPoolMaxTotalThreadCount());
 
         // Handle race condition for lazy services. Here is what can happen:
         // - the service dies (not processed by init yet).
diff --git a/libs/binder/RpcTrusty.cpp b/libs/binder/RpcTrusty.cpp
index ea49eef..3b53b05 100644
--- a/libs/binder/RpcTrusty.cpp
+++ b/libs/binder/RpcTrusty.cpp
@@ -26,8 +26,12 @@
 
 using android::base::unique_fd;
 
-sp<IBinder> RpcTrustyConnect(const char* device, const char* port) {
+sp<RpcSession> RpcTrustyConnectWithSessionInitializer(
+        const char* device, const char* port,
+        std::function<void(sp<RpcSession>&)> sessionInitializer) {
     auto session = RpcSession::make(RpcTransportCtxFactoryTipcAndroid::make());
+    // using the callback to initialize the session
+    sessionInitializer(session);
     auto request = [=] {
         int tipcFd = tipc_connect(device, port);
         if (tipcFd < 0) {
@@ -40,6 +44,11 @@
         LOG(ERROR) << "Failed to set up Trusty client. Error: " << statusToString(status).c_str();
         return nullptr;
     }
+    return session;
+}
+
+sp<IBinder> RpcTrustyConnect(const char* device, const char* port) {
+    auto session = RpcTrustyConnectWithSessionInitializer(device, port, [](auto) {});
     return session->getRootObject();
 }
 
diff --git a/libs/binder/include_trusty/binder/RpcTrusty.h b/libs/binder/include_trusty/binder/RpcTrusty.h
index f124e0c..b034b9b 100644
--- a/libs/binder/include_trusty/binder/RpcTrusty.h
+++ b/libs/binder/include_trusty/binder/RpcTrusty.h
@@ -22,4 +22,8 @@
 
 sp<IBinder> RpcTrustyConnect(const char* device, const char* port);
 
+sp<RpcSession> RpcTrustyConnectWithSessionInitializer(
+        const char* device, const char* port,
+        std::function<void(sp<RpcSession>&)> sessionInitializer);
+
 } // namespace android
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 33d28e6..8ae7537 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -182,4 +182,8 @@
     name: "libbinder_ndk",
     symbol_file: "libbinder_ndk.map.txt",
     first_version: "29",
+    export_header_libs: [
+        "libbinder_ndk_headers",
+        "libbinder_ndk_helper_headers",
+    ],
 }
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 0ec6183..a135796 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -30,6 +30,7 @@
     apex_available: [
         "//apex_available:platform",
         "com.android.compos",
+        "com.android.rkpd",
         "com.android.uwb",
         "com.android.virt",
     ],
@@ -80,6 +81,7 @@
     apex_available: [
         "//apex_available:platform",
         "com.android.compos",
+        "com.android.rkpd",
         "com.android.uwb",
         "com.android.virt",
     ],
@@ -138,6 +140,7 @@
     apex_available: [
         "//apex_available:platform",
         "com.android.compos",
+        "com.android.rkpd",
         "com.android.uwb",
         "com.android.virt",
     ],
diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp
index f169390..5ebc27f 100644
--- a/libs/binder/rust/rpcbinder/Android.bp
+++ b/libs/binder/rust/rpcbinder/Android.bp
@@ -16,7 +16,7 @@
     ],
     rustlibs: [
         "libbinder_ndk_sys",
-        "libbinder_rpc_unstable_bindgen",
+        "libbinder_rpc_unstable_bindgen_sys",
         "libbinder_rs",
         "libdowncast_rs",
         "liblibc",
@@ -29,6 +29,35 @@
     min_sdk_version: "Tiramisu",
 }
 
+// Build a separate rust_library rather than depending directly on libbinder_rpc_unstable_bindgen,
+// to work around the fact that rust_bindgen targets only produce rlibs and not dylibs, which would
+// result in duplicate conflicting versions of libbinder_ndk_sys. This will hopefully be fixed in
+// the build system, at which point we can delete this target and go back to using
+// libbinder_rpc_unstable_bindgen directly.
+rust_library {
+    name: "libbinder_rpc_unstable_bindgen_sys",
+    crate_name: "binder_rpc_unstable_bindgen",
+    srcs: [
+        ":libbinder_rpc_unstable_bindgen",
+    ],
+    visibility: [":__subpackages__"],
+    rustlibs: [
+        "libbinder_ndk_sys",
+    ],
+    shared_libs: [
+        "libbinder_rpc_unstable",
+        "libutils",
+    ],
+    apex_available: [
+        "com.android.compos",
+        "com.android.uwb",
+        "com.android.virt",
+    ],
+    min_sdk_version: "Tiramisu",
+    lints: "none",
+    clippy_lints: "none",
+}
+
 // TODO(b/184872979): remove once the RPC Binder API is stabilised.
 rust_bindgen {
     name: "libbinder_rpc_unstable_bindgen",
@@ -36,6 +65,15 @@
     crate_name: "binder_rpc_unstable_bindgen",
     visibility: [":__subpackages__"],
     source_stem: "bindings",
+    bindgen_flags: [
+        "--blocklist-type",
+        "AIBinder",
+        "--raw-line",
+        "use binder_ndk_sys::AIBinder;",
+    ],
+    rustlibs: [
+        "libbinder_ndk_sys",
+    ],
     shared_libs: [
         "libbinder_rpc_unstable",
         "libutils",
@@ -52,6 +90,9 @@
     name: "libbinder_rpc_unstable_bindgen_test",
     srcs: [":libbinder_rpc_unstable_bindgen"],
     crate_name: "binder_rpc_unstable_bindgen",
+    rustlibs: [
+        "libbinder_ndk_sys",
+    ],
     test_suites: ["general-tests"],
     auto_gen_config: true,
     clippy_lints: "none",
diff --git a/libs/binder/rust/rpcbinder/src/client.rs b/libs/binder/rust/rpcbinder/src/client.rs
index dfc6f06..743800b 100644
--- a/libs/binder/rust/rpcbinder/src/client.rs
+++ b/libs/binder/rust/rpcbinder/src/client.rs
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-use binder::{
-    unstable_api::{new_spibinder, AIBinder},
-    FromIBinder, SpIBinder, StatusCode, Strong,
-};
+use binder::{unstable_api::new_spibinder, FromIBinder, SpIBinder, StatusCode, Strong};
 use std::os::{
     raw::{c_int, c_void},
     unix::io::RawFd,
@@ -27,7 +24,7 @@
 pub fn get_vsock_rpc_service(cid: u32, port: u32) -> Option<SpIBinder> {
     // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership can
     // safely be taken by new_spibinder.
-    unsafe { new_spibinder(binder_rpc_unstable_bindgen::RpcClient(cid, port) as *mut AIBinder) }
+    unsafe { new_spibinder(binder_rpc_unstable_bindgen::RpcClient(cid, port)) }
 }
 
 /// Connects to an RPC Binder server for a particular interface over vsock.
@@ -54,7 +51,7 @@
         new_spibinder(binder_rpc_unstable_bindgen::RpcPreconnectedClient(
             Some(request_fd_wrapper),
             param,
-        ) as *mut AIBinder)
+        ))
     }
 }
 
diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs
index d98a439..aeb23c6 100644
--- a/libs/binder/rust/rpcbinder/src/server.rs
+++ b/libs/binder/rust/rpcbinder/src/server.rs
@@ -14,7 +14,10 @@
  * limitations under the License.
  */
 
-use binder::{unstable_api::AsNative, SpIBinder};
+use binder::{
+    unstable_api::{AIBinder, AsNative},
+    SpIBinder,
+};
 use std::{os::raw, ptr::null_mut};
 
 /// Runs a binder RPC server, serving the supplied binder service implementation on the given vsock
@@ -44,7 +47,7 @@
     F: FnOnce(),
 {
     fn run_server(&mut self, mut service: SpIBinder, port: u32) -> bool {
-        let service = service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder;
+        let service = service.as_native_mut();
         let param = self.as_void_ptr();
 
         // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
@@ -106,10 +109,7 @@
     }
 }
 
-unsafe extern "C" fn factory_wrapper(
-    cid: u32,
-    context: *mut raw::c_void,
-) -> *mut binder_rpc_unstable_bindgen::AIBinder {
+unsafe extern "C" fn factory_wrapper(cid: u32, context: *mut raw::c_void) -> *mut AIBinder {
     // SAFETY: `context` was created from an `&mut RpcServerFactoryRef` by
     // `run_rpc_server_with_factory`, and we are still within the lifetime of the value it is
     // pointing to.
@@ -117,7 +117,7 @@
     let factory = factory_ptr.as_mut().unwrap();
 
     if let Some(mut service) = factory(cid) {
-        service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder
+        service.as_native_mut()
     } else {
         null_mut()
     }
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 3a6dadd..dee05d0 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -296,7 +296,7 @@
     /// Must be called with a valid pointer to a `T` object. After this call,
     /// the pointer will be invalid and should not be dereferenced.
     unsafe extern "C" fn on_destroy(object: *mut c_void) {
-        Box::from_raw(object as *mut T);
+        drop(Box::from_raw(object as *mut T));
     }
 
     /// Called whenever a new, local `AIBinder` object is needed of a specific
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 21b0354..9a6d4df 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -85,6 +85,11 @@
     return base::StringPrintf("unexpected state %d", wstatus);
 }
 
+static void debugBacktrace(pid_t pid) {
+    std::cerr << "TAKING BACKTRACE FOR PID " << pid << std::endl;
+    system((std::string("debuggerd -b ") + std::to_string(pid)).c_str());
+}
+
 class Process {
 public:
     Process(Process&&) = default;
@@ -125,6 +130,8 @@
     // Kill the process. Avoid if possible. Shutdown gracefully via an RPC instead.
     void terminate() { kill(mPid, SIGTERM); }
 
+    pid_t getPid() { return mPid; }
+
 private:
     std::function<void(int wstatus)> mCustomExitStatusCheck;
     pid_t mPid = 0;
@@ -173,7 +180,9 @@
 
             wp<RpcSession> weakSession = session;
             session = nullptr;
-            EXPECT_EQ(nullptr, weakSession.promote()) << "Leaked session";
+
+            EXPECT_EQ(nullptr, weakSession.promote())
+                    << (debugBacktrace(host.getPid()), debugBacktrace(getpid()), "Leaked session");
         }
     }
 };
diff --git a/libs/binderthreadstate/test.cpp b/libs/binderthreadstate/test.cpp
index 2f73137..df1f35d 100644
--- a/libs/binderthreadstate/test.cpp
+++ b/libs/binderthreadstate/test.cpp
@@ -73,6 +73,15 @@
     CHECK(ret.isOk()) << ret;
 }
 
+static std::string getStackPointerDebugInfo() {
+    const void* hwbinderSp = android::hardware::IPCThreadState::self()->getServingStackPointer();
+    const void* binderSp = android::IPCThreadState::self()->getServingStackPointer();
+
+    std::stringstream ss;
+    ss << "(hwbinder sp: " << hwbinderSp << " binder sp: " << binderSp << ")";
+    return ss.str();
+}
+
 static inline std::ostream& operator<<(std::ostream& o, const BinderCallType& s) {
     return o << static_cast<std::underlying_type_t<BinderCallType>>(s);
 }
@@ -88,17 +97,21 @@
         return android::hardware::Status::ok();
     }
     Return<void> call(int32_t idx) {
+        bool doCallHidl = thisId == kP1Id && idx % 4 < 2;
+
         LOG(INFO) << "HidlServer CALL " << thisId << " to " << otherId << " at idx: " << idx
-                  << " with tid: " << gettid();
-        CHECK_EQ(BinderCallType::HWBINDER, getCurrentServingCall());
+                  << " with tid: " << gettid() << " calling " << (doCallHidl ? "HIDL" : "AIDL");
+        CHECK_EQ(BinderCallType::HWBINDER, getCurrentServingCall())
+                << " before call " << getStackPointerDebugInfo();
         if (idx > 0) {
-            if (thisId == kP1Id && idx % 4 < 2) {
+            if (doCallHidl) {
                 callHidl(otherId, idx - 1);
             } else {
                 callAidl(otherId, idx - 1);
             }
         }
-        CHECK_EQ(BinderCallType::HWBINDER, getCurrentServingCall());
+        CHECK_EQ(BinderCallType::HWBINDER, getCurrentServingCall())
+                << " after call " << getStackPointerDebugInfo();
         return android::hardware::Status::ok();
     }
 };
@@ -113,17 +126,20 @@
         return Status::ok();
     }
     Status call(int32_t idx) {
+        bool doCallHidl = thisId == kP2Id && idx % 4 < 2;
         LOG(INFO) << "AidlServer CALL " << thisId << " to " << otherId << " at idx: " << idx
-                  << " with tid: " << gettid();
-        CHECK_EQ(BinderCallType::BINDER, getCurrentServingCall());
+                  << " with tid: " << gettid() << " calling " << (doCallHidl ? "HIDL" : "AIDL");
+        CHECK_EQ(BinderCallType::BINDER, getCurrentServingCall())
+                << " before call " << getStackPointerDebugInfo();
         if (idx > 0) {
-            if (thisId == kP2Id && idx % 4 < 2) {
+            if (doCallHidl) {
                 callHidl(otherId, idx - 1);
             } else {
                 callAidl(otherId, idx - 1);
             }
         }
-        CHECK_EQ(BinderCallType::BINDER, getCurrentServingCall());
+        CHECK_EQ(BinderCallType::BINDER, getCurrentServingCall())
+                << " after call " << getStackPointerDebugInfo();
         return Status::ok();
     }
 };
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 751721e..72bd1fb 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -627,12 +627,11 @@
 
 SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
       : mId(other.mId),
-        mForceSynchronous(other.mForceSynchronous),
         mTransactionNestCount(other.mTransactionNestCount),
         mAnimation(other.mAnimation),
         mEarlyWakeupStart(other.mEarlyWakeupStart),
         mEarlyWakeupEnd(other.mEarlyWakeupEnd),
-        mContainsBuffer(other.mContainsBuffer),
+        mMayContainBuffer(other.mMayContainBuffer),
         mDesiredPresentTime(other.mDesiredPresentTime),
         mIsAutoTimestamp(other.mIsAutoTimestamp),
         mFrameTimelineInfo(other.mFrameTimelineInfo),
@@ -662,12 +661,10 @@
 
 status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
     const uint64_t transactionId = parcel->readUint64();
-    const uint32_t forceSynchronous = parcel->readUint32();
     const uint32_t transactionNestCount = parcel->readUint32();
     const bool animation = parcel->readBool();
     const bool earlyWakeupStart = parcel->readBool();
     const bool earlyWakeupEnd = parcel->readBool();
-    const bool containsBuffer = parcel->readBool();
     const int64_t desiredPresentTime = parcel->readInt64();
     const bool isAutoTimestamp = parcel->readBool();
     FrameTimelineInfo frameTimelineInfo;
@@ -740,12 +737,10 @@
 
     // Parsing was successful. Update the object.
     mId = transactionId;
-    mForceSynchronous = forceSynchronous;
     mTransactionNestCount = transactionNestCount;
     mAnimation = animation;
     mEarlyWakeupStart = earlyWakeupStart;
     mEarlyWakeupEnd = earlyWakeupEnd;
-    mContainsBuffer = containsBuffer;
     mDesiredPresentTime = desiredPresentTime;
     mIsAutoTimestamp = isAutoTimestamp;
     mFrameTimelineInfo = frameTimelineInfo;
@@ -772,12 +767,10 @@
     const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers();
 
     parcel->writeUint64(mId);
-    parcel->writeUint32(mForceSynchronous);
     parcel->writeUint32(mTransactionNestCount);
     parcel->writeBool(mAnimation);
     parcel->writeBool(mEarlyWakeupStart);
     parcel->writeBool(mEarlyWakeupEnd);
-    parcel->writeBool(mContainsBuffer);
     parcel->writeInt64(mDesiredPresentTime);
     parcel->writeBool(mIsAutoTimestamp);
     mFrameTimelineInfo.writeToParcel(parcel);
@@ -876,7 +869,7 @@
 
     mInputWindowCommands.merge(other.mInputWindowCommands);
 
-    mContainsBuffer |= other.mContainsBuffer;
+    mMayContainBuffer |= other.mMayContainBuffer;
     mEarlyWakeupStart = mEarlyWakeupStart || other.mEarlyWakeupStart;
     mEarlyWakeupEnd = mEarlyWakeupEnd || other.mEarlyWakeupEnd;
     mApplyToken = other.mApplyToken;
@@ -892,8 +885,7 @@
     mDisplayStates.clear();
     mListenerCallbacks.clear();
     mInputWindowCommands.clear();
-    mContainsBuffer = false;
-    mForceSynchronous = 0;
+    mMayContainBuffer = false;
     mTransactionNestCount = 0;
     mAnimation = false;
     mEarlyWakeupStart = false;
@@ -915,12 +907,13 @@
     uncacheBuffer.token = BufferCache::getInstance().getToken();
     uncacheBuffer.id = cacheId;
 
-    sf->setTransactionState(FrameTimelineInfo{}, {}, {}, 0, Transaction::getDefaultApplyToken(), {},
-                            systemTime(), true, uncacheBuffer, false, {}, generateId());
+    sf->setTransactionState(FrameTimelineInfo{}, {}, {}, ISurfaceComposer::eOneWay,
+                            Transaction::getDefaultApplyToken(), {}, systemTime(), true,
+                            uncacheBuffer, false, {}, generateId());
 }
 
 void SurfaceComposerClient::Transaction::cacheBuffers() {
-    if (!mContainsBuffer) {
+    if (!mMayContainBuffer) {
         return;
     }
 
@@ -1005,27 +998,25 @@
     Vector<DisplayState> displayStates;
     uint32_t flags = 0;
 
-    mForceSynchronous |= synchronous;
-
     for (auto const& kv : mComposerStates){
         composerStates.add(kv.second);
     }
 
     displayStates = std::move(mDisplayStates);
 
-    if (mForceSynchronous) {
+    if (synchronous) {
         flags |= ISurfaceComposer::eSynchronous;
     }
     if (mAnimation) {
         flags |= ISurfaceComposer::eAnimation;
     }
     if (oneWay) {
-      if (mForceSynchronous) {
-          ALOGE("Transaction attempted to set synchronous and one way at the same time"
-                " this is an invalid request. Synchronous will win for safety");
-      } else {
-          flags |= ISurfaceComposer::eOneWay;
-      }
+        if (synchronous) {
+            ALOGE("Transaction attempted to set synchronous and one way at the same time"
+                  " this is an invalid request. Synchronous will win for safety");
+        } else {
+            flags |= ISurfaceComposer::eOneWay;
+        }
     }
 
     // If both mEarlyWakeupStart and mEarlyWakeupEnd are set
@@ -1464,7 +1455,6 @@
     s->what &= ~layer_state_t::eBufferChanged;
     s->bufferData = nullptr;
 
-    mContainsBuffer = false;
     return bufferData;
 }
 
@@ -1495,7 +1485,6 @@
     if (buffer == nullptr) {
         s->what &= ~layer_state_t::eBufferChanged;
         s->bufferData = nullptr;
-        mContainsBuffer = false;
         return *this;
     }
 
@@ -1530,7 +1519,7 @@
                                        const std::vector<SurfaceControlStats>&) {},
                                     nullptr);
 
-    mContainsBuffer = true;
+    mMayContainBuffer = true;
     return *this;
 }
 
@@ -2025,7 +2014,6 @@
     s.layerStackSpaceRect = layerStackRect;
     s.orientedDisplaySpaceRect = displayRect;
     s.what |= DisplayState::eDisplayProjectionChanged;
-    mForceSynchronous = true; // TODO: do we actually still need this?
 }
 
 void SurfaceComposerClient::Transaction::setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height) {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 3c7b162..c8927ad 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -133,7 +133,7 @@
         eLayerOpaque = 0x02,         // SURFACE_OPAQUE
         eLayerSkipScreenshot = 0x40, // SKIP_SCREENSHOT
         eLayerSecure = 0x80,         // SECURE
-        // Queue up BufferStateLayer buffers instead of dropping the oldest buffer when this flag is
+        // Queue up layer buffers instead of dropping the oldest buffer when this flag is
         // set. This blocks the client until all the buffers have been presented. If the buffers
         // have presentation timestamps, then we may drop buffers.
         eEnableBackpressure = 0x100,       // ENABLE_BACKPRESSURE
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 8c47ebc..61d4714 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -403,14 +403,15 @@
 
         uint64_t mId;
 
-        uint32_t mForceSynchronous = 0;
         uint32_t mTransactionNestCount = 0;
         bool mAnimation = false;
         bool mEarlyWakeupStart = false;
         bool mEarlyWakeupEnd = false;
 
-        // Indicates that the Transaction contains a buffer that should be cached
-        bool mContainsBuffer = false;
+        // Indicates that the Transaction may contain buffers that should be cached. The reason this
+        // is only a guess is that buffers can be removed before cache is called. This is only a
+        // hint that at some point a buffer was added to this transaction before apply was called.
+        bool mMayContainBuffer = false;
 
         // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction
         // to be presented. When it is not possible to present at exactly that time, it will be
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 2b7483d..579b28e 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -87,6 +87,8 @@
             return "AMBIGUOUS_GESTURE";
         case MotionClassification::DEEP_PRESS:
             return "DEEP_PRESS";
+        case MotionClassification::TWO_FINGER_SWIPE:
+            return "TWO_FINGER_SWIPE";
     }
 }
 
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index 5990ee0..163a2fe 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -23,6 +23,8 @@
 
 namespace android {
 
+// clang-format off
+
 // NOTE: If you add a new keycode here you must also add it to several other files.
 //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
 #define KEYCODES_SEQUENCE \
@@ -333,7 +335,11 @@
     DEFINE_KEYCODE(DEMO_APP_4), \
     DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_DOWN), \
     DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_UP), \
-    DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_TOGGLE)
+    DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_TOGGLE), \
+    DEFINE_KEYCODE(STYLUS_BUTTON_PRIMARY), \
+    DEFINE_KEYCODE(STYLUS_BUTTON_SECONDARY), \
+    DEFINE_KEYCODE(STYLUS_BUTTON_TERTIARY), \
+    DEFINE_KEYCODE(STYLUS_BUTTON_TAIL)
 
 // NOTE: If you add a new axis here you must also add it to several other files.
 //       Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
@@ -387,7 +393,6 @@
     DEFINE_AXIS(GENERIC_15), \
     DEFINE_AXIS(GENERIC_16)
 
-
 // NOTE: If you add new LEDs here, you must also add them to Input.h
 #define LEDS_SEQUENCE \
     DEFINE_LED(NUM_LOCK), \
@@ -412,6 +417,8 @@
     DEFINE_FLAG(GESTURE), \
     DEFINE_FLAG(WAKE)
 
+// clang-format on
+
 // --- InputEventLookup ---
 const std::unordered_map<std::string, int> InputEventLookup::KEYCODES = {KEYCODES_SEQUENCE};
 
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 527a75b..4a4f734 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -55,6 +55,22 @@
 // Nanoseconds per milliseconds.
 static const nsecs_t NANOS_PER_MS = 1000000;
 
+// All axes supported for velocity tracking, mapped to their default strategies.
+// Although other strategies are available for testing and comparison purposes,
+// the default strategy is the one that applications will actually use.  Be very careful
+// when adjusting the default strategy because it can dramatically affect
+// (often in a bad way) the user experience.
+static const std::map<int32_t, VelocityTracker::Strategy> DEFAULT_STRATEGY_BY_AXIS =
+        {{AMOTION_EVENT_AXIS_X, VelocityTracker::Strategy::LSQ2},
+         {AMOTION_EVENT_AXIS_Y, VelocityTracker::Strategy::LSQ2},
+         {AMOTION_EVENT_AXIS_SCROLL, VelocityTracker::Strategy::IMPULSE}};
+
+// Axes specifying location on a 2D plane (i.e. X and Y).
+static const std::set<int32_t> PLANAR_AXES = {AMOTION_EVENT_AXIS_X, AMOTION_EVENT_AXIS_Y};
+
+// Axes whose motion values are differential values (i.e. deltas).
+static const std::set<int32_t> DIFFERENTIAL_AXES = {AMOTION_EVENT_AXIS_SCROLL};
+
 // Threshold for determining that a pointer has stopped moving.
 // Some input devices do not send ACTION_MOVE events in the case where a pointer has
 // stopped.  We need to detect this case so that we can accurately predict the
@@ -125,12 +141,6 @@
 
 // --- VelocityTracker ---
 
-const std::map<int32_t, VelocityTracker::Strategy> VelocityTracker::DEFAULT_STRATEGY_BY_AXIS =
-        {{AMOTION_EVENT_AXIS_X, VelocityTracker::Strategy::LSQ2},
-         {AMOTION_EVENT_AXIS_Y, VelocityTracker::Strategy::LSQ2}};
-
-const std::set<int32_t> VelocityTracker::PLANAR_AXES = {AMOTION_EVENT_AXIS_X, AMOTION_EVENT_AXIS_Y};
-
 VelocityTracker::VelocityTracker(const Strategy strategy)
       : mLastEventTime(0),
         mCurrentPointerIdBits(0),
@@ -141,11 +151,14 @@
 }
 
 void VelocityTracker::configureStrategy(int32_t axis) {
+    const bool isDifferentialAxis = DIFFERENTIAL_AXES.find(axis) != DIFFERENTIAL_AXES.end();
+
     std::unique_ptr<VelocityTrackerStrategy> createdStrategy;
     if (mOverrideStrategy != VelocityTracker::Strategy::DEFAULT) {
-        createdStrategy = createStrategy(mOverrideStrategy);
+        createdStrategy = createStrategy(mOverrideStrategy, isDifferentialAxis /* deltaValues */);
     } else {
-        createdStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY_BY_AXIS.at(axis));
+        createdStrategy = createStrategy(DEFAULT_STRATEGY_BY_AXIS.at(axis),
+                                         isDifferentialAxis /* deltaValues */);
     }
 
     LOG_ALWAYS_FATAL_IF(createdStrategy == nullptr,
@@ -154,11 +167,11 @@
 }
 
 std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy(
-        VelocityTracker::Strategy strategy) {
+        VelocityTracker::Strategy strategy, bool deltaValues) {
     switch (strategy) {
         case VelocityTracker::Strategy::IMPULSE:
             ALOGI_IF(DEBUG_STRATEGY, "Initializing impulse strategy");
-            return std::make_unique<ImpulseVelocityTrackerStrategy>();
+            return std::make_unique<ImpulseVelocityTrackerStrategy>(deltaValues);
 
         case VelocityTracker::Strategy::LSQ1:
             return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1);
@@ -283,9 +296,7 @@
     case AMOTION_EVENT_ACTION_HOVER_ENTER:
         // Clear all pointers on down before adding the new movement.
         clear();
-        for (int32_t axis : PLANAR_AXES) {
-            axesToProcess.insert(axis);
-        }
+        axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end());
         break;
     case AMOTION_EVENT_ACTION_POINTER_DOWN: {
         // Start a new movement trace for a pointer that just went down.
@@ -294,16 +305,12 @@
         BitSet32 downIdBits;
         downIdBits.markBit(event->getPointerId(event->getActionIndex()));
         clearPointers(downIdBits);
-        for (int32_t axis : PLANAR_AXES) {
-            axesToProcess.insert(axis);
-        }
+        axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end());
         break;
     }
     case AMOTION_EVENT_ACTION_MOVE:
     case AMOTION_EVENT_ACTION_HOVER_MOVE:
-        for (int32_t axis : PLANAR_AXES) {
-            axesToProcess.insert(axis);
-        }
+        axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end());
         break;
     case AMOTION_EVENT_ACTION_POINTER_UP:
     case AMOTION_EVENT_ACTION_UP: {
@@ -328,6 +335,9 @@
         // before adding the movement.
         return;
     }
+    case AMOTION_EVENT_ACTION_SCROLL:
+        axesToProcess.insert(AMOTION_EVENT_AXIS_SCROLL);
+        break;
     default:
         // Ignore all other actions.
         return;
@@ -1004,7 +1014,8 @@
 
 // --- ImpulseVelocityTrackerStrategy ---
 
-ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy() : mIndex(0) {}
+ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy(bool deltaValues)
+      : mDeltaValues(deltaValues), mIndex(0) {}
 
 ImpulseVelocityTrackerStrategy::~ImpulseVelocityTrackerStrategy() {
 }
@@ -1112,7 +1123,8 @@
     return (work < 0 ? -1.0 : 1.0) * sqrtf(fabsf(work)) * sqrt2;
 }
 
-static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t count) {
+static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t count,
+                                      bool deltaValues) {
     // The input should be in reversed time order (most recent sample at index i=0)
     // t[i] is in nanoseconds, but due to FP arithmetic, convert to seconds inside this function
     static constexpr float SECONDS_PER_NANO = 1E-9;
@@ -1123,12 +1135,26 @@
     if (t[1] > t[0]) { // Algorithm will still work, but not perfectly
         ALOGE("Samples provided to calculateImpulseVelocity in the wrong order");
     }
+
+    // If the data values are delta values, we do not have to calculate deltas here.
+    // We can use the delta values directly, along with the calculated time deltas.
+    // Since the data value input is in reversed time order:
+    //      [a] for non-delta inputs, instantenous velocity = (x[i] - x[i-1])/(t[i] - t[i-1])
+    //      [b] for delta inputs, instantenous velocity = -x[i-1]/(t[i] - t[i - 1])
+    // e.g., let the non-delta values are: V = [2, 3, 7], the equivalent deltas are D = [2, 1, 4].
+    // Since the input is in reversed time order, the input values for this function would be
+    // V'=[7, 3, 2] and D'=[4, 1, 2] for the non-delta and delta values, respectively.
+    //
+    // The equivalent of {(V'[2] - V'[1]) = 2 - 3 = -1} would be {-D'[1] = -1}
+    // Similarly, the equivalent of {(V'[1] - V'[0]) = 3 - 7 = -4} would be {-D'[0] = -4}
+
     if (count == 2) { // if 2 points, basic linear calculation
         if (t[1] == t[0]) {
             ALOGE("Events have identical time stamps t=%" PRId64 ", setting velocity = 0", t[0]);
             return 0;
         }
-        return (x[1] - x[0]) / (SECONDS_PER_NANO * (t[1] - t[0]));
+        const float deltaX = deltaValues ? -x[0] : x[1] - x[0];
+        return deltaX / (SECONDS_PER_NANO * (t[1] - t[0]));
     }
     // Guaranteed to have at least 3 points here
     float work = 0;
@@ -1138,7 +1164,8 @@
             continue;
         }
         float vprev = kineticEnergyToVelocity(work); // v[i-1]
-        float vcurr = (x[i] - x[i-1]) / (SECONDS_PER_NANO * (t[i] - t[i-1])); // v[i]
+        const float deltaX = deltaValues ? -x[i-1] : x[i] - x[i-1];
+        float vcurr = deltaX / (SECONDS_PER_NANO * (t[i] - t[i-1])); // v[i]
         work += (vcurr - vprev) * fabsf(vcurr);
         if (i == count - 1) {
             work *= 0.5; // initial condition, case 2) above
@@ -1177,7 +1204,7 @@
         return false; // no data
     }
     outEstimator->coeff[0] = 0;
-    outEstimator->coeff[1] = calculateImpulseVelocity(time, positions, m);
+    outEstimator->coeff[1] = calculateImpulseVelocity(time, positions, m, mDeltaValues);
     outEstimator->coeff[2] = 0;
 
     outEstimator->time = newestMovement.eventTime;
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 0b7def3..8b00b5c 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -87,7 +87,7 @@
     }
 };
 
-struct MotionEventEntry {
+struct PlanarMotionEventEntry {
     std::chrono::nanoseconds eventTime;
     std::vector<Position> positions;
 };
@@ -136,15 +136,43 @@
     return AMOTION_EVENT_ACTION_MOVE;
 }
 
-static std::vector<MotionEvent> createMotionEventStream(
-        const std::vector<MotionEventEntry>& motions) {
+static std::vector<MotionEvent> createAxisScrollMotionEventStream(
+        const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions) {
+    std::vector<MotionEvent> events;
+    for (const auto& [timeStamp, value] : motions) {
+        EXPECT_TRUE(!isnan(value)) << "The entry at pointerId must be valid";
+
+        PointerCoords coords[1];
+        coords[0].setAxisValue(AMOTION_EVENT_AXIS_SCROLL, value);
+
+        PointerProperties properties[1];
+        properties[0].id = DEFAULT_POINTER_ID;
+
+        MotionEvent event;
+        ui::Transform identityTransform;
+        event.initialize(InputEvent::nextId(), 5 /*deviceId*/, AINPUT_SOURCE_ROTARY_ENCODER,
+                         ADISPLAY_ID_NONE, INVALID_HMAC, AMOTION_EVENT_ACTION_SCROLL,
+                         0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE,
+                         0 /*buttonState*/, MotionClassification::NONE, identityTransform,
+                         0 /*xPrecision*/, 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                         AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/,
+                         timeStamp.count(), 1 /*pointerCount*/, properties, coords);
+
+        events.emplace_back(event);
+    }
+
+    return events;
+}
+
+static std::vector<MotionEvent> createTouchMotionEventStream(
+        const std::vector<PlanarMotionEventEntry>& motions) {
     if (motions.empty()) {
         ADD_FAILURE() << "Need at least 1 sample to create a MotionEvent. Received empty vector.";
     }
 
     std::vector<MotionEvent> events;
     for (size_t i = 0; i < motions.size(); i++) {
-        const MotionEventEntry& entry = motions[i];
+        const PlanarMotionEventEntry& entry = motions[i];
         BitSet32 pointers = getValidPointers(entry.positions);
         const uint32_t pointerCount = pointers.count();
 
@@ -155,8 +183,8 @@
         } else if ((i == motions.size() - 1) && pointerCount == 1) {
             action = AMOTION_EVENT_ACTION_UP;
         } else {
-            const MotionEventEntry& previousEntry = motions[i-1];
-            const MotionEventEntry& nextEntry = motions[i+1];
+            const PlanarMotionEventEntry& previousEntry = motions[i-1];
+            const PlanarMotionEventEntry& nextEntry = motions[i+1];
             action = resolveAction(previousEntry.positions, entry.positions, nextEntry.positions);
         }
 
@@ -196,11 +224,12 @@
 }
 
 static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy,
-                                    const std::vector<MotionEventEntry>& motions, int32_t axis,
-                                    float targetVelocity, uint32_t pointerId = DEFAULT_POINTER_ID) {
+                                    const std::vector<PlanarMotionEventEntry>& motions,
+                                    int32_t axis, float targetVelocity,
+                                    uint32_t pointerId = DEFAULT_POINTER_ID) {
     VelocityTracker vt(strategy);
 
-    std::vector<MotionEvent> events = createMotionEventStream(motions);
+    std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
     for (MotionEvent event : events) {
         vt.addMovement(&event);
     }
@@ -208,10 +237,33 @@
     checkVelocity(vt.getVelocity(axis, pointerId).value_or(0), targetVelocity);
 }
 
-static void computeAndCheckQuadraticEstimate(const std::vector<MotionEventEntry>& motions,
-        const std::array<float, 3>& coefficients) {
+static void computeAndCheckAxisScrollVelocity(
+        const VelocityTracker::Strategy strategy,
+        const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions,
+        std::optional<float> targetVelocity) {
+    VelocityTracker vt(strategy);
+
+    std::vector<MotionEvent> events = createAxisScrollMotionEventStream(motions);
+    for (const MotionEvent& event : events) {
+        vt.addMovement(&event);
+    }
+
+    std::optional<float> velocity = vt.getVelocity(AMOTION_EVENT_AXIS_SCROLL, DEFAULT_POINTER_ID);
+    if (velocity && !targetVelocity) {
+        FAIL() << "Expected no velocity, but found " << *velocity;
+    }
+    if (!velocity && targetVelocity) {
+        FAIL() << "Expected  velocity, but found no velocity";
+    }
+    if (velocity) {
+        checkVelocity(*velocity, *targetVelocity);
+    }
+}
+
+static void computeAndCheckQuadraticEstimate(const std::vector<PlanarMotionEventEntry>& motions,
+                                             const std::array<float, 3>& coefficients) {
     VelocityTracker vt(VelocityTracker::Strategy::LSQ2);
-    std::vector<MotionEvent> events = createMotionEventStream(motions);
+    std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
     for (MotionEvent event : events) {
         vt.addMovement(&event);
     }
@@ -269,7 +321,7 @@
 }
 
 TEST_F(VelocityTrackerTest, TestGetComputedVelocity) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
             {235089067457000ns, {{528.00, 0}}}, {235089084684000ns, {{527.00, 0}}},
             {235089093349000ns, {{527.00, 0}}}, {235089095677625ns, {{527.00, 0}}},
             {235089101859000ns, {{527.00, 0}}}, {235089110378000ns, {{528.00, 0}}},
@@ -281,7 +333,7 @@
             {235089162955851ns, {{560.66, 0}}}, // ACTION_UP
     };
     VelocityTracker vt(VelocityTracker::Strategy::IMPULSE);
-    std::vector<MotionEvent> events = createMotionEventStream(motions);
+    std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
     for (const MotionEvent& event : events) {
         vt.addMovement(&event);
     }
@@ -333,7 +385,7 @@
     // Same coordinate is reported 2 times in a row
     // It is difficult to determine the correct answer here, but at least the direction
     // of the reported velocity should be positive.
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
             {0ms, {{273, 0}}},
             {12585us, {{293, 0}}},
             {14730us, {{293, 0}}},
@@ -345,7 +397,7 @@
 
 TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) {
     // Same coordinate is reported 3 times in a row
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
             {0ms, {{293, 0}}},
             {6132us, {{293, 0}}},
             {11283us, {{293, 0}}},
@@ -357,7 +409,7 @@
 
 TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) {
     // Fixed velocity at 5 points per 10 milliseconds
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
             {0ms, {{0, 0}}}, {10ms, {{5, 0}}}, {20ms, {{10, 0}}}, {20ms, {{10, 0}}}, // ACTION_UP
     };
     computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 500);
@@ -381,7 +433,7 @@
 // --------------- Recorded by hand on swordfish ---------------------------------------------------
 TEST_F(VelocityTrackerTest, SwordfishFlingDown) {
     // Recording of a fling on Swordfish that could cause a fling in the wrong direction
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{271, 96}} },
         { 16071042ns, {{269.786346, 106.922775}} },
         { 35648403ns, {{267.983063, 156.660034}} },
@@ -416,7 +468,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpSlow1) {
     // Sailfish - fling up - slow - 1
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235089067457000ns, {{528.00, 983.00}} },
         { 235089084684000ns, {{527.00, 981.00}} },
         { 235089093349000ns, {{527.00, 977.00}} },
@@ -448,7 +500,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpSlow2) {
     // Sailfish - fling up - slow - 2
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235110560704000ns, {{522.00, 1107.00}} },
         { 235110575764000ns, {{522.00, 1107.00}} },
         { 235110584385000ns, {{522.00, 1107.00}} },
@@ -477,7 +529,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpSlow3) {
     // Sailfish - fling up - slow - 3
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 792536237000ns, {{580.00, 1317.00}} },
         { 792541538987ns, {{580.63, 1311.94}} },
         { 792544613000ns, {{581.00, 1309.00}} },
@@ -511,7 +563,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFaster1) {
     // Sailfish - fling up - faster - 1
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235160420675000ns, {{610.00, 1042.00}} },
         { 235160428220000ns, {{609.00, 1026.00}} },
         { 235160436544000ns, {{609.00, 1024.00}} },
@@ -545,7 +597,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFaster2) {
     // Sailfish - fling up - faster - 2
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 847153808000ns, {{576.00, 1264.00}} },
         { 847171174000ns, {{576.00, 1262.00}} },
         { 847179640000ns, {{576.00, 1257.00}} },
@@ -571,7 +623,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFaster3) {
     // Sailfish - fling up - faster - 3
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235200532789000ns, {{507.00, 1084.00}} },
         { 235200549221000ns, {{507.00, 1083.00}} },
         { 235200557841000ns, {{507.00, 1081.00}} },
@@ -597,7 +649,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFast1) {
     // Sailfish - fling up - fast - 1
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 920922149000ns, {{561.00, 1412.00}} },
         { 920930185000ns, {{559.00, 1377.00}} },
         { 920930262463ns, {{558.98, 1376.66}} },
@@ -626,7 +678,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFast2) {
     // Sailfish - fling up - fast - 2
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235247153233000ns, {{518.00, 1168.00}} },
         { 235247170452000ns, {{517.00, 1167.00}} },
         { 235247178908000ns, {{515.00, 1159.00}} },
@@ -649,7 +701,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingUpFast3) {
     // Sailfish - fling up - fast - 3
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235302568736000ns, {{529.00, 1167.00}} },
         { 235302576644000ns, {{523.00, 1140.00}} },
         { 235302579395063ns, {{520.91, 1130.61}} },
@@ -670,7 +722,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownSlow1) {
     // Sailfish - fling down - slow - 1
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235655749552755ns, {{582.00, 432.49}} },
         { 235655750638000ns, {{582.00, 433.00}} },
         { 235655758865000ns, {{582.00, 440.00}} },
@@ -704,7 +756,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownSlow2) {
     // Sailfish - fling down - slow - 2
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235671152083370ns, {{485.24, 558.28}} },
         { 235671154126000ns, {{485.00, 559.00}} },
         { 235671162497000ns, {{484.00, 566.00}} },
@@ -738,7 +790,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownSlow3) {
     // Sailfish - fling down - slow - 3
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 170983201000ns, {{557.00, 533.00}} },
         { 171000668000ns, {{556.00, 534.00}} },
         { 171007359750ns, {{554.73, 535.27}} },
@@ -765,7 +817,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFaster1) {
     // Sailfish - fling down - faster - 1
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235695280333000ns, {{558.00, 451.00}} },
         { 235695283971237ns, {{558.43, 454.45}} },
         { 235695289038000ns, {{559.00, 462.00}} },
@@ -795,7 +847,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFaster2) {
     // Sailfish - fling down - faster - 2
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235709624766000ns, {{535.00, 579.00}} },
         { 235709642256000ns, {{534.00, 580.00}} },
         { 235709643350278ns, {{533.94, 580.06}} },
@@ -826,7 +878,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFaster3) {
     // Sailfish - fling down - faster - 3
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235727628927000ns, {{540.00, 440.00}} },
         { 235727636810000ns, {{537.00, 454.00}} },
         { 235727646176000ns, {{536.00, 454.00}} },
@@ -855,7 +907,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFast1) {
     // Sailfish - fling down - fast - 1
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235762352849000ns, {{467.00, 286.00}} },
         { 235762360250000ns, {{443.00, 344.00}} },
         { 235762362787412ns, {{434.77, 363.89}} },
@@ -876,7 +928,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFast2) {
     // Sailfish - fling down - fast - 2
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 235772487188000ns, {{576.00, 204.00}} },
         { 235772495159000ns, {{553.00, 236.00}} },
         { 235772503568000ns, {{551.00, 240.00}} },
@@ -897,7 +949,7 @@
 
 TEST_F(VelocityTrackerTest, SailfishFlingDownFast3) {
     // Sailfish - fling down - fast - 3
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 507650295000ns, {{628.00, 233.00}} },
         { 507658234000ns, {{605.00, 269.00}} },
         { 507666784000ns, {{601.00, 274.00}} },
@@ -928,7 +980,7 @@
  * part of the fitted data), this can cause large velocity values to be reported instead.
  */
 TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_ThreeFingerTap) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 0us,      {{1063, 1128}, {NAN, NAN}, {NAN, NAN}} },
         { 10800us,  {{1063, 1128}, {682, 1318}, {NAN, NAN}} }, // POINTER_DOWN
         { 10800us,  {{1063, 1128}, {682, 1318}, {397, 1747}} }, // POINTER_DOWN
@@ -955,7 +1007,7 @@
  * affected by the liftoff.
  */
 TEST_F(VelocityTrackerTest, ShortDelayBeforeActionUp) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
             {0ms, {{10, 0}}}, {10ms, {{20, 0}}}, {20ms, {{30, 0}}}, {30ms, {{30, 0}}}, // ACTION_UP
     };
     computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
@@ -969,7 +1021,7 @@
  * should be assumed to have stopped.
  */
 TEST_F(VelocityTrackerTest, LongDelayBeforeActionUp) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
             {0ms, {{10, 0}}},
             {10ms, {{20, 0}}},
             {20ms, {{30, 0}}},
@@ -985,7 +1037,7 @@
  * The final velocity should be reported as zero for all pointers.
  */
 TEST_F(VelocityTrackerTest, LongDelayBeforeActionPointerUp) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
             {0ms, {{10, 0}}},
             {10ms, {{20, 0}, {100, 0}}},
             {20ms, {{30, 0}, {200, 0}}},
@@ -1029,7 +1081,7 @@
  * In the test, we would convert these coefficients to (0*(1E3)^0, 0*(1E3)^1, 1*(1E3)^2).
  */
 TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Constant) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{1, 1}} }, // 0 s
         { 1ms, {{1, 1}} }, // 0.001 s
         { 2ms, {{1, 1}} }, // 0.002 s
@@ -1047,7 +1099,7 @@
  * Straight line y = x :: the constant and quadratic coefficients are zero.
  */
 TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Linear) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{-2, -2}} },
         { 1ms, {{-1, -1}} },
         { 2ms, {{-0, -0}} },
@@ -1065,7 +1117,7 @@
  * Parabola
  */
 TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{1, 1}} },
         { 1ms, {{4, 4}} },
         { 2ms, {{8, 8}} },
@@ -1083,7 +1135,7 @@
  * Parabola
  */
 TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic2) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{1, 1}} },
         { 1ms, {{4, 4}} },
         { 2ms, {{9, 9}} },
@@ -1101,7 +1153,7 @@
  * Parabola :: y = x^2 :: the constant and linear coefficients are zero.
  */
 TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic3) {
-    std::vector<MotionEventEntry> motions = {
+    std::vector<PlanarMotionEventEntry> motions = {
         { 0ms, {{4, 4}} },
         { 1ms, {{1, 1}} },
         { 2ms, {{0, 0}} },
@@ -1115,4 +1167,100 @@
     computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({0, 0E3, 1E6}));
 }
 
+// Recorded by hand on sailfish, but only the diffs are taken to test cumulative axis velocity.
+TEST_F(VelocityTrackerTest, AxisScrollVelocity) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {
+            {235089067457000ns, 0.00}, {235089084684000ns, -1.00}, {235089093349000ns, 0.00},
+            {235089095677625ns, 0.00}, {235089101859000ns, 0.00},  {235089110378000ns, 0.00},
+            {235089112497111ns, 0.25}, {235089118760000ns, 1.75},  {235089126686000ns, 4.00},
+            {235089129316820ns, 1.33}, {235089135199000ns, 3.67},  {235089144297000ns, 6.00},
+            {235089146136443ns, 1.21}, {235089152923000ns, 5.79},  {235089160784000ns, 6.00},
+            {235089162955851ns, 1.66},
+    };
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {764.345703});
+}
+
+// --------------- Recorded by hand on a Wear OS device using a rotating side button ---------------
+TEST_F(VelocityTrackerTest, AxisScrollVelocity_ScrollDown) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {
+            {224598065152ns, -0.050100}, {224621871104ns, -0.133600}, {224645464064ns, -0.551100},
+            {224669171712ns, -0.801600}, {224687063040ns, -1.035400}, {224706691072ns, -0.484300},
+            {224738213888ns, -0.334000}, {224754401280ns, -0.083500},
+    };
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {-27.86});
+}
+
+TEST_F(VelocityTrackerTest, AxisScrollVelocity_ScrollUp) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {
+            {269606010880ns, 0.050100}, {269626064896ns, 0.217100}, {269641973760ns, 0.267200},
+            {269658079232ns, 0.267200}, {269674217472ns, 0.267200}, {269690683392ns, 0.367400},
+            {269706133504ns, 0.551100}, {269722173440ns, 0.501000},
+    };
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {31.92});
+}
+
+TEST_F(VelocityTrackerTest, AxisScrollVelocity_ScrollDown_ThenUp_ThenDown) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {
+            {2580534001664ns, -0.033400}, {2580549992448ns, -0.133600},
+            {2580566769664ns, -0.250500}, {2580581974016ns, -0.183700},
+            {2580597964800ns, -0.267200}, {2580613955584ns, -0.551100},
+            {2580635189248ns, -0.601200}, {2580661927936ns, -0.450900},
+            {2580683161600ns, -0.417500}, {2580705705984ns, -0.150300},
+            {2580722745344ns, -0.016700}, {2580786446336ns, 0.050100},
+            {2580801912832ns, 0.150300},  {2580822360064ns, 0.300600},
+            {2580838088704ns, 0.300600},  {2580854341632ns, 0.400800},
+            {2580869808128ns, 0.517700},  {2580886061056ns, 0.501000},
+            {2580905984000ns, 0.350700},  {2580921974784ns, 0.350700},
+            {2580937965568ns, 0.066800},  {2580974665728ns, 0.016700},
+            {2581034434560ns, -0.066800}, {2581049901056ns, -0.116900},
+            {2581070610432ns, -0.317300}, {2581086076928ns, -0.200400},
+            {2581101805568ns, -0.233800}, {2581118058496ns, -0.417500},
+            {2581134049280ns, -0.417500}, {2581150040064ns, -0.367400},
+            {2581166030848ns, -0.267200}, {2581181759488ns, -0.150300},
+            {2581199847424ns, -0.066800},
+    };
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {-9.73});
+}
+
+// ------------------------------- Hand generated test cases ---------------------------------------
+TEST_F(VelocityTrackerTest, AxisScrollVelocity_SimilarDifferentialValues) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {{1ns, 2.12},  {3ns, 2.12},
+                                                                       {7ns, 2.12},  {8ns, 2.12},
+                                                                       {15ns, 2.12}, {18ns, 2.12}};
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {1690236059.86});
+}
+
+TEST_F(VelocityTrackerTest, AxisScrollVelocity_OnlyTwoValues) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {{1ms, 5}, {2ms, 10}};
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {10000});
+}
+
+TEST_F(VelocityTrackerTest, AxisScrollVelocity_ConstantVelocity) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {{1ms, 20}, {2ms, 20},
+                                                                       {3ms, 20}, {4ms, 20},
+                                                                       {5ms, 20}, {6ms, 20}};
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {20000});
+}
+
+TEST_F(VelocityTrackerTest, AxisScrollVelocity_NoMotion) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {{1ns, 0}, {2ns, 0},
+                                                                       {3ns, 0}, {4ns, 0},
+                                                                       {5ns, 0}, {6ns, 0}};
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, {0});
+}
+
+TEST_F(VelocityTrackerTest, AxisScrollVelocity_NoData) {
+    std::vector<std::pair<std::chrono::nanoseconds, float>> motions = {};
+
+    computeAndCheckAxisScrollVelocity(VelocityTracker::Strategy::IMPULSE, motions, std::nullopt);
+}
+
 } // namespace android
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index fbfecf6..3503a9e 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -61,6 +61,9 @@
 
     // Android O
     first_version: "26",
+    export_header_libs: [
+        "libnativewindow_ndk_headers",
+    ],
 }
 
 cc_library {
diff --git a/libs/sensor/fuzz/bittube_fuzzer/bittube_fuzzer.cpp b/libs/sensor/fuzz/bittube_fuzzer/bittube_fuzzer.cpp
index 6f10a67..6a61d36 100644
--- a/libs/sensor/fuzz/bittube_fuzzer/bittube_fuzzer.cpp
+++ b/libs/sensor/fuzz/bittube_fuzzer/bittube_fuzzer.cpp
@@ -24,14 +24,14 @@
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     FuzzedDataProvider fdp(data, size);
-    BitTube bittube(size);
+    sp<BitTube> bittube(new BitTube(size));
     Parcel parcel[5];
-    bittube.writeToParcel(parcel);
+    bittube->writeToParcel(parcel);
     sp<BitTube> tube(new BitTube(size));
-    bittube.sendObjects<uint8_t>(tube, data, size);
+    bittube->sendObjects<uint8_t>(tube, data, size);
     uint8_t recvData[size];
     for (int i = 0; i < size; i++) recvData[i] = fdp.ConsumeIntegral<uint8_t>();
-    bittube.recvObjects<uint8_t>(tube, recvData, size);
+    bittube->recvObjects<uint8_t>(tube, recvData, size);
 
     return 0;
 }
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index 57be686..dbe475b 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -270,7 +270,7 @@
 
     // Send a callback when a GraphicBuffer dies.
     //
-    // This is used for BufferStateLayer caching. GraphicBuffers are refcounted per process. When
+    // This is used for layer caching. GraphicBuffers are refcounted per process. When
     // A GraphicBuffer doesn't have any more sp<> in a process, it is destroyed. This causes
     // problems when trying to implicitcly cache across process boundaries. Ideally, both sides
     // of the cache would hold onto wp<> references. When an app dropped its sp<>, the GraphicBuffer
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index c1e935a..62cf255 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -12,7 +12,10 @@
     name: "libETC1",
     srcs: ["ETC1/etc1.cpp"],
     host_supported: true,
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 
     target: {
         android: {
@@ -37,6 +40,9 @@
     symbol_file: "libEGL.map.txt",
     first_version: "9",
     unversioned_until: "current",
+    export_header_libs: [
+        "libEGL_headers",
+    ],
 }
 
 ndk_library {
@@ -44,6 +50,9 @@
     symbol_file: "libGLESv1_CM.map.txt",
     first_version: "9",
     unversioned_until: "current",
+    export_header_libs: [
+        "libGLESv1_CM_headers",
+    ],
 }
 
 ndk_library {
@@ -51,6 +60,9 @@
     symbol_file: "libGLESv2.map.txt",
     first_version: "9",
     unversioned_until: "current",
+    export_header_libs: [
+        "libGLESv2_headers",
+    ],
 }
 
 ndk_library {
@@ -58,6 +70,9 @@
     symbol_file: "libGLESv3.map.txt",
     first_version: "18",
     unversioned_until: "current",
+    export_header_libs: [
+        "libGLESv3_headers",
+    ],
 }
 
 cc_defaults {
@@ -170,7 +185,11 @@
         "libEGL_getProcAddress",
         "libEGL_blobCache",
     ],
-    ldflags: ["-Wl,--exclude-libs=ALL,--Bsymbolic-functions"],
+    ldflags: [
+        "-Wl,--exclude-libs=libEGL_getProcAddress.a",
+        "-Wl,--exclude-libs=libEGL_blobCache.a",
+        "-Wl,--Bsymbolic-functions",
+    ],
     export_include_dirs: ["EGL/include"],
     stubs: {
         symbol_file: "libEGL.map.txt",
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index efa67db..8348d6c 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -28,7 +28,7 @@
 // Cache size limits.
 static const size_t maxKeySize = 12 * 1024;
 static const size_t maxValueSize = 64 * 1024;
-static const size_t maxTotalSize = 2 * 1024 * 1024;
+static const size_t maxTotalSize = 32 * 1024 * 1024;
 
 // The time in seconds to wait before saving newly inserted cache entries.
 static const unsigned int deferredSaveDelay = 4;
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 5b286dc..ddcd51f 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -85,12 +85,12 @@
         "libstatspull",
         "libstatssocket",
         "libutils",
-        "libui",
         "server_configurable_flags",
     ],
     static_libs: [
         "libattestation",
         "libpalmrejection",
+        "libui-types",
     ],
 }
 
@@ -139,6 +139,7 @@
         "InputListener.cpp",
         "InputReaderBase.cpp",
         "InputThread.cpp",
+        "NotifyArgs.cpp",
         "VibrationElement.cpp",
     ],
 }
@@ -152,7 +153,6 @@
         "libcutils",
         "libinput",
         "liblog",
-        "libui",
         "libutils",
     ],
     header_libs: [
@@ -191,7 +191,16 @@
         "libinputservice_test",
         "Bug-115739809",
         "StructLayout_test",
+
+        // native fuzzers
         "inputflinger_latencytracker_fuzzer",
+        "inputflinger_cursor_input_fuzzer",
+        "inputflinger_keyboard_input_fuzzer",
+        "inputflinger_multitouch_input_fuzzer",
+        "inputflinger_switch_input_fuzzer",
+        "inputflinger_input_reader_fuzzer",
+        "inputflinger_blocking_queue_fuzzer",
+        "inputflinger_input_classifier_fuzzer",
 
         // Java/Kotlin targets
         "CtsWindowManagerDeviceTestCases",
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index dce327e..0972e22 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -31,308 +31,29 @@
 
 namespace android {
 
-// --- NotifyConfigurationChangedArgs ---
+// --- InputListenerInterface ---
 
-NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime)
-      : NotifyArgs(id, eventTime) {}
+// Helper to std::visit with lambdas.
+template <typename... V>
+struct Visitor : V... {};
+// explicit deduction guide (not needed as of C++20)
+template <typename... V>
+Visitor(V...) -> Visitor<V...>;
 
-NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(
-        const NotifyConfigurationChangedArgs& other)
-      : NotifyArgs(other.id, other.eventTime) {}
-
-bool NotifyConfigurationChangedArgs::operator==(const NotifyConfigurationChangedArgs& rhs) const {
-    return id == rhs.id && eventTime == rhs.eventTime;
-}
-
-void NotifyConfigurationChangedArgs::notify(InputListenerInterface& listener) const {
-    listener.notifyConfigurationChanged(this);
-}
-
-// --- NotifyKeyArgs ---
-
-NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, 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)
-      : NotifyArgs(id, eventTime),
-        deviceId(deviceId),
-        source(source),
-        displayId(displayId),
-        policyFlags(policyFlags),
-        action(action),
-        flags(flags),
-        keyCode(keyCode),
-        scanCode(scanCode),
-        metaState(metaState),
-        downTime(downTime),
-        readTime(readTime) {}
-
-NotifyKeyArgs::NotifyKeyArgs(const NotifyKeyArgs& other)
-      : NotifyArgs(other.id, other.eventTime),
-        deviceId(other.deviceId),
-        source(other.source),
-        displayId(other.displayId),
-        policyFlags(other.policyFlags),
-        action(other.action),
-        flags(other.flags),
-        keyCode(other.keyCode),
-        scanCode(other.scanCode),
-        metaState(other.metaState),
-        downTime(other.downTime),
-        readTime(other.readTime) {}
-
-bool NotifyKeyArgs::operator==(const NotifyKeyArgs& rhs) const {
-    return id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
-            deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId &&
-            policyFlags == rhs.policyFlags && action == rhs.action && flags == rhs.flags &&
-            keyCode == rhs.keyCode && scanCode == rhs.scanCode && metaState == rhs.metaState &&
-            downTime == rhs.downTime;
-}
-
-void NotifyKeyArgs::notify(InputListenerInterface& listener) const {
-    listener.notifyKey(this);
-}
-
-// --- NotifyMotionArgs ---
-
-NotifyMotionArgs::NotifyMotionArgs(
-        int32_t id, nsecs_t eventTime, nsecs_t readTime, 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)
-      : NotifyArgs(id, eventTime),
-        deviceId(deviceId),
-        source(source),
-        displayId(displayId),
-        policyFlags(policyFlags),
-        action(action),
-        actionButton(actionButton),
-        flags(flags),
-        metaState(metaState),
-        buttonState(buttonState),
-        classification(classification),
-        edgeFlags(edgeFlags),
-        pointerCount(pointerCount),
-        xPrecision(xPrecision),
-        yPrecision(yPrecision),
-        xCursorPosition(xCursorPosition),
-        yCursorPosition(yCursorPosition),
-        downTime(downTime),
-        readTime(readTime),
-        videoFrames(videoFrames) {
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        this->pointerProperties[i].copyFrom(pointerProperties[i]);
-        this->pointerCoords[i].copyFrom(pointerCoords[i]);
-    }
-}
-
-NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other)
-      : NotifyArgs(other.id, other.eventTime),
-        deviceId(other.deviceId),
-        source(other.source),
-        displayId(other.displayId),
-        policyFlags(other.policyFlags),
-        action(other.action),
-        actionButton(other.actionButton),
-        flags(other.flags),
-        metaState(other.metaState),
-        buttonState(other.buttonState),
-        classification(other.classification),
-        edgeFlags(other.edgeFlags),
-        pointerCount(other.pointerCount),
-        xPrecision(other.xPrecision),
-        yPrecision(other.yPrecision),
-        xCursorPosition(other.xCursorPosition),
-        yCursorPosition(other.yCursorPosition),
-        downTime(other.downTime),
-        readTime(other.readTime),
-        videoFrames(other.videoFrames) {
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        pointerProperties[i].copyFrom(other.pointerProperties[i]);
-        pointerCoords[i].copyFrom(other.pointerCoords[i]);
-    }
-}
-
-static inline bool isCursorPositionEqual(float lhs, float rhs) {
-    return (isnan(lhs) && isnan(rhs)) || lhs == rhs;
-}
-
-bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const {
-    bool equal = id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
-            deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId &&
-            policyFlags == rhs.policyFlags && action == rhs.action &&
-            actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState &&
-            buttonState == rhs.buttonState && classification == rhs.classification &&
-            edgeFlags == rhs.edgeFlags &&
-            pointerCount == rhs.pointerCount
-            // PointerProperties and PointerCoords are compared separately below
-            && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision &&
-            isCursorPositionEqual(xCursorPosition, rhs.xCursorPosition) &&
-            isCursorPositionEqual(yCursorPosition, rhs.yCursorPosition) &&
-            downTime == rhs.downTime && videoFrames == rhs.videoFrames;
-    if (!equal) {
-        return false;
-    }
-
-    for (size_t i = 0; i < pointerCount; i++) {
-        equal =
-                pointerProperties[i] == rhs.pointerProperties[i]
-                && pointerCoords[i] == rhs.pointerCoords[i];
-        if (!equal) {
-            return false;
-        }
-    }
-    return true;
-}
-
-std::string NotifyMotionArgs::dump() const {
-    std::string coords;
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        if (!coords.empty()) {
-            coords += ", ";
-        }
-        coords += StringPrintf("{%" PRIu32 ": ", i);
-        coords +=
-                StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f pressure=%.1f", pointerProperties[i].id,
-                             pointerCoords[i].getX(), pointerCoords[i].getY(),
-                             pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
-        const int32_t toolType = pointerProperties[i].toolType;
-        if (toolType != AMOTION_EVENT_TOOL_TYPE_FINGER) {
-            coords += StringPrintf(" toolType=%s", motionToolTypeToString(toolType));
-        }
-        const float major = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
-        const float minor = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
-        const float orientation = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
-        if (major != 0 || minor != 0) {
-            coords += StringPrintf(" major=%.1f minor=%.1f orientation=%.1f", major, minor,
-                                   orientation);
-        }
-        coords += "}";
-    }
-    return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32
-                        ", source=%s, action=%s, pointerCount=%" PRIu32
-                        " pointers=%s, flags=0x%08x)",
-                        id, eventTime, deviceId, inputEventSourceToString(source).c_str(),
-                        MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str(),
-                        flags);
-}
-
-void NotifyMotionArgs::notify(InputListenerInterface& listener) const {
-    listener.notifyMotion(this);
-}
-
-// --- NotifySwitchArgs ---
-
-NotifySwitchArgs::NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags,
-                                   uint32_t switchValues, uint32_t switchMask)
-      : NotifyArgs(id, eventTime),
-        policyFlags(policyFlags),
-        switchValues(switchValues),
-        switchMask(switchMask) {}
-
-NotifySwitchArgs::NotifySwitchArgs(const NotifySwitchArgs& other)
-      : NotifyArgs(other.id, other.eventTime),
-        policyFlags(other.policyFlags),
-        switchValues(other.switchValues),
-        switchMask(other.switchMask) {}
-
-bool NotifySwitchArgs::operator==(const NotifySwitchArgs rhs) const {
-    return id == rhs.id && eventTime == rhs.eventTime && policyFlags == rhs.policyFlags &&
-            switchValues == rhs.switchValues && switchMask == rhs.switchMask;
-}
-
-void NotifySwitchArgs::notify(InputListenerInterface& listener) const {
-    listener.notifySwitch(this);
-}
-
-// --- NotifySensorArgs ---
-
-NotifySensorArgs::NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
-                                   InputDeviceSensorType sensorType,
-                                   InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
-                                   nsecs_t hwTimestamp, std::vector<float> values)
-      : NotifyArgs(id, eventTime),
-        deviceId(deviceId),
-        source(source),
-        sensorType(sensorType),
-        accuracy(accuracy),
-        accuracyChanged(accuracyChanged),
-        hwTimestamp(hwTimestamp),
-        values(std::move(values)) {}
-
-NotifySensorArgs::NotifySensorArgs(const NotifySensorArgs& other)
-      : NotifyArgs(other.id, other.eventTime),
-        deviceId(other.deviceId),
-        source(other.source),
-        sensorType(other.sensorType),
-        accuracy(other.accuracy),
-        accuracyChanged(other.accuracyChanged),
-        hwTimestamp(other.hwTimestamp),
-        values(other.values) {}
-
-bool NotifySensorArgs::operator==(const NotifySensorArgs rhs) const {
-    return id == rhs.id && eventTime == rhs.eventTime && sensorType == rhs.sensorType &&
-            accuracy == rhs.accuracy && accuracyChanged == rhs.accuracyChanged &&
-            hwTimestamp == rhs.hwTimestamp && values == rhs.values;
-}
-
-void NotifySensorArgs::notify(InputListenerInterface& listener) const {
-    listener.notifySensor(this);
-}
-
-// --- NotifyVibratorStateArgs ---
-
-NotifyVibratorStateArgs::NotifyVibratorStateArgs(int32_t id, nsecs_t eventTime, int32_t deviceId,
-                                                 bool isOn)
-      : NotifyArgs(id, eventTime), deviceId(deviceId), isOn(isOn) {}
-
-NotifyVibratorStateArgs::NotifyVibratorStateArgs(const NotifyVibratorStateArgs& other)
-      : NotifyArgs(other.id, other.eventTime), deviceId(other.deviceId), isOn(other.isOn) {}
-
-bool NotifyVibratorStateArgs::operator==(const NotifyVibratorStateArgs rhs) const {
-    return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId &&
-            isOn == rhs.isOn;
-}
-
-void NotifyVibratorStateArgs::notify(InputListenerInterface& listener) const {
-    listener.notifyVibratorState(this);
-}
-
-// --- NotifyDeviceResetArgs ---
-
-NotifyDeviceResetArgs::NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId)
-      : NotifyArgs(id, eventTime), deviceId(deviceId) {}
-
-NotifyDeviceResetArgs::NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other)
-      : NotifyArgs(other.id, other.eventTime), deviceId(other.deviceId) {}
-
-bool NotifyDeviceResetArgs::operator==(const NotifyDeviceResetArgs& rhs) const {
-    return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId;
-}
-
-void NotifyDeviceResetArgs::notify(InputListenerInterface& listener) const {
-    listener.notifyDeviceReset(this);
-}
-
-// --- NotifyPointerCaptureChangedArgs ---
-
-NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
-        int32_t id, nsecs_t eventTime, const PointerCaptureRequest& request)
-      : NotifyArgs(id, eventTime), request(request) {}
-
-NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
-        const NotifyPointerCaptureChangedArgs& other)
-      : NotifyArgs(other.id, other.eventTime), request(other.request) {}
-
-bool NotifyPointerCaptureChangedArgs::operator==(const NotifyPointerCaptureChangedArgs& rhs) const {
-    return id == rhs.id && eventTime == rhs.eventTime && request == rhs.request;
-}
-
-void NotifyPointerCaptureChangedArgs::notify(InputListenerInterface& listener) const {
-    listener.notifyPointerCaptureChanged(this);
+void InputListenerInterface::notify(const NotifyArgs& generalArgs) {
+    Visitor v{
+            [&](const NotifyConfigurationChangedArgs& args) { notifyConfigurationChanged(&args); },
+            [&](const NotifyKeyArgs& args) { notifyKey(&args); },
+            [&](const NotifyMotionArgs& args) { notifyMotion(&args); },
+            [&](const NotifySwitchArgs& args) { notifySwitch(&args); },
+            [&](const NotifySensorArgs& args) { notifySensor(&args); },
+            [&](const NotifyVibratorStateArgs& args) { notifyVibratorState(&args); },
+            [&](const NotifyDeviceResetArgs& args) { notifyDeviceReset(&args); },
+            [&](const NotifyPointerCaptureChangedArgs& args) {
+                notifyPointerCaptureChanged(&args);
+            },
+    };
+    std::visit(v, generalArgs);
 }
 
 // --- QueuedInputListener ---
@@ -350,47 +71,47 @@
 void QueuedInputListener::notifyConfigurationChanged(
         const NotifyConfigurationChangedArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(std::make_unique<NotifyConfigurationChangedArgs>(*args));
+    mArgsQueue.emplace_back(*args);
 }
 
 void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(std::make_unique<NotifyKeyArgs>(*args));
+    mArgsQueue.emplace_back(*args);
 }
 
 void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(std::make_unique<NotifyMotionArgs>(*args));
+    mArgsQueue.emplace_back(*args);
 }
 
 void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(std::make_unique<NotifySwitchArgs>(*args));
+    mArgsQueue.emplace_back(*args);
 }
 
 void QueuedInputListener::notifySensor(const NotifySensorArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(std::make_unique<NotifySensorArgs>(*args));
+    mArgsQueue.emplace_back(*args);
 }
 
 void QueuedInputListener::notifyVibratorState(const NotifyVibratorStateArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(std::make_unique<NotifyVibratorStateArgs>(*args));
+    mArgsQueue.emplace_back(*args);
 }
 
 void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(std::make_unique<NotifyDeviceResetArgs>(*args));
+    mArgsQueue.emplace_back(*args);
 }
 
 void QueuedInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
     traceEvent(__func__, args->id);
-    mArgsQueue.emplace_back(std::make_unique<NotifyPointerCaptureChangedArgs>(*args));
+    mArgsQueue.emplace_back(*args);
 }
 
 void QueuedInputListener::flush() {
-    for (const std::unique_ptr<NotifyArgs>& args : mArgsQueue) {
-        args->notify(mInnerListener);
+    for (const NotifyArgs& args : mArgsQueue) {
+        mInnerListener.notify(args);
     }
     mArgsQueue.clear();
 }
diff --git a/services/inputflinger/InputProcessor.cpp b/services/inputflinger/InputProcessor.cpp
index 0749441..02d62bf 100644
--- a/services/inputflinger/InputProcessor.cpp
+++ b/services/inputflinger/InputProcessor.cpp
@@ -22,6 +22,7 @@
 #include <android-base/stringprintf.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
+#include <input/Input.h>
 #include <inttypes.h>
 #include <log/log.h>
 #include <algorithm>
@@ -123,40 +124,38 @@
 
 // --- ClassifierEvent ---
 
-ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args)
-      : type(ClassifierEventType::MOTION), args(std::move(args)){};
-ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args)
-      : type(ClassifierEventType::DEVICE_RESET), args(std::move(args)){};
-ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args)
-      : type(type), args(std::move(args)){};
+ClassifierEvent::ClassifierEvent(const NotifyMotionArgs& args)
+      : type(ClassifierEventType::MOTION), args(args){};
 
-ClassifierEvent::ClassifierEvent(ClassifierEvent&& other)
-      : type(other.type), args(std::move(other.args)){};
+ClassifierEvent::ClassifierEvent(const NotifyDeviceResetArgs& args)
+      : type(ClassifierEventType::DEVICE_RESET), args(args){};
+
+ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::optional<NotifyArgs> args)
+      : type(type), args(args){};
 
 ClassifierEvent& ClassifierEvent::operator=(ClassifierEvent&& other) {
     type = other.type;
-    args = std::move(other.args);
+    args = other.args;
     return *this;
 }
 
 ClassifierEvent ClassifierEvent::createHalResetEvent() {
-    return ClassifierEvent(ClassifierEventType::HAL_RESET, nullptr);
+    return ClassifierEvent(ClassifierEventType::HAL_RESET, std::nullopt);
 }
 
 ClassifierEvent ClassifierEvent::createExitEvent() {
-    return ClassifierEvent(ClassifierEventType::EXIT, nullptr);
+    return ClassifierEvent(ClassifierEventType::EXIT, std::nullopt);
 }
 
 std::optional<int32_t> ClassifierEvent::getDeviceId() const {
     switch (type) {
         case ClassifierEventType::MOTION: {
-            NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(args.get());
-            return motionArgs->deviceId;
+            const NotifyMotionArgs& motionArgs = std::get<NotifyMotionArgs>(*args);
+            return motionArgs.deviceId;
         }
         case ClassifierEventType::DEVICE_RESET: {
-            NotifyDeviceResetArgs* deviceResetArgs =
-                    static_cast<NotifyDeviceResetArgs*>(args.get());
-            return deviceResetArgs->deviceId;
+            const NotifyDeviceResetArgs& deviceResetArgs = std::get<NotifyDeviceResetArgs>(*args);
+            return deviceResetArgs.deviceId;
         }
         case ClassifierEventType::HAL_RESET: {
             return std::nullopt;
@@ -212,12 +211,12 @@
         bool halResponseOk = true;
         switch (event.type) {
             case ClassifierEventType::MOTION: {
-                NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get());
-                common::MotionEvent motionEvent = notifyMotionArgsToHalMotionEvent(*motionArgs);
+                NotifyMotionArgs& motionArgs = std::get<NotifyMotionArgs>(*event.args);
+                common::MotionEvent motionEvent = notifyMotionArgsToHalMotionEvent(motionArgs);
                 common::Classification classification;
                 ndk::ScopedAStatus response = mService->classify(motionEvent, &classification);
                 if (response.isOk()) {
-                    updateClassification(motionArgs->deviceId, motionArgs->eventTime,
+                    updateClassification(motionArgs.deviceId, motionArgs.eventTime,
                                          getMotionClassification(classification));
                 }
                 break;
@@ -307,8 +306,7 @@
         updateLastDownTime(args.deviceId, args.downTime);
     }
 
-    ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args));
-    enqueueEvent(std::move(event));
+    enqueueEvent(args);
     return getClassification(args.deviceId);
 }
 
@@ -328,7 +326,7 @@
         std::optional<int32_t> eventDeviceId = event.getDeviceId();
         return eventDeviceId && (*eventDeviceId == deviceId);
     });
-    enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args));
+    enqueueEvent(args);
 }
 
 void MotionClassifier::dump(std::string& dump) {
@@ -436,7 +434,13 @@
             mQueuedListener.notifyMotion(args);
         } else {
             NotifyMotionArgs newArgs(*args);
-            newArgs.classification = mMotionClassifier->classify(newArgs);
+            const MotionClassification newClassification = mMotionClassifier->classify(newArgs);
+            LOG_ALWAYS_FATAL_IF(args->classification != MotionClassification::NONE &&
+                                        newClassification != MotionClassification::NONE,
+                                "Conflicting classifications %s (new) and %s (old)!",
+                                motionClassificationToString(newClassification),
+                                motionClassificationToString(args->classification));
+            newArgs.classification = newClassification;
             mQueuedListener.notifyMotion(&newArgs);
         }
     } // release lock
diff --git a/services/inputflinger/InputProcessor.h b/services/inputflinger/InputProcessor.h
index bbf391e..f4d02b6 100644
--- a/services/inputflinger/InputProcessor.h
+++ b/services/inputflinger/InputProcessor.h
@@ -35,12 +35,12 @@
 
 struct ClassifierEvent {
     ClassifierEventType type;
-    std::unique_ptr<NotifyArgs> args;
+    std::optional<NotifyArgs> args;
 
-    ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args);
-    ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args);
-    ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args);
-    ClassifierEvent(ClassifierEvent&& other);
+    ClassifierEvent(ClassifierEventType type, std::optional<NotifyArgs> args);
+    ClassifierEvent(const NotifyMotionArgs& args);
+    ClassifierEvent(const NotifyDeviceResetArgs& args);
+    ClassifierEvent(ClassifierEvent&& other) = default;
     ClassifierEvent& operator=(ClassifierEvent&& other);
 
     // Convenience function to create a HAL_RESET event
diff --git a/services/inputflinger/NotifyArgs.cpp b/services/inputflinger/NotifyArgs.cpp
new file mode 100644
index 0000000..1f37774
--- /dev/null
+++ b/services/inputflinger/NotifyArgs.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#define LOG_TAG "NotifyArgs"
+
+#define ATRACE_TAG ATRACE_TAG_INPUT
+
+#include "NotifyArgs.h"
+
+#include <android-base/stringprintf.h>
+#include <android/log.h>
+#include <math.h>
+#include <utils/Trace.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+
+// --- NotifyConfigurationChangedArgs ---
+
+NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime)
+      : id(id), eventTime(eventTime) {}
+
+// --- NotifyKeyArgs ---
+
+NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, 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)
+      : id(id),
+        eventTime(eventTime),
+        deviceId(deviceId),
+        source(source),
+        displayId(displayId),
+        policyFlags(policyFlags),
+        action(action),
+        flags(flags),
+        keyCode(keyCode),
+        scanCode(scanCode),
+        metaState(metaState),
+        downTime(downTime),
+        readTime(readTime) {}
+
+// --- NotifyMotionArgs ---
+
+NotifyMotionArgs::NotifyMotionArgs(
+        int32_t id, nsecs_t eventTime, nsecs_t readTime, 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)
+      : id(id),
+        eventTime(eventTime),
+        deviceId(deviceId),
+        source(source),
+        displayId(displayId),
+        policyFlags(policyFlags),
+        action(action),
+        actionButton(actionButton),
+        flags(flags),
+        metaState(metaState),
+        buttonState(buttonState),
+        classification(classification),
+        edgeFlags(edgeFlags),
+        pointerCount(pointerCount),
+        xPrecision(xPrecision),
+        yPrecision(yPrecision),
+        xCursorPosition(xCursorPosition),
+        yCursorPosition(yCursorPosition),
+        downTime(downTime),
+        readTime(readTime),
+        videoFrames(videoFrames) {
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        this->pointerProperties[i].copyFrom(pointerProperties[i]);
+        this->pointerCoords[i].copyFrom(pointerCoords[i]);
+    }
+}
+
+NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other)
+      : id(other.id),
+        eventTime(other.eventTime),
+        deviceId(other.deviceId),
+        source(other.source),
+        displayId(other.displayId),
+        policyFlags(other.policyFlags),
+        action(other.action),
+        actionButton(other.actionButton),
+        flags(other.flags),
+        metaState(other.metaState),
+        buttonState(other.buttonState),
+        classification(other.classification),
+        edgeFlags(other.edgeFlags),
+        pointerCount(other.pointerCount),
+        xPrecision(other.xPrecision),
+        yPrecision(other.yPrecision),
+        xCursorPosition(other.xCursorPosition),
+        yCursorPosition(other.yCursorPosition),
+        downTime(other.downTime),
+        readTime(other.readTime),
+        videoFrames(other.videoFrames) {
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        pointerProperties[i].copyFrom(other.pointerProperties[i]);
+        pointerCoords[i].copyFrom(other.pointerCoords[i]);
+    }
+}
+
+static inline bool isCursorPositionEqual(float lhs, float rhs) {
+    return (isnan(lhs) && isnan(rhs)) || lhs == rhs;
+}
+
+bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const {
+    bool equal = id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
+            deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId &&
+            policyFlags == rhs.policyFlags && action == rhs.action &&
+            actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState &&
+            buttonState == rhs.buttonState && classification == rhs.classification &&
+            edgeFlags == rhs.edgeFlags &&
+            pointerCount == rhs.pointerCount
+            // PointerProperties and PointerCoords are compared separately below
+            && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision &&
+            isCursorPositionEqual(xCursorPosition, rhs.xCursorPosition) &&
+            isCursorPositionEqual(yCursorPosition, rhs.yCursorPosition) &&
+            downTime == rhs.downTime && videoFrames == rhs.videoFrames;
+    if (!equal) {
+        return false;
+    }
+
+    for (size_t i = 0; i < pointerCount; i++) {
+        equal = pointerProperties[i] == rhs.pointerProperties[i] &&
+                pointerCoords[i] == rhs.pointerCoords[i];
+        if (!equal) {
+            return false;
+        }
+    }
+    return true;
+}
+
+std::string NotifyMotionArgs::dump() const {
+    std::string coords;
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        if (!coords.empty()) {
+            coords += ", ";
+        }
+        coords += StringPrintf("{%" PRIu32 ": ", i);
+        coords +=
+                StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f pressure=%.1f", pointerProperties[i].id,
+                             pointerCoords[i].getX(), pointerCoords[i].getY(),
+                             pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+        const int32_t toolType = pointerProperties[i].toolType;
+        if (toolType != AMOTION_EVENT_TOOL_TYPE_FINGER) {
+            coords += StringPrintf(" toolType=%s", motionToolTypeToString(toolType));
+        }
+        const float major = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
+        const float minor = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
+        const float orientation = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+        if (major != 0 || minor != 0) {
+            coords += StringPrintf(" major=%.1f minor=%.1f orientation=%.1f", major, minor,
+                                   orientation);
+        }
+        coords += "}";
+    }
+    return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32
+                        ", source=%s, action=%s, pointerCount=%" PRIu32
+                        " pointers=%s, flags=0x%08x)",
+                        id, eventTime, deviceId, inputEventSourceToString(source).c_str(),
+                        MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str(),
+                        flags);
+}
+
+// --- NotifySwitchArgs ---
+
+NotifySwitchArgs::NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags,
+                                   uint32_t switchValues, uint32_t switchMask)
+      : id(id),
+        eventTime(eventTime),
+        policyFlags(policyFlags),
+        switchValues(switchValues),
+        switchMask(switchMask) {}
+
+// --- NotifySensorArgs ---
+
+NotifySensorArgs::NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+                                   InputDeviceSensorType sensorType,
+                                   InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
+                                   nsecs_t hwTimestamp, std::vector<float> values)
+      : id(id),
+        eventTime(eventTime),
+        deviceId(deviceId),
+        source(source),
+        sensorType(sensorType),
+        accuracy(accuracy),
+        accuracyChanged(accuracyChanged),
+        hwTimestamp(hwTimestamp),
+        values(std::move(values)) {}
+
+// --- NotifyVibratorStateArgs ---
+
+NotifyVibratorStateArgs::NotifyVibratorStateArgs(int32_t id, nsecs_t eventTime, int32_t deviceId,
+                                                 bool isOn)
+      : id(id), eventTime(eventTime), deviceId(deviceId), isOn(isOn) {}
+
+// --- NotifyDeviceResetArgs ---
+
+NotifyDeviceResetArgs::NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId)
+      : id(id), eventTime(eventTime), deviceId(deviceId) {}
+
+// --- NotifyPointerCaptureChangedArgs ---
+
+NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
+        int32_t id, nsecs_t eventTime, const PointerCaptureRequest& request)
+      : id(id), eventTime(eventTime), request(request) {}
+
+} // namespace android
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
index 75071d5..e5c19af 100644
--- a/services/inputflinger/benchmarks/Android.bp
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -26,7 +26,6 @@
         "libinputreporter",
         "liblog",
         "libstatslog",
-        "libui",
         "libutils",
     ],
     static_libs: [
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index cdad9c9..eb79b76 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -63,7 +63,6 @@
         "libstatslog",
         "libstatspull",
         "libstatssocket",
-        "libui",
         "libgui",
         "libutils",
         "server_configurable_flags",
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index f6872d1..630d21a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -41,7 +41,6 @@
 #include <input/InputTransport.h>
 #include <limits.h>
 #include <stddef.h>
-#include <ui/Region.h>
 #include <unistd.h>
 #include <utils/BitSet.h>
 #include <utils/Looper.h>
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index c8ab6c5..8647bcb 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -21,230 +21,10 @@
 #include <input/Input.h>
 #include <input/InputDevice.h>
 #include <input/TouchVideoFrame.h>
+#include "NotifyArgs.h"
 
 namespace android {
 
-class InputListenerInterface;
-
-
-/* Superclass of all input event argument objects */
-struct NotifyArgs {
-    int32_t id;
-    nsecs_t eventTime;
-
-    inline NotifyArgs() : id(0), eventTime(0) {}
-
-    inline explicit NotifyArgs(int32_t id, nsecs_t eventTime) : id(id), eventTime(eventTime) {}
-
-    virtual ~NotifyArgs() { }
-
-    virtual void notify(InputListenerInterface& listener) const = 0;
-};
-
-
-/* Describes a configuration change event. */
-struct NotifyConfigurationChangedArgs : public NotifyArgs {
-
-    inline NotifyConfigurationChangedArgs() { }
-
-    bool operator==(const NotifyConfigurationChangedArgs& rhs) const;
-
-    NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime);
-
-    NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other);
-
-    virtual ~NotifyConfigurationChangedArgs() { }
-
-    void notify(InputListenerInterface& listener) const override;
-};
-
-
-/* Describes a key event. */
-struct NotifyKeyArgs : public NotifyArgs {
-    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;
-    nsecs_t readTime;
-
-    inline NotifyKeyArgs() { }
-
-    NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, 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);
-
-    bool operator==(const NotifyKeyArgs& rhs) const;
-
-    NotifyKeyArgs(const NotifyKeyArgs& other);
-
-    virtual ~NotifyKeyArgs() { }
-
-    void notify(InputListenerInterface& listener) const override;
-};
-
-
-/* Describes a motion event. */
-struct NotifyMotionArgs : public NotifyArgs {
-    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;
-    /**
-     * Classification of the current touch gesture
-     */
-    MotionClassification classification;
-    int32_t edgeFlags;
-
-    uint32_t pointerCount;
-    PointerProperties pointerProperties[MAX_POINTERS];
-    PointerCoords pointerCoords[MAX_POINTERS];
-    float xPrecision;
-    float yPrecision;
-    /**
-     * Mouse cursor position when this event is reported relative to the origin of the specified
-     * display. Only valid if this is a mouse event (originates from a mouse or from a trackpad in
-     * gestures enabled mode.
-     */
-    float xCursorPosition;
-    float yCursorPosition;
-    nsecs_t downTime;
-    nsecs_t readTime;
-    std::vector<TouchVideoFrame> videoFrames;
-
-    inline NotifyMotionArgs() { }
-
-    NotifyMotionArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, 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(const NotifyMotionArgs& other);
-
-    virtual ~NotifyMotionArgs() { }
-
-    bool operator==(const NotifyMotionArgs& rhs) const;
-
-    void notify(InputListenerInterface& listener) const override;
-
-    std::string dump() const;
-};
-
-/* Describes a sensor event. */
-struct NotifySensorArgs : public NotifyArgs {
-    int32_t deviceId;
-    uint32_t source;
-    InputDeviceSensorType sensorType;
-    InputDeviceSensorAccuracy accuracy;
-    bool accuracyChanged;
-    nsecs_t hwTimestamp;
-    std::vector<float> values;
-
-    inline NotifySensorArgs() {}
-
-    NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
-                     InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy,
-                     bool accuracyChanged, nsecs_t hwTimestamp, std::vector<float> values);
-
-    NotifySensorArgs(const NotifySensorArgs& other);
-
-    bool operator==(const NotifySensorArgs rhs) const;
-
-    ~NotifySensorArgs() override {}
-
-    void notify(InputListenerInterface& listener) const override;
-};
-
-/* Describes a switch event. */
-struct NotifySwitchArgs : public NotifyArgs {
-    uint32_t policyFlags;
-    uint32_t switchValues;
-    uint32_t switchMask;
-
-    inline NotifySwitchArgs() { }
-
-    NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags, uint32_t switchValues,
-                     uint32_t switchMask);
-
-    NotifySwitchArgs(const NotifySwitchArgs& other);
-
-    bool operator==(const NotifySwitchArgs rhs) const;
-
-    virtual ~NotifySwitchArgs() { }
-
-    void notify(InputListenerInterface& listener) const override;
-};
-
-
-/* Describes a device reset event, such as when a device is added,
- * reconfigured, or removed. */
-struct NotifyDeviceResetArgs : public NotifyArgs {
-    int32_t deviceId;
-
-    inline NotifyDeviceResetArgs() { }
-
-    NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId);
-
-    NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other);
-
-    bool operator==(const NotifyDeviceResetArgs& rhs) const;
-
-    virtual ~NotifyDeviceResetArgs() { }
-
-    void notify(InputListenerInterface& listener) const override;
-};
-
-/* Describes a change in the state of Pointer Capture. */
-struct NotifyPointerCaptureChangedArgs : public NotifyArgs {
-    // The sequence number of the Pointer Capture request, if enabled.
-    PointerCaptureRequest request;
-
-    inline NotifyPointerCaptureChangedArgs() {}
-
-    NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
-
-    NotifyPointerCaptureChangedArgs(const NotifyPointerCaptureChangedArgs& other);
-
-    bool operator==(const NotifyPointerCaptureChangedArgs& rhs) const;
-
-    virtual ~NotifyPointerCaptureChangedArgs() {}
-
-    void notify(InputListenerInterface& listener) const override;
-};
-
-/* Describes a vibrator state event. */
-struct NotifyVibratorStateArgs : public NotifyArgs {
-    int32_t deviceId;
-    bool isOn;
-
-    inline NotifyVibratorStateArgs() {}
-
-    NotifyVibratorStateArgs(int32_t id, nsecs_t eventTIme, int32_t deviceId, bool isOn);
-
-    NotifyVibratorStateArgs(const NotifyVibratorStateArgs& other);
-
-    bool operator==(const NotifyVibratorStateArgs rhs) const;
-
-    virtual ~NotifyVibratorStateArgs() {}
-
-    void notify(InputListenerInterface& listener) const override;
-};
-
 /*
  * The interface used by the InputReader to notify the InputListener about input events.
  */
@@ -263,6 +43,8 @@
     virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) = 0;
     virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) = 0;
     virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) = 0;
+
+    void notify(const NotifyArgs& args);
 };
 
 /*
@@ -287,7 +69,7 @@
 
 private:
     InputListenerInterface& mInnerListener;
-    std::vector<std::unique_ptr<NotifyArgs>> mArgsQueue;
+    std::vector<NotifyArgs> mArgsQueue;
 };
 
 } // namespace android
diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h
new file mode 100644
index 0000000..611b1aa
--- /dev/null
+++ b/services/inputflinger/include/NotifyArgs.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2022 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 <vector>
+
+#include <input/Input.h>
+#include <input/InputDevice.h>
+#include <input/TouchVideoFrame.h>
+
+namespace android {
+
+/* Describes a configuration change event. */
+struct NotifyConfigurationChangedArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    inline NotifyConfigurationChangedArgs() {}
+
+    NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime);
+
+    bool operator==(const NotifyConfigurationChangedArgs& rhs) const = default;
+
+    NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other) = default;
+};
+
+/* Describes a key event. */
+struct NotifyKeyArgs {
+    int32_t id;
+    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;
+    nsecs_t readTime;
+
+    inline NotifyKeyArgs() {}
+
+    NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, 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);
+
+    bool operator==(const NotifyKeyArgs& rhs) const = default;
+
+    NotifyKeyArgs(const NotifyKeyArgs& other) = default;
+};
+
+/* Describes a motion event. */
+struct NotifyMotionArgs {
+    int32_t id;
+    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;
+    /**
+     * Classification of the current touch gesture
+     */
+    MotionClassification classification;
+    int32_t edgeFlags;
+
+    uint32_t pointerCount;
+    PointerProperties pointerProperties[MAX_POINTERS];
+    PointerCoords pointerCoords[MAX_POINTERS];
+    float xPrecision;
+    float yPrecision;
+    /**
+     * Mouse cursor position when this event is reported relative to the origin of the specified
+     * display. Only valid if this is a mouse event (originates from a mouse or from a trackpad in
+     * gestures enabled mode.
+     */
+    float xCursorPosition;
+    float yCursorPosition;
+    nsecs_t downTime;
+    nsecs_t readTime;
+    std::vector<TouchVideoFrame> videoFrames;
+
+    inline NotifyMotionArgs() {}
+
+    NotifyMotionArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, 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(const NotifyMotionArgs& other);
+
+    bool operator==(const NotifyMotionArgs& rhs) const;
+
+    std::string dump() const;
+};
+
+/* Describes a sensor event. */
+struct NotifySensorArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    int32_t deviceId;
+    uint32_t source;
+    InputDeviceSensorType sensorType;
+    InputDeviceSensorAccuracy accuracy;
+    bool accuracyChanged;
+    nsecs_t hwTimestamp;
+    std::vector<float> values;
+
+    inline NotifySensorArgs() {}
+
+    NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+                     InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy,
+                     bool accuracyChanged, nsecs_t hwTimestamp, std::vector<float> values);
+
+    NotifySensorArgs(const NotifySensorArgs& other) = default;
+};
+
+/* Describes a switch event. */
+struct NotifySwitchArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    uint32_t policyFlags;
+    uint32_t switchValues;
+    uint32_t switchMask;
+
+    inline NotifySwitchArgs() {}
+
+    NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags, uint32_t switchValues,
+                     uint32_t switchMask);
+
+    NotifySwitchArgs(const NotifySwitchArgs& other) = default;
+
+    bool operator==(const NotifySwitchArgs& rhs) const = default;
+};
+
+/* Describes a device reset event, such as when a device is added,
+ * reconfigured, or removed. */
+struct NotifyDeviceResetArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    int32_t deviceId;
+
+    inline NotifyDeviceResetArgs() {}
+
+    NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId);
+
+    NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other) = default;
+
+    bool operator==(const NotifyDeviceResetArgs& rhs) const = default;
+};
+
+/* Describes a change in the state of Pointer Capture. */
+struct NotifyPointerCaptureChangedArgs {
+    // The sequence number of the Pointer Capture request, if enabled.
+    int32_t id;
+    nsecs_t eventTime;
+
+    PointerCaptureRequest request;
+
+    inline NotifyPointerCaptureChangedArgs() {}
+
+    NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
+
+    NotifyPointerCaptureChangedArgs(const NotifyPointerCaptureChangedArgs& other) = default;
+};
+
+/* Describes a vibrator state event. */
+struct NotifyVibratorStateArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    int32_t deviceId;
+    bool isOn;
+
+    inline NotifyVibratorStateArgs() {}
+
+    NotifyVibratorStateArgs(int32_t id, nsecs_t eventTIme, int32_t deviceId, bool isOn);
+
+    NotifyVibratorStateArgs(const NotifyVibratorStateArgs& other) = default;
+};
+
+using NotifyArgs = std::variant<NotifyConfigurationChangedArgs, NotifyKeyArgs, NotifyMotionArgs,
+                                NotifySensorArgs, NotifySwitchArgs, NotifyDeviceResetArgs,
+                                NotifyPointerCaptureChangedArgs, NotifyVibratorStateArgs>;
+
+} // namespace android
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 01146a3..0f87201 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -69,12 +69,12 @@
         "libinput",
         "liblog",
         "libstatslog",
-        "libui",
         "libutils",
         "libPlatformProperties",
     ],
     static_libs: [
         "libc++fs",
+        "libui-types",
     ],
     header_libs: [
         "libbatteryservice_headers",
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index c9d21dc..ca7e426 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -76,6 +76,8 @@
 static constexpr int32_t FF_STRONG_MAGNITUDE_CHANNEL_IDX = 0;
 static constexpr int32_t FF_WEAK_MAGNITUDE_CHANNEL_IDX = 1;
 
+static constexpr size_t EVENT_BUFFER_SIZE = 256;
+
 // Mapping for input battery class node IDs lookup.
 // https://www.kernel.org/doc/Documentation/power/power_supply_class.txt
 static const std::unordered_map<std::string, InputBatteryClass> BATTERY_CLASSES =
@@ -117,7 +119,8 @@
          {"brightness", InputLightClass::BRIGHTNESS},
          {"multi_index", InputLightClass::MULTI_INDEX},
          {"multi_intensity", InputLightClass::MULTI_INTENSITY},
-         {"max_brightness", InputLightClass::MAX_BRIGHTNESS}};
+         {"max_brightness", InputLightClass::MAX_BRIGHTNESS},
+         {"kbd_backlight", InputLightClass::KEYBOARD_BACKLIGHT}};
 
 // Mapping for input multicolor led class node names.
 // https://www.kernel.org/doc/html/latest/leds/leds-class-multicolor.html
@@ -365,15 +368,26 @@
         info.path = nodePath;
         info.name = nodePath.filename();
         info.maxBrightness = std::nullopt;
-        size_t nameStart = info.name.rfind(":");
-        if (nameStart != std::string::npos) {
-            // Trim the name to color name
-            info.name = info.name.substr(nameStart + 1);
-            // Set InputLightClass flag for colors
-            const auto it = LIGHT_CLASSES.find(info.name);
-            if (it != LIGHT_CLASSES.end()) {
-                info.flags |= it->second;
+
+        // Light name should follow the naming pattern <name>:<color>:<function>
+        // Refer kernel docs /leds/leds-class.html for valid supported LED names.
+        std::regex indexPattern("([a-zA-Z0-9_.:]*:)?([a-zA-Z0-9_.]*):([a-zA-Z0-9_.]*)");
+        std::smatch results;
+
+        if (std::regex_match(info.name, results, indexPattern)) {
+            // regex_match will return full match at index 0 and <name> at index 1. For RawLightInfo
+            // we only care about sections <color> and <function> which will be at index 2 and 3.
+            for (int i = 2; i <= 3; i++) {
+                const auto it = LIGHT_CLASSES.find(results.str(i));
+                if (it != LIGHT_CLASSES.end()) {
+                    info.flags |= it->second;
+                }
             }
+
+            // Set name of the raw light to <function> which represents playerIDs for LEDs that
+            // turn on/off based on the current player ID (Refer to PeripheralController.cpp for
+            // player ID logic)
+            info.name = results.str(3);
         }
         // Scan the path for all the files
         // Refer to https://www.kernel.org/doc/Documentation/leds/leds-class.txt
@@ -1404,17 +1418,28 @@
     }
 
     const auto& path = *sysfsRootPathOpt;
-    for (const auto& [id, dev] : mDevices) {
-        if (dev->associatedDevice && dev->associatedDevice->sysfsRootPath == path) {
-            return dev->associatedDevice;
-        }
-    }
 
-    return std::make_shared<AssociatedDevice>(
+    std::shared_ptr<const AssociatedDevice> associatedDevice = std::make_shared<AssociatedDevice>(
             AssociatedDevice{.sysfsRootPath = path,
                              .countryCode = readCountryCodeLocked(path),
                              .batteryInfos = readBatteryConfiguration(path),
                              .lightInfos = readLightsConfiguration(path)});
+
+    bool associatedDeviceChanged = false;
+    for (const auto& [id, dev] : mDevices) {
+        if (dev->associatedDevice && dev->associatedDevice->sysfsRootPath == path) {
+            if (*associatedDevice != *dev->associatedDevice) {
+                associatedDeviceChanged = true;
+                dev->associatedDevice = associatedDevice;
+            }
+            associatedDevice = dev->associatedDevice;
+        }
+    }
+    ALOGI_IF(associatedDeviceChanged,
+             "The AssociatedDevice changed for path '%s'. Using new AssociatedDevice: %s",
+             path.c_str(), associatedDevice->dump().c_str());
+
+    return associatedDevice;
 }
 
 void EventHub::vibrate(int32_t deviceId, const VibrationElement& element) {
@@ -1621,15 +1646,12 @@
     return std::nullopt;
 }
 
-size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
-    ALOG_ASSERT(bufferSize >= 1);
-
+std::vector<RawEvent> EventHub::getEvents(int timeoutMillis) {
     std::scoped_lock _l(mLock);
 
-    struct input_event readBuffer[bufferSize];
+    std::array<input_event, EVENT_BUFFER_SIZE> readBuffer;
 
-    RawEvent* event = buffer;
-    size_t capacity = bufferSize;
+    std::vector<RawEvent> events;
     bool awoken = false;
     for (;;) {
         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -1649,15 +1671,17 @@
         for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) {
             std::unique_ptr<Device> device = std::move(*it);
             ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());
-            event->when = now;
-            event->deviceId = (device->id == mBuiltInKeyboardId)
+            const int32_t deviceId = (device->id == mBuiltInKeyboardId)
                     ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
                     : device->id;
-            event->type = DEVICE_REMOVED;
-            event += 1;
+            events.push_back({
+                    .when = now,
+                    .deviceId = deviceId,
+                    .type = DEVICE_REMOVED,
+            });
             it = mClosingDevices.erase(it);
             mNeedToSendFinishedDeviceScan = true;
-            if (--capacity == 0) {
+            if (events.size() == EVENT_BUFFER_SIZE) {
                 break;
             }
         }
@@ -1672,10 +1696,12 @@
             std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
             mOpeningDevices.pop_back();
             ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
-            event->when = now;
-            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
-            event->type = DEVICE_ADDED;
-            event += 1;
+            const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
+            events.push_back({
+                    .when = now,
+                    .deviceId = deviceId,
+                    .type = DEVICE_ADDED,
+            });
 
             // Try to find a matching video device by comparing device names
             for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
@@ -1693,17 +1719,18 @@
                 ALOGW("Device id %d exists, replaced.", device->id);
             }
             mNeedToSendFinishedDeviceScan = true;
-            if (--capacity == 0) {
+            if (events.size() == EVENT_BUFFER_SIZE) {
                 break;
             }
         }
 
         if (mNeedToSendFinishedDeviceScan) {
             mNeedToSendFinishedDeviceScan = false;
-            event->when = now;
-            event->type = FINISHED_DEVICE_SCAN;
-            event += 1;
-            if (--capacity == 0) {
+            events.push_back({
+                    .when = now,
+                    .type = FINISHED_DEVICE_SCAN,
+            });
+            if (events.size() == EVENT_BUFFER_SIZE) {
                 break;
             }
         }
@@ -1767,12 +1794,13 @@
             // This must be an input event
             if (eventItem.events & EPOLLIN) {
                 int32_t readSize =
-                        read(device->fd, readBuffer, sizeof(struct input_event) * capacity);
+                        read(device->fd, readBuffer.data(),
+                             sizeof(decltype(readBuffer)::value_type) * readBuffer.size());
                 if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
                     // Device was removed before INotify noticed.
                     ALOGW("could not get event, removed? (fd: %d size: %" PRId32
-                          " bufferSize: %zu capacity: %zu errno: %d)\n",
-                          device->fd, readSize, bufferSize, capacity, errno);
+                          " capacity: %zu errno: %d)\n",
+                          device->fd, readSize, readBuffer.size(), errno);
                     deviceChanged = true;
                     closeDeviceLocked(*device);
                 } else if (readSize < 0) {
@@ -1782,21 +1810,21 @@
                 } else if ((readSize % sizeof(struct input_event)) != 0) {
                     ALOGE("could not get event (wrong size: %d)", readSize);
                 } else {
-                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
+                    const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
 
-                    size_t count = size_t(readSize) / sizeof(struct input_event);
+                    const size_t count = size_t(readSize) / sizeof(struct input_event);
                     for (size_t i = 0; i < count; i++) {
                         struct input_event& iev = readBuffer[i];
-                        event->when = processEventTimestamp(iev);
-                        event->readTime = systemTime(SYSTEM_TIME_MONOTONIC);
-                        event->deviceId = deviceId;
-                        event->type = iev.type;
-                        event->code = iev.code;
-                        event->value = iev.value;
-                        event += 1;
-                        capacity -= 1;
+                        events.push_back({
+                                .when = processEventTimestamp(iev),
+                                .readTime = systemTime(SYSTEM_TIME_MONOTONIC),
+                                .deviceId = deviceId,
+                                .type = iev.type,
+                                .code = iev.code,
+                                .value = iev.value,
+                        });
                     }
-                    if (capacity == 0) {
+                    if (events.size() >= EVENT_BUFFER_SIZE) {
                         // The result buffer is full.  Reset the pending event index
                         // so we will try to read the device again on the next iteration.
                         mPendingEventIndex -= 1;
@@ -1832,7 +1860,7 @@
         }
 
         // Return now if we have collected any events or if we were explicitly awoken.
-        if (event != buffer || awoken) {
+        if (!events.empty() || awoken) {
             break;
         }
 
@@ -1878,7 +1906,7 @@
     }
 
     // All done, return the number of events we read.
-    return event - buffer;
+    return events;
 }
 
 std::vector<TouchVideoFrame> EventHub::getVideoFrames(int32_t deviceId) {
@@ -2633,4 +2661,9 @@
     std::unique_lock<std::mutex> lock(mLock);
 }
 
+std::string EventHub::AssociatedDevice::dump() const {
+    return StringPrintf("path=%s, numBatteries=%zu, numLight=%zu", sysfsRootPath.c_str(),
+                        batteryInfos.size(), lightInfos.size());
+}
+
 } // namespace android
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 44a005e..6b9b9f1 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -234,6 +234,10 @@
 }
 
 void InputDevice::removeEventHubDevice(int32_t eventHubId) {
+    if (mController != nullptr && mController->getEventHubId() == eventHubId) {
+        // Delete mController, since the corresponding eventhub device is going away
+        mController = nullptr;
+    }
     mDevices.erase(eventHubId);
 }
 
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 4c38ce8..8650876 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -120,14 +120,14 @@
         }
     } // release lock
 
-    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
+    std::vector<RawEvent> events = mEventHub->getEvents(timeoutMillis);
 
     { // acquire lock
         std::scoped_lock _l(mLock);
         mReaderIsAliveCondition.notify_all();
 
-        if (count) {
-            processEventsLocked(mEventBuffer, count);
+        if (!events.empty()) {
+            processEventsLocked(events.data(), events.size());
         }
 
         if (mNextTimeout != LLONG_MAX) {
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index eaf5b51..cedbacb 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -16,6 +16,7 @@
 
 #include <locale>
 #include <regex>
+#include <set>
 
 #include <ftl/enum.h>
 
@@ -70,7 +71,7 @@
 
     // If the light node doesn't have max brightness, use the default max brightness.
     int rawMaxBrightness = rawInfoOpt->maxBrightness.value_or(MAX_BRIGHTNESS);
-    float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
+    float ratio = static_cast<float>(MAX_BRIGHTNESS) / rawMaxBrightness;
     // Scale the returned brightness in [0, rawMaxBrightness] to [0, 255]
     if (rawMaxBrightness != MAX_BRIGHTNESS) {
         brightness = brightness * ratio;
@@ -89,7 +90,7 @@
     }
     // If the light node doesn't have max brightness, use the default max brightness.
     int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS);
-    float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
+    float ratio = static_cast<float>(MAX_BRIGHTNESS) / rawMaxBrightness;
     // Scale the requested brightness in [0, 255] to [0, rawMaxBrightness]
     if (rawMaxBrightness != MAX_BRIGHTNESS) {
         brightness = ceil(brightness / ratio);
@@ -271,7 +272,8 @@
 
     for (const auto& [lightId, light] : mLights) {
         // Input device light doesn't support ordinal, always pass 1.
-        InputDeviceLightInfo lightInfo(light->name, light->id, light->type, 1 /* ordinal */);
+        InputDeviceLightInfo lightInfo(light->name, light->id, light->type, light->capabilityFlags,
+                                       1 /* ordinal */);
         deviceInfo->addLightInfo(lightInfo);
     }
 }
@@ -284,6 +286,8 @@
             dump += StringPrintf(INDENT4 "Id: %d", lightId);
             dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str());
             dump += StringPrintf(INDENT4 "Type: %s", ftl::enum_string(light->type).c_str());
+            dump += StringPrintf(INDENT4 "Capability flags: %s",
+                                 light->capabilityFlags.string().c_str());
             light->dump(dump);
         }
     }
@@ -363,6 +367,8 @@
     std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds;
     // Map from player Id to raw light Id
     std::unordered_map<int32_t, int32_t> playerIdLightIds;
+    // Set of Keyboard backlights
+    std::set<int32_t> keyboardBacklightIds;
 
     // Check raw lights
     const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
@@ -391,6 +397,10 @@
                 }
             }
         }
+        // Check if this is a Keyboard backlight
+        if (rawInfo->flags.test(InputLightClass::KEYBOARD_BACKLIGHT)) {
+            keyboardBacklightIds.insert(rawId);
+        }
         // Check if this is an LED of RGB light
         if (rawInfo->flags.test(InputLightClass::RED)) {
             hasRedLed = true;
@@ -431,8 +441,21 @@
             ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED),
                   rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
         }
+        bool isKeyboardBacklight = keyboardBacklightIds.find(rawRgbIds.at(LightColor::RED)) !=
+                        keyboardBacklightIds.end() &&
+                keyboardBacklightIds.find(rawRgbIds.at(LightColor::GREEN)) !=
+                        keyboardBacklightIds.end() &&
+                keyboardBacklightIds.find(rawRgbIds.at(LightColor::BLUE)) !=
+                        keyboardBacklightIds.end() &&
+                (!rawGlobalId.has_value() ||
+                 keyboardBacklightIds.find(rawGlobalId.value()) != keyboardBacklightIds.end());
+
         std::unique_ptr<Light> light =
-                std::make_unique<RgbLight>(getDeviceContext(), ++mNextId, rawRgbIds, rawGlobalId);
+                std::make_unique<RgbLight>(getDeviceContext(), ++mNextId,
+                                           isKeyboardBacklight
+                                                   ? InputDeviceLightType::KEYBOARD_BACKLIGHT
+                                                   : InputDeviceLightType::INPUT,
+                                           rawRgbIds, rawGlobalId);
         mLights.insert_or_assign(light->id, std::move(light));
         // Remove from raw light info as they've been composed a RBG light.
         rawInfos.erase(rawRgbIds.at(LightColor::RED));
@@ -445,6 +468,10 @@
 
     // Check the rest of raw light infos
     for (const auto& [rawId, rawInfo] : rawInfos) {
+        InputDeviceLightType type = keyboardBacklightIds.find(rawId) != keyboardBacklightIds.end()
+                ? InputDeviceLightType::KEYBOARD_BACKLIGHT
+                : InputDeviceLightType::INPUT;
+
         // If the node is multi-color led, construct a MULTI_COLOR light
         if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) &&
             rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) {
@@ -453,7 +480,7 @@
             }
             std::unique_ptr<Light> light =
                     std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name, ++mNextId,
-                                                      rawInfo.id);
+                                                      type, rawInfo.id);
             mLights.insert_or_assign(light->id, std::move(light));
             continue;
         }
@@ -462,7 +489,7 @@
             ALOGD("Mono light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
         }
         std::unique_ptr<Light> light = std::make_unique<MonoLight>(getDeviceContext(), rawInfo.name,
-                                                                   ++mNextId, rawInfo.id);
+                                                                   ++mNextId, type, rawInfo.id);
 
         mLights.insert_or_assign(light->id, std::move(light));
     }
diff --git a/services/inputflinger/reader/controller/PeripheralController.h b/services/inputflinger/reader/controller/PeripheralController.h
index 25cf435..8ac42c3 100644
--- a/services/inputflinger/reader/controller/PeripheralController.h
+++ b/services/inputflinger/reader/controller/PeripheralController.h
@@ -67,6 +67,7 @@
         std::string name;
         int32_t id;
         InputDeviceLightType type;
+        ftl::Flags<InputDeviceLightCapability> capabilityFlags;
 
         virtual bool setLightColor(int32_t color) { return false; }
         virtual std::optional<int32_t> getLightColor() { return std::nullopt; }
@@ -81,8 +82,10 @@
 
     struct MonoLight : public Light {
         explicit MonoLight(InputDeviceContext& context, const std::string& name, int32_t id,
-                           int32_t rawId)
-              : Light(context, name, id, InputDeviceLightType::MONO), rawId(rawId) {}
+                           InputDeviceLightType type, int32_t rawId)
+              : Light(context, name, id, type), rawId(rawId) {
+            capabilityFlags |= InputDeviceLightCapability::BRIGHTNESS;
+        }
         int32_t rawId;
 
         bool setLightColor(int32_t color) override;
@@ -91,15 +94,15 @@
     };
 
     struct RgbLight : public Light {
-        explicit RgbLight(InputDeviceContext& context, int32_t id,
+        explicit RgbLight(InputDeviceContext& context, int32_t id, InputDeviceLightType type,
                           const std::unordered_map<LightColor, int32_t>& rawRgbIds,
                           std::optional<int32_t> rawGlobalId)
-              : Light(context, "RGB", id, InputDeviceLightType::RGB),
-                rawRgbIds(rawRgbIds),
-                rawGlobalId(rawGlobalId) {
+              : Light(context, "RGB", id, type), rawRgbIds(rawRgbIds), rawGlobalId(rawGlobalId) {
             brightness = rawGlobalId.has_value()
                     ? getRawLightBrightness(rawGlobalId.value()).value_or(MAX_BRIGHTNESS)
                     : MAX_BRIGHTNESS;
+            capabilityFlags |= InputDeviceLightCapability::BRIGHTNESS;
+            capabilityFlags |= InputDeviceLightCapability::RGB;
         }
         // Map from color to raw light id.
         std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds;
@@ -114,8 +117,11 @@
 
     struct MultiColorLight : public Light {
         explicit MultiColorLight(InputDeviceContext& context, const std::string& name, int32_t id,
-                                 int32_t rawId)
-              : Light(context, name, id, InputDeviceLightType::MULTI_COLOR), rawId(rawId) {}
+                                 InputDeviceLightType type, int32_t rawId)
+              : Light(context, name, id, type), rawId(rawId) {
+            capabilityFlags |= InputDeviceLightCapability::BRIGHTNESS;
+            capabilityFlags |= InputDeviceLightCapability::RGB;
+        }
         int32_t rawId;
 
         bool setLightColor(int32_t color) override;
@@ -131,7 +137,7 @@
         // Map from player Id to raw light Id
         std::unordered_map<int32_t, int32_t> rawLightIds;
 
-        bool setLightPlayerId(int32_t palyerId) override;
+        bool setLightPlayerId(int32_t playerId) override;
         std::optional<int32_t> getLightPlayerId() override;
         void dump(std::string& dump) override;
     };
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 5cf2214..6933ec7 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -174,6 +174,8 @@
     MULTI_INTENSITY = 0x00000040,
     /* The input light has max brightness node. */
     MAX_BRIGHTNESS = 0x00000080,
+    /* The input light has kbd_backlight name */
+    KEYBOARD_BACKLIGHT = 0x00000100,
 };
 
 enum class InputBatteryClass : uint32_t {
@@ -193,6 +195,9 @@
     ftl::Flags<InputLightClass> flags;
     std::array<int32_t, COLOR_NUM> rgbIndex;
     std::filesystem::path path;
+
+    bool operator==(const RawLightInfo&) const = default;
+    bool operator!=(const RawLightInfo&) const = default;
 };
 
 /* Describes a raw battery. */
@@ -201,6 +206,9 @@
     std::string name;
     ftl::Flags<InputBatteryClass> flags;
     std::filesystem::path path;
+
+    bool operator==(const RawBatteryInfo&) const = default;
+    bool operator!=(const RawBatteryInfo&) const = default;
 };
 
 /*
@@ -280,7 +288,7 @@
      *
      * Returns the number of events obtained, or 0 if the timeout expired.
      */
-    virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) = 0;
+    virtual std::vector<RawEvent> getEvents(int timeoutMillis) = 0;
     virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) = 0;
     virtual base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
             int32_t deviceId, int32_t absCode) const = 0;
@@ -498,7 +506,7 @@
     bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
                                uint8_t* outFlags) const override final;
 
-    size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override final;
+    std::vector<RawEvent> getEvents(int timeoutMillis) override final;
     std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override final;
 
     bool hasScanCode(int32_t deviceId, int32_t scanCode) const override final;
@@ -549,6 +557,10 @@
         hardware::input::InputDeviceCountryCode countryCode;
         std::unordered_map<int32_t /*batteryId*/, RawBatteryInfo> batteryInfos;
         std::unordered_map<int32_t /*lightId*/, RawLightInfo> lightInfos;
+
+        bool operator==(const AssociatedDevice&) const = default;
+        bool operator!=(const AssociatedDevice&) const = default;
+        std::string dump() const;
     };
 
     struct Device {
@@ -582,7 +594,7 @@
 
         // A shared_ptr of a device associated with the input device.
         // The input devices that have the same sysfs path have the same associated device.
-        const std::shared_ptr<const AssociatedDevice> associatedDevice;
+        std::shared_ptr<const AssociatedDevice> associatedDevice;
 
         int32_t controllerNumber;
 
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index fbce87f..012d43f 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -170,10 +170,6 @@
 
     InputReaderConfiguration mConfig GUARDED_BY(mLock);
 
-    // The event queue.
-    static const int EVENT_BUFFER_SIZE = 256;
-    RawEvent mEventBuffer[EVENT_BUFFER_SIZE] GUARDED_BY(mLock);
-
     // An input device can represent a collection of EventHub devices. This map provides a way
     // to lookup the input device instance from the EventHub device id.
     std::unordered_map<int32_t /*eventHubId*/, std::shared_ptr<InputDevice>> mDevices
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 8c241f2..4cd2cce 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -1950,7 +1950,8 @@
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
-                       mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                       mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                       MotionClassification::NONE);
         mCurrentMotionAborted = true;
     }
 }
@@ -1970,7 +1971,8 @@
                            mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
-                           mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                           mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                           MotionClassification::NONE);
         }
     } else {
         // There may be pointers going up and pointers going down and pointers moving
@@ -2005,7 +2007,8 @@
                            mLastCookedState.cookedPointerData.pointerProperties,
                            mLastCookedState.cookedPointerData.pointerCoords,
                            mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId,
-                           mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                           mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                           MotionClassification::NONE);
             dispatchedIdBits.clearBit(upId);
             mCurrentCookedState.cookedPointerData.canceledIdBits.clearBit(upId);
         }
@@ -2020,7 +2023,8 @@
                            mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1,
-                           mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                           mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                           MotionClassification::NONE);
         }
 
         // Dispatch pointer down events using the new pointer locations.
@@ -2038,7 +2042,8 @@
                            mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits,
-                           downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                           downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                           MotionClassification::NONE);
         }
     }
 }
@@ -2054,7 +2059,7 @@
                        mLastCookedState.cookedPointerData.pointerCoords,
                        mLastCookedState.cookedPointerData.idToIndex,
                        mLastCookedState.cookedPointerData.hoveringIdBits, -1, mOrientedXPrecision,
-                       mOrientedYPrecision, mDownTime);
+                       mOrientedYPrecision, mDownTime, MotionClassification::NONE);
         mSentHoverEnter = false;
     }
 }
@@ -2071,7 +2076,8 @@
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex,
                            mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
-                           mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                           mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                           MotionClassification::NONE);
             mSentHoverEnter = true;
         }
 
@@ -2081,7 +2087,8 @@
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex,
                        mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
-                       mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                       mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                       MotionClassification::NONE);
     }
 }
 
@@ -2098,7 +2105,8 @@
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
-                       mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                       mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                       MotionClassification::NONE);
     }
 }
 
@@ -2115,7 +2123,8 @@
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
-                       mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                       mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                       MotionClassification::NONE);
     }
 }
 
@@ -2502,6 +2511,10 @@
     // Send events!
     int32_t metaState = getContext()->getGlobalMetaState();
     int32_t buttonState = mCurrentCookedState.buttonState;
+    const MotionClassification classification =
+            mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE
+            ? MotionClassification::TWO_FINGER_SWIPE
+            : MotionClassification::NONE;
 
     uint32_t flags = 0;
 
@@ -2542,7 +2555,7 @@
                            flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                            mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
                            mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
-                           mPointerGesture.downTime);
+                           mPointerGesture.downTime, classification);
 
             dispatchedGestureIdBits.clear();
         } else {
@@ -2561,7 +2574,7 @@
                                AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties,
                                mPointerGesture.lastGestureCoords,
                                mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0,
-                               0, mPointerGesture.downTime);
+                               0, mPointerGesture.downTime, classification);
 
                 dispatchedGestureIdBits.clearBit(id);
             }
@@ -2575,7 +2588,7 @@
                        mPointerGesture.currentGestureProperties,
                        mPointerGesture.currentGestureCoords,
                        mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
-                       mPointerGesture.downTime);
+                       mPointerGesture.downTime, classification);
     }
 
     // Send motion events for all pointers that went down.
@@ -2595,7 +2608,7 @@
                            mPointerGesture.currentGestureProperties,
                            mPointerGesture.currentGestureCoords,
                            mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0,
-                           0, mPointerGesture.downTime);
+                           0, mPointerGesture.downTime, classification);
         }
     }
 
@@ -2606,7 +2619,8 @@
                        mPointerGesture.currentGestureProperties,
                        mPointerGesture.currentGestureCoords,
                        mPointerGesture.currentGestureIdToIndex,
-                       mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime);
+                       mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime,
+                       MotionClassification::NONE);
     } else if (dispatchedGestureIdBits.isEmpty() && !mPointerGesture.lastGestureIdBits.isEmpty()) {
         // Synthesize a hover move event after all pointers go up to indicate that
         // the pointer is hovering again even if the user is not currently touching
@@ -2653,6 +2667,10 @@
 }
 
 void TouchInputMapper::abortPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+    const MotionClassification classification =
+            mPointerGesture.lastGestureMode == PointerGesture::Mode::SWIPE
+            ? MotionClassification::TWO_FINGER_SWIPE
+            : MotionClassification::NONE;
     // Cancel previously dispatches pointers.
     if (!mPointerGesture.lastGestureIdBits.isEmpty()) {
         int32_t metaState = getContext()->getGlobalMetaState();
@@ -2661,7 +2679,7 @@
                        metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                        mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
                        mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1,
-                       0, 0, mPointerGesture.downTime);
+                       0, 0, mPointerGesture.downTime, classification);
     }
 
     // Reset the current pointer gesture.
@@ -3611,7 +3629,8 @@
                                       int32_t edgeFlags, const PointerProperties* properties,
                                       const PointerCoords* coords, const uint32_t* idToIndex,
                                       BitSet32 idBits, int32_t changedId, float xPrecision,
-                                      float yPrecision, nsecs_t downTime) {
+                                      float yPrecision, nsecs_t downTime,
+                                      MotionClassification classification) {
     PointerCoords pointerCoords[MAX_POINTERS];
     PointerProperties pointerProperties[MAX_POINTERS];
     uint32_t pointerCount = 0;
@@ -3659,9 +3678,9 @@
                   [this](TouchVideoFrame& frame) { frame.rotate(this->mInputDeviceOrientation); });
     NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
                           policyFlags, action, actionButton, flags, metaState, buttonState,
-                          MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
-                          pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
-                          downTime, std::move(frames));
+                          classification, edgeFlags, pointerCount, pointerProperties, pointerCoords,
+                          xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime,
+                          std::move(frames));
     getListener().notifyMotion(&args);
 }
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index bd4cff6..31806e1 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -779,7 +779,8 @@
                         int32_t action, int32_t actionButton, int32_t flags, int32_t metaState,
                         int32_t buttonState, int32_t edgeFlags, const PointerProperties* properties,
                         const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits,
-                        int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime);
+                        int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime,
+                        MotionClassification classification);
 
     // Updates pointer coords and properties for pointers with specified ids that have moved.
     // Returns true if any of them changed.
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
index 6ef6e44..9380c71 100644
--- a/services/inputflinger/tests/EventHub_test.cpp
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -99,8 +99,6 @@
 };
 
 std::vector<RawEvent> EventHubTest::getEvents(std::optional<size_t> expectedEvents) {
-    static constexpr size_t EVENT_BUFFER_SIZE = 256;
-    std::array<RawEvent, EVENT_BUFFER_SIZE> eventBuffer;
     std::vector<RawEvent> events;
 
     while (true) {
@@ -108,12 +106,12 @@
         if (expectedEvents) {
             timeout = 2s;
         }
-        const size_t count =
-                mEventHub->getEvents(timeout.count(), eventBuffer.data(), eventBuffer.size());
-        if (count == 0) {
+
+        std::vector<RawEvent> newEvents = mEventHub->getEvents(timeout.count());
+        if (newEvents.empty()) {
             break;
         }
-        events.insert(events.end(), eventBuffer.begin(), eventBuffer.begin() + count);
+        events.insert(events.end(), newEvents.begin(), newEvents.end());
         if (expectedEvents && events.size() >= *expectedEvents) {
             break;
         }
diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp
index 22ae894..ca548be 100644
--- a/services/inputflinger/tests/InputFlingerService_test.cpp
+++ b/services/inputflinger/tests/InputFlingerService_test.cpp
@@ -33,8 +33,6 @@
 #include <inttypes.h>
 #include <linux/uinput.h>
 #include <log/log.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
 #include <chrono>
 #include <thread>
 #include <unordered_map>
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index ee6993c..78ea692 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -826,15 +826,14 @@
         mExcludedDevices = devices;
     }
 
-    size_t getEvents(int, RawEvent* buffer, size_t bufferSize) override {
+    std::vector<RawEvent> getEvents(int) override {
         std::scoped_lock lock(mLock);
 
-        const size_t filledSize = std::min(mEvents.size(), bufferSize);
-        std::copy(mEvents.begin(), mEvents.begin() + filledSize, buffer);
+        std::vector<RawEvent> buffer;
+        std::swap(buffer, mEvents);
 
-        mEvents.erase(mEvents.begin(), mEvents.begin() + filledSize);
         mEventsCondition.notify_all();
-        return filledSize;
+        return buffer;
     }
 
     std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override {
@@ -2972,6 +2971,21 @@
     ASSERT_EQ(DISPLAY_UNIQUE_ID, mDevice->getAssociatedDisplayUniqueId());
 }
 
+/**
+ * This test reproduces a crash caused by a dangling reference that remains after device is added
+ * and removed. The reference is accessed in InputDevice::dump(..);
+ */
+TEST_F(InputDeviceTest, DumpDoesNotCrash) {
+    constexpr int32_t TEST_EVENTHUB_ID = 10;
+    mFakeEventHub->addDevice(TEST_EVENTHUB_ID, "Test EventHub device", InputDeviceClass::BATTERY);
+
+    InputDevice device(mReader->getContext(), 1 /*id*/, 2 /*generation*/, {} /*identifier*/);
+    device.addEventHubDevice(TEST_EVENTHUB_ID, true /*populateMappers*/);
+    device.removeEventHubDevice(TEST_EVENTHUB_ID);
+    std::string dumpStr, eventHubDevStr;
+    device.dump(dumpStr, eventHubDevStr);
+}
+
 // --- InputMapperTest ---
 
 class InputMapperTest : public testing::Test {
@@ -9751,6 +9765,7 @@
     ASSERT_EQ(1U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     ASSERT_NO_FATAL_FAILURE(
             assertPointerCoords(motionArgs.pointerCoords[0], 0, 0, 1, 0, 0, 0, 0, 0, 0, 0));
 
@@ -9772,6 +9787,7 @@
     ASSERT_EQ(1U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], 0,
                                                 movingDistance * mPointerMovementScale, 1, 0, 0, 0,
                                                 0, 0, 0, 0));
@@ -9809,6 +9825,7 @@
     ASSERT_EQ(1U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     ASSERT_NO_FATAL_FAILURE(
             assertPointerCoords(motionArgs.pointerCoords[0], 0, 0, 1, 0, 0, 0, 0, 0, 0, 0));
 
@@ -9830,6 +9847,7 @@
     ASSERT_EQ(1U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification);
     // New coordinate is the scaled relative coordinate from the initial coordinate.
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], 0,
                                                 movingDistance * mPointerMovementScale, 1, 0, 0, 0,
@@ -9863,6 +9881,7 @@
     ASSERT_EQ(1U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     // One pointer for PRESS, and its coordinate is used as the origin for pointer coordinates.
     ASSERT_NO_FATAL_FAILURE(
             assertPointerCoords(motionArgs.pointerCoords[0], 0, 0, 1, 0, 0, 0, 0, 0, 0, 0));
@@ -9892,9 +9911,11 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     ASSERT_EQ(2U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN, motionArgs.action & AMOTION_EVENT_ACTION_MASK);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     // Two pointers' scaled relative coordinates from their initial centroid.
     // Initial y coordinates are 0 as y1 and y2 have the same value.
     float cookedX1 = (x1 - x2) / 2 * mPointerXZoomScale;
@@ -9924,6 +9945,7 @@
     ASSERT_EQ(2U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], cookedX1,
                                                 movingDistance * 2 * mPointerMovementScale, 1, 0, 0,
                                                 0, 0, 0, 0, 0));
@@ -10099,7 +10121,7 @@
 
 TEST_F(LightControllerTest, MonoLight) {
     RawLightInfo infoMono = {.id = 1,
-                             .name = "Mono",
+                             .name = "mono_light",
                              .maxBrightness = 255,
                              .flags = InputLightClass::BRIGHTNESS,
                              .path = ""};
@@ -10110,7 +10132,29 @@
     controller.populateDeviceInfo(&info);
     std::vector<InputDeviceLightInfo> lights = info.getLights();
     ASSERT_EQ(1U, lights.size());
-    ASSERT_EQ(InputDeviceLightType::MONO, lights[0].type);
+    ASSERT_EQ(InputDeviceLightType::INPUT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+
+    ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_BRIGHTNESS));
+    ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_BRIGHTNESS);
+}
+
+TEST_F(LightControllerTest, MonoKeyboardBacklight) {
+    RawLightInfo infoMono = {.id = 1,
+                             .name = "mono_keyboard_backlight",
+                             .maxBrightness = 255,
+                             .flags = InputLightClass::BRIGHTNESS |
+                                     InputLightClass::KEYBOARD_BACKLIGHT,
+                             .path = ""};
+    mFakeEventHub->addRawLightInfo(infoMono.id, std::move(infoMono));
+
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    std::vector<InputDeviceLightInfo> lights = info.getLights();
+    ASSERT_EQ(1U, lights.size());
+    ASSERT_EQ(InputDeviceLightType::KEYBOARD_BACKLIGHT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
 
     ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_BRIGHTNESS));
     ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_BRIGHTNESS);
@@ -10141,7 +10185,85 @@
     controller.populateDeviceInfo(&info);
     std::vector<InputDeviceLightInfo> lights = info.getLights();
     ASSERT_EQ(1U, lights.size());
-    ASSERT_EQ(InputDeviceLightType::RGB, lights[0].type);
+    ASSERT_EQ(InputDeviceLightType::INPUT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
+
+    ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
+    ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
+}
+
+TEST_F(LightControllerTest, CorrectRGBKeyboardBacklight) {
+    RawLightInfo infoRed = {.id = 1,
+                            .name = "red_keyboard_backlight",
+                            .maxBrightness = 255,
+                            .flags = InputLightClass::BRIGHTNESS | InputLightClass::RED |
+                                    InputLightClass::KEYBOARD_BACKLIGHT,
+                            .path = ""};
+    RawLightInfo infoGreen = {.id = 2,
+                              .name = "green_keyboard_backlight",
+                              .maxBrightness = 255,
+                              .flags = InputLightClass::BRIGHTNESS | InputLightClass::GREEN |
+                                      InputLightClass::KEYBOARD_BACKLIGHT,
+                              .path = ""};
+    RawLightInfo infoBlue = {.id = 3,
+                             .name = "blue_keyboard_backlight",
+                             .maxBrightness = 255,
+                             .flags = InputLightClass::BRIGHTNESS | InputLightClass::BLUE |
+                                     InputLightClass::KEYBOARD_BACKLIGHT,
+                             .path = ""};
+    mFakeEventHub->addRawLightInfo(infoRed.id, std::move(infoRed));
+    mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen));
+    mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue));
+
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    std::vector<InputDeviceLightInfo> lights = info.getLights();
+    ASSERT_EQ(1U, lights.size());
+    ASSERT_EQ(InputDeviceLightType::KEYBOARD_BACKLIGHT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
+
+    ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
+    ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
+}
+
+TEST_F(LightControllerTest, IncorrectRGBKeyboardBacklight) {
+    RawLightInfo infoRed = {.id = 1,
+                            .name = "red",
+                            .maxBrightness = 255,
+                            .flags = InputLightClass::BRIGHTNESS | InputLightClass::RED,
+                            .path = ""};
+    RawLightInfo infoGreen = {.id = 2,
+                              .name = "green",
+                              .maxBrightness = 255,
+                              .flags = InputLightClass::BRIGHTNESS | InputLightClass::GREEN,
+                              .path = ""};
+    RawLightInfo infoBlue = {.id = 3,
+                             .name = "blue",
+                             .maxBrightness = 255,
+                             .flags = InputLightClass::BRIGHTNESS | InputLightClass::BLUE,
+                             .path = ""};
+    RawLightInfo infoGlobal = {.id = 3,
+                               .name = "global_keyboard_backlight",
+                               .maxBrightness = 255,
+                               .flags = InputLightClass::BRIGHTNESS | InputLightClass::GLOBAL |
+                                       InputLightClass::KEYBOARD_BACKLIGHT,
+                               .path = ""};
+    mFakeEventHub->addRawLightInfo(infoRed.id, std::move(infoRed));
+    mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen));
+    mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue));
+    mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoGlobal));
+
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    std::vector<InputDeviceLightInfo> lights = info.getLights();
+    ASSERT_EQ(1U, lights.size());
+    ASSERT_EQ(InputDeviceLightType::INPUT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
 
     ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
     ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
@@ -10149,7 +10271,7 @@
 
 TEST_F(LightControllerTest, MultiColorRGBLight) {
     RawLightInfo infoColor = {.id = 1,
-                              .name = "red",
+                              .name = "multi_color",
                               .maxBrightness = 255,
                               .flags = InputLightClass::BRIGHTNESS |
                                       InputLightClass::MULTI_INTENSITY |
@@ -10163,7 +10285,34 @@
     controller.populateDeviceInfo(&info);
     std::vector<InputDeviceLightInfo> lights = info.getLights();
     ASSERT_EQ(1U, lights.size());
-    ASSERT_EQ(InputDeviceLightType::MULTI_COLOR, lights[0].type);
+    ASSERT_EQ(InputDeviceLightType::INPUT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
+
+    ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
+    ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
+}
+
+TEST_F(LightControllerTest, MultiColorRGBKeyboardBacklight) {
+    RawLightInfo infoColor = {.id = 1,
+                              .name = "multi_color_keyboard_backlight",
+                              .maxBrightness = 255,
+                              .flags = InputLightClass::BRIGHTNESS |
+                                      InputLightClass::MULTI_INTENSITY |
+                                      InputLightClass::MULTI_INDEX |
+                                      InputLightClass::KEYBOARD_BACKLIGHT,
+                              .path = ""};
+
+    mFakeEventHub->addRawLightInfo(infoColor.id, std::move(infoColor));
+
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    std::vector<InputDeviceLightInfo> lights = info.getLights();
+    ASSERT_EQ(1U, lights.size());
+    ASSERT_EQ(InputDeviceLightType::KEYBOARD_BACKLIGHT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
 
     ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
     ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
@@ -10201,6 +10350,8 @@
     std::vector<InputDeviceLightInfo> lights = info.getLights();
     ASSERT_EQ(1U, lights.size());
     ASSERT_EQ(InputDeviceLightType::PLAYER_ID, lights[0].type);
+    ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
 
     ASSERT_FALSE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
     ASSERT_TRUE(controller.setLightPlayerId(lights[0].id, LIGHT_PLAYER_ID));
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index f4ecba2..55c2db6 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -21,7 +21,6 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
-
 cc_fuzz {
     name: "inputflinger_latencytracker_fuzzer",
     defaults: [
@@ -34,7 +33,6 @@
         "libbase",
         "libbinder",
         "liblog",
-        "libui",
         "libutils",
         "libinput",
         "libinputflinger",
@@ -43,7 +41,7 @@
         "LatencyTrackerFuzzer.cpp",
     ],
     fuzz_config: {
-       cc: ["android-framework-input@google.com"],
+        cc: ["android-framework-input@google.com"],
     },
 }
 
@@ -63,7 +61,6 @@
         "libcutils",
         "liblog",
         "libutils",
-        "libui",
         "libinput",
         "libinputflinger",
         "libinputreader",
@@ -75,7 +72,7 @@
         "libinputreader_headers",
     ],
     fuzz_config: {
-       cc: ["android-framework-input@google.com"],
+        cc: ["android-framework-input@google.com"],
     },
 }
 
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index f5bd297..a9f5a3a 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -169,7 +169,6 @@
     std::shared_ptr<FuzzEventHub> fuzzEventHub = std::make_shared<FuzzEventHub>(fdp);
     std::unique_ptr<FuzzInputReader> reader =
             std::make_unique<FuzzInputReader>(fuzzEventHub, fuzzPolicy, fuzzListener);
-    fuzzEventHub->addEvents(fdp);
     size_t patternCount = fdp->ConsumeIntegralInRange<size_t>(1, 260);
     VibrationSequence pattern(patternCount);
     for (size_t i = 0; i < patternCount; ++i) {
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 53a7b16..03c2266 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -114,30 +114,12 @@
     InputDeviceIdentifier mIdentifier;
     std::vector<TouchVideoFrame> mVideoFrames;
     PropertyMap mFuzzConfig;
-    size_t mCount = 0;
-    std::array<RawEvent, kMaxSize> mBuf;
     std::shared_ptr<FuzzedDataProvider> mFdp;
 
 public:
     FuzzEventHub(std::shared_ptr<FuzzedDataProvider> fdp) : mFdp(std::move(fdp)) {}
     ~FuzzEventHub() {}
     void addProperty(std::string key, std::string value) { mFuzzConfig.addProperty(key, value); }
-    void addEvents(std::shared_ptr<FuzzedDataProvider> fdp) {
-        mCount = fdp->ConsumeIntegralInRange<size_t>(0, kMaxSize);
-
-        for (size_t i = 0; i < mCount; ++i) {
-            int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
-                                              : fdp->ConsumeIntegral<int32_t>();
-            int32_t code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
-                                              : fdp->ConsumeIntegral<int32_t>();
-            mBuf[i] = {fdp->ConsumeIntegral<nsecs_t>(),
-                       fdp->ConsumeIntegral<nsecs_t>(),
-                       fdp->ConsumeIntegral<int32_t>(),
-                       type,
-                       code,
-                       fdp->ConsumeIntegral<int32_t>()};
-        }
-    }
 
     ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override {
         return ftl::Flags<InputDeviceClass>(mFdp->ConsumeIntegral<uint32_t>());
@@ -168,10 +150,24 @@
         return mFdp->ConsumeIntegral<status_t>();
     }
     void setExcludedDevices(const std::vector<std::string>& devices) override {}
-    size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override {
-        for (size_t i = 0; i < mCount; ++i) buffer[i] = mBuf[i];
-
-        return mCount;
+    std::vector<RawEvent> getEvents(int timeoutMillis) override {
+        std::vector<RawEvent> events;
+        const size_t count = mFdp->ConsumeIntegralInRange<size_t>(0, kMaxSize);
+        for (size_t i = 0; i < count; ++i) {
+            int32_t type = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kValidTypes)
+                                               : mFdp->ConsumeIntegral<int32_t>();
+            int32_t code = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kValidCodes)
+                                               : mFdp->ConsumeIntegral<int32_t>();
+            events.push_back({
+                    .when = mFdp->ConsumeIntegral<nsecs_t>(),
+                    .readTime = mFdp->ConsumeIntegral<nsecs_t>(),
+                    .deviceId = mFdp->ConsumeIntegral<int32_t>(),
+                    .type = type,
+                    .code = code,
+                    .value = mFdp->ConsumeIntegral<int32_t>(),
+            });
+        }
+        return events;
     }
     std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override { return mVideoFrames; }
 
diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp
index 8d6a9bd..3de51a4 100644
--- a/services/stats/StatsAidl.cpp
+++ b/services/stats/StatsAidl.cpp
@@ -66,6 +66,61 @@
                 AStatsEvent_writeBool(event,
                     atomValue.get<VendorAtomValue::boolValue>());
                 break;
+            case VendorAtomValue::repeatedIntValue: {
+                const std::optional<std::vector<int>>& repeatedIntValue =
+                        atomValue.get<VendorAtomValue::repeatedIntValue>();
+                AStatsEvent_writeInt32Array(event, repeatedIntValue->data(),
+                                            repeatedIntValue->size());
+                break;
+            }
+            case VendorAtomValue::repeatedLongValue: {
+                const std::optional<std::vector<int64_t>>& repeatedLongValue =
+                        atomValue.get<VendorAtomValue::repeatedLongValue>();
+                AStatsEvent_writeInt64Array(event, repeatedLongValue->data(),
+                                            repeatedLongValue->size());
+                break;
+            }
+            case VendorAtomValue::repeatedFloatValue: {
+                const std::optional<std::vector<float>>& repeatedFloatValue =
+                        atomValue.get<VendorAtomValue::repeatedFloatValue>();
+                AStatsEvent_writeFloatArray(event, repeatedFloatValue->data(),
+                                            repeatedFloatValue->size());
+                break;
+            }
+            case VendorAtomValue::repeatedStringValue: {
+                const std::optional<std::vector<std::optional<std::string>>>& repeatedStringValue =
+                        atomValue.get<VendorAtomValue::repeatedStringValue>();
+                const std::vector<std::optional<std::string>>& repeatedStringVector =
+                        *repeatedStringValue;
+                const char* cStringArray[repeatedStringVector.size()];
+
+                for (int i = 0; i < repeatedStringVector.size(); ++i) {
+                    cStringArray[i] = repeatedStringVector[i]->c_str();
+                }
+
+                AStatsEvent_writeStringArray(event, cStringArray, repeatedStringVector.size());
+                break;
+            }
+            case VendorAtomValue::repeatedBoolValue: {
+                const std::optional<std::vector<bool>>& repeatedBoolValue =
+                        atomValue.get<VendorAtomValue::repeatedBoolValue>();
+                const std::vector<bool>& repeatedBoolVector = *repeatedBoolValue;
+                bool boolArray[repeatedBoolValue->size()];
+
+                for (int i = 0; i < repeatedBoolVector.size(); ++i) {
+                    boolArray[i] = repeatedBoolVector[i];
+                }
+
+                AStatsEvent_writeBoolArray(event, boolArray, repeatedBoolVector.size());
+                break;
+            }
+            case VendorAtomValue::byteArrayValue: {
+                const std::optional<std::vector<uint8_t>>& byteArrayValue =
+                        atomValue.get<VendorAtomValue::byteArrayValue>();
+
+                AStatsEvent_writeByteArray(event, byteArrayValue->data(), byteArrayValue->size());
+                break;
+            }
         }
     }
     AStatsEvent_build(event);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index 3faa068..6832ae1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -18,9 +18,10 @@
 
 #include <TimeStats/TimeStats.h>
 #include <utils/Timers.h>
-
 #include <memory>
 
+#include "Feature.h"
+
 namespace android {
 
 class HWComposer;
@@ -72,6 +73,8 @@
     // TODO(b/121291683): These will become private/internal
     virtual void preComposition(CompositionRefreshArgs&) = 0;
 
+    virtual FeatureFlags getFeatureFlags() const = 0;
+
     // Debugging
     virtual void dump(std::string&) const = 0;
 };
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Feature.h
similarity index 66%
rename from services/surfaceflinger/BufferStateLayer.h
rename to services/surfaceflinger/CompositionEngine/include/compositionengine/Feature.h
index e53e1c1..ee8000a 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Feature.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 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.
@@ -16,13 +16,15 @@
 
 #pragma once
 
-#include "Layer.h"
+#include <ftl/flags.h>
+#include <cstdint>
 
-namespace android {
+namespace android::compositionengine {
 
-class BufferStateLayer : public Layer {
-public:
-    explicit BufferStateLayer(const LayerCreationArgs& args) : Layer(args){};
+enum class Feature : int32_t {
+    kSnapshotLayerMetadata = 1 << 0,
 };
 
-} // namespace android
+using FeatureFlags = ftl::Flags<Feature>;
+
+} // namespace android::compositionengine
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index a738da0..fe8cad5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -39,6 +39,10 @@
 
 class Fence;
 
+namespace gui {
+struct LayerMetadata;
+}
+
 namespace compositionengine {
 
 struct LayerFECompositionState;
@@ -144,6 +148,8 @@
     // Whether the layer should be rendered with rounded corners.
     virtual bool hasRoundedCorners() const = 0;
     virtual void setWasClientComposed(const sp<Fence>&) {}
+    virtual const gui::LayerMetadata* getMetadata() const = 0;
+    virtual const gui::LayerMetadata* getRelativeMetadata() const = 0;
 };
 
 // TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index 0907926..dd4dbe9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -48,6 +48,8 @@
 
     void preComposition(CompositionRefreshArgs&) override;
 
+    FeatureFlags getFeatureFlags() const override;
+
     // Debugging
     void dump(std::string&) const override;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index f953d0b..a48cc6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -53,6 +53,8 @@
 
     MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
 
+    MOCK_CONST_METHOD0(getFeatureFlags, FeatureFlags());
+
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index be0dbce..14922a4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -53,6 +53,8 @@
     MOCK_CONST_METHOD0(getDebugName, const char*());
     MOCK_CONST_METHOD0(getSequence, int32_t());
     MOCK_CONST_METHOD0(hasRoundedCorners, bool());
+    MOCK_CONST_METHOD0(getMetadata, gui::LayerMetadata*());
+    MOCK_CONST_METHOD0(getRelativeMetadata, gui::LayerMetadata*());
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 855507e..a4e1fff 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -140,6 +140,10 @@
     mNeedsAnotherUpdate = needsAnotherUpdate;
 }
 
+FeatureFlags CompositionEngine::getFeatureFlags() const {
+    return {};
+}
+
 void CompositionEngine::dump(std::string&) const {
     // The base class has no state to dump, but derived classes might.
 }
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index d7704a8..9102139 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -70,6 +70,7 @@
     MOCK_METHOD1(disconnectDisplay, void(HalDisplayId));
     MOCK_CONST_METHOD1(hasDeviceComposition, bool(const std::optional<DisplayId>&));
     MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(HalDisplayId));
+    MOCK_METHOD(nsecs_t, getPresentTimestamp, (PhysicalDisplayId), (const, override));
     MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(HalDisplayId, HWC2::Layer*));
     MOCK_METHOD3(setOutputBuffer,
                  status_t(HalVirtualDisplayId, const sp<Fence>&, const sp<GraphicBuffer>&));
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 029e449..f8115eb 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -192,17 +192,16 @@
 }
 
 void DisplayDevice::setActiveMode(DisplayModeId modeId, const display::DisplaySnapshot& snapshot) {
-    const auto modeOpt = snapshot.displayModes().get(modeId);
-    LOG_ALWAYS_FATAL_IF(!modeOpt, "Unknown mode");
+    const auto fpsOpt = snapshot.displayModes().get(modeId).transform(
+            [](const DisplayModePtr& mode) { return mode->getFps(); });
 
-    mActiveMode = modeOpt->get();
-    const Fps fps = mActiveMode->getFps();
+    LOG_ALWAYS_FATAL_IF(!fpsOpt, "Unknown mode");
+    const Fps fps = *fpsOpt;
 
     ATRACE_INT(mActiveModeFPSTrace.c_str(), fps.getIntValue());
 
-    if (mRefreshRateConfigs) {
-        mRefreshRateConfigs->setActiveModeId(modeId);
-    }
+    mRefreshRateConfigs->setActiveModeId(modeId);
+
     if (mRefreshRateOverlay) {
         mRefreshRateOverlay->changeRefreshRate(fps);
     }
@@ -224,10 +223,6 @@
                                                     constraints, outTimeline);
 }
 
-const DisplayModePtr& DisplayDevice::getActiveMode() const {
-    return mActiveMode;
-}
-
 nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
     const auto physicalId = getPhysicalId();
     if (!mHwComposer.isConnected(physicalId)) {
@@ -240,17 +235,7 @@
         return vsyncPeriod;
     }
 
-    return getActiveMode()->getFps().getPeriodNsecs();
-}
-
-nsecs_t DisplayDevice::getRefreshTimestamp() const {
-    const nsecs_t now = systemTime(CLOCK_MONOTONIC);
-    const auto vsyncPeriodNanos = getVsyncPeriodFromHWC();
-    return now - ((now - mLastHwVsync) % vsyncPeriodNanos);
-}
-
-void DisplayDevice::onVsync(nsecs_t timestamp) {
-    mLastHwVsync = timestamp;
+    return refreshRateConfigs().getActiveModePtr()->getVsyncPeriod();
 }
 
 ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
@@ -312,8 +297,10 @@
 }
 
 void DisplayDevice::persistBrightness(bool needsComposite) {
-    if (needsComposite && mStagedBrightness && mBrightness != *mStagedBrightness) {
-        getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
+    if (mStagedBrightness && mBrightness != *mStagedBrightness) {
+        if (needsComposite) {
+            getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
+        }
         mBrightness = *mStagedBrightness;
     }
     mStagedBrightness = std::nullopt;
@@ -449,7 +436,7 @@
     mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, showSpinnner);
     mRefreshRateOverlay->setLayerStack(getLayerStack());
     mRefreshRateOverlay->setViewport(getSize());
-    mRefreshRateOverlay->changeRefreshRate(getActiveMode()->getFps());
+    mRefreshRateOverlay->changeRefreshRate(getActiveMode().getFps());
 }
 
 bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId,
@@ -490,7 +477,7 @@
     }
 
     // Check if we are already at the desired mode
-    if (getActiveMode()->getId() == info.mode->getId()) {
+    if (refreshRateConfigs().getActiveModePtr()->getId() == info.mode->getId()) {
         return false;
     }
 
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index d79a6b5..510df81 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -189,8 +189,6 @@
     /* ------------------------------------------------------------------------
      * Display mode management.
      */
-    const DisplayModePtr& getActiveMode() const;
-
     struct ActiveModeInfo {
         DisplayModePtr mode;
         scheduler::DisplayModeEvent event = scheduler::DisplayModeEvent::None;
@@ -207,6 +205,10 @@
         return mUpcomingActiveMode;
     }
 
+    const DisplayMode& getActiveMode() const REQUIRES(kMainThreadContext) {
+        return mRefreshRateConfigs->getActiveMode();
+    }
+
     // Precondition: DisplaySnapshot must contain a mode with DisplayModeId.
     void setActiveMode(DisplayModeId, const display::DisplaySnapshot&) REQUIRES(kMainThreadContext);
 
@@ -226,14 +228,12 @@
     }
 
     // Enables an overlay to be displayed with the current refresh rate
-    void enableRefreshRateOverlay(bool enable, bool showSpinner);
+    void enableRefreshRateOverlay(bool enable, bool showSpinner) REQUIRES(kMainThreadContext);
     bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
     bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
     void animateRefreshRateOverlay();
 
-    void onVsync(nsecs_t timestamp);
     nsecs_t getVsyncPeriodFromHWC() const;
-    nsecs_t getRefreshTimestamp() const;
 
     status_t setRefreshRatePolicy(
             const std::optional<scheduler::RefreshRateConfigs::Policy>& policy,
@@ -265,13 +265,11 @@
 
     static ui::Transform::RotationFlags sPrimaryDisplayRotationFlags;
 
-    // allow initial power mode as null.
+    // Allow nullopt as initial power mode.
     std::optional<hardware::graphics::composer::hal::PowerMode> mPowerMode;
-    DisplayModePtr mActiveMode;
-    std::optional<float> mStagedBrightness = std::nullopt;
-    float mBrightness = -1.f;
 
-    std::atomic<nsecs_t> mLastHwVsync = 0;
+    std::optional<float> mStagedBrightness;
+    float mBrightness = -1.f;
 
     // TODO(b/182939859): Remove special cases for primary display.
     const bool mIsPrimary;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 486eaf8..96399e2 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -70,7 +70,7 @@
 struct ComposerCallback {
     virtual void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) = 0;
     virtual void onComposerHalRefresh(hal::HWDisplayId) = 0;
-    virtual void onComposerHalVsync(hal::HWDisplayId, int64_t timestamp,
+    virtual void onComposerHalVsync(hal::HWDisplayId, nsecs_t timestamp,
                                     std::optional<hal::VsyncPeriodNanos>) = 0;
     virtual void onComposerHalVsyncPeriodTimingChanged(hal::HWDisplayId,
                                                        const hal::VsyncPeriodChangeTimeline&) = 0;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 15d5041..0a4ad97 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -144,7 +144,7 @@
     return mUpdateDeviceProductInfoOnHotplugReconnect;
 }
 
-bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) {
+bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, nsecs_t timestamp) {
     const auto displayId = toPhysicalDisplayId(hwcDisplayId);
     if (!displayId) {
         LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display");
@@ -160,13 +160,13 @@
         // with the same timestamp when turning the display off and on. This
         // is a bug in the HWC implementation, but filter the extra events
         // out here so they don't cause havoc downstream.
-        if (timestamp == displayData.lastHwVsync) {
+        if (timestamp == displayData.lastPresentTimestamp) {
             ALOGW("Ignoring duplicate VSYNC event from HWC for display %s (t=%" PRId64 ")",
                   to_string(*displayId).c_str(), timestamp);
             return false;
         }
 
-        displayData.lastHwVsync = timestamp;
+        displayData.lastPresentTimestamp = timestamp;
     }
 
     const auto tag = "HW_VSYNC_" + to_string(*displayId);
@@ -485,6 +485,11 @@
     return mDisplayData.at(displayId).lastPresentFence;
 }
 
+nsecs_t HWComposer::getPresentTimestamp(PhysicalDisplayId displayId) const {
+    RETURN_IF_INVALID_DISPLAY(displayId, 0);
+    return mDisplayData.at(displayId).lastPresentTimestamp;
+}
+
 sp<Fence> HWComposer::getLayerReleaseFence(HalDisplayId displayId, HWC2::Layer* layer) const {
     RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
     const auto& displayFences = mDisplayData.at(displayId).releaseFences;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 92a8f30..6c43d8b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -160,8 +160,9 @@
     // reset state when a display is disconnected
     virtual void disconnectDisplay(HalDisplayId) = 0;
 
-    // get the present fence received from the last call to present.
+    // Get the present fence/timestamp received from the last call to present.
     virtual sp<Fence> getPresentFence(HalDisplayId) const = 0;
+    virtual nsecs_t getPresentTimestamp(PhysicalDisplayId) const = 0;
 
     // Get last release fence for the given layer
     virtual sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const = 0;
@@ -214,7 +215,7 @@
     // TODO(b/157555476): Remove when the framework has proper support for headless mode
     virtual bool updatesDeviceProductInfoOnHotplugReconnect() const = 0;
 
-    virtual bool onVsync(hal::HWDisplayId, int64_t timestamp) = 0;
+    virtual bool onVsync(hal::HWDisplayId, nsecs_t timestamp) = 0;
     virtual void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) = 0;
 
     virtual bool isConnected(PhysicalDisplayId) const = 0;
@@ -343,8 +344,9 @@
     // reset state when a display is disconnected
     void disconnectDisplay(HalDisplayId) override;
 
-    // get the present fence received from the last call to present.
+    // Get the present fence/timestamp received from the last call to present.
     sp<Fence> getPresentFence(HalDisplayId) const override;
+    nsecs_t getPresentTimestamp(PhysicalDisplayId) const override;
 
     // Get last release fence for the given layer
     sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const override;
@@ -387,7 +389,7 @@
 
     bool updatesDeviceProductInfoOnHotplugReconnect() const override;
 
-    bool onVsync(hal::HWDisplayId, int64_t timestamp) override;
+    bool onVsync(hal::HWDisplayId, nsecs_t timestamp) override;
     void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) override;
 
     bool isConnected(PhysicalDisplayId) const override;
@@ -456,7 +458,10 @@
 
     struct DisplayData {
         std::unique_ptr<HWC2::Display> hwcDisplay;
+
         sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
+        nsecs_t lastPresentTimestamp = 0;
+
         std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
 
         bool validateWasSkipped;
@@ -466,8 +471,6 @@
 
         std::mutex vsyncEnabledLock;
         hal::Vsync vsyncEnabled GUARDED_BY(vsyncEnabledLock) = hal::Vsync::DISABLE;
-
-        nsecs_t lastHwVsync = 0;
     };
 
     std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 08b71c2..410e438 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -65,7 +65,6 @@
 #include <mutex>
 #include <sstream>
 
-#include "BufferStateLayer.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWComposer.h"
 #include "FrameTimeline.h"
@@ -165,7 +164,6 @@
         mLayerCreationFlags(args.flags),
         mBorderEnabled(false),
         mTextureName(args.textureName),
-        mCompositionState{mFlinger->getCompositionEngine().createLayerFECompositionState()},
         mHwcSlotGenerator(sp<HwcSlotGenerator>::make()) {
     ALOGV("Creating Layer %s", getDebugName());
 
@@ -239,6 +237,12 @@
     mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
     mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp;
     mDrawingState.dataspace = ui::Dataspace::V0_SRGB;
+
+    mSnapshot->sequence = sequence;
+    mSnapshot->name = getDebugName();
+    mSnapshot->textureName = mTextureName;
+    mSnapshot->premultipliedAlpha = mPremultipliedAlpha;
+    mSnapshot->transform = {};
 }
 
 void Layer::onFirstRef() {
@@ -504,40 +508,40 @@
                                         : Hwc2::IComposerClient::BlendMode::COVERAGE;
     }
 
-    auto* compositionState = editCompositionState();
-    compositionState->outputFilter = getOutputFilter();
-    compositionState->isVisible = isVisible();
-    compositionState->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
-    compositionState->shadowRadius = mEffectiveShadowRadius;
+    auto* snapshot = editLayerSnapshot();
+    snapshot->outputFilter = getOutputFilter();
+    snapshot->isVisible = isVisible();
+    snapshot->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
+    snapshot->shadowRadius = mEffectiveShadowRadius;
 
-    compositionState->contentDirty = contentDirty;
+    snapshot->contentDirty = contentDirty;
     contentDirty = false;
 
-    compositionState->geomLayerBounds = mBounds;
-    compositionState->geomLayerTransform = getTransform();
-    compositionState->geomInverseLayerTransform = compositionState->geomLayerTransform.inverse();
-    compositionState->transparentRegionHint = getActiveTransparentRegion(drawingState);
+    snapshot->geomLayerBounds = mBounds;
+    snapshot->geomLayerTransform = getTransform();
+    snapshot->geomInverseLayerTransform = snapshot->geomLayerTransform.inverse();
+    snapshot->transparentRegionHint = getActiveTransparentRegion(drawingState);
 
-    compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
-    compositionState->alpha = alpha;
-    compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
-    compositionState->blurRegions = drawingState.blurRegions;
-    compositionState->stretchEffect = getStretchEffect();
+    snapshot->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
+    snapshot->alpha = alpha;
+    snapshot->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+    snapshot->blurRegions = drawingState.blurRegions;
+    snapshot->stretchEffect = getStretchEffect();
 }
 
 void Layer::prepareGeometryCompositionState() {
     const auto& drawingState{getDrawingState()};
-    auto* compositionState = editCompositionState();
+    auto* snapshot = editLayerSnapshot();
 
-    compositionState->geomBufferSize = getBufferSize(drawingState);
-    compositionState->geomContentCrop = getBufferCrop();
-    compositionState->geomCrop = getCrop(drawingState);
-    compositionState->geomBufferTransform = getBufferTransform();
-    compositionState->geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse();
-    compositionState->geomUsesSourceCrop = usesSourceCrop();
-    compositionState->isSecure = isSecure();
+    snapshot->geomBufferSize = getBufferSize(drawingState);
+    snapshot->geomContentCrop = getBufferCrop();
+    snapshot->geomCrop = getCrop(drawingState);
+    snapshot->geomBufferTransform = getBufferTransform();
+    snapshot->geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse();
+    snapshot->geomUsesSourceCrop = usesSourceCrop();
+    snapshot->isSecure = isSecure();
 
-    compositionState->metadata.clear();
+    snapshot->metadata.clear();
     const auto& supportedMetadata = mFlinger->getHwComposer().getSupportedLayerGenericMetadata();
     for (const auto& [key, mandatory] : supportedMetadata) {
         const auto& genericLayerMetadataCompatibilityMap =
@@ -553,45 +557,45 @@
             continue;
         }
 
-        compositionState->metadata
-                .emplace(key, compositionengine::GenericLayerMetadataEntry{mandatory, it->second});
+        snapshot->metadata.emplace(key,
+                                   compositionengine::GenericLayerMetadataEntry{mandatory,
+                                                                                it->second});
     }
 }
 
 void Layer::preparePerFrameCompositionState() {
     const auto& drawingState{getDrawingState()};
-    auto* compositionState = editCompositionState();
+    auto* snapshot = editLayerSnapshot();
 
-    compositionState->forceClientComposition = false;
+    snapshot->forceClientComposition = false;
 
-    compositionState->isColorspaceAgnostic = isColorSpaceAgnostic();
-    compositionState->dataspace = getDataSpace();
-    compositionState->colorTransform = getColorTransform();
-    compositionState->colorTransformIsIdentity = !hasColorTransform();
-    compositionState->surfaceDamage = surfaceDamageRegion;
-    compositionState->hasProtectedContent = isProtected();
-    compositionState->dimmingEnabled = isDimmingEnabled();
+    snapshot->isColorspaceAgnostic = isColorSpaceAgnostic();
+    snapshot->dataspace = getDataSpace();
+    snapshot->colorTransform = getColorTransform();
+    snapshot->colorTransformIsIdentity = !hasColorTransform();
+    snapshot->surfaceDamage = surfaceDamageRegion;
+    snapshot->hasProtectedContent = isProtected();
+    snapshot->dimmingEnabled = isDimmingEnabled();
 
     const bool usesRoundedCorners = hasRoundedCorners();
 
-    compositionState->isOpaque =
-            isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
+    snapshot->isOpaque = isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
 
     // Force client composition for special cases known only to the front-end.
     // Rounded corners no longer force client composition, since we may use a
     // hole punch so that the layer will appear to have rounded corners.
     if (isHdrY410() || drawShadows() || drawingState.blurRegions.size() > 0 ||
-        compositionState->stretchEffect.hasEffect()) {
-        compositionState->forceClientComposition = true;
+        snapshot->stretchEffect.hasEffect()) {
+        snapshot->forceClientComposition = true;
     }
     // If there are no visible region changes, we still need to update blur parameters.
-    compositionState->blurRegions = drawingState.blurRegions;
-    compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+    snapshot->blurRegions = drawingState.blurRegions;
+    snapshot->backgroundBlurRadius = drawingState.backgroundBlurRadius;
 
     // Layer framerate is used in caching decisions.
     // Retrieve it from the scheduler which maintains an instance of LayerHistory, and store it in
     // LayerFECompositionState where it would be visible to Flattener.
-    compositionState->fps = mFlinger->getLayerFramerate(systemTime(), getSequence());
+    snapshot->fps = mFlinger->getLayerFramerate(systemTime(), getSequence());
 
     if (hasBufferOrSidebandStream()) {
         preparePerFrameBufferCompositionState();
@@ -602,41 +606,41 @@
 
 void Layer::preparePerFrameBufferCompositionState() {
     // Sideband layers
-    auto* compositionState = editCompositionState();
-    if (compositionState->sidebandStream.get() && !compositionState->sidebandStreamHasFrame) {
-        compositionState->compositionType =
+    auto* snapshot = editLayerSnapshot();
+    if (snapshot->sidebandStream.get() && !snapshot->sidebandStreamHasFrame) {
+        snapshot->compositionType =
                 aidl::android::hardware::graphics::composer3::Composition::SIDEBAND;
         return;
     } else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) {
-        compositionState->compositionType =
+        snapshot->compositionType =
                 aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
     } else {
         // Normal buffer layers
-        compositionState->hdrMetadata = mBufferInfo.mHdrMetadata;
-        compositionState->compositionType = mPotentialCursor
+        snapshot->hdrMetadata = mBufferInfo.mHdrMetadata;
+        snapshot->compositionType = mPotentialCursor
                 ? aidl::android::hardware::graphics::composer3::Composition::CURSOR
                 : aidl::android::hardware::graphics::composer3::Composition::DEVICE;
     }
 
-    compositionState->buffer = getBuffer();
-    compositionState->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
+    snapshot->buffer = getBuffer();
+    snapshot->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
             ? 0
             : mBufferInfo.mBufferSlot;
-    compositionState->acquireFence = mBufferInfo.mFence;
-    compositionState->frameNumber = mBufferInfo.mFrameNumber;
-    compositionState->sidebandStreamHasFrame = false;
+    snapshot->acquireFence = mBufferInfo.mFence;
+    snapshot->frameNumber = mBufferInfo.mFrameNumber;
+    snapshot->sidebandStreamHasFrame = false;
 }
 
 void Layer::preparePerFrameEffectsCompositionState() {
-    auto* compositionState = editCompositionState();
-    compositionState->color = getColor();
-    compositionState->compositionType =
+    auto* snapshot = editLayerSnapshot();
+    snapshot->color = getColor();
+    snapshot->compositionType =
             aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR;
 }
 
 void Layer::prepareCursorCompositionState() {
     const State& drawingState{getDrawingState()};
-    auto* compositionState = editCompositionState();
+    auto* snapshot = editLayerSnapshot();
 
     // Apply the layer's transform, followed by the display's global transform
     // Here we're guaranteed that the layer's transform preserves rects
@@ -645,7 +649,7 @@
     Rect bounds = reduce(win, getActiveTransparentRegion(drawingState));
     Rect frame(getTransform().transform(bounds));
 
-    compositionState->cursorFrame = frame;
+    snapshot->cursorFrame = frame;
 }
 
 sp<compositionengine::LayerFE> Layer::asLayerFE() const {
@@ -687,31 +691,30 @@
         compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
     ATRACE_CALL();
 
-    if (!getCompositionState()) {
+    const auto* snapshot = getLayerSnapshot();
+    if (!snapshot) {
         return {};
     }
 
-    FloatRect bounds = getBounds();
-    half alpha = getAlpha();
-
     compositionengine::LayerFE::LayerSettings layerSettings;
-    layerSettings.geometry.boundaries = bounds;
-    layerSettings.geometry.positionTransform = getTransform().asMatrix4();
+    layerSettings.geometry.boundaries =
+            reduce(snapshot->geomLayerBounds, snapshot->transparentRegionHint);
+    layerSettings.geometry.positionTransform = snapshot->geomLayerTransform.asMatrix4();
 
     // skip drawing content if the targetSettings indicate the content will be occluded
     const bool drawContent = targetSettings.realContentIsVisible || targetSettings.clearContent;
     layerSettings.skipContentDraw = !drawContent;
 
     if (hasColorTransform()) {
-        layerSettings.colorTransform = getColorTransform();
+        layerSettings.colorTransform = snapshot->colorTransform;
     }
 
-    const auto roundedCornerState = getRoundedCornerState();
+    const auto& roundedCornerState = snapshot->roundedCorner;
     layerSettings.geometry.roundedCornersRadius = roundedCornerState.radius;
     layerSettings.geometry.roundedCornersCrop = roundedCornerState.cropRect;
 
-    layerSettings.alpha = alpha;
-    layerSettings.sourceDataspace = getDataSpace();
+    layerSettings.alpha = snapshot->alpha;
+    layerSettings.sourceDataspace = snapshot->dataspace;
 
     // Override the dataspace transfer from 170M to sRGB if the device configuration requests this.
     // We do this here instead of in buffer info so that dumpsys can still report layers that are
@@ -728,26 +731,24 @@
     layerSettings.whitePointNits = targetSettings.whitePointNits;
     switch (targetSettings.blurSetting) {
         case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled:
-            layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
-            layerSettings.blurRegions = getBlurRegions();
-            layerSettings.blurRegionTransform =
-                    getActiveTransform(getDrawingState()).inverse().asMatrix4();
+            layerSettings.backgroundBlurRadius = snapshot->backgroundBlurRadius;
+            layerSettings.blurRegions = snapshot->blurRegions;
+            layerSettings.blurRegionTransform = snapshot->geomInverseLayerTransform.asMatrix4();
             break;
         case LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly:
-            layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
+            layerSettings.backgroundBlurRadius = snapshot->backgroundBlurRadius;
             break;
         case LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly:
-            layerSettings.blurRegions = getBlurRegions();
-            layerSettings.blurRegionTransform =
-                    getActiveTransform(getDrawingState()).inverse().asMatrix4();
+            layerSettings.blurRegions = snapshot->blurRegions;
+            layerSettings.blurRegionTransform = snapshot->geomInverseLayerTransform.asMatrix4();
             break;
         case LayerFE::ClientCompositionTargetSettings::BlurSetting::Disabled:
         default:
             break;
     }
-    layerSettings.stretchEffect = getStretchEffect();
+    layerSettings.stretchEffect = snapshot->stretchEffect;
     // Record the name of the layer for debugging further down the stack.
-    layerSettings.name = getName();
+    layerSettings.name = snapshot->name;
 
     if (hasEffect() && !hasBufferOrSidebandStream()) {
         prepareEffectsClientComposition(layerSettings, targetSettings);
@@ -768,7 +769,7 @@
 
     // If layer is blacked out, force alpha to 1 so that we draw a black color layer.
     layerSettings.alpha = blackout ? 1.0f : 0.0f;
-    layerSettings.name = getName();
+    layerSettings.name = getLayerSnapshot()->name;
 }
 
 void Layer::prepareEffectsClientComposition(
@@ -786,39 +787,41 @@
 void Layer::prepareBufferStateClientComposition(
         compositionengine::LayerFE::LayerSettings& layerSettings,
         compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
-    if (CC_UNLIKELY(!mBufferInfo.mBuffer)) {
-        // For surfaceview of tv sideband, there is no activeBuffer
-        // in bufferqueue, we need return LayerSettings.
+    ATRACE_CALL();
+    const auto* snapshot = getLayerSnapshot();
+    if (CC_UNLIKELY(!snapshot->externalTexture)) {
+        // If there is no buffer for the layer or we have sidebandstream where there is no
+        // activeBuffer, then we need to return LayerSettings.
         return;
     }
-    const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
-            ((isSecure() || isProtected()) && !targetSettings.isSecure);
+    const bool blackOutLayer =
+            (snapshot->hasProtectedContent && !targetSettings.supportsProtectedContent) ||
+            ((snapshot->isSecure || snapshot->hasProtectedContent) && !targetSettings.isSecure);
     const bool bufferCanBeUsedAsHwTexture =
-            mBufferInfo.mBuffer->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
+            snapshot->externalTexture->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
     if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
         ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
-                 mName.c_str());
+                 snapshot->name.c_str());
         prepareClearClientComposition(layerSettings, true /* blackout */);
         return;
     }
 
-    const State& s(getDrawingState());
-    layerSettings.source.buffer.buffer = mBufferInfo.mBuffer;
-    layerSettings.source.buffer.isOpaque = isOpaque(s);
-    layerSettings.source.buffer.fence = mBufferInfo.mFence;
-    layerSettings.source.buffer.textureName = mTextureName;
-    layerSettings.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
-    layerSettings.source.buffer.isY410BT2020 = isHdrY410();
-    bool hasSmpte2086 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::SMPTE2086;
-    bool hasCta861_3 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::CTA861_3;
+    layerSettings.source.buffer.buffer = snapshot->externalTexture;
+    layerSettings.source.buffer.isOpaque = snapshot->contentOpaque;
+    layerSettings.source.buffer.fence = snapshot->acquireFence;
+    layerSettings.source.buffer.textureName = snapshot->textureName;
+    layerSettings.source.buffer.usePremultipliedAlpha = snapshot->premultipliedAlpha;
+    layerSettings.source.buffer.isY410BT2020 = snapshot->isHdrY410;
+    bool hasSmpte2086 = snapshot->hdrMetadata.validTypes & HdrMetadata::SMPTE2086;
+    bool hasCta861_3 = snapshot->hdrMetadata.validTypes & HdrMetadata::CTA861_3;
     float maxLuminance = 0.f;
     if (hasSmpte2086 && hasCta861_3) {
-        maxLuminance = std::min(mBufferInfo.mHdrMetadata.smpte2086.maxLuminance,
-                                mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel);
+        maxLuminance = std::min(snapshot->hdrMetadata.smpte2086.maxLuminance,
+                                snapshot->hdrMetadata.cta8613.maxContentLightLevel);
     } else if (hasSmpte2086) {
-        maxLuminance = mBufferInfo.mHdrMetadata.smpte2086.maxLuminance;
+        maxLuminance = snapshot->hdrMetadata.smpte2086.maxLuminance;
     } else if (hasCta861_3) {
-        maxLuminance = mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel;
+        maxLuminance = snapshot->hdrMetadata.cta8613.maxContentLightLevel;
     } else {
         switch (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) {
             case HAL_DATASPACE_TRANSFER_ST2084:
@@ -829,17 +832,17 @@
         }
     }
     layerSettings.source.buffer.maxLuminanceNits = maxLuminance;
-    layerSettings.frameNumber = mCurrentFrameNumber;
-    layerSettings.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0;
+    layerSettings.frameNumber = snapshot->frameNumber;
+    layerSettings.bufferId = snapshot->externalTexture->getId();
 
-    const bool useFiltering =
-            targetSettings.needsFiltering || mNeedsFiltering || bufferNeedsFiltering();
+    const bool useFiltering = targetSettings.needsFiltering ||
+            snapshot->geomLayerTransform.needsBilinearFiltering() || snapshot->bufferNeedsFiltering;
 
     // Query the texture matrix given our current filtering mode.
     float textureMatrix[16];
     getDrawingTransformMatrix(useFiltering, textureMatrix);
 
-    if (getTransformToDisplayInverse()) {
+    if (snapshot->geomBufferUsesDisplayInverseTransform) {
         /*
          * the code below applies the primary display's inverse transform to
          * the texture transform
@@ -856,25 +859,22 @@
          * of a camera where the buffer remains in native orientation,
          * we want the pixels to always be upright.
          */
-        sp<Layer> p = mDrawingParent.promote();
-        if (p != nullptr) {
-            const auto parentTransform = p->getTransform();
-            tr = tr * inverseOrientation(parentTransform.getOrientation());
-        }
+        const auto parentTransform = snapshot->transform;
+        tr = tr * inverseOrientation(parentTransform.getOrientation());
 
         // and finally apply it to the original texture matrix
         const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
         memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
     }
 
-    const Rect win{getBounds()};
-    float bufferWidth = getBufferSize(s).getWidth();
-    float bufferHeight = getBufferSize(s).getHeight();
+    const Rect win{layerSettings.geometry.boundaries};
+    float bufferWidth = snapshot->bufferSize.getWidth();
+    float bufferHeight = snapshot->bufferSize.getHeight();
 
-    // BufferStateLayers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
+    // Layers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
     // been set and there is no parent layer bounds. In that case, the scale is meaningless so
     // ignore them.
-    if (!getBufferSize(s).isValid()) {
+    if (!snapshot->bufferSize.isValid()) {
         bufferWidth = float(win.right) - float(win.left);
         bufferHeight = float(win.bottom) - float(win.top);
     }
@@ -2182,35 +2182,17 @@
 
 void Layer::prepareShadowClientComposition(LayerFE::LayerSettings& caster,
                                            const Rect& layerStackRect) const {
-    renderengine::ShadowSettings state = mFlinger->mDrawingState.globalShadowSettings;
-
-    // Note: this preserves existing behavior of shadowing the entire layer and not cropping it if
-    // transparent regions are present. This may not be necessary since shadows are typically cast
-    // by layers without transparent regions.
-    state.boundaries = mBounds;
+    const auto* snapshot = getLayerSnapshot();
+    renderengine::ShadowSettings state = snapshot->shadowSettings;
+    if (state.length <= 0.f || (state.ambientColor.a <= 0.f && state.spotColor.a <= 0.f)) {
+        return;
+    }
 
     // Shift the spot light x-position to the middle of the display and then
     // offset it by casting layer's screen pos.
-    state.lightPos.x = (layerStackRect.width() / 2.f) - mScreenBounds.left;
-    state.lightPos.y -= mScreenBounds.top;
-
-    state.length = mEffectiveShadowRadius;
-
-    if (state.length > 0.f) {
-        const float casterAlpha = caster.alpha;
-        const bool casterIsOpaque =
-                ((caster.source.buffer.buffer != nullptr) && caster.source.buffer.isOpaque);
-
-        // If the casting layer is translucent, we need to fill in the shadow underneath the layer.
-        // Otherwise the generated shadow will only be shown around the casting layer.
-        state.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f);
-        state.ambientColor *= casterAlpha;
-        state.spotColor *= casterAlpha;
-
-        if (state.ambientColor.a > 0.f && state.spotColor.a > 0.f) {
-            caster.shadow = state;
-        }
-    }
+    state.lightPos.x = (layerStackRect.width() / 2.f) - snapshot->transformedBounds.left;
+    state.lightPos.y -= snapshot->transformedBounds.top;
+    caster.shadow = state;
 }
 
 bool Layer::findInHierarchy(const sp<Layer>& l) {
@@ -3035,17 +3017,6 @@
 
     mFlinger->getTransactionCallbackInvoker().addCallbackHandles(mDrawingState.callbackHandles,
                                                                  jankData);
-
-    sp<Fence> releaseFence = Fence::NO_FENCE;
-    for (auto& handle : mDrawingState.callbackHandles) {
-        if (handle->releasePreviousBuffer &&
-            mDrawingState.releaseBufferEndpoint == handle->listener) {
-            releaseFence =
-                    handle->previousReleaseFence ? handle->previousReleaseFence : Fence::NO_FENCE;
-            break;
-        }
-    }
-
     mDrawingState.callbackHandles = {};
 }
 
@@ -3177,8 +3148,7 @@
                       const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
                       bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime,
                       const FrameTimelineInfo& info) {
-    ATRACE_CALL();
-
+    ATRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false"));
     if (!buffer) {
         return false;
     }
@@ -3187,6 +3157,7 @@
             bufferData.flags.test(BufferData::BufferDataChange::frameNumberChanged);
     const uint64_t frameNumber =
             frameNumberChanged ? bufferData.frameNumber : mDrawingState.frameNumber + 1;
+    ATRACE_FORMAT_INSTANT("setBuffer %s - %" PRIu64, getDebugName(), frameNumber);
 
     if (mDrawingState.buffer) {
         mReleasePreviousBuffer = true;
@@ -3419,13 +3390,14 @@
 
 bool Layer::latchSidebandStream(bool& recomputeVisibleRegions) {
     // We need to update the sideband stream if the layer has both a buffer and a sideband stream.
-    editCompositionState()->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get();
+    auto* snapshot = editLayerSnapshot();
+    snapshot->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get();
 
     if (mSidebandStreamChanged.exchange(false)) {
         const State& s(getDrawingState());
         // mSidebandStreamChanged was true
         mSidebandStream = s.sidebandStream;
-        editCompositionState()->sidebandStream = mSidebandStream;
+        snapshot->sidebandStream = mSidebandStream;
         if (mSidebandStream != nullptr) {
             setTransactionFlags(eTransactionNeeded);
             mFlinger->setTransactionFlags(eTraversalNeeded);
@@ -3538,7 +3510,7 @@
 sp<Layer> Layer::createClone() {
     LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
     args.textureName = mTextureName;
-    sp<BufferStateLayer> layer = mFlinger->getFactory().createBufferStateLayer(args);
+    sp<Layer> layer = mFlinger->getFactory().createBufferStateLayer(args);
     layer->mHwcSlotGenerator = mHwcSlotGenerator;
     layer->setInitialValuesForClone(sp<Layer>::fromExisting(this));
     return layer;
@@ -3825,12 +3797,15 @@
     }
 }
 
-compositionengine::LayerFECompositionState* Layer::editCompositionState() {
-    return mCompositionState.get();
+const Layer::LayerSnapshot* Layer::getLayerSnapshot() const {
+    return mSnapshot.get();
 }
 
+Layer::LayerSnapshot* Layer::editLayerSnapshot() {
+    return mSnapshot.get();
+}
 const compositionengine::LayerFECompositionState* Layer::getCompositionState() const {
-    return mCompositionState.get();
+    return mSnapshot.get();
 }
 
 void Layer::useSurfaceDamage() {
@@ -3943,9 +3918,14 @@
             mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
         } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
                    displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
-            // The HWC doesn't support present fences, so use the refresh
-            // timestamp instead.
-            const nsecs_t actualPresentTime = display->getRefreshTimestamp();
+            // The HWC doesn't support present fences, so use the present timestamp instead.
+            const nsecs_t presentTimestamp =
+                    mFlinger->getHwComposer().getPresentTimestamp(*displayId);
+
+            const nsecs_t now = systemTime(CLOCK_MONOTONIC);
+            const nsecs_t vsyncPeriod = display->getVsyncPeriodFromHWC();
+            const nsecs_t actualPresentTime = now - ((now - presentTimestamp) % vsyncPeriod);
+
             mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
                                                  refreshRate, renderRate, vote, gameMode);
             mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(),
@@ -4225,13 +4205,79 @@
         return;
     }
 
+    auto* snapshot = editLayerSnapshot();
     if (updateGeometry) {
         prepareBasicGeometryCompositionState();
         prepareGeometryCompositionState();
+        snapshot->roundedCorner = getRoundedCornerState();
+        snapshot->stretchEffect = getStretchEffect();
+        snapshot->transformedBounds = mScreenBounds;
+        if (mEffectiveShadowRadius > 0.f) {
+            snapshot->shadowSettings = mFlinger->mDrawingState.globalShadowSettings;
+
+            // Note: this preserves existing behavior of shadowing the entire layer and not cropping
+            // it if transparent regions are present. This may not be necessary since shadows are
+            // typically cast by layers without transparent regions.
+            snapshot->shadowSettings.boundaries = mBounds;
+
+            const float casterAlpha = snapshot->alpha;
+            const bool casterIsOpaque =
+                    ((mBufferInfo.mBuffer != nullptr) && isOpaque(mDrawingState));
+
+            // If the casting layer is translucent, we need to fill in the shadow underneath the
+            // layer. Otherwise the generated shadow will only be shown around the casting layer.
+            snapshot->shadowSettings.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f);
+            snapshot->shadowSettings.ambientColor *= casterAlpha;
+            snapshot->shadowSettings.spotColor *= casterAlpha;
+        }
+        snapshot->shadowSettings.length = mEffectiveShadowRadius;
     }
+    snapshot->contentOpaque = isOpaque(mDrawingState);
+    snapshot->isHdrY410 = isHdrY410();
+    snapshot->bufferNeedsFiltering = bufferNeedsFiltering();
+    sp<Layer> p = mDrawingParent.promote();
+    if (p != nullptr) {
+        snapshot->transform = p->getTransform();
+    } else {
+        snapshot->transform.reset();
+    }
+    snapshot->bufferSize = getBufferSize(mDrawingState);
+    snapshot->externalTexture = mBufferInfo.mBuffer;
     preparePerFrameCompositionState();
 }
 
+void Layer::updateMetadataSnapshot(const LayerMetadata& parentMetadata) {
+    mSnapshot->layerMetadata = parentMetadata;
+    mSnapshot->layerMetadata.merge(mDrawingState.metadata);
+    for (const sp<Layer>& child : mDrawingChildren) {
+        child->updateMetadataSnapshot(mSnapshot->layerMetadata);
+    }
+}
+
+void Layer::updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
+                                           std::unordered_set<Layer*>& visited) {
+    if (visited.find(this) != visited.end()) {
+        ALOGW("Cycle containing layer %s detected in z-order relatives", getDebugName());
+        return;
+    }
+    visited.insert(this);
+
+    mSnapshot->relativeLayerMetadata = relativeLayerMetadata;
+
+    if (mDrawingState.zOrderRelatives.empty()) {
+        return;
+    }
+    LayerMetadata childRelativeLayerMetadata = mSnapshot->relativeLayerMetadata;
+    childRelativeLayerMetadata.merge(mSnapshot->layerMetadata);
+    for (wp<Layer> weakRelative : mDrawingState.zOrderRelatives) {
+        sp<Layer> relative = weakRelative.promote();
+        if (!relative) {
+            continue;
+        }
+        relative->updateRelativeMetadataSnapshot(childRelativeLayerMetadata, visited);
+    }
+}
+
 // ---------------------------------------------------------------------------
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 5030fd8..4ff86e5 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -38,6 +38,7 @@
 #include <utils/Timers.h>
 
 #include <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <scheduler/Fps.h>
 #include <scheduler/Seamlessness.h>
 
@@ -144,6 +145,31 @@
         bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; }
     };
 
+    // LayerSnapshot stores Layer state used by Composition Engine and Render Engine. Composition
+    // Engine uses a pointer to LayerSnapshot (as LayerFECompositionState*) and the LayerSettings
+    // passed to Render Engine are created using properties stored on this struct.
+    //
+    // TODO(b/238781169) Implement LayerFE as a separate subclass. Migrate LayerSnapshot to that
+    // LayerFE subclass.
+    struct LayerSnapshot : public compositionengine::LayerFECompositionState {
+        int32_t sequence;
+        std::string name;
+        uint32_t textureName;
+        bool contentOpaque;
+        RoundedCornerState roundedCorner;
+        StretchEffect stretchEffect;
+        FloatRect transformedBounds;
+        renderengine::ShadowSettings shadowSettings;
+        bool premultipliedAlpha;
+        bool isHdrY410;
+        bool bufferNeedsFiltering;
+        ui::Transform transform;
+        Rect bufferSize;
+        std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+        LayerMetadata layerMetadata;
+        LayerMetadata relativeLayerMetadata;
+    };
+
     using FrameRate = scheduler::LayerInfo::FrameRate;
     using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
 
@@ -392,7 +418,9 @@
     ui::Dataspace getRequestedDataSpace() const;
 
     virtual sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const;
-    compositionengine::LayerFECompositionState* editCompositionState();
+
+    const LayerSnapshot* getLayerSnapshot() const;
+    LayerSnapshot* editLayerSnapshot();
 
     // If we have received a new buffer this frame, we will pass its surface
     // damage down to hardware composer. Otherwise, we must send a region with
@@ -585,6 +613,12 @@
         mClearClientCompositionFenceOnLayerDisplayed = false;
     }
 
+    const LayerMetadata* getMetadata() const override { return &mSnapshot->layerMetadata; }
+
+    const LayerMetadata* getRelativeMetadata() const override {
+        return &mSnapshot->relativeLayerMetadata;
+    }
+
     const char* getDebugName() const override;
 
     bool setShadowRadius(float shadowRadius);
@@ -867,7 +901,17 @@
     bool simpleBufferUpdate(const layer_state_t&) const;
 
     static bool isOpaqueFormat(PixelFormat format);
+
+    // Updates the LayerSnapshot. This must be called prior to sending layer data to
+    // CompositionEngine or RenderEngine (i.e. before calling CompositionEngine::present or
+    // Layer::prepareClientComposition).
+    //
+    // TODO(b/238781169) Remove direct calls to RenderEngine::drawLayers that don't go through
+    // CompositionEngine to create a single path for composing layers.
     void updateSnapshot(bool updateGeometry);
+    void updateMetadataSnapshot(const LayerMetadata& parentMetadata);
+    void updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
+                                        std::unordered_set<Layer*>& visited);
 
 protected:
     friend class impl::SurfaceInterceptor;
@@ -1166,8 +1210,6 @@
     // the mStateLock.
     ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
 
-    std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
-
     ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
     uint64_t mPreviousReleasedFrameNumber = 0;
 
@@ -1200,6 +1242,8 @@
     ui::Transform mRequestedTransform;
 
     sp<HwcSlotGenerator> mHwcSlotGenerator;
+
+    std::unique_ptr<LayerSnapshot> mSnapshot = std::make_unique<LayerSnapshot>();
 };
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 5705255..8dd3b0f 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -203,25 +203,14 @@
         return 0.0f;
     }
 
-    // (b/133849373) ROT_90 screencap images produced upside down
-    auto area = sample_area;
-    if (orientation & ui::Transform::ROT_90) {
-        area.top = height - area.top;
-        area.bottom = height - area.bottom;
-        std::swap(area.top, area.bottom);
-
-        area.left = width - area.left;
-        area.right = width - area.right;
-        std::swap(area.left, area.right);
-    }
-
-    const uint32_t pixelCount = (area.bottom - area.top) * (area.right - area.left);
+    const uint32_t pixelCount =
+            (sample_area.bottom - sample_area.top) * (sample_area.right - sample_area.left);
     uint32_t accumulatedLuma = 0;
 
     // Calculates luma with approximation of Rec. 709 primaries
-    for (int32_t row = area.top; row < area.bottom; ++row) {
+    for (int32_t row = sample_area.top; row < sample_area.bottom; ++row) {
         const uint32_t* rowBase = data + row * stride;
-        for (int32_t column = area.left; column < area.right; ++column) {
+        for (int32_t column = sample_area.left; column < sample_area.right; ++column) {
             uint32_t pixel = rowBase[column];
             const uint32_t r = pixel & 0xFF;
             const uint32_t g = (pixel >> 8) & 0xFF;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 00886f0..3cb052c 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -48,24 +48,6 @@
     } fixedRateBelowThresholdLayersScore;
 };
 
-template <typename Iterator>
-const DisplayModePtr& getMaxScoreRefreshRate(Iterator begin, Iterator end) {
-    const auto it =
-            std::max_element(begin, end, [](RefreshRateScore max, RefreshRateScore current) {
-                const auto& [modeIt, overallScore, _] = current;
-
-                std::string name = to_string(modeIt->second->getFps());
-                ALOGV("%s scores %.2f", name.c_str(), overallScore);
-
-                ATRACE_INT(name.c_str(), static_cast<int>(std::round(overallScore * 100)));
-
-                constexpr float kEpsilon = 0.0001f;
-                return overallScore > max.overallScore * (1 + kEpsilon);
-            });
-
-    return it->modeIt->second;
-}
-
 constexpr RefreshRateConfigs::GlobalSignals kNoSignals;
 
 std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) {
@@ -137,6 +119,34 @@
 
 } // namespace
 
+struct RefreshRateConfigs::RefreshRateScoreComparator {
+    bool operator()(const RefreshRateScore& lhs, const RefreshRateScore& rhs) const {
+        const auto& [modeIt, overallScore, _] = lhs;
+
+        std::string name = to_string(modeIt->second->getFps());
+        ALOGV("%s sorting scores %.2f", name.c_str(), overallScore);
+
+        ATRACE_INT(name.c_str(), static_cast<int>(std::round(overallScore * 100)));
+
+        constexpr float kEpsilon = 0.0001f;
+        if (std::abs(overallScore - rhs.overallScore) > kEpsilon) {
+            return overallScore > rhs.overallScore;
+        }
+
+        // If overallScore tie we will pick the higher refresh rate if
+        // high refresh rate is the priority else the lower refresh rate.
+        if (refreshRateOrder == RefreshRateOrder::Descending) {
+            using fps_approx_ops::operator>;
+            return modeIt->second->getFps() > rhs.modeIt->second->getFps();
+        } else {
+            using fps_approx_ops::operator<;
+            return modeIt->second->getFps() < rhs.modeIt->second->getFps();
+        }
+    }
+
+    const RefreshRateOrder refreshRateOrder;
+};
+
 std::string RefreshRateConfigs::Policy::toString() const {
     return base::StringPrintf("{defaultModeId=%d, allowGroupSwitching=%s"
                               ", primaryRange=%s, appRequestRange=%s}",
@@ -218,6 +228,13 @@
     return 0;
 }
 
+float RefreshRateConfigs::calculateRefreshRateScoreForFps(Fps refreshRate) const {
+    const float ratio =
+            refreshRate.getValue() / mAppRequestRefreshRates.back()->second->getFps().getValue();
+    // Use ratio^2 to get a lower score the more we get further from peak
+    return ratio * ratio;
+}
+
 float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate,
                                                     bool isSeamlessSwitch) const {
     // Slightly prefer seamless switches.
@@ -226,10 +243,7 @@
 
     // If the layer wants Max, give higher score to the higher refresh rate
     if (layer.vote == LayerVoteType::Max) {
-        const auto& maxRefreshRate = mAppRequestRefreshRates.back()->second;
-        const auto ratio = refreshRate.getValue() / maxRefreshRate->getFps().getValue();
-        // use ratio^2 to get a lower score the more we get further from peak
-        return ratio * ratio;
+        return calculateRefreshRateScoreForFps(refreshRate);
     }
 
     if (layer.vote == LayerVoteType::ExplicitExact) {
@@ -258,24 +272,24 @@
             kNonExactMatchingPenalty;
 }
 
-auto RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
-                                            GlobalSignals signals) const
-        -> std::pair<DisplayModePtr, GlobalSignals> {
+auto RefreshRateConfigs::getRankedRefreshRates(const std::vector<LayerRequirement>& layers,
+                                               GlobalSignals signals) const
+        -> std::pair<std::vector<RefreshRateRanking>, GlobalSignals> {
     std::lock_guard lock(mLock);
 
-    if (mGetBestRefreshRateCache &&
-        mGetBestRefreshRateCache->arguments == std::make_pair(layers, signals)) {
-        return mGetBestRefreshRateCache->result;
+    if (mGetRankedRefreshRatesCache &&
+        mGetRankedRefreshRatesCache->arguments == std::make_pair(layers, signals)) {
+        return mGetRankedRefreshRatesCache->result;
     }
 
-    const auto result = getBestRefreshRateLocked(layers, signals);
-    mGetBestRefreshRateCache = GetBestRefreshRateCache{{layers, signals}, result};
+    const auto result = getRankedRefreshRatesLocked(layers, signals);
+    mGetRankedRefreshRatesCache = GetRankedRefreshRatesCache{{layers, signals}, result};
     return result;
 }
 
-auto RefreshRateConfigs::getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers,
-                                                  GlobalSignals signals) const
-        -> std::pair<DisplayModePtr, GlobalSignals> {
+auto RefreshRateConfigs::getRankedRefreshRatesLocked(const std::vector<LayerRequirement>& layers,
+                                                     GlobalSignals signals) const
+        -> std::pair<std::vector<RefreshRateRanking>, GlobalSignals> {
     using namespace fps_approx_ops;
     ATRACE_CALL();
     ALOGV("%s: %zu layers", __func__, layers.size());
@@ -285,8 +299,8 @@
     // Keep the display at max refresh rate for the duration of powering on the display.
     if (signals.powerOnImminent) {
         ALOGV("Power On Imminent");
-        const auto& max = getMaxRefreshRateByPolicyLocked(activeMode.getGroup());
-        return {max, GlobalSignals{.powerOnImminent = true}};
+        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Descending),
+                GlobalSignals{.powerOnImminent = true}};
     }
 
     int noVoteLayers = 0;
@@ -295,7 +309,6 @@
     int explicitDefaultVoteLayers = 0;
     int explicitExactOrMultipleVoteLayers = 0;
     int explicitExact = 0;
-    float maxExplicitWeight = 0;
     int seamedFocusedLayers = 0;
 
     for (const auto& layer : layers) {
@@ -311,15 +324,12 @@
                 break;
             case LayerVoteType::ExplicitDefault:
                 explicitDefaultVoteLayers++;
-                maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
                 break;
             case LayerVoteType::ExplicitExactOrMultiple:
                 explicitExactOrMultipleVoteLayers++;
-                maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
                 break;
             case LayerVoteType::ExplicitExact:
                 explicitExact++;
-                maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
                 break;
             case LayerVoteType::Heuristic:
                 break;
@@ -348,9 +358,9 @@
     // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
     // selected a refresh rate to see if we should apply touch boost.
     if (signals.touch && !hasExplicitVoteLayers) {
-        const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup);
-        ALOGV("TouchBoost - choose %s", to_string(max->getFps()).c_str());
-        return {max, GlobalSignals{.touch = true}};
+        ALOGV("Touch Boost");
+        return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending),
+                GlobalSignals{.touch = true}};
     }
 
     // If the primary range consists of a single refresh rate then we can only
@@ -360,22 +370,22 @@
             isApproxEqual(policy->primaryRange.min, policy->primaryRange.max);
 
     if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
-        const DisplayModePtr& min = getMinRefreshRateByPolicyLocked();
-        ALOGV("Idle - choose %s", to_string(min->getFps()).c_str());
-        return {min, GlobalSignals{.idle = true}};
+        ALOGV("Idle");
+        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Ascending),
+                GlobalSignals{.idle = true}};
     }
 
     if (layers.empty() || noVoteLayers == layers.size()) {
-        const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup);
-        ALOGV("no layers with votes - choose %s", to_string(max->getFps()).c_str());
-        return {max, kNoSignals};
+        ALOGV("No layers with votes");
+        return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending),
+                kNoSignals};
     }
 
     // Only if all layers want Min we should return Min
     if (noVoteLayers + minVoteLayers == layers.size()) {
-        const DisplayModePtr& min = getMinRefreshRateByPolicyLocked();
-        ALOGV("all layers Min - choose %s", to_string(min->getFps()).c_str());
-        return {min, kNoSignals};
+        ALOGV("All layers Min");
+        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Ascending),
+                kNoSignals};
     }
 
     // Find the best refresh rate based on score
@@ -522,22 +532,29 @@
     }
 
     // Now that we scored all the refresh rates we need to pick the one that got the highest
-    // overallScore. In case of a tie we will pick the higher refresh rate if any of the layers
-    // wanted Max, or the lower otherwise.
-    const DisplayModePtr& bestRefreshRate = maxVoteLayers > 0
-            ? getMaxScoreRefreshRate(scores.rbegin(), scores.rend())
-            : getMaxScoreRefreshRate(scores.begin(), scores.end());
+    // overallScore. Sort the scores based on their overallScore in descending order of priority.
+    const RefreshRateOrder refreshRateOrder =
+            maxVoteLayers > 0 ? RefreshRateOrder::Descending : RefreshRateOrder::Ascending;
+    std::sort(scores.begin(), scores.end(),
+              RefreshRateScoreComparator{.refreshRateOrder = refreshRateOrder});
+    std::vector<RefreshRateRanking> rankedRefreshRates;
+    rankedRefreshRates.reserve(scores.size());
+
+    std::transform(scores.begin(), scores.end(), back_inserter(rankedRefreshRates),
+                   [](const RefreshRateScore& score) {
+                       return RefreshRateRanking{score.modeIt->second, score.overallScore};
+                   });
 
     if (primaryRangeIsSingleRate) {
         // If we never scored any layers, then choose the rate from the primary
         // range instead of picking a random score from the app range.
         if (std::all_of(scores.begin(), scores.end(),
                         [](RefreshRateScore score) { return score.overallScore == 0; })) {
-            const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup);
-            ALOGV("layers not scored - choose %s", to_string(max->getFps()).c_str());
-            return {max, kNoSignals};
+            ALOGV("Layers not scored");
+            return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending),
+                    kNoSignals};
         } else {
-            return {bestRefreshRate, kNoSignals};
+            return {rankedRefreshRates, kNoSignals};
         }
     }
 
@@ -545,8 +562,6 @@
     // interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit
     // vote we should not change it if we get a touch event. Only apply touch boost if it will
     // actually increase the refresh rate over the normal selection.
-    const DisplayModePtr& touchRefreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
-
     const bool touchBoostForExplicitExact = [&] {
         if (mSupportsFrameRateOverrideByContent) {
             // Enable touch boost if there are other layers besides exact
@@ -557,15 +572,18 @@
         }
     }();
 
+    const auto& touchRefreshRates =
+            getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending);
     using fps_approx_ops::operator<;
 
     if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
-        bestRefreshRate->getFps() < touchRefreshRate->getFps()) {
-        ALOGV("TouchBoost - choose %s", to_string(touchRefreshRate->getFps()).c_str());
-        return {touchRefreshRate, GlobalSignals{.touch = true}};
+        scores.front().modeIt->second->getFps() <
+                touchRefreshRates.front().displayModePtr->getFps()) {
+        ALOGV("Touch Boost");
+        return {touchRefreshRates, GlobalSignals{.touch = true}};
     }
 
-    return {bestRefreshRate, kNoSignals};
+    return {rankedRefreshRates, kNoSignals};
 }
 
 std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>>
@@ -670,11 +688,13 @@
             continue;
         }
 
-        // Now that we scored all the refresh rates we need to pick the one that got the highest
-        // score.
+        // Now that we scored all the refresh rates we need to pick the lowest refresh rate
+        // that got the highest score.
         const DisplayModePtr& bestRefreshRate =
-                getMaxScoreRefreshRate(scores.begin(), scores.end());
-
+                std::min_element(scores.begin(), scores.end(),
+                                 RefreshRateScoreComparator{.refreshRateOrder =
+                                                                    RefreshRateOrder::Ascending})
+                        ->modeIt->second;
         frameRateOverrides.emplace(uid, bestRefreshRate->getFps());
     }
 
@@ -715,16 +735,6 @@
     return mPrimaryRefreshRates.front()->second;
 }
 
-DisplayModePtr RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
-    std::lock_guard lock(mLock);
-    return getMaxRefreshRateByPolicyLocked();
-}
-
-const DisplayModePtr& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked() const {
-    const int anchorGroup = getActiveModeItLocked()->second->getGroup();
-    return getMaxRefreshRateByPolicyLocked(anchorGroup);
-}
-
 const DisplayModePtr& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
     for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); ++it) {
         const auto& mode = (*it)->second;
@@ -733,14 +743,41 @@
         }
     }
 
-    const auto& activeMode = *getActiveModeItLocked()->second;
-    ALOGE("Can't find max refresh rate by policy with the same mode group as the current mode %s",
-          to_string(activeMode).c_str());
+    ALOGE("Can't find max refresh rate by policy with the same group %d", anchorGroup);
 
     // Default to the highest refresh rate.
     return mPrimaryRefreshRates.back()->second;
 }
 
+std::vector<RefreshRateRanking> RefreshRateConfigs::getRefreshRatesByPolicyLocked(
+        std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder) const {
+    std::vector<RefreshRateRanking> rankings;
+    const auto makeRanking = [&](const DisplayModeIterator it) REQUIRES(mLock) {
+        const auto& mode = it->second;
+        const bool inverseScore = (refreshRateOrder == RefreshRateOrder::Ascending);
+        const float score = calculateRefreshRateScoreForFps(mode->getFps());
+        if (!anchorGroupOpt || mode->getGroup() == anchorGroupOpt) {
+            rankings.push_back(RefreshRateRanking{mode, inverseScore ? 1.0f / score : score});
+        }
+    };
+
+    if (refreshRateOrder == RefreshRateOrder::Ascending) {
+        std::for_each(mPrimaryRefreshRates.begin(), mPrimaryRefreshRates.end(), makeRanking);
+    } else {
+        std::for_each(mPrimaryRefreshRates.rbegin(), mPrimaryRefreshRates.rend(), makeRanking);
+    }
+
+    if (!rankings.empty() || !anchorGroupOpt) {
+        return rankings;
+    }
+
+    ALOGW("Can't find %s refresh rate by policy with the same mode group"
+          " as the mode group %d",
+          refreshRateOrder == RefreshRateOrder::Ascending ? "min" : "max", anchorGroupOpt.value());
+
+    return getRefreshRatesByPolicyLocked(/*anchorGroupOpt*/ std::nullopt, refreshRateOrder);
+}
+
 DisplayModePtr RefreshRateConfigs::getActiveModePtr() const {
     std::lock_guard lock(mLock);
     return getActiveModeItLocked()->second;
@@ -760,9 +797,9 @@
 void RefreshRateConfigs::setActiveModeId(DisplayModeId modeId) {
     std::lock_guard lock(mLock);
 
-    // Invalidate the cached invocation to getBestRefreshRate. This forces
-    // the refresh rate to be recomputed on the next call to getBestRefreshRate.
-    mGetBestRefreshRateCache.reset();
+    // Invalidate the cached invocation to getRankedRefreshRates. This forces
+    // the refresh rate to be recomputed on the next call to getRankedRefreshRates.
+    mGetRankedRefreshRatesCache.reset();
 
     mActiveModeIt = mDisplayModes.find(modeId);
     LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
@@ -797,9 +834,9 @@
 void RefreshRateConfigs::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) {
     std::lock_guard lock(mLock);
 
-    // Invalidate the cached invocation to getBestRefreshRate. This forces
-    // the refresh rate to be recomputed on the next call to getBestRefreshRate.
-    mGetBestRefreshRateCache.reset();
+    // Invalidate the cached invocation to getRankedRefreshRates. This forces
+    // the refresh rate to be recomputed on the next call to getRankedRefreshRates.
+    mGetRankedRefreshRatesCache.reset();
 
     mDisplayModes = std::move(modes);
     mActiveModeIt = mDisplayModes.find(activeModeId);
@@ -843,7 +880,7 @@
         ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
         return BAD_VALUE;
     }
-    mGetBestRefreshRateCache.reset();
+    mGetRankedRefreshRatesCache.reset();
     Policy previousPolicy = *getCurrentPolicyLocked();
     mDisplayManagerPolicy = policy;
     if (*getCurrentPolicyLocked() == previousPolicy) {
@@ -858,7 +895,7 @@
     if (policy && !isPolicyValidLocked(*policy)) {
         return BAD_VALUE;
     }
-    mGetBestRefreshRateCache.reset();
+    mGetRankedRefreshRatesCache.reset();
     Policy previousPolicy = *getCurrentPolicyLocked();
     mOverridePolicy = policy;
     if (*getCurrentPolicyLocked() == previousPolicy) {
@@ -958,7 +995,8 @@
         return KernelIdleTimerAction::TurnOff;
     }
 
-    const DisplayModePtr& maxByPolicy = getMaxRefreshRateByPolicyLocked();
+    const DisplayModePtr& maxByPolicy =
+            getMaxRefreshRateByPolicyLocked(getActiveModeItLocked()->second->getGroup());
     if (minByPolicy == maxByPolicy) {
         // Turn on the timer when the min of the primary range is below the device min.
         if (const Policy* currentPolicy = getCurrentPolicyLocked();
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 19bcb94..0642fcb 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -44,6 +44,15 @@
     return static_cast<DisplayModeEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
 }
 
+struct RefreshRateRanking {
+    DisplayModePtr displayModePtr;
+    float score = 0.0f;
+
+    bool operator==(const RefreshRateRanking& ranking) const {
+        return displayModePtr == ranking.displayModePtr && score == ranking.score;
+    }
+};
+
 using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
 
 /**
@@ -195,9 +204,9 @@
         }
     };
 
-    // Returns the refresh rate that best fits the given layers, and whether the refresh rate was
-    // chosen based on touch boost and/or idle timer.
-    std::pair<DisplayModePtr, GlobalSignals> getBestRefreshRate(
+    // Returns the list in the descending order of refresh rates desired
+    // based on their overall score, and the GlobalSignals that were considered.
+    std::pair<std::vector<RefreshRateRanking>, GlobalSignals> getRankedRefreshRates(
             const std::vector<LayerRequirement>&, GlobalSignals) const EXCLUDES(mLock);
 
     FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
@@ -208,10 +217,6 @@
     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
-    // uses the primary range, not the app request range.
-    DisplayModePtr getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
-
     void setActiveModeId(DisplayModeId) EXCLUDES(mLock) REQUIRES(kMainThreadContext);
 
     // See mActiveModeIt for thread safety.
@@ -343,7 +348,7 @@
     // See mActiveModeIt for thread safety.
     DisplayModeIterator getActiveModeItLocked() const REQUIRES(mLock);
 
-    std::pair<DisplayModePtr, GlobalSignals> getBestRefreshRateLocked(
+    std::pair<std::vector<RefreshRateRanking>, GlobalSignals> getRankedRefreshRatesLocked(
             const std::vector<LayerRequirement>&, GlobalSignals) const REQUIRES(mLock);
 
     // Returns number of display frames and remainder when dividing the layer refresh period by
@@ -356,12 +361,23 @@
 
     // Returns the highest refresh rate according to the current policy. May change at runtime. Only
     // uses the primary range, not the app request range.
-    const DisplayModePtr& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock);
     const DisplayModePtr& getMaxRefreshRateByPolicyLocked(int anchorGroup) const REQUIRES(mLock);
 
+    struct RefreshRateScoreComparator;
+
+    enum class RefreshRateOrder { Ascending, Descending };
+
+    // Returns the rankings in RefreshRateOrder. May change at runtime.
+    // Only uses the primary range, not the app request range.
+    std::vector<RefreshRateRanking> getRefreshRatesByPolicyLocked(std::optional<int> anchorGroupOpt,
+                                                                  RefreshRateOrder) const
+            REQUIRES(mLock);
+
     const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
     bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
 
+    // Returns the refresh rate score as a ratio to max refresh rate, which has a score of 1.
+    float calculateRefreshRateScoreForFps(Fps refreshRate) const REQUIRES(mLock);
     // calculates a score for a layer. Used to determine the display refresh rate
     // and the frame rate override for certains applications.
     float calculateLayerScoreLocked(const LayerRequirement&, Fps refreshRate,
@@ -410,11 +426,11 @@
     const Config mConfig;
     bool mSupportsFrameRateOverrideByContent;
 
-    struct GetBestRefreshRateCache {
+    struct GetRankedRefreshRatesCache {
         std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
-        std::pair<DisplayModePtr, GlobalSignals> result;
+        std::pair<std::vector<RefreshRateRanking>, GlobalSignals> result;
     };
-    mutable std::optional<GetBestRefreshRateCache> mGetBestRefreshRateCache GUARDED_BY(mLock);
+    mutable std::optional<GetRankedRefreshRatesCache> mGetRankedRefreshRatesCache GUARDED_BY(mLock);
 
     // Declare mIdleTimer last to ensure its thread joins before the mutex/callbacks are destroyed.
     std::mutex mIdleTimerCallbacksMutex;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index ff6b461..6d68bac 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -674,7 +674,9 @@
         if (currentState == newState) return {};
         currentState = std::forward<T>(newState);
 
-        std::tie(newMode, consideredSignals) = chooseDisplayMode();
+        const auto [rankings, signals] = getRankedDisplayModes();
+        newMode = rankings.front().displayModePtr;
+        consideredSignals = signals;
         frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
 
         if (mPolicy.mode == newMode) {
@@ -699,7 +701,8 @@
     return consideredSignals;
 }
 
-auto Scheduler::chooseDisplayMode() -> std::pair<DisplayModePtr, GlobalSignals> {
+auto Scheduler::getRankedDisplayModes()
+        -> std::pair<std::vector<RefreshRateRanking>, GlobalSignals> {
     ATRACE_CALL();
 
     const auto configs = holdRefreshRateConfigs();
@@ -712,14 +715,14 @@
                                 .idle = mPolicy.idleTimer == TimerState::Expired,
                                 .powerOnImminent = powerOnImminent};
 
-    return configs->getBestRefreshRate(mPolicy.contentRequirements, signals);
+    return configs->getRankedRefreshRates(mPolicy.contentRequirements, signals);
 }
 
 DisplayModePtr Scheduler::getPreferredDisplayMode() {
     std::lock_guard<std::mutex> lock(mPolicyLock);
     // Make sure the stored mode is up to date.
     if (mPolicy.mode) {
-        mPolicy.mode = chooseDisplayMode().first;
+        mPolicy.mode = getRankedDisplayModes().first.front().displayModePtr;
     }
     return mPolicy.mode;
 }
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index afb3459..f567205 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -95,8 +95,8 @@
     ~ISchedulerCallback() = default;
 };
 
-class Scheduler : impl::MessageQueue {
-    using Impl = impl::MessageQueue;
+class Scheduler : android::impl::MessageQueue {
+    using Impl = android::impl::MessageQueue;
 
 public:
     Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags);
@@ -130,7 +130,7 @@
     ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*,
                                       std::chrono::nanoseconds workDuration,
                                       std::chrono::nanoseconds readyDuration,
-                                      impl::EventThread::InterceptVSyncsCallback);
+                                      android::impl::EventThread::InterceptVSyncsCallback);
 
     sp<IDisplayEventConnection> createDisplayEventConnection(
             ConnectionHandle, EventRegistrationFlags eventRegistration = {});
@@ -269,16 +269,18 @@
     template <typename S, typename T>
     GlobalSignals applyPolicy(S Policy::*, T&&) EXCLUDES(mPolicyLock);
 
-    // Returns the display mode that fulfills the policy, and the signals that were considered.
-    std::pair<DisplayModePtr, GlobalSignals> chooseDisplayMode() REQUIRES(mPolicyLock);
+    // Returns the list of display modes in descending order of their priority that fulfills the
+    // policy, and the signals that were considered.
+    std::pair<std::vector<RefreshRateRanking>, GlobalSignals> getRankedDisplayModes()
+            REQUIRES(mPolicyLock);
 
     bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock);
 
     void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateConfigsLock);
 
-    impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const
+    android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const
             EXCLUDES(mRefreshRateConfigsLock);
-    impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
+    android::impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
 
     std::shared_ptr<RefreshRateConfigs> holdRefreshRateConfigs() const
             EXCLUDES(mRefreshRateConfigsLock) {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e31490c..7d3fc98 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -106,7 +106,6 @@
 
 #include <ui/DisplayIdentification.h>
 #include "BackgroundExecutor.h"
-#include "BufferStateLayer.h"
 #include "Client.h"
 #include "Colorizer.h"
 #include "Display/DisplayMap.h"
@@ -681,7 +680,7 @@
 
     sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger")));
 
-    static_cast<void>(mScheduler->schedule([=] {
+    static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) {
         if (input == nullptr) {
             ALOGE("Failed to link to input service");
         } else {
@@ -709,8 +708,9 @@
 
         mBootStage = BootStage::FINISHED;
 
-        if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
-            FTL_FAKE_GUARD(mStateLock, enableRefreshRateOverlay(true));
+        if (base::GetBoolProperty("sf.debug.show_refresh_rate_overlay"s, false)) {
+            ftl::FakeGuard guard(mStateLock);
+            enableRefreshRateOverlay(true);
         }
     }));
 }
@@ -814,16 +814,37 @@
         enableHalVirtualDisplays(true);
     }
 
-    // Process any initial hotplug and resulting display changes.
+    // Process hotplug for displays connected at boot.
     LOG_ALWAYS_FATAL_IF(!configureLocked(),
                         "Initial display configuration failed: HWC did not hotplug");
-    processDisplayChangesLocked();
 
-    const auto display = getDefaultDisplayDeviceLocked();
+    // Commit primary display.
+    sp<const DisplayDevice> display;
+    if (const auto indexOpt = mCurrentState.getDisplayIndex(getPrimaryDisplayIdLocked())) {
+        const auto& displays = mCurrentState.displays;
+
+        const auto& token = displays.keyAt(*indexOpt);
+        const auto& state = displays.valueAt(*indexOpt);
+
+        processDisplayAdded(token, state);
+        mDrawingState.displays.add(token, state);
+
+        display = getDefaultDisplayDeviceLocked();
+    }
+
     LOG_ALWAYS_FATAL_IF(!display, "Failed to configure the primary display");
     LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(display->getPhysicalId()),
                         "Primary display is disconnected");
 
+    // TODO(b/241285876): The Scheduler needlessly depends on creating the CompositionEngine part of
+    // the DisplayDevice, hence the above commit of the primary display. Remove that special case by
+    // initializing the Scheduler after configureLocked, once decoupled from DisplayDevice.
+    initScheduler(display);
+    dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
+
+    // Commit secondary display(s).
+    processDisplayChangesLocked();
+
     // initialize our drawing state
     mDrawingState = mCurrentState;
 
@@ -1039,7 +1060,7 @@
 
     const PhysicalDisplayId displayId = snapshot.displayId();
 
-    info->activeDisplayModeId = display->getActiveMode()->getId().value();
+    info->activeDisplayModeId = display->refreshRateConfigs().getActiveModePtr()->getId().value();
     info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
     info->supportedColorModes = getDisplayColorModes(displayId);
     info->hdrCapabilities = display->getHdrCapabilities();
@@ -1163,7 +1184,7 @@
         return;
     }
 
-    if (display->getActiveMode()->getResolution() != upcomingModeInfo.mode->getResolution()) {
+    if (display->getActiveMode().getResolution() != upcomingModeInfo.mode->getResolution()) {
         auto& state = mCurrentState.displays.editValueFor(display->getDisplayToken());
         // We need to generate new sequenceId in order to recreate the display (and this
         // way the framebuffer).
@@ -1249,7 +1270,7 @@
         ALOGV("%s changing active mode to %d(%s) for display %s", __func__, desiredModeId.value(),
               to_string(*refreshRateOpt).c_str(), to_string(display->getId()).c_str());
 
-        if (display->getActiveMode()->getId() == desiredModeId) {
+        if (display->getActiveMode().getId() == desiredModeId) {
             // we are already in the requested mode, there is nothing left to do
             desiredActiveModeChangeDone(display);
             continue;
@@ -1298,7 +1319,7 @@
         const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately);
         const auto desiredActiveMode = display->getDesiredActiveMode();
         if (desiredActiveMode &&
-            display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) {
+            display->getActiveMode().getId() == desiredActiveMode->mode->getId()) {
             desiredActiveModeChangeDone(display);
         }
     }
@@ -1872,17 +1893,12 @@
     ATRACE_FORMAT("onComposerHalVsync%s", tracePeriod.c_str());
 
     Mutex::Autolock lock(mStateLock);
-    const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
-    if (displayId) {
-        const auto token = getPhysicalDisplayTokenLocked(*displayId);
-        const auto display = getDisplayDeviceLocked(token);
-        display->onVsync(timestamp);
-    }
 
     if (!getHwComposer().onVsync(hwcDisplayId, timestamp)) {
         return;
     }
 
+    const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
     const bool isActiveDisplay =
             displayId && getPhysicalDisplayTokenLocked(*displayId) == mActiveDisplayToken;
     if (!isActiveDisplay) {
@@ -2087,7 +2103,7 @@
             activeDisplay->getPowerMode() == hal::PowerMode::ON;
     if (mPowerHintSessionEnabled) {
         const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
-        const Period vsyncPeriod = Period::fromNs(display->getActiveMode()->getVsyncPeriod());
+        const Period vsyncPeriod = Period::fromNs(display->getActiveMode().getVsyncPeriod());
         mPowerAdvisor->setCommitStart(frameTime);
         mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime);
 
@@ -2190,6 +2206,13 @@
     }
     mPowerAdvisor->setDisplays(displayIds);
 
+    const bool updateTaskMetadata = mCompositionEngine->getFeatureFlags().test(
+            compositionengine::Feature::kSnapshotLayerMetadata);
+    if (updateTaskMetadata && (mVisibleRegionsDirty || mLayerMetadataSnapshotNeeded)) {
+        updateLayerMetadataSnapshot();
+        mLayerMetadataSnapshotNeeded = false;
+    }
+
     if (DOES_CONTAIN_BORDER) {
         refreshArgs.borderInfoList.clear();
         mDrawingState.traverse([&refreshArgs](Layer* layer) {
@@ -2953,10 +2976,13 @@
     LOG_FATAL_IF(!displaySurface);
     auto display = setupNewDisplayDeviceInternal(displayToken, std::move(compositionDisplay), state,
                                                  displaySurface, producer);
-    if (display->isPrimary()) {
-        initScheduler(display);
-    }
-    if (!state.isVirtual()) {
+
+    if (mScheduler && !display->isVirtual()) {
+        // Display modes are reloaded on hotplug reconnect.
+        if (display->isPrimary()) {
+            mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());
+        }
+
         dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
     }
 
@@ -3061,7 +3087,7 @@
 
 void SurfaceFlinger::updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay) {
     mVsyncConfiguration->reset();
-    const Fps refreshRate = activeDisplay->refreshRateConfigs().getActiveMode().getFps();
+    const Fps refreshRate = activeDisplay->getActiveMode().getFps();
     updatePhaseConfiguration(refreshRate);
     mRefreshRateStats->setRefreshRate(refreshRate);
 }
@@ -3368,20 +3394,16 @@
     mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
 }
 
-void SurfaceFlinger::initScheduler(const sp<DisplayDevice>& display) {
-    if (mScheduler) {
-        // If the scheduler is already initialized, this means that we received
-        // a hotplug(connected) on the primary display. In that case we should
-        // update the scheduler with the most recent display information.
-        ALOGW("Scheduler already initialized, updating instead");
-        mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());
-        return;
-    }
-    const auto currRefreshRate = display->getActiveMode()->getFps();
-    mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, currRefreshRate,
-                                                                      hal::PowerMode::OFF);
+void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
+    LOG_ALWAYS_FATAL_IF(mScheduler);
 
-    mVsyncConfiguration = getFactory().createVsyncConfiguration(currRefreshRate);
+    const auto activeModePtr = display->refreshRateConfigs().getActiveModePtr();
+    const Fps activeRefreshRate = activeModePtr->getFps();
+    mRefreshRateStats =
+            std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, activeRefreshRate,
+                                                          hal::PowerMode::OFF);
+
+    mVsyncConfiguration = getFactory().createVsyncConfiguration(activeRefreshRate);
     mVsyncModulator = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
 
     using Feature = scheduler::Feature;
@@ -3414,7 +3436,7 @@
     mScheduler->startTimers();
 
     const auto configs = mVsyncConfiguration->getCurrentConfigs();
-    const nsecs_t vsyncPeriod = currRefreshRate.getPeriodNsecs();
+    const nsecs_t vsyncPeriod = activeRefreshRate.getPeriodNsecs();
     mAppConnectionHandle =
             mScheduler->createConnection("app", mFrameTimeline->getTokenManager(),
                                          /*workDuration=*/configs.late.appWorkDuration,
@@ -3442,7 +3464,7 @@
     // 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->onPrimaryDisplayModeChanged(mAppConnectionHandle, display->getActiveMode());
+    mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
 }
 
 void SurfaceFlinger::updatePhaseConfiguration(const Fps& refreshRate) {
@@ -4425,7 +4447,10 @@
             layer->setGameModeForTree(static_cast<GameMode>(gameMode));
         }
 
-        if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded;
+        if (layer->setMetadata(s.metadata)) {
+            flags |= eTraversalNeeded;
+            mLayerMetadataSnapshotNeeded = true;
+        }
     }
     if (what & layer_state_t::eColorSpaceAgnosticChanged) {
         if (layer->setColorSpaceAgnostic(s.colorSpaceAgnostic)) {
@@ -4915,6 +4940,25 @@
 
         const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
 
+        // Traversal of drawing state must happen on the main thread.
+        // Otherwise, SortedVector may have shared ownership during concurrent
+        // traversals, which can result in use-after-frees.
+        std::string compositionLayers;
+        mScheduler
+                ->schedule([&] {
+                    StringAppendF(&compositionLayers, "Composition layers\n");
+                    mDrawingState.traverseInZOrder([&](Layer* layer) {
+                        auto* compositionState = layer->getCompositionState();
+                        if (!compositionState || !compositionState->isVisible) return;
+
+                        android::base::StringAppendF(&compositionLayers, "* Layer %p (%s)\n", layer,
+                                                     layer->getDebugName() ? layer->getDebugName()
+                                                                           : "<unknown>");
+                        compositionState->dump(compositionLayers);
+                    });
+                })
+                .get();
+
         bool dumpLayers = true;
         {
             TimedLock lock(mStateLock, s2ns(1), __func__);
@@ -4927,7 +4971,7 @@
                 (it->second)(args, asProto, result);
                 dumpLayers = false;
             } else if (!asProto) {
-                dumpAllLocked(args, result);
+                dumpAllLocked(args, compositionLayers, result);
             }
         }
 
@@ -5234,7 +5278,8 @@
     }
 }
 
-void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const {
+void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers,
+                                   std::string& result) const {
     const bool colorize = !args.empty() && args[0] == String16("--color");
     Colorizer colorizer(colorize);
 
@@ -5282,18 +5327,7 @@
     StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers.load());
     colorizer.reset(result);
 
-    {
-        StringAppendF(&result, "Composition layers\n");
-        mDrawingState.traverseInZOrder([&](Layer* layer) {
-            auto* compositionState = layer->getCompositionState();
-            if (!compositionState || !compositionState->isVisible) return;
-
-            android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer,
-                                         layer->getDebugName() ? layer->getDebugName()
-                                                               : "<unknown>");
-            compositionState->dump(result);
-        });
-    }
+    result.append(compositionLayers);
 
     colorizer.bold(result);
     StringAppendF(&result, "Displays (%zu entries)\n", mDisplays.size());
@@ -5331,10 +5365,10 @@
 
     if (const auto display = getDefaultDisplayDeviceLocked()) {
         std::string fps, xDpi, yDpi;
-        if (const auto activeMode = display->getActiveMode()) {
-            fps = to_string(activeMode->getFps());
+        if (const auto activeModePtr = display->refreshRateConfigs().getActiveModePtr()) {
+            fps = to_string(activeModePtr->getFps());
 
-            const auto dpi = activeMode->getDpi();
+            const auto dpi = activeModePtr->getDpi();
             xDpi = base::StringPrintf("%.2f", dpi.x);
             yDpi = base::StringPrintf("%.2f", dpi.y);
         } else {
@@ -5834,19 +5868,17 @@
                 return NO_ERROR;
             }
             case 1034: {
-                auto future = mScheduler->schedule([&] {
-                    switch (n = data.readInt32()) {
-                        case 0:
-                        case 1:
-                            FTL_FAKE_GUARD(mStateLock,
-                                           enableRefreshRateOverlay(static_cast<bool>(n)));
-                            break;
-                        default: {
-                            reply->writeBool(
-                                    FTL_FAKE_GUARD(mStateLock, isRefreshRateOverlayEnabled()));
-                        }
-                    }
-                });
+                auto future = mScheduler->schedule(
+                        [&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
+                            switch (n = data.readInt32()) {
+                                case 0:
+                                case 1:
+                                    enableRefreshRateOverlay(static_cast<bool>(n));
+                                    break;
+                                default:
+                                    reply->writeBool(isRefreshRateOverlayEnabled());
+                            }
+                        });
 
                 future.wait();
                 return NO_ERROR;
@@ -6636,6 +6668,11 @@
     std::vector<Layer*> renderedLayers;
     bool disableBlurs = false;
     traverseLayers([&](Layer* layer) {
+        // Layer::prepareClientComposition uses the layer's snapshot to populate the resulting
+        // LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings are
+        // generated with the layer's current buffer and geometry.
+        layer->updateSnapshot(true /* updateGeometry */);
+
         disableBlurs |= layer->getDrawingState().sidebandStream != nullptr;
 
         Region clip(renderArea.getBounds());
@@ -6782,12 +6819,12 @@
 
     // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
     // be depending in this callback.
-    const auto activeMode = display->getActiveMode();
+    const auto activeModePtr = display->refreshRateConfigs().getActiveModePtr();
     if (isDisplayActiveLocked(display)) {
-        mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode);
+        mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
         toggleKernelIdleTimer();
     } else {
-        mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode);
+        mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
     }
 
     auto preferredModeOpt =
@@ -7077,7 +7114,7 @@
     mRegionSamplingThread->onCompositionComplete(mScheduler->getScheduledFrameTime());
 }
 
-void SurfaceFlinger::onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay) {
+void SurfaceFlinger::onActiveDisplaySizeChanged(const sp<const DisplayDevice>& activeDisplay) {
     mScheduler->onActiveDisplayAreaChanged(activeDisplay->getWidth() * activeDisplay->getHeight());
     getRenderEngine().onActiveDisplaySizeChanged(activeDisplay->getSize());
 }
@@ -7208,6 +7245,31 @@
     return true;
 }
 
+void SurfaceFlinger::updateLayerMetadataSnapshot() {
+    LayerMetadata parentMetadata;
+    for (const auto& layer : mDrawingState.layersSortedByZ) {
+        layer->updateMetadataSnapshot(parentMetadata);
+    }
+
+    std::unordered_set<Layer*> visited;
+    mDrawingState.traverse([&visited](Layer* layer) {
+        if (visited.find(layer) != visited.end()) {
+            return;
+        }
+
+        // If the layer isRelativeOf, then either it's relative metadata will be set
+        // recursively when updateRelativeMetadataSnapshot is called on its relative parent or
+        // it's relative parent has been deleted. Clear the layer's relativeLayerMetadata to ensure
+        // that layers with deleted relative parents don't hold stale relativeLayerMetadata.
+        if (layer->getDrawingState().isRelativeOf) {
+            layer->editLayerSnapshot()->relativeLayerMetadata = {};
+            return;
+        }
+
+        layer->updateRelativeMetadataSnapshot({}, visited);
+    });
+}
+
 // gui::ISurfaceComposer
 
 binder::Status SurfaceComposerAIDL::bootFinished() {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f7684a0..85d11ba 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -333,7 +333,6 @@
 
 private:
     friend class BufferLayer;
-    friend class BufferStateLayer;
     friend class Client;
     friend class FpsReporter;
     friend class TunnelModeEnabledReporter;
@@ -374,7 +373,20 @@
 
         const LayerVector::StateSet stateSet = LayerVector::StateSet::Invalid;
         LayerVector layersSortedByZ;
-        DefaultKeyedVector< wp<IBinder>, DisplayDeviceState> displays;
+
+        // TODO(b/241285876): Replace deprecated DefaultKeyedVector with ftl::SmallMap.
+        DefaultKeyedVector<wp<IBinder>, DisplayDeviceState> displays;
+
+        std::optional<size_t> getDisplayIndex(PhysicalDisplayId displayId) const {
+            for (size_t i = 0; i < displays.size(); i++) {
+                const auto& state = displays.valueAt(i);
+                if (state.physical && state.physical->id == displayId) {
+                    return i;
+                }
+            }
+
+            return {};
+        }
 
         bool colorMatrixChanged = true;
         mat4 colorMatrix;
@@ -596,7 +608,7 @@
     void binderDied(const wp<IBinder>& who) override;
 
     // HWC2::ComposerCallback overrides:
-    void onComposerHalVsync(hal::HWDisplayId, int64_t timestamp,
+    void onComposerHalVsync(hal::HWDisplayId, nsecs_t timestamp,
                             std::optional<hal::VsyncPeriodNanos>) override;
     void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) override;
     void onComposerHalRefresh(hal::HWDisplayId) override;
@@ -687,6 +699,7 @@
     bool latchBuffers();
 
     void updateLayerGeometry();
+    void updateLayerMetadataSnapshot();
 
     void updateInputFlinger();
     void persistDisplayBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
@@ -695,7 +708,7 @@
     void commitInputWindowCommands() REQUIRES(mStateLock);
     void updateCursorAsync();
 
-    void initScheduler(const sp<DisplayDevice>& display) REQUIRES(mStateLock);
+    void initScheduler(const sp<const DisplayDevice>&) REQUIRES(mStateLock);
     void updatePhaseConfiguration(const Fps&) REQUIRES(mStateLock);
     void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod);
 
@@ -1027,12 +1040,13 @@
     void onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay)
             REQUIRES(mStateLock, kMainThreadContext);
 
-    void onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay);
+    void onActiveDisplaySizeChanged(const sp<const DisplayDevice>&);
 
     /*
      * Debugging & dumpsys
      */
-    void dumpAllLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
+    void dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers,
+                       std::string& result) const REQUIRES(mStateLock);
     void dumpHwcLayersMinidumpLocked(std::string& result) const REQUIRES(mStateLock);
 
     void appendSfConfigString(std::string& result) const;
@@ -1163,6 +1177,9 @@
     bool mSomeDataspaceChanged = false;
     bool mForceTransactionDisplayChange = false;
 
+    // Set if LayerMetadata has changed since the last LayerMetadata snapshot.
+    bool mLayerMetadataSnapshotNeeded = false;
+
     // Tracks layers that have pending frames which are candidates for being
     // latched.
     std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithQueuedFrames;
@@ -1328,7 +1345,7 @@
 
     std::unique_ptr<Hwc2::PowerAdvisor> mPowerAdvisor;
 
-    void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock);
+    void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock, kMainThreadContext);
 
     // Flag used to set override desired display mode from backdoor
     bool mDebugDisplayModeSetByBackdoor = false;
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 319d014..3e30dcb 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -22,7 +22,6 @@
 #include <cutils/properties.h>
 #include <ui/GraphicBuffer.h>
 
-#include "BufferStateLayer.h"
 #include "DisplayDevice.h"
 #include "FrameTracer/FrameTracer.h"
 #include "Layer.h"
@@ -89,8 +88,8 @@
     return compositionengine::impl::createCompositionEngine();
 }
 
-sp<BufferStateLayer> DefaultFactory::createBufferStateLayer(const LayerCreationArgs& args) {
-    return sp<BufferStateLayer>::make(args);
+sp<Layer> DefaultFactory::createBufferStateLayer(const LayerCreationArgs& args) {
+    return sp<Layer>::make(args);
 }
 
 sp<Layer> DefaultFactory::createEffectLayer(const LayerCreationArgs& args) {
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 6602240..6fca402 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -41,7 +41,7 @@
     std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
             const sp<IGraphicBufferProducer>&) override;
     std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override;
-    sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) override;
+    sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) override;
     sp<Layer> createEffectLayer(const LayerCreationArgs& args) override;
     std::unique_ptr<FrameTracer> createFrameTracer() override;
     std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index dc2afd3..6d18ade 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -31,7 +31,6 @@
 typedef int32_t PixelFormat;
 
 class BufferLayerConsumer;
-class BufferStateLayer;
 class DisplayDevice;
 class FrameTracer;
 class GraphicBuffer;
@@ -89,7 +88,7 @@
 
     virtual std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() = 0;
 
-    virtual sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) = 0;
+    virtual sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) = 0;
     virtual sp<Layer> createEffectLayer(const LayerCreationArgs& args) = 0;
     virtual std::unique_ptr<FrameTracer> createFrameTracer() = 0;
     virtual std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 6501e20..8817178 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -80,8 +80,8 @@
         return compositionengine::impl::createCompositionEngine();
     }
 
-    sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) {
-        return sp<BufferStateLayer>::make(args);
+    sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) {
+        return sp<Layer>::make(args);
     }
 
     sp<Layer> createEffectLayer(const LayerCreationArgs& args) { return sp<Layer>::make(args); }
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 2297618..69dbfe0 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -30,7 +30,6 @@
 #include <ui/DisplayStatInfo.h>
 #include <ui/DynamicDisplayInfo.h>
 
-#include "BufferStateLayer.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/ComposerHal.h"
 #include "FrameTimeline/FrameTimeline.h"
@@ -355,9 +354,7 @@
         return compositionengine::impl::createCompositionEngine();
     }
 
-    sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs &) override {
-        return nullptr;
-    }
+    sp<Layer> createBufferStateLayer(const LayerCreationArgs &) override { return nullptr; }
 
     sp<Layer> createEffectLayer(const LayerCreationArgs &args) override {
         return sp<Layer>::make(args);
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index 9ece260..0a142c3 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  *
  */
-#include <BufferStateLayer.h>
 #include <Client.h>
 #include <DisplayDevice.h>
 #include <LayerRenderArea.h>
@@ -109,8 +108,7 @@
 void LayerFuzzer::invokeBufferStateLayer() {
     TestableSurfaceFlinger flinger;
     sp<Client> client = sp<Client>::make(sp<SurfaceFlinger>::fromExisting(flinger.flinger()));
-    sp<BufferStateLayer> layer =
-            sp<BufferStateLayer>::make(createLayerCreationArgs(&flinger, client));
+    sp<Layer> layer = sp<Layer>::make(createLayerCreationArgs(&flinger, client));
     sp<Fence> fence = sp<Fence>::make();
     const std::shared_ptr<FenceTime> fenceTime = std::make_shared<FenceTime>(fence);
 
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index 3fc2b7e..66bac44 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -322,7 +322,7 @@
     LayerCreationArgs args(flinger.flinger(), client,
                            mFdp.ConsumeRandomLengthString(kRandomStringLength) /*name*/,
                            mFdp.ConsumeIntegral<uint16_t>() /*layerFlags*/, LayerMetadata());
-    sp<Layer> layer = sp<BufferStateLayer>::make(args);
+    sp<Layer> layer = sp<Layer>::make(args);
 
     layer->setFrameRateSelectionPriority(mFdp.ConsumeIntegral<int16_t>());
 }
@@ -350,7 +350,7 @@
     const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
     std::vector<LayerRequirement> layers = {{.weight = mFdp.ConsumeFloatingPoint<float>()}};
 
-    refreshRateConfigs.getBestRefreshRate(layers, globalSignals);
+    refreshRateConfigs.getRankedRefreshRates(layers, globalSignals);
 
     layers[0].name = mFdp.ConsumeRandomLengthString(kRandomStringLength);
     layers[0].ownerUid = mFdp.ConsumeIntegral<uint16_t>();
diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp
index 973a439..7287dd0 100644
--- a/services/surfaceflinger/layerproto/Android.bp
+++ b/services/surfaceflinger/layerproto/Android.bp
@@ -44,10 +44,11 @@
 }
 
 java_library_static {
-    name: "layersprotosnano",
+    name: "layersprotoslite",
     host_supported: true,
     proto: {
-        type: "nano",
+        type: "lite",
+        include_dirs: ["external/protobuf/src"],
     },
     srcs: ["*.proto"],
     sdk_version: "core_platform",
@@ -56,7 +57,7 @@
             jarjar_rules: "jarjar-rules.txt",
         },
         host: {
-            static_libs: ["libprotobuf-java-nano"],
+            static_libs: ["libprotobuf-java-lite"],
         },
     },
 }
diff --git a/services/surfaceflinger/tests/LayerBorder_test.cpp b/services/surfaceflinger/tests/LayerBorder_test.cpp
index 0d55ec1..85108ad 100644
--- a/services/surfaceflinger/tests/LayerBorder_test.cpp
+++ b/services/surfaceflinger/tests/LayerBorder_test.cpp
@@ -215,13 +215,13 @@
     });
 }
 
-TEST_F(LayerBorderTest, BufferStateLayer) {
+TEST_F(LayerBorderTest, LayerWithBuffer) {
     asTransaction([&](Transaction& t) {
         t.hide(mEffectLayer1);
         t.hide(mEffectLayer2);
         t.show(mContainerLayer);
 
-        sp<SurfaceControl> bufferStateLayer =
+        sp<SurfaceControl> layer =
                 mClient->createSurface(String8("BufferState"), 0 /* width */, 0 /* height */,
                                        PIXEL_FORMAT_RGBA_8888,
                                        ISurfaceComposerClient::eFXSurfaceBufferState,
@@ -236,9 +236,9 @@
         TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 200, 200), Color::GREEN);
         TransactionUtils::fillGraphicBufferColor(buffer, Rect(200, 200, 400, 400), Color::BLUE);
 
-        t.setBuffer(bufferStateLayer, buffer);
-        t.setPosition(bufferStateLayer, 100, 100);
-        t.show(bufferStateLayer);
+        t.setBuffer(layer, buffer);
+        t.setPosition(layer, 100, 100);
+        t.show(layer);
         t.enableBorder(mContainerLayer, true, 20, mColorOrange);
     });
 }
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 1460fe1..26dbc76 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -51,7 +51,7 @@
         LayerTransactionTest::TearDown();
     }
 
-    virtual sp<SurfaceControl> createBufferStateLayer() {
+    virtual sp<SurfaceControl> createLayerWithBuffer() {
         return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
     }
 
@@ -164,7 +164,7 @@
 
 TEST_F(LayerCallbackTest, BufferColor) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -183,7 +183,7 @@
 
 TEST_F(LayerCallbackTest, NoBufferNoColor) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -206,7 +206,7 @@
 
 TEST_F(LayerCallbackTest, BufferNoColor) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -228,7 +228,7 @@
 
 TEST_F(LayerCallbackTest, NoBufferColor) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -266,7 +266,7 @@
 
 TEST_F(LayerCallbackTest, OffScreen) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -288,8 +288,8 @@
 
 TEST_F(LayerCallbackTest, MergeBufferNoColor) {
     sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
@@ -322,8 +322,8 @@
 
 TEST_F(LayerCallbackTest, MergeNoBufferColor) {
     sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
@@ -357,8 +357,8 @@
 
 TEST_F(LayerCallbackTest, MergeOneBufferOneColor) {
     sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
@@ -392,8 +392,8 @@
 }
 TEST_F(LayerCallbackTest, Merge_SameCallback) {
     sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback;
@@ -418,7 +418,7 @@
 
 TEST_F(LayerCallbackTest, Merge_SameLayer) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
@@ -485,7 +485,7 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -510,7 +510,7 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions_NoStateChange) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -541,7 +541,7 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions_SameStateChange) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -579,8 +579,8 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions_Merge) {
     sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
@@ -799,7 +799,7 @@
 // TODO (b/183181768): Fix & re-enable
 TEST_F(LayerCallbackTest, DISABLED_MultipleTransactions_SingleFrame) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -823,7 +823,7 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_NoStateChange) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     // Normal call to set up test
     Transaction transaction;
@@ -858,7 +858,7 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_SameStateChange) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     // Normal call to set up test
     Transaction transaction;
@@ -901,7 +901,7 @@
 
 TEST_F(LayerCallbackTest, DesiredPresentTime) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -925,7 +925,7 @@
 
 TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback1;
@@ -971,7 +971,7 @@
 // TODO (b/183181768): Fix & re-enable
 TEST_F(LayerCallbackTest, DISABLED_DesiredPresentTime_OutOfOrder) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback1;
@@ -1015,7 +1015,7 @@
 
 TEST_F(LayerCallbackTest, DesiredPresentTime_Past) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -1039,7 +1039,7 @@
 
 TEST_F(LayerCallbackTest, ExpectedPresentTime) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -1065,8 +1065,8 @@
 // b202394221
 TEST_F(LayerCallbackTest, EmptyBufferStateChanges) {
     sp<SurfaceControl> bufferLayer, emptyBufferLayer;
-    ASSERT_NO_FATAL_FAILURE(bufferLayer = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(emptyBufferLayer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(emptyBufferLayer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -1120,7 +1120,7 @@
 
 TEST_F(LayerCallbackTest, CommitCallbackOffscreenLayer) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
     sp<SurfaceControl> offscreenLayer =
             createSurface(mClient, "Offscreen Layer", 0, 0, PIXEL_FORMAT_RGBA_8888,
                           ISurfaceComposerClient::eFXSurfaceBufferState, layer.get());
@@ -1151,7 +1151,7 @@
 
 TEST_F(LayerCallbackTest, TransactionCommittedCallback_BSL) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index bbe7ae8..bf7cae9 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -399,7 +399,7 @@
             .apply();
     ASSERT_NO_FATAL_FAILURE(
             fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layerR, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layerR, Color::RED, 32, 32));
     getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
 }
 
@@ -482,7 +482,7 @@
     }
 }
 
-// RED: Color layer base color and BufferQueueLayer/BufferStateLayer fill
+// RED: Color layer base color and Layer buffer fill
 // BLUE: prior background color
 // GREEN: final background color
 // BLACK: no color or fill
@@ -516,7 +516,7 @@
         case ISurfaceComposerClient::eFXSurfaceBufferState:
             ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType));
             if (bufferFill) {
-                ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, fillColor, width, height));
+                ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, fillColor, width, height));
                 expectedColor = fillColor;
             }
             Transaction().setCrop(layer, Rect(0, 0, width, height)).apply();
@@ -832,7 +832,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
     const Rect crop(8, 8, 24, 24);
 
     Transaction().setCrop(layer, crop).apply();
@@ -863,7 +863,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     {
         SCOPED_TRACE("empty rect");
@@ -944,7 +944,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     const Rect crop(8, 8, 24, 24);
     Transaction().setPosition(layer, 32, 32).setCrop(layer, crop).apply();
@@ -972,7 +972,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
     const Rect frame(8, 8, 24, 24);
 
     Transaction t;
@@ -988,7 +988,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     Transaction t;
     {
@@ -1014,7 +1014,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 10, 10));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 10, 10));
 
     // A layer with a buffer will have a computed size that matches the buffer size.
     auto shot = getScreenCapture();
@@ -1026,11 +1026,11 @@
     sp<SurfaceControl> parent, child;
     ASSERT_NO_FATAL_FAILURE(
             parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(parent, Color::RED, 32, 32));
 
     ASSERT_NO_FATAL_FAILURE(
             child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(child, Color::BLUE, 10, 10));
 
     Transaction().reparent(child, parent).apply();
 
@@ -1047,7 +1047,7 @@
 
     ASSERT_NO_FATAL_FAILURE(
             child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(child, Color::BLUE, 10, 10));
 
     Transaction().reparent(child, parent).apply();
 
@@ -1061,7 +1061,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     std::this_thread::sleep_for(500ms);
 
@@ -1080,9 +1080,9 @@
             child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
     Transaction().reparent(child, parent).apply();
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(parent, Color::RED, 32, 32));
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(child, Color::BLUE, 10, 10));
     Rect childDst(0, 16, 32, 32);
     Transaction t;
     TransactionUtils::setFrame(t, child, Rect(0, 0, 10, 10), childDst);
@@ -1099,7 +1099,7 @@
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
@@ -1111,7 +1111,7 @@
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     {
         SCOPED_TRACE("set buffer 1");
@@ -1120,7 +1120,7 @@
         shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::BLUE, 32, 32));
 
     {
         SCOPED_TRACE("set buffer 2");
@@ -1129,7 +1129,7 @@
         shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     {
         SCOPED_TRACE("set buffer 3");
@@ -1148,7 +1148,7 @@
     ASSERT_NO_FATAL_FAILURE(
             layer2 = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::RED, 64, 64));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer1, Color::RED, 64, 64));
 
     {
         SCOPED_TRACE("set layer 1 buffer red");
@@ -1156,7 +1156,7 @@
         shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::BLUE, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer2, Color::BLUE, 32, 32));
 
     {
         SCOPED_TRACE("set layer 2 buffer blue");
@@ -1166,7 +1166,7 @@
         shot->expectColor(Rect(0, 32, 32, 64), Color::RED);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::GREEN, 64, 64));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer1, Color::GREEN, 64, 64));
     {
         SCOPED_TRACE("set layer 1 buffer green");
         auto shot = getScreenCapture();
@@ -1175,7 +1175,7 @@
         shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::WHITE, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer2, Color::WHITE, 32, 32));
 
     {
         SCOPED_TRACE("set layer 2 buffer white");
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 0e8f3dd..774c1d7 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -137,8 +137,8 @@
         postBufferQueueLayerBuffer(layer);
     }
 
-    virtual void fillBufferStateLayerColor(const sp<SurfaceControl>& layer, const Color& color,
-                                           int32_t bufferWidth, int32_t bufferHeight) {
+    virtual void fillBufferLayerColor(const sp<SurfaceControl>& layer, const Color& color,
+                                      int32_t bufferWidth, int32_t bufferHeight) {
         sp<GraphicBuffer> buffer =
                 sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth),
                                         static_cast<uint32_t>(bufferHeight), PIXEL_FORMAT_RGBA_8888,
@@ -159,7 +159,7 @@
                 fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight);
                 break;
             case ISurfaceComposerClient::eFXSurfaceBufferState:
-                fillBufferStateLayerColor(layer, color, bufferWidth, bufferHeight);
+                fillBufferLayerColor(layer, color, bufferWidth, bufferHeight);
                 break;
             default:
                 ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType;
diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp
index c206e1f..cbd54e7 100644
--- a/services/surfaceflinger/tests/LayerTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp
@@ -32,7 +32,7 @@
 
     Transaction().setTransformToDisplayInverse(layer, false).apply();
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::GREEN, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::GREEN, 32, 32));
 
     Transaction().setTransformToDisplayInverse(layer, true).apply();
 }
@@ -161,7 +161,7 @@
 TEST_F(LayerTransactionTest, BufferTakesPriorityOverBlur) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
     Transaction().setBackgroundBlurRadius(layer, 5).apply();
     {
         SCOPED_TRACE("BufferTakesPriorityOverBlur");
@@ -174,7 +174,7 @@
 TEST_F(LayerTransactionTest, BufferTakesPriorityOverColor) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
     Transaction().setColor(layer, {Color::GREEN.r, Color::GREEN.g, Color::GREEN.b}).apply();
     {
         SCOPED_TRACE("BufferTakesPriorityOverColor");
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index a921aa8..faaef5d 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -193,14 +193,14 @@
         shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
     }
 
-    sp<SurfaceControl> bufferStateLayer =
-            createLayer("BufferStateLayer", 200, 200, ISurfaceComposerClient::eFXSurfaceBufferState,
+    sp<SurfaceControl> layer =
+            createLayer("Layer", 200, 200, ISurfaceComposerClient::eFXSurfaceBufferState,
                         mChildLayer.get());
-    fillBufferStateLayerColor(bufferStateLayer, Color::BLUE, 200, 200);
-    Transaction().show(bufferStateLayer).apply();
+    fillBufferLayerColor(layer, Color::BLUE, 200, 200);
+    Transaction().show(layer).apply();
 
     {
-        SCOPED_TRACE("Initial Mirror BufferStateLayer");
+        SCOPED_TRACE("Initial Mirror Layer");
         auto shot = screenshot();
         // Buffer mirror
         shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE);
@@ -208,9 +208,9 @@
         shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
     }
 
-    fillBufferStateLayerColor(bufferStateLayer, Color::WHITE, 200, 200);
+    fillBufferLayerColor(layer, Color::WHITE, 200, 200);
     {
-        SCOPED_TRACE("Update BufferStateLayer");
+        SCOPED_TRACE("Update Layer");
         auto shot = screenshot();
         // Buffer mirror
         shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
@@ -218,9 +218,9 @@
         shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
     }
 
-    Transaction().reparent(bufferStateLayer, nullptr).apply();
+    Transaction().reparent(layer, nullptr).apply();
     {
-        SCOPED_TRACE("Removed BufferStateLayer");
+        SCOPED_TRACE("Removed Layer");
         auto shot = screenshot();
         // Buffer mirror
         shot->expectColor(Rect(550, 550, 750, 750), Color::GREEN);
@@ -283,7 +283,7 @@
     sp<SurfaceControl> grandchild =
             createLayer("Grandchild layer", 50, 50, ISurfaceComposerClient::eFXSurfaceBufferState,
                         mChildLayer.get());
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(grandchild, Color::BLUE, 50, 50));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(grandchild, Color::BLUE, 50, 50));
     Rect childBounds = Rect(50, 50, 450, 450);
 
     asTransaction([&](Transaction& t) {
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index d78c8a9..3a5e532 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -371,7 +371,7 @@
     ScreenCaptureResults captureResults;
     ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(args, captureResults));
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(child, Color::RED, 32, 32));
     SurfaceComposerClient::Transaction().apply(true);
     ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(args, captureResults));
     ScreenCapture sc(captureResults.buffer, captureResults.capturedHdrLayers);
@@ -449,8 +449,8 @@
                                                  ISurfaceComposerClient::eFXSurfaceBufferState,
                                                  redLayer.get());
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(blueLayer, Color::BLUE, 30, 30));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(blueLayer, Color::BLUE, 30, 30));
 
     SurfaceComposerClient::Transaction()
             .setLayer(redLayer, INT32_MAX - 1)
@@ -484,8 +484,8 @@
                                                  ISurfaceComposerClient::eFXSurfaceBufferState,
                                                  redLayer.get());
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(blueLayer, Color::BLUE, 30, 30));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(blueLayer, Color::BLUE, 30, 30));
 
     SurfaceComposerClient::Transaction()
             .setLayer(redLayer, INT32_MAX - 1)
@@ -549,8 +549,8 @@
                         ISurfaceComposerClient::eSecure |
                                 ISurfaceComposerClient::eFXSurfaceBufferState,
                         redLayer.get());
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(secureLayer, Color::BLUE, 30, 30));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(secureLayer, Color::BLUE, 30, 30));
 
     auto redLayerHandle = redLayer->getHandle();
     Transaction()
@@ -803,7 +803,7 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
                                                 mBGSurfaceControl.get()));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
     Transaction().show(layer).setLayer(layer, INT32_MAX).apply();
 
     LayerCaptureArgs captureArgs;
@@ -825,7 +825,7 @@
     mCapture->expectColor(Rect(0, 0, 32, 32),
                           Color{expectedColor, expectedColor, expectedColor, 255}, tolerance);
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::BLUE, 32, 32));
     ScreenCapture::captureLayers(&mCapture, captureArgs);
 
     expectedColor = luminance.b * 255;
@@ -838,7 +838,7 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
                                                 mBGSurfaceControl.get()));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     Transaction().show(layer).hide(mFGSurfaceControl).reparent(layer, nullptr).apply();
 
@@ -865,7 +865,7 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
                                                 mBGSurfaceControl.get()));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLACK, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::BLACK, 32, 32));
     Transaction()
             .show(layer)
             .setLayer(layer, INT32_MAX)
@@ -885,7 +885,7 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
                                                 mBGSurfaceControl.get()));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLACK, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::BLACK, 32, 32));
     Transaction()
             .show(layer)
             .setLayer(layer, INT32_MAX)
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index dec14d0..4469df0 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -109,6 +109,7 @@
         "SurfaceFlinger_SetDisplayStateTest.cpp",
         "SurfaceFlinger_SetPowerModeInternalTest.cpp",
         "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
+        "SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp",
         "SchedulerTest.cpp",
         "SetFrameRateTest.cpp",
         "RefreshRateConfigsTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
index 9082a22..c1cbbfb 100644
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CachingTest.cpp
@@ -20,7 +20,8 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <gui/BufferQueue.h>
-#include "BufferStateLayer.h"
+
+#include "HwcSlotGenerator.h"
 
 namespace android {
 
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 9485f48..7148c11 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -491,7 +491,7 @@
     static constexpr IComposerClient::BlendMode BLENDMODE =
             IComposerClient::BlendMode::PREMULTIPLIED;
 
-    static void setupLatchedBuffer(CompositionTest* test, sp<BufferStateLayer> layer) {
+    static void setupLatchedBuffer(CompositionTest* test, sp<Layer> layer) {
         Mock::VerifyAndClear(test->mRenderEngine);
 
         const auto buffer = std::make_shared<
@@ -515,7 +515,7 @@
         Mock::VerifyAndClear(test->mRenderEngine);
     }
 
-    static void setupLayerState(CompositionTest* test, sp<BufferStateLayer> layer) {
+    static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
         setupLatchedBuffer(test, layer);
     }
 
@@ -699,7 +699,7 @@
     using Base = BaseLayerProperties<SidebandLayerProperties>;
     static constexpr IComposerClient::BlendMode BLENDMODE = IComposerClient::BlendMode::NONE;
 
-    static void setupLayerState(CompositionTest* test, sp<BufferStateLayer> layer) {
+    static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
         sp<NativeHandle> stream =
                 NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
                                      false);
@@ -780,14 +780,14 @@
 struct CursorLayerProperties : public BaseLayerProperties<CursorLayerProperties> {
     using Base = BaseLayerProperties<CursorLayerProperties>;
 
-    static void setupLayerState(CompositionTest* test, sp<BufferStateLayer> layer) {
+    static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
         Base::setupLayerState(test, layer);
         test->mFlinger.setLayerPotentialCursor(layer, true);
     }
 };
 
 struct NoLayerVariant {
-    using FlingerLayerType = sp<BufferStateLayer>;
+    using FlingerLayerType = sp<Layer>;
 
     static FlingerLayerType createLayer(CompositionTest*) { return FlingerLayerType(); }
     static void injectLayer(CompositionTest*, FlingerLayerType) {}
@@ -892,17 +892,17 @@
 template <typename LayerProperties>
 struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> {
     using Base = BaseLayerVariant<LayerProperties>;
-    using FlingerLayerType = sp<BufferStateLayer>;
+    using FlingerLayerType = sp<Layer>;
 
     static FlingerLayerType createLayer(CompositionTest* test) {
         test->mFlinger.mutableTexturePool().push_back(DEFAULT_TEXTURE_ID);
 
         FlingerLayerType layer =
-                Base::template createLayerWithFactory<BufferStateLayer>(test, [test]() {
+                Base::template createLayerWithFactory<Layer>(test, [test]() {
                     LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer",
                                            LayerProperties::LAYER_FLAGS, LayerMetadata());
                     args.textureName = test->mFlinger.mutableTexturePool().back();
-                    return sp<BufferStateLayer>::make(args);
+                    return sp<Layer>::make(args);
                 });
 
         LayerProperties::setupLayerState(test, layer);
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
index 9789df5..1cd9e49 100644
--- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -24,7 +24,6 @@
 #include <gtest/gtest.h>
 #include <gui/LayerMetadata.h>
 
-#include "BufferStateLayer.h"
 #include "FpsReporter.h"
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
@@ -79,7 +78,7 @@
     static constexpr int32_t PRIORITY_UNSET = -1;
 
     void setupScheduler();
-    sp<BufferStateLayer> createBufferStateLayer(LayerMetadata metadata);
+    sp<Layer> createBufferStateLayer(LayerMetadata metadata);
 
     TestableSurfaceFlinger mFlinger;
     mock::FrameTimeline mFrameTimeline =
@@ -115,10 +114,10 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-sp<BufferStateLayer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) {
+sp<Layer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) {
     sp<Client> client;
     LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata);
-    return sp<BufferStateLayer>::make(args);
+    return sp<Layer>::make(args);
 }
 
 void FpsReporterTest::setupScheduler() {
diff --git a/services/surfaceflinger/tests/unittests/GameModeTest.cpp b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
index cd857c3..29aa717 100644
--- a/services/surfaceflinger/tests/unittests/GameModeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
@@ -53,11 +53,10 @@
         ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
     }
 
-    sp<BufferStateLayer> createBufferStateLayer() {
+    sp<Layer> createLayer() {
         sp<Client> client;
-        LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
-                               LayerMetadata());
-        return sp<BufferStateLayer>::make(args);
+        LayerCreationArgs args(mFlinger.flinger(), client, "layer", 0, LayerMetadata());
+        return sp<Layer>::make(args);
     }
 
     void setupScheduler() {
@@ -108,9 +107,9 @@
 };
 
 TEST_F(GameModeTest, SetGameModeSetsForAllCurrentChildren) {
-    sp<BufferStateLayer> rootLayer = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer1 = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer2 = createBufferStateLayer();
+    sp<Layer> rootLayer = createLayer();
+    sp<Layer> childLayer1 = createLayer();
+    sp<Layer> childLayer2 = createLayer();
     rootLayer->addChild(childLayer1);
     rootLayer->addChild(childLayer2);
     rootLayer->setGameModeForTree(GameMode::Performance);
@@ -121,8 +120,8 @@
 }
 
 TEST_F(GameModeTest, AddChildAppliesGameModeFromParent) {
-    sp<BufferStateLayer> rootLayer = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer = createBufferStateLayer();
+    sp<Layer> rootLayer = createLayer();
+    sp<Layer> childLayer = createLayer();
     rootLayer->setGameModeForTree(GameMode::Performance);
     rootLayer->addChild(childLayer);
 
@@ -131,8 +130,8 @@
 }
 
 TEST_F(GameModeTest, RemoveChildResetsGameMode) {
-    sp<BufferStateLayer> rootLayer = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer = createBufferStateLayer();
+    sp<Layer> rootLayer = createLayer();
+    sp<Layer> childLayer = createLayer();
     rootLayer->setGameModeForTree(GameMode::Performance);
     rootLayer->addChild(childLayer);
 
@@ -144,9 +143,9 @@
 }
 
 TEST_F(GameModeTest, ReparentingDoesNotOverrideMetadata) {
-    sp<BufferStateLayer> rootLayer = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer1 = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer2 = createBufferStateLayer();
+    sp<Layer> rootLayer = createLayer();
+    sp<Layer> childLayer1 = createLayer();
+    sp<Layer> childLayer2 = createLayer();
     rootLayer->setGameModeForTree(GameMode::Standard);
     rootLayer->addChild(childLayer1);
 
diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
index 14304d1..ee42e19 100644
--- a/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
@@ -29,7 +29,7 @@
     sp<Client> client;
     LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS,
                            LayerMetadata());
-    return sp<BufferStateLayer>::make(args);
+    return sp<Layer>::make(args);
 }
 
 sp<Layer> EffectLayerFactory::createLayer(TestableSurfaceFlinger& flinger) {
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 5d9b2a8..a706c4b 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -41,6 +41,7 @@
 
 struct TestableRefreshRateConfigs : RefreshRateConfigs {
     using RefreshRateConfigs::RefreshRateConfigs;
+    using RefreshRateConfigs::RefreshRateOrder;
 
     void setActiveModeId(DisplayModeId modeId) {
         ftl::FakeGuard guard(kMainThreadContext);
@@ -67,19 +68,30 @@
         return getMinRefreshRateByPolicyLocked();
     }
 
+    DisplayModePtr getMaxRefreshRateByPolicy() const {
+        std::lock_guard lock(mLock);
+        return getMaxRefreshRateByPolicyLocked(getActiveModeItLocked()->second->getGroup());
+    }
+
+    std::vector<RefreshRateRanking> getRefreshRatesByPolicy(
+            std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder) const {
+        std::lock_guard lock(mLock);
+        return RefreshRateConfigs::getRefreshRatesByPolicyLocked(anchorGroupOpt, refreshRateOrder);
+    }
+
     const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
 
-    using RefreshRateConfigs::GetBestRefreshRateCache;
-    auto& mutableGetBestRefreshRateCache() { return mGetBestRefreshRateCache; }
+    using RefreshRateConfigs::GetRankedRefreshRatesCache;
+    auto& mutableGetRankedRefreshRatesCache() { return mGetRankedRefreshRatesCache; }
 
-    auto getBestRefreshRateAndSignals(const std::vector<LayerRequirement>& layers,
-                                      GlobalSignals signals) const {
-        return RefreshRateConfigs::getBestRefreshRate(layers, signals);
+    auto getRankedRefreshRatesAndSignals(const std::vector<LayerRequirement>& layers,
+                                         GlobalSignals signals) const {
+        return RefreshRateConfigs::getRankedRefreshRates(layers, signals);
     }
 
     DisplayModePtr getBestRefreshRate(const std::vector<LayerRequirement>& layers = {},
                                       GlobalSignals signals = {}) const {
-        return getBestRefreshRateAndSignals(layers, signals).first;
+        return getRankedRefreshRatesAndSignals(layers, signals).first.front().displayModePtr;
     }
 };
 
@@ -977,16 +989,116 @@
     EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 }
 
+TEST_F(RefreshRateConfigsTest, getMaxRefreshRatesByPolicy) {
+    // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
+    // different group.
+    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
+    const std::vector<RefreshRateRanking>& expectedRefreshRates = {RefreshRateRanking{kMode90},
+                                                                   RefreshRateRanking{kMode60},
+                                                                   RefreshRateRanking{kMode30}};
+
+    const std::vector<RefreshRateRanking>& refreshRates =
+            configs.getRefreshRatesByPolicy(configs.getActiveMode().getGroup(),
+                                            TestableRefreshRateConfigs::RefreshRateOrder::
+                                                    Descending);
+
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
+                << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, getMinRefreshRatesByPolicy) {
+    // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
+    // different group.
+    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
+    const std::vector<RefreshRateRanking>& expectedRefreshRates = {RefreshRateRanking{kMode30},
+                                                                   RefreshRateRanking{kMode60},
+                                                                   RefreshRateRanking{kMode90}};
+
+    const std::vector<RefreshRateRanking>& refreshRates =
+            configs.getRefreshRatesByPolicy(configs.getActiveMode().getGroup(),
+                                            TestableRefreshRateConfigs::RefreshRateOrder::
+                                                    Ascending);
+
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
+                << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, getMinRefreshRatesByPolicyOutsideTheGroup) {
+    // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
+    // different group.
+    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId72);
+    const std::vector<RefreshRateRanking>& expectedRefreshRates = {RefreshRateRanking{kMode30},
+                                                                   RefreshRateRanking{kMode60},
+                                                                   RefreshRateRanking{kMode90}};
+
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}), 0);
+
+    const std::vector<RefreshRateRanking>& refreshRates =
+            configs.getRefreshRatesByPolicy(/*anchorGroupOpt*/ std::nullopt,
+                                            TestableRefreshRateConfigs::RefreshRateOrder::
+                                                    Ascending);
+
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
+                << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, getMaxRefreshRatesByPolicyOutsideTheGroup) {
+    // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
+    // different group.
+    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId72);
+    const std::vector<RefreshRateRanking>& expectedRefreshRates = {RefreshRateRanking{kMode90},
+                                                                   RefreshRateRanking{kMode60},
+                                                                   RefreshRateRanking{kMode30}};
+
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}), 0);
+
+    const std::vector<RefreshRateRanking>& refreshRates =
+            configs.getRefreshRatesByPolicy(/*anchorGroupOpt*/ std::nullopt,
+                                            TestableRefreshRateConfigs::RefreshRateOrder::
+                                                    Descending);
+
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
+                << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+    }
+}
+
 TEST_F(RefreshRateConfigsTest, powerOnImminentConsidered) {
     RefreshRateConfigs configs(kModes_60_90, kModeId60);
+    std::vector<RefreshRateRanking> expectedRefreshRates = {RefreshRateRanking{kMode90},
+                                                            RefreshRateRanking{kMode60}};
 
-    auto [refreshRate, signals] = configs.getBestRefreshRate({}, {});
+    auto [refreshRates, signals] = configs.getRankedRefreshRates({}, {});
     EXPECT_FALSE(signals.powerOnImminent);
-    EXPECT_EQ(kMode90, refreshRate);
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
+                << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+    }
 
-    std::tie(refreshRate, signals) = configs.getBestRefreshRate({}, {.powerOnImminent = true});
+    std::tie(refreshRates, signals) = configs.getRankedRefreshRates({}, {.powerOnImminent = true});
     EXPECT_TRUE(signals.powerOnImminent);
-    EXPECT_EQ(kMode90, refreshRate);
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
+                << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+    }
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -994,22 +1106,35 @@
     lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
 
-    std::tie(refreshRate, signals) = configs.getBestRefreshRate(layers, {.powerOnImminent = false});
-    EXPECT_FALSE(signals.powerOnImminent);
-    EXPECT_EQ(kMode60, refreshRate);
-
-    std::tie(refreshRate, signals) = configs.getBestRefreshRate(layers, {.powerOnImminent = true});
+    std::tie(refreshRates, signals) =
+            configs.getRankedRefreshRates(layers, {.powerOnImminent = true});
     EXPECT_TRUE(signals.powerOnImminent);
-    EXPECT_EQ(kMode90, refreshRate);
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
+                << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+    }
+
+    expectedRefreshRates = {RefreshRateRanking{kMode60}, RefreshRateRanking{kMode90}};
+    std::tie(refreshRates, signals) =
+            configs.getRankedRefreshRates(layers, {.powerOnImminent = false});
+    EXPECT_FALSE(signals.powerOnImminent);
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
+                << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
+    }
 }
 
 TEST_F(RefreshRateConfigsTest, touchConsidered) {
     RefreshRateConfigs configs(kModes_60_90, kModeId60);
 
-    auto [_, signals] = configs.getBestRefreshRate({}, {});
+    auto [_, signals] = configs.getRankedRefreshRates({}, {});
     EXPECT_FALSE(signals.touch);
 
-    std::tie(std::ignore, signals) = configs.getBestRefreshRate({}, {.touch = true});
+    std::tie(std::ignore, signals) = configs.getRankedRefreshRates({}, {.touch = true});
     EXPECT_TRUE(signals.touch);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
@@ -1022,16 +1147,16 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true});
+    std::tie(std::ignore, signals) = configs.getRankedRefreshRates(layers, {.touch = true});
     EXPECT_TRUE(signals.touch);
 
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.desiredRefreshRate = 60_Hz;
-    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr1.name = "60Hz ExplicitDefault";
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true});
+    std::tie(std::ignore, signals) = configs.getRankedRefreshRates(layers, {.touch = true});
     EXPECT_FALSE(signals.touch);
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -1040,16 +1165,16 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true});
+    std::tie(std::ignore, signals) = configs.getRankedRefreshRates(layers, {.touch = true});
     EXPECT_TRUE(signals.touch);
 
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.desiredRefreshRate = 60_Hz;
-    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr1.name = "60Hz ExplicitDefault";
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true});
+    std::tie(std::ignore, signals) = configs.getRankedRefreshRates(layers, {.touch = true});
     EXPECT_FALSE(signals.touch);
 }
 
@@ -1187,9 +1312,10 @@
     lr.name = "60Hz ExplicitDefault";
     lr.focused = true;
 
-    const auto [mode, signals] = configs.getBestRefreshRate(layers, {.touch = true, .idle = true});
+    const auto [mode, signals] =
+            configs.getRankedRefreshRates(layers, {.touch = true, .idle = true});
 
-    EXPECT_EQ(mode, kMode60);
+    EXPECT_EQ(mode.begin()->displayModePtr, kMode60);
     EXPECT_FALSE(signals.touch);
 }
 
@@ -1209,14 +1335,147 @@
     EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.idle = true}));
 }
 
+TEST_F(RefreshRateConfigsTest, testDisplayModeOrdering) {
+    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f},
+                                            {.weight = 1.f},
+                                            {.weight = 1.f},
+                                            {.weight = 1.f},
+                                            {.weight = 1.f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+    auto& lr3 = layers[2];
+    auto& lr4 = layers[3];
+    auto& lr5 = layers[4];
+
+    lr1.desiredRefreshRate = 90_Hz;
+    lr1.name = "90Hz";
+    lr1.focused = true;
+
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.name = "60Hz";
+    lr2.focused = true;
+
+    lr3.desiredRefreshRate = 72_Hz;
+    lr3.name = "72Hz";
+    lr3.focused = true;
+
+    lr4.desiredRefreshRate = 120_Hz;
+    lr4.name = "120Hz";
+    lr4.focused = true;
+
+    lr5.desiredRefreshRate = 30_Hz;
+    lr5.name = "30Hz";
+    lr5.focused = true;
+
+    std::vector<RefreshRateRanking> expectedRankings = {
+            RefreshRateRanking{kMode120}, RefreshRateRanking{kMode90}, RefreshRateRanking{kMode72},
+            RefreshRateRanking{kMode60},  RefreshRateRanking{kMode30},
+    };
+
+    std::vector<RefreshRateRanking> actualOrder =
+            configs.getRankedRefreshRatesAndSignals(layers, {}).first;
+    ASSERT_EQ(expectedRankings.size(), actualOrder.size());
+    for (size_t i = 0; i < expectedRankings.size(); ++i) {
+        EXPECT_EQ(expectedRankings[i].displayModePtr, actualOrder[i].displayModePtr)
+                << "Expected fps " << expectedRankings[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << actualOrder[i].displayModePtr->getFps().getIntValue();
+    }
+
+    lr1.vote = LayerVoteType::Max;
+    lr1.name = "Max";
+
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.name = "60Hz";
+
+    lr3.desiredRefreshRate = 72_Hz;
+    lr3.name = "72Hz";
+
+    lr4.desiredRefreshRate = 90_Hz;
+    lr4.name = "90Hz";
+
+    lr5.desiredRefreshRate = 120_Hz;
+    lr5.name = "120Hz";
+
+    expectedRankings = {
+            RefreshRateRanking{kMode120}, RefreshRateRanking{kMode90}, RefreshRateRanking{kMode72},
+            RefreshRateRanking{kMode60},  RefreshRateRanking{kMode30},
+    };
+
+    actualOrder = configs.getRankedRefreshRatesAndSignals(layers, {}).first;
+
+    ASSERT_EQ(expectedRankings.size(), actualOrder.size());
+    for (size_t i = 0; i < expectedRankings.size(); ++i) {
+        EXPECT_EQ(expectedRankings[i].displayModePtr, actualOrder[i].displayModePtr)
+                << "Expected fps " << expectedRankings[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << actualOrder[i].displayModePtr->getFps().getIntValue();
+    }
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 30_Hz;
+    lr1.name = "30Hz";
+
+    lr2.desiredRefreshRate = 120_Hz;
+    lr2.name = "120Hz";
+
+    lr3.desiredRefreshRate = 60_Hz;
+    lr3.name = "60Hz";
+
+    lr5.desiredRefreshRate = 72_Hz;
+    lr5.name = "72Hz";
+
+    expectedRankings = {
+            RefreshRateRanking{kMode30},  RefreshRateRanking{kMode60}, RefreshRateRanking{kMode90},
+            RefreshRateRanking{kMode120}, RefreshRateRanking{kMode72},
+    };
+
+    actualOrder = configs.getRankedRefreshRatesAndSignals(layers, {}).first;
+    ASSERT_EQ(expectedRankings.size(), actualOrder.size());
+    for (size_t i = 0; i < expectedRankings.size(); ++i) {
+        EXPECT_EQ(expectedRankings[i].displayModePtr, actualOrder[i].displayModePtr)
+                << "Expected fps " << expectedRankings[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << actualOrder[i].displayModePtr->getFps().getIntValue();
+    }
+
+    lr1.desiredRefreshRate = 120_Hz;
+    lr1.name = "120Hz";
+    lr1.weight = 0.0f;
+
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.name = "60Hz";
+    lr2.vote = LayerVoteType::NoVote;
+
+    lr3.name = "60Hz-2";
+    lr3.vote = LayerVoteType::Heuristic;
+
+    lr4.vote = LayerVoteType::ExplicitExact;
+
+    lr5.desiredRefreshRate = 120_Hz;
+    lr5.name = "120Hz-2";
+
+    expectedRankings = {
+            RefreshRateRanking{kMode90}, RefreshRateRanking{kMode60}, RefreshRateRanking{kMode120},
+            RefreshRateRanking{kMode72}, RefreshRateRanking{kMode30},
+    };
+
+    actualOrder = configs.getRankedRefreshRatesAndSignals(layers, {}).first;
+    ASSERT_EQ(expectedRankings.size(), actualOrder.size());
+    for (size_t i = 0; i < expectedRankings.size(); ++i) {
+        EXPECT_EQ(expectedRankings[i].displayModePtr, actualOrder[i].displayModePtr)
+                << "Expected fps " << expectedRankings[i].displayModePtr->getFps().getIntValue()
+                << " Actual fps " << actualOrder[i].displayModePtr->getFps().getIntValue();
+    }
+}
+
 TEST_F(RefreshRateConfigsTest,
        getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
     TestableRefreshRateConfigs configs(kModes_60_90, kModeId90);
 
     EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
 
-    const auto [mode, signals] = configs.getBestRefreshRateAndSignals({}, {});
-    EXPECT_EQ(mode, kMode90);
+    const auto [mode, signals] = configs.getRankedRefreshRatesAndSignals({}, {});
+    EXPECT_EQ(mode.front().displayModePtr, kMode90);
     EXPECT_FALSE(signals.touch);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1593,11 +1852,12 @@
         layers[0].desiredRefreshRate = 90_Hz;
 
         const auto [refreshRate, signals] =
-                configs.getBestRefreshRateAndSignals(layers, {.touch = touchActive, .idle = true});
+                configs.getRankedRefreshRatesAndSignals(layers,
+                                                        {.touch = touchActive, .idle = true});
 
         // Refresh rate will be chosen by either touch state or idle state.
         EXPECT_EQ(!touchActive, signals.idle);
-        return refreshRate->getId();
+        return refreshRate.front().displayModePtr->getId();
     };
 
     EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
@@ -1756,24 +2016,26 @@
     using GlobalSignals = RefreshRateConfigs::GlobalSignals;
     const auto args = std::make_pair(std::vector<LayerRequirement>{},
                                      GlobalSignals{.touch = true, .idle = true});
-    const auto result = std::make_pair(kMode90, GlobalSignals{.touch = true});
 
-    configs.mutableGetBestRefreshRateCache() = {args, result};
+    const auto result = std::make_pair(std::vector<RefreshRateRanking>{RefreshRateRanking{kMode90}},
+                                       GlobalSignals{.touch = true});
 
-    EXPECT_EQ(result, configs.getBestRefreshRateAndSignals(args.first, args.second));
+    configs.mutableGetRankedRefreshRatesCache() = {args, result};
+
+    EXPECT_EQ(result, configs.getRankedRefreshRatesAndSignals(args.first, args.second));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_WritesCache) {
     TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
 
-    EXPECT_FALSE(configs.mutableGetBestRefreshRateCache());
+    EXPECT_FALSE(configs.mutableGetRankedRefreshRatesCache());
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
     RefreshRateConfigs::GlobalSignals globalSignals{.touch = true, .idle = true};
 
-    const auto result = configs.getBestRefreshRateAndSignals(layers, globalSignals);
+    const auto result = configs.getRankedRefreshRatesAndSignals(layers, globalSignals);
 
-    const auto& cache = configs.mutableGetBestRefreshRateCache();
+    const auto& cache = configs.mutableGetRankedRefreshRatesCache();
     ASSERT_TRUE(cache);
 
     EXPECT_EQ(cache->arguments, std::make_pair(layers, globalSignals));
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index abf1786..ac63a0e 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -21,7 +21,6 @@
 #include <gtest/gtest.h>
 #include <gui/LayerMetadata.h>
 
-#include "BufferStateLayer.h"
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
@@ -58,7 +57,7 @@
     static constexpr int32_t PRIORITY_UNSET = -1;
 
     void setupScheduler();
-    sp<BufferStateLayer> createBufferStateLayer();
+    sp<Layer> createBufferStateLayer();
     sp<Layer> createEffectLayer();
 
     void setParent(Layer* child, Layer* parent);
@@ -87,12 +86,11 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-
-sp<BufferStateLayer> RefreshRateSelectionTest::createBufferStateLayer() {
+sp<Layer> RefreshRateSelectionTest::createBufferStateLayer() {
     sp<Client> client;
     LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", LAYER_FLAGS,
                            LayerMetadata());
-    return sp<BufferStateLayer>::make(args);
+    return sp<Layer>::make(args);
 }
 
 sp<Layer> RefreshRateSelectionTest::createEffectLayer() {
diff --git a/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
index f19e554..409e1ef 100644
--- a/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
@@ -106,40 +106,6 @@
                 testing::Eq(0.0));
 }
 
-// workaround for b/133849373
-TEST_F(RegionSamplingTest, orientation_90) {
-    std::generate(buffer.begin(), buffer.end(),
-                  [n = 0]() mutable { return (n++ > (kStride * kHeight >> 1)) ? kBlack : kWhite; });
-
-    Rect tl_region{0, 0, 4, 4};
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0,
-                           tl_region),
-                testing::Eq(1.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180,
-                           tl_region),
-                testing::Eq(1.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90,
-                           tl_region),
-                testing::Eq(0.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270,
-                           tl_region),
-                testing::Eq(0.0));
-
-    Rect br_region{kWidth - 4, kHeight - 4, kWidth, kHeight};
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0,
-                           br_region),
-                testing::Eq(0.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180,
-                           br_region),
-                testing::Eq(0.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90,
-                           br_region),
-                testing::Eq(1.0));
-    EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270,
-                           br_region),
-                testing::Eq(1.0));
-}
-
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 51c6bea..dfcfd91 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -24,7 +24,6 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
-#include "BufferStateLayer.h"
 #include "Layer.h"
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 57937dc..6b7e353 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -20,6 +20,7 @@
 
 #include "DisplayTransactionTestHelpers.h"
 
+#include <ftl/fake_guard.h>
 #include <scheduler/Fps.h>
 
 namespace android {
@@ -111,8 +112,10 @@
 }
 
 TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRequired) {
+    ftl::FakeGuard guard(kMainThreadContext);
+
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
@@ -121,7 +124,7 @@
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
     ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
@@ -134,7 +137,7 @@
 
     Mock::VerifyAndClearExpectations(mComposer);
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
 
     // Verify that the next commit will complete the mode change and send
     // a onModeChanged event to the framework.
@@ -144,10 +147,12 @@
     Mock::VerifyAndClearExpectations(mAppEventThread);
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90);
 }
 
 TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefreshRequired) {
+    ftl::FakeGuard guard(kMainThreadContext);
+
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
 
     mFlinger.onActiveDisplayChanged(mDisplay);
@@ -157,7 +162,7 @@
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
     ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     // and complete the mode change.
@@ -172,15 +177,17 @@
     mFlinger.commit();
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90);
 }
 
 TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) {
+    ftl::FakeGuard guard(kMainThreadContext);
+
     // Test that if we call setDesiredDisplayModeSpecs while a previous mode change
     // is still being processed the later call will be respected.
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
@@ -214,12 +221,14 @@
     mFlinger.commit();
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId120);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId120);
 }
 
 TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefreshRequired) {
+    ftl::FakeGuard guard(kMainThreadContext);
+
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
@@ -228,7 +237,7 @@
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
     ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90_4K);
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     // and complete the mode change.
@@ -263,7 +272,7 @@
     mDisplay = mFlinger.getDisplay(displayToken);
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId90_4K);
+    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90_4K);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index ec2c2b4..073c459 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -17,6 +17,8 @@
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
+#include <ftl/fake_guard.h>
+
 #include "DisplayHardware/DisplayMode.h"
 
 #include "DisplayTransactionTestHelpers.h"
@@ -265,7 +267,7 @@
     // --------------------------------------------------------------------
     // Postconditions
 
-    ASSERT_TRUE(device != nullptr);
+    ASSERT_NE(nullptr, device);
     EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
     EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
     EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
@@ -282,8 +284,8 @@
               device->receivesInput());
 
     if constexpr (Case::Display::CONNECTION_TYPE::value) {
-        EXPECT_NE(nullptr, device->getActiveMode());
-        EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode()->getHwcId());
+        ftl::FakeGuard guard(kMainThreadContext);
+        EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode().getHwcId());
     }
 }
 
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
new file mode 100644
index 0000000..0cf3bdf
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
@@ -0,0 +1,212 @@
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+#include "TestableSurfaceFlinger.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockVsyncController.h"
+
+namespace android {
+
+using testing::_;
+using testing::Return;
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+class SurfaceFlingerUpdateLayerMetadataSnapshotTest : public testing::Test {
+public:
+    SurfaceFlingerUpdateLayerMetadataSnapshotTest() { setupScheduler(); }
+
+protected:
+    void setupScheduler() {
+        auto eventThread = std::make_unique<mock::EventThread>();
+        auto sfEventThread = std::make_unique<mock::EventThread>();
+
+        EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*eventThread, createEventConnection(_, _))
+                .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
+                                                                 mock::EventThread::kCallingUid,
+                                                                 ResyncCallback())));
+
+        EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+                .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
+                                                                 mock::EventThread::kCallingUid,
+                                                                 ResyncCallback())));
+
+        auto vsyncController = std::make_unique<mock::VsyncController>();
+        auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+
+        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+        EXPECT_CALL(*vsyncTracker, currentPeriod())
+                .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+        mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+                                std::move(eventThread), std::move(sfEventThread));
+    }
+
+    sp<Layer> createLayer(const char* name, LayerMetadata layerMetadata) {
+        return sp<Layer>::make(
+                LayerCreationArgs{mFlinger.flinger(), nullptr, name, 0, layerMetadata});
+    }
+
+    TestableSurfaceFlinger mFlinger;
+};
+
+class LayerMetadataBuilder {
+public:
+    LayerMetadataBuilder(LayerMetadata layerMetadata = {}) : mLayerMetadata(layerMetadata) {}
+
+    LayerMetadataBuilder& setInt32(uint32_t key, int32_t value) {
+        mLayerMetadata.setInt32(key, value);
+        return *this;
+    }
+
+    LayerMetadata build() { return mLayerMetadata; }
+
+private:
+    LayerMetadata mLayerMetadata;
+};
+
+bool operator==(const LayerMetadata& lhs, const LayerMetadata& rhs) {
+    return lhs.mMap == rhs.mMap;
+}
+
+std::ostream& operator<<(std::ostream& stream, const LayerMetadata& layerMetadata) {
+    stream << "LayerMetadata{";
+    for (auto it = layerMetadata.mMap.cbegin(); it != layerMetadata.mMap.cend(); it++) {
+        if (it != layerMetadata.mMap.cbegin()) {
+            stream << ", ";
+        }
+        stream << layerMetadata.itemToString(it->first, ":");
+    }
+    return stream << "}";
+}
+
+// Test that the snapshot's layer metadata is set.
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesSnapshotMetadata) {
+    auto layerMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 1).build();
+    auto layer = createLayer("layer", layerMetadata);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layer);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    ASSERT_EQ(layer->getLayerSnapshot()->layerMetadata, layerMetadata);
+}
+
+// Test that snapshot layer metadata is set by merging the child's metadata on top of its
+// parent's metadata.
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, mergesSnapshotMetadata) {
+    auto layerAMetadata = LayerMetadataBuilder()
+                                  .setInt32(METADATA_OWNER_UID, 1)
+                                  .setInt32(METADATA_TASK_ID, 2)
+                                  .build();
+    auto layerA = createLayer("parent", layerAMetadata);
+    auto layerBMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 3).build();
+    auto layerB = createLayer("child", layerBMetadata);
+    layerA->addChild(layerB);
+    layerA->commitChildList();
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    ASSERT_EQ(layerA->getLayerSnapshot()->layerMetadata, layerAMetadata);
+    auto expectedChildMetadata =
+            LayerMetadataBuilder(layerAMetadata).setInt32(METADATA_TASK_ID, 3).build();
+    ASSERT_EQ(layerB->getLayerSnapshot()->layerMetadata, expectedChildMetadata);
+}
+
+// Test that snapshot relative layer metadata is set to the parent's layer metadata merged on top of
+// that parent's relative layer metadata.
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesRelativeMetadata) {
+    auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 1).build();
+    auto layerA = createLayer("relative-parent", layerAMetadata);
+    auto layerAHandle = layerA->getHandle();
+    auto layerBMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 2).build();
+    auto layerB = createLayer("relative-child", layerBMetadata);
+    layerB->setRelativeLayer(layerAHandle, 1);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerB);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    ASSERT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
+    ASSERT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+}
+
+// Test that snapshot relative layer metadata is set correctly when a layer is interleaved within
+// two other layers.
+//
+// Layer
+//      A
+//     / \
+//    B   D
+//   /
+//  C
+//
+// Z-order Relatives
+//    B <- D <- C
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesRelativeMetadataInterleaved) {
+    auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build();
+    auto layerA = createLayer("layer-a", layerAMetadata);
+    auto layerBMetadata = LayerMetadataBuilder()
+                                  .setInt32(METADATA_TASK_ID, 2)
+                                  .setInt32(METADATA_OWNER_PID, 3)
+                                  .build();
+    auto layerB = createLayer("layer-b", layerBMetadata);
+    auto layerBHandle = layerB->getHandle();
+    auto layerC = createLayer("layer-c", {});
+    auto layerDMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 4).build();
+    auto layerD = createLayer("layer-d", layerDMetadata);
+    auto layerDHandle = layerD->getHandle();
+    layerB->addChild(layerC);
+    layerA->addChild(layerB);
+    layerA->addChild(layerD);
+    layerC->setRelativeLayer(layerDHandle, 1);
+    layerD->setRelativeLayer(layerBHandle, 1);
+    layerA->commitChildList();
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    auto expectedLayerDRelativeMetadata = LayerMetadataBuilder()
+                                                  // From layer A, parent of relative parent
+                                                  .setInt32(METADATA_OWNER_UID, 1)
+                                                  // From layer B, relative parent
+                                                  .setInt32(METADATA_TASK_ID, 2)
+                                                  .setInt32(METADATA_OWNER_PID, 3)
+                                                  .build();
+    ASSERT_EQ(layerD->getLayerSnapshot()->relativeLayerMetadata, expectedLayerDRelativeMetadata);
+    auto expectedLayerCRelativeMetadata =
+            LayerMetadataBuilder()
+                    // From layer A, parent of relative parent
+                    .setInt32(METADATA_OWNER_UID, 1)
+                    // From layer B, relative parent of relative parent
+                    .setInt32(METADATA_OWNER_PID, 3)
+                    // From layer D, relative parent
+                    .setInt32(METADATA_TASK_ID, 4)
+                    .build();
+    ASSERT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, expectedLayerCRelativeMetadata);
+}
+
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest,
+       updatesRelativeMetadataMultipleRelativeChildren) {
+    auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build();
+    auto layerA = createLayer("layer-a", layerAMetadata);
+    auto layerAHandle = layerA->getHandle();
+    auto layerB = createLayer("layer-b", {});
+    auto layerC = createLayer("layer-c", {});
+    layerB->setRelativeLayer(layerAHandle, 1);
+    layerC->setRelativeLayer(layerAHandle, 2);
+    layerA->commitChildList();
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerB);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerC);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    ASSERT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
+    ASSERT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+    ASSERT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 1ce6e18..13389a1 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -30,7 +30,6 @@
 #include <ftl/fake_guard.h>
 #include <gui/ScreenCaptureResults.h>
 
-#include "BufferStateLayer.h"
 #include "DisplayDevice.h"
 #include "FakeVsyncConfiguration.h"
 #include "FrameTracer/FrameTracer.h"
@@ -120,9 +119,7 @@
         return compositionengine::impl::createCompositionEngine();
     }
 
-    sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs&) override {
-        return nullptr;
-    }
+    sp<Layer> createBufferStateLayer(const LayerCreationArgs&) override { return nullptr; }
 
     sp<Layer> createEffectLayer(const LayerCreationArgs&) override { return nullptr; }
 
@@ -288,7 +285,7 @@
                                        const sp<NativeHandle>& sidebandStream) {
         layer->mDrawingState.sidebandStream = sidebandStream;
         layer->mSidebandStream = sidebandStream;
-        layer->editCompositionState()->sidebandStream = sidebandStream;
+        layer->editLayerSnapshot()->sidebandStream = sidebandStream;
     }
 
     void setLayerCompositionType(const sp<Layer>& layer,
@@ -480,6 +477,8 @@
         return mFlinger->mirrorLayer(args, mirrorFromHandle, outHandle, outLayerId);
     }
 
+    void updateLayerMetadataSnapshot() { mFlinger->updateLayerMetadataSnapshot(); }
+
     /* ------------------------------------------------------------------------
      * Read-only access to private data to assert post-conditions.
      */
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index efb9e0c..b493d11 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -347,8 +347,7 @@
         state.state.bufferData->acquireFence = std::move(fence);
         state.state.layerId = layerId;
         state.state.surface =
-                sp<BufferStateLayer>::make(
-                        LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {}))
+                sp<Layer>::make(LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {}))
                         ->getHandle();
         state.state.bufferData->flags = BufferData::BufferDataChange::fenceChanged;
 
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index cd64325..1173d1c 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -56,11 +56,11 @@
         ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
     }
 
-    sp<BufferStateLayer> createBufferStateLayer() {
+    sp<Layer> createLayer() {
         sp<Client> client;
         LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
                                LayerMetadata());
-        return sp<BufferStateLayer>::make(args);
+        return sp<Layer>::make(args);
     }
 
     void commitTransaction(Layer* layer) {
@@ -101,7 +101,7 @@
     FenceToFenceTimeMap fenceFactory;
 
     void BLASTTransactionSendsFrameTracerEvents() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
 
         sp<Fence> fence(sp<Fence>::make());
         int32_t layerId = layer->getSequence();
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index d5823c3..ae03db4 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -56,11 +56,10 @@
         ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
     }
 
-    sp<BufferStateLayer> createBufferStateLayer() {
+    sp<Layer> createLayer() {
         sp<Client> client;
-        LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
-                               LayerMetadata());
-        return sp<BufferStateLayer>::make(args);
+        LayerCreationArgs args(mFlinger.flinger(), client, "layer", 0, LayerMetadata());
+        return sp<Layer>::make(args);
     }
 
     void commitTransaction(Layer* layer) {
@@ -101,7 +100,7 @@
     FenceToFenceTimeMap fenceFactory;
 
     void PresentedSurfaceFrameForBufferlessTransaction() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
         FrameTimelineInfo ftInfo;
         ftInfo.vsyncId = 1;
         ftInfo.inputEventId = 0;
@@ -116,7 +115,7 @@
     }
 
     void PresentedSurfaceFrameForBufferTransaction() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
         sp<Fence> fence(sp<Fence>::make());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
         BufferData bufferData;
@@ -150,7 +149,7 @@
     }
 
     void DroppedSurfaceFrameForBufferTransaction() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
 
         sp<Fence> fence1(sp<Fence>::make());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
@@ -208,7 +207,7 @@
     }
 
     void BufferlessSurfaceFramePromotedToBufferSurfaceFrame() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
         FrameTimelineInfo ftInfo;
         ftInfo.vsyncId = 1;
         ftInfo.inputEventId = 0;
@@ -249,7 +248,7 @@
     }
 
     void BufferlessSurfaceFrameNotCreatedIfBufferSufaceFrameExists() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
         sp<Fence> fence(sp<Fence>::make());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
         BufferData bufferData;
@@ -275,7 +274,7 @@
     }
 
     void MultipleSurfaceFramesPresentedTogether() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
         FrameTimelineInfo ftInfo;
         ftInfo.vsyncId = 1;
         ftInfo.inputEventId = 0;
@@ -336,7 +335,7 @@
     }
 
     void PendingSurfaceFramesRemovedAfterClassification() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
 
         sp<Fence> fence1(sp<Fence>::make());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
@@ -388,7 +387,7 @@
     }
 
     void BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
 
         sp<Fence> fence1(sp<Fence>::make());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
@@ -477,7 +476,7 @@
     }
 
     void MultipleCommitsBeforeLatch() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
         uint32_t surfaceFramesPendingClassification = 0;
         std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> bufferlessSurfaceFrames;
         for (int i = 0; i < 10; i += 2) {
diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
index 45ebb85..da87f1d 100644
--- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
@@ -22,7 +22,6 @@
 #include <gtest/gtest.h>
 #include <gui/LayerMetadata.h>
 
-#include "BufferStateLayer.h"
 #include "TestableSurfaceFlinger.h"
 #include "TunnelModeEnabledReporter.h"
 #include "mock/DisplayHardware/MockComposer.h"
@@ -64,7 +63,7 @@
 
     void setupScheduler();
     void setupComposer(uint32_t virtualDisplayCount);
-    sp<BufferStateLayer> createBufferStateLayer(LayerMetadata metadata);
+    sp<Layer> createBufferStateLayer(LayerMetadata metadata);
 
     TestableSurfaceFlinger mFlinger;
     Hwc2::mock::Composer* mComposer = nullptr;
@@ -95,11 +94,10 @@
     mTunnelModeEnabledReporter->removeListener(mTunnelModeEnabledListener);
 }
 
-sp<BufferStateLayer> TunnelModeEnabledReporterTest::createBufferStateLayer(
-        LayerMetadata metadata = {}) {
+sp<Layer> TunnelModeEnabledReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) {
     sp<Client> client;
     LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata);
-    return sp<BufferStateLayer>::make(args);
+    return sp<Layer>::make(args);
 }
 
 void TunnelModeEnabledReporterTest::setupScheduler() {
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 48d05cb..0d94f4c 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -18,14 +18,12 @@
 
 #include <gmock/gmock.h>
 
-#include "BufferStateLayer.h"
-
 namespace android::mock {
 
-class MockLayer : public BufferStateLayer {
+class MockLayer : public Layer {
 public:
     MockLayer(SurfaceFlinger* flinger, std::string name)
-          : BufferStateLayer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {
+          : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {
         EXPECT_CALL(*this, getDefaultFrameRateCompatibility())
                 .WillOnce(testing::Return(scheduler::LayerInfo::FrameRateCompatibility::Default));
     }
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 5719b5c..a87f82f 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -27,6 +27,9 @@
     symbol_file: "libvulkan.map.txt",
     first_version: "24",
     unversioned_until: "current",
+    export_header_libs: [
+        "ndk_vulkan_headers",
+    ],
 }
 
 cc_library_shared {