Merge "Use SurfaceListener instead of IProducerListener" into main
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 21dc5c1..49def82 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -802,6 +802,10 @@
 }
 
 void RpcSession::setSessionSpecificRoot(const sp<IBinder>& sessionSpecificRoot) {
+    LOG_ALWAYS_FATAL_IF(mSessionSpecificRootObject != nullptr,
+                        "Session specific root object already set");
+    LOG_ALWAYS_FATAL_IF(mForServer != nullptr,
+                        "Session specific root object cannot be set for a server");
     mSessionSpecificRootObject = sessionSpecificRoot;
 }
 
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index e11adb8..e48e94f 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -517,10 +517,10 @@
 
 // --- RawAbsoluteAxisInfo ---
 
-std::ostream& operator<<(std::ostream& out, const std::optional<RawAbsoluteAxisInfo>& info) {
-    if (info) {
-        out << "min=" << info->minValue << ", max=" << info->maxValue << ", flat=" << info->flat
-            << ", fuzz=" << info->fuzz << ", resolution=" << info->resolution;
+std::ostream& operator<<(std::ostream& out, const RawAbsoluteAxisInfo& info) {
+    if (info.valid) {
+        out << "min=" << info.minValue << ", max=" << info.maxValue << ", flat=" << info.flat
+            << ", fuzz=" << info.fuzz << ", resolution=" << info.resolution;
     } else {
         out << "unknown range";
     }
@@ -649,6 +649,7 @@
             continue;
         }
         auto& [axisInfo, value] = absState[axis];
+        axisInfo.valid = true;
         axisInfo.minValue = info.minimum;
         axisInfo.maxValue = info.maximum;
         axisInfo.flat = info.flat;
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 657126a..feae6b6 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -71,14 +71,18 @@
 
 /* Describes an absolute axis. */
 struct RawAbsoluteAxisInfo {
+    bool valid{false}; // true if the information is valid, false otherwise
+
     int32_t minValue{};   // minimum value
     int32_t maxValue{};   // maximum value
     int32_t flat{};       // center flat position, eg. flat == 8 means center is between -8 and 8
     int32_t fuzz{};       // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
     int32_t resolution{}; // resolution in units per mm or radians per mm
+
+    inline void clear() { *this = RawAbsoluteAxisInfo(); }
 };
 
-std::ostream& operator<<(std::ostream& out, const std::optional<RawAbsoluteAxisInfo>& info);
+std::ostream& operator<<(std::ostream& out, const RawAbsoluteAxisInfo& info);
 
 /*
  * Input device classes.
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index f2fdc37..086c26f 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -305,17 +305,22 @@
     inline int32_t getDeviceControllerNumber() const {
         return mEventHub->getDeviceControllerNumber(mId);
     }
-    inline std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t code) const {
+    inline status_t getAbsoluteAxisInfo(int32_t code, RawAbsoluteAxisInfo* axisInfo) const {
         std::optional<RawAbsoluteAxisInfo> info = mEventHub->getAbsoluteAxisInfo(mId, code);
+        if (!info.has_value()) {
+            axisInfo->clear();
+            return NAME_NOT_FOUND;
+        }
+        *axisInfo = *info;
 
         // Validate axis info for InputDevice.
-        if (info && info->minValue == info->maxValue) {
+        if (axisInfo->valid && axisInfo->minValue == axisInfo->maxValue) {
             // Historically, we deem axes with the same min and max values as invalid to avoid
             // dividing by zero when scaling by max - min.
             // TODO(b/291772515): Perform axis info validation on a per-axis basis when it is used.
-            return std::nullopt;
+            axisInfo->valid = false;
         }
-        return info;
+        return OK;
     }
     inline bool hasRelativeAxis(int32_t code) const {
         return mEventHub->hasRelativeAxis(mId, code);
@@ -430,7 +435,8 @@
     }
 
     inline bool hasAbsoluteAxis(int32_t code) const {
-        return mEventHub->getAbsoluteAxisInfo(mId, code).has_value();
+        std::optional<RawAbsoluteAxisInfo> info = mEventHub->getAbsoluteAxisInfo(mId, code);
+        return info.has_value() && info->valid;
     }
     inline bool isKeyPressed(int32_t scanCode) const {
         return mEventHub->getScanCodeState(mId, scanCode) == AKEY_STATE_DOWN;
diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
index c8e7790..90685de 100644
--- a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
+++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
@@ -16,7 +16,6 @@
 
 #include "CapturedTouchpadEventConverter.h"
 
-#include <optional>
 #include <sstream>
 
 #include <android-base/stringprintf.h>
@@ -54,33 +53,32 @@
         mMotionAccumulator(motionAccumulator),
         mHasTouchMinor(deviceContext.hasAbsoluteAxis(ABS_MT_TOUCH_MINOR)),
         mHasToolMinor(deviceContext.hasAbsoluteAxis(ABS_MT_WIDTH_MINOR)) {
-    if (std::optional<RawAbsoluteAxisInfo> orientation =
-                deviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION);
-        orientation) {
-        if (orientation->maxValue > 0) {
-            mOrientationScale = M_PI_2 / orientation->maxValue;
-        } else if (orientation->minValue < 0) {
-            mOrientationScale = -M_PI_2 / orientation->minValue;
+    RawAbsoluteAxisInfo orientationInfo;
+    deviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &orientationInfo);
+    if (orientationInfo.valid) {
+        if (orientationInfo.maxValue > 0) {
+            mOrientationScale = M_PI_2 / orientationInfo.maxValue;
+        } else if (orientationInfo.minValue < 0) {
+            mOrientationScale = -M_PI_2 / orientationInfo.minValue;
         }
     }
 
     // TODO(b/275369880): support touch.pressure.calibration and .scale properties when captured.
-    if (std::optional<RawAbsoluteAxisInfo> pressure =
-                deviceContext.getAbsoluteAxisInfo(ABS_MT_PRESSURE);
-        pressure && pressure->maxValue > 0) {
-        mPressureScale = 1.0 / pressure->maxValue;
+    RawAbsoluteAxisInfo pressureInfo;
+    deviceContext.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &pressureInfo);
+    if (pressureInfo.valid && pressureInfo.maxValue > 0) {
+        mPressureScale = 1.0 / pressureInfo.maxValue;
     }
 
-    std::optional<RawAbsoluteAxisInfo> touchMajor =
-            deviceContext.getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR);
-    std::optional<RawAbsoluteAxisInfo> toolMajor =
-            deviceContext.getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR);
-    mHasTouchMajor = touchMajor.has_value();
-    mHasToolMajor = toolMajor.has_value();
-    if (mHasTouchMajor && touchMajor->maxValue != 0) {
-        mSizeScale = 1.0f / touchMajor->maxValue;
-    } else if (mHasToolMajor && toolMajor->maxValue != 0) {
-        mSizeScale = 1.0f / toolMajor->maxValue;
+    RawAbsoluteAxisInfo touchMajorInfo, toolMajorInfo;
+    deviceContext.getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &touchMajorInfo);
+    deviceContext.getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &toolMajorInfo);
+    mHasTouchMajor = touchMajorInfo.valid;
+    mHasToolMajor = toolMajorInfo.valid;
+    if (mHasTouchMajor && touchMajorInfo.maxValue != 0) {
+        mSizeScale = 1.0f / touchMajorInfo.maxValue;
+    } else if (mHasToolMajor && toolMajorInfo.maxValue != 0) {
+        mSizeScale = 1.0f / toolMajorInfo.maxValue;
     }
 }
 
@@ -115,13 +113,15 @@
     tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOOL_MAJOR, ABS_MT_WIDTH_MAJOR);
     tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOOL_MINOR, ABS_MT_WIDTH_MINOR);
 
-    if (mDeviceContext.hasAbsoluteAxis(ABS_MT_PRESSURE)) {
+    RawAbsoluteAxisInfo pressureInfo;
+    mDeviceContext.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &pressureInfo);
+    if (pressureInfo.valid) {
         info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, SOURCE, 0, 1, 0, 0, 0);
     }
 
-    if (std::optional<RawAbsoluteAxisInfo> orientation =
-                mDeviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION);
-        orientation && (orientation->maxValue > 0 || orientation->minValue < 0)) {
+    RawAbsoluteAxisInfo orientationInfo;
+    mDeviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &orientationInfo);
+    if (orientationInfo.valid && (orientationInfo.maxValue > 0 || orientationInfo.minValue < 0)) {
         info.addMotionRange(AMOTION_EVENT_AXIS_ORIENTATION, SOURCE, -M_PI_2, M_PI_2, 0, 0, 0);
     }
 
@@ -133,10 +133,11 @@
 void CapturedTouchpadEventConverter::tryAddRawMotionRange(InputDeviceInfo& deviceInfo,
                                                           int32_t androidAxis,
                                                           int32_t evdevAxis) const {
-    std::optional<RawAbsoluteAxisInfo> info = mDeviceContext.getAbsoluteAxisInfo(evdevAxis);
-    if (info) {
-        deviceInfo.addMotionRange(androidAxis, SOURCE, info->minValue, info->maxValue, info->flat,
-                                  info->fuzz, info->resolution);
+    RawAbsoluteAxisInfo info;
+    mDeviceContext.getAbsoluteAxisInfo(evdevAxis, &info);
+    if (info.valid) {
+        deviceInfo.addMotionRange(androidAxis, SOURCE, info.minValue, info.maxValue, info.flat,
+                                  info.fuzz, info.resolution);
     }
 }
 
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 7cc8940..3af1d04 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -33,7 +33,7 @@
 
 void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
     InputMapper::populateDeviceInfo(info);
-    if (mRawPressureAxis) {
+    if (mRawPressureAxis.valid) {
         info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f,
                             0.0f, 0.0f);
     }
@@ -50,7 +50,7 @@
 std::list<NotifyArgs> ExternalStylusInputMapper::reconfigure(nsecs_t when,
                                                              const InputReaderConfiguration& config,
                                                              ConfigurationChanges changes) {
-    mRawPressureAxis = getAbsoluteAxisInfo(ABS_PRESSURE);
+    getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis);
     mTouchButtonAccumulator.configure();
     return {};
 }
@@ -82,10 +82,10 @@
         mStylusState.toolType = ToolType::STYLUS;
     }
 
-    if (mRawPressureAxis) {
+    if (mRawPressureAxis.valid) {
         auto rawPressure = static_cast<float>(mSingleTouchMotionAccumulator.getAbsolutePressure());
-        mStylusState.pressure = (rawPressure - mRawPressureAxis->minValue) /
-                static_cast<float>(mRawPressureAxis->maxValue - mRawPressureAxis->minValue);
+        mStylusState.pressure = (rawPressure - mRawPressureAxis.minValue) /
+                static_cast<float>(mRawPressureAxis.maxValue - mRawPressureAxis.minValue);
     } else if (mTouchButtonAccumulator.hasButtonTouch()) {
         mStylusState.pressure = mTouchButtonAccumulator.isHovering() ? 0.0f : 1.0f;
     }
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
index d48fd9b..c040a7b 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -16,8 +16,6 @@
 
 #pragma once
 
-#include <optional>
-
 #include "InputMapper.h"
 
 #include "SingleTouchMotionAccumulator.h"
@@ -45,7 +43,7 @@
 
 private:
     SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
-    std::optional<RawAbsoluteAxisInfo> mRawPressureAxis;
+    RawAbsoluteAxisInfo mRawPressureAxis;
     TouchButtonAccumulator mTouchButtonAccumulator;
 
     StylusState mStylusState;
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index c44c48c..b6c5c98 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -18,7 +18,6 @@
 
 #include "InputMapper.h"
 
-#include <optional>
 #include <sstream>
 
 #include <ftl/enum.h>
@@ -117,16 +116,15 @@
     return {};
 }
 
-std::optional<RawAbsoluteAxisInfo> InputMapper::getAbsoluteAxisInfo(int32_t axis) {
-    return getDeviceContext().getAbsoluteAxisInfo(axis);
+status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) {
+    return getDeviceContext().getAbsoluteAxisInfo(axis, axisInfo);
 }
 
 void InputMapper::bumpGeneration() {
     getDeviceContext().bumpGeneration();
 }
 
-void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump,
-                                          const std::optional<RawAbsoluteAxisInfo>& axis,
+void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis,
                                           const char* name) {
     std::stringstream out;
     out << INDENT4 << name << ": " << axis << "\n";
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index e5afcc7..2c51448 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -16,8 +16,6 @@
 
 #pragma once
 
-#include <optional>
-
 #include "EventHub.h"
 #include "InputDevice.h"
 #include "InputListener.h"
@@ -128,11 +126,10 @@
     explicit InputMapper(InputDeviceContext& deviceContext,
                          const InputReaderConfiguration& readerConfig);
 
-    std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t axis);
+    status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo);
     void bumpGeneration();
 
-    static void dumpRawAbsoluteAxisInfo(std::string& dump,
-                                        const std::optional<RawAbsoluteAxisInfo>& axis,
+    static void dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis,
                                         const char* name);
     static void dumpStylusState(std::string& dump, const StylusState& state);
 };
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 3091714..41e018d 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -117,8 +117,9 @@
                 continue; // axis must be claimed by a different device
             }
 
-            if (std::optional<RawAbsoluteAxisInfo> rawAxisInfo = getAbsoluteAxisInfo(abs);
-                rawAxisInfo) {
+            RawAbsoluteAxisInfo rawAxisInfo;
+            getAbsoluteAxisInfo(abs, &rawAxisInfo);
+            if (rawAxisInfo.valid) {
                 // Map axis.
                 AxisInfo axisInfo;
                 const bool explicitlyMapped = !getDeviceContext().mapAxis(abs, &axisInfo);
@@ -128,7 +129,7 @@
                     axisInfo.mode = AxisInfo::MODE_NORMAL;
                     axisInfo.axis = -1;
                 }
-                mAxes.insert({abs, createAxis(axisInfo, rawAxisInfo.value(), explicitlyMapped)});
+                mAxes.insert({abs, createAxis(axisInfo, rawAxisInfo, explicitlyMapped)});
             }
         }
 
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 3ea3c20..1986fe2 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -133,7 +133,7 @@
 
         bool isHovering = mTouchButtonAccumulator.getToolType() != ToolType::MOUSE &&
                 (mTouchButtonAccumulator.isHovering() ||
-                 (mRawPointerAxes.pressure && inSlot.getPressure() <= 0));
+                 (mRawPointerAxes.pressure.valid && inSlot.getPressure() <= 0));
         outPointer.isHovering = isHovering;
 
         // Assign pointer id using tracking id if available.
@@ -189,23 +189,21 @@
 void MultiTouchInputMapper::configureRawPointerAxes() {
     TouchInputMapper::configureRawPointerAxes();
 
-    // We can safely assume that ABS_MT_POSITION_X and _Y axes will be available, as EventHub won't
-    // classify a device as multitouch if they're not present.
-    mRawPointerAxes.x = getAbsoluteAxisInfo(ABS_MT_POSITION_X).value();
-    mRawPointerAxes.y = getAbsoluteAxisInfo(ABS_MT_POSITION_Y).value();
-    mRawPointerAxes.touchMajor = getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR);
-    mRawPointerAxes.touchMinor = getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR);
-    mRawPointerAxes.toolMajor = getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR);
-    mRawPointerAxes.toolMinor = getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR);
-    mRawPointerAxes.orientation = getAbsoluteAxisInfo(ABS_MT_ORIENTATION);
-    mRawPointerAxes.pressure = getAbsoluteAxisInfo(ABS_MT_PRESSURE);
-    mRawPointerAxes.distance = getAbsoluteAxisInfo(ABS_MT_DISTANCE);
-    mRawPointerAxes.trackingId = getAbsoluteAxisInfo(ABS_MT_TRACKING_ID);
-    mRawPointerAxes.slot = getAbsoluteAxisInfo(ABS_MT_SLOT);
+    getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
+    getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
+    getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
+    getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
+    getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
+    getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
+    getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
+    getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
+    getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
+    getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
+    getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
 
-    if (mRawPointerAxes.trackingId && mRawPointerAxes.slot && mRawPointerAxes.slot->minValue == 0 &&
-        mRawPointerAxes.slot->maxValue > 0) {
-        size_t slotCount = mRawPointerAxes.slot->maxValue + 1;
+    if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
+        mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
+        size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
         if (slotCount > MAX_SLOTS) {
             ALOGW("MultiTouch Device %s reported %zu slots but the framework "
                   "only supports a maximum of %zu slots at this time.",
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index 4233f78..d7f2993 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -133,8 +133,9 @@
                           .test(InputDeviceClass::SENSOR))) {
                 continue;
             }
-            if (std::optional<RawAbsoluteAxisInfo> rawAxisInfo = getAbsoluteAxisInfo(abs);
-                rawAxisInfo) {
+            RawAbsoluteAxisInfo rawAxisInfo;
+            getAbsoluteAxisInfo(abs, &rawAxisInfo);
+            if (rawAxisInfo.valid) {
                 AxisInfo axisInfo;
                 // Axis doesn't need to be mapped, as sensor mapper doesn't generate any motion
                 // input events
@@ -145,7 +146,7 @@
                 if (ret.ok()) {
                     InputDeviceSensorType sensorType = (*ret).first;
                     int32_t sensorDataIndex = (*ret).second;
-                    const Axis& axis = createAxis(axisInfo, rawAxisInfo.value());
+                    const Axis& axis = createAxis(axisInfo, rawAxisInfo);
                     parseSensorConfiguration(sensorType, abs, sensorDataIndex, axis);
 
                     mAxes.insert({abs, axis});
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
index 869feb4..140bb0c 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
@@ -44,7 +44,7 @@
 
         bool isHovering = mTouchButtonAccumulator.getToolType() != ToolType::MOUSE &&
                 (mTouchButtonAccumulator.isHovering() ||
-                 (mRawPointerAxes.pressure &&
+                 (mRawPointerAxes.pressure.valid &&
                   mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0));
         outState->rawPointerData.markIdBit(0, isHovering);
 
@@ -72,15 +72,13 @@
 void SingleTouchInputMapper::configureRawPointerAxes() {
     TouchInputMapper::configureRawPointerAxes();
 
-    // We can safely assume that ABS_X and _Y axes will be available, as EventHub won't classify a
-    // device as a touch device if they're not present.
-    mRawPointerAxes.x = getAbsoluteAxisInfo(ABS_X).value();
-    mRawPointerAxes.y = getAbsoluteAxisInfo(ABS_Y).value();
-    mRawPointerAxes.pressure = getAbsoluteAxisInfo(ABS_PRESSURE);
-    mRawPointerAxes.toolMajor = getAbsoluteAxisInfo(ABS_TOOL_WIDTH);
-    mRawPointerAxes.distance = getAbsoluteAxisInfo(ABS_DISTANCE);
-    mRawPointerAxes.tiltX = getAbsoluteAxisInfo(ABS_TILT_X);
-    mRawPointerAxes.tiltY = getAbsoluteAxisInfo(ABS_TILT_Y);
+    getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x);
+    getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y);
+    getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure);
+    getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor);
+    getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance);
+    getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX);
+    getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY);
 }
 
 bool SingleTouchInputMapper::hasStylus() const {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 984e217..2d89208 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -600,10 +600,10 @@
     const float diagonalSize = hypotf(mDisplayBounds.width, mDisplayBounds.height);
 
     // Size factors.
-    if (mRawPointerAxes.touchMajor && mRawPointerAxes.touchMajor->maxValue != 0) {
-        mSizeScale = 1.0f / mRawPointerAxes.touchMajor->maxValue;
-    } else if (mRawPointerAxes.toolMajor && mRawPointerAxes.toolMajor->maxValue != 0) {
-        mSizeScale = 1.0f / mRawPointerAxes.toolMajor->maxValue;
+    if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) {
+        mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue;
+    } else if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) {
+        mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue;
     } else {
         mSizeScale = 0.0f;
     }
@@ -618,18 +618,18 @@
             .resolution = 0,
     };
 
-    if (mRawPointerAxes.touchMajor) {
-        mRawPointerAxes.touchMajor->resolution =
-                clampResolution("touchMajor", mRawPointerAxes.touchMajor->resolution);
-        mOrientedRanges.touchMajor->resolution = mRawPointerAxes.touchMajor->resolution;
+    if (mRawPointerAxes.touchMajor.valid) {
+        mRawPointerAxes.touchMajor.resolution =
+                clampResolution("touchMajor", mRawPointerAxes.touchMajor.resolution);
+        mOrientedRanges.touchMajor->resolution = mRawPointerAxes.touchMajor.resolution;
     }
 
     mOrientedRanges.touchMinor = mOrientedRanges.touchMajor;
     mOrientedRanges.touchMinor->axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
-    if (mRawPointerAxes.touchMinor) {
-        mRawPointerAxes.touchMinor->resolution =
-                clampResolution("touchMinor", mRawPointerAxes.touchMinor->resolution);
-        mOrientedRanges.touchMinor->resolution = mRawPointerAxes.touchMinor->resolution;
+    if (mRawPointerAxes.touchMinor.valid) {
+        mRawPointerAxes.touchMinor.resolution =
+                clampResolution("touchMinor", mRawPointerAxes.touchMinor.resolution);
+        mOrientedRanges.touchMinor->resolution = mRawPointerAxes.touchMinor.resolution;
     }
 
     mOrientedRanges.toolMajor = InputDeviceInfo::MotionRange{
@@ -641,18 +641,18 @@
             .fuzz = 0,
             .resolution = 0,
     };
-    if (mRawPointerAxes.toolMajor) {
-        mRawPointerAxes.toolMajor->resolution =
-                clampResolution("toolMajor", mRawPointerAxes.toolMajor->resolution);
-        mOrientedRanges.toolMajor->resolution = mRawPointerAxes.toolMajor->resolution;
+    if (mRawPointerAxes.toolMajor.valid) {
+        mRawPointerAxes.toolMajor.resolution =
+                clampResolution("toolMajor", mRawPointerAxes.toolMajor.resolution);
+        mOrientedRanges.toolMajor->resolution = mRawPointerAxes.toolMajor.resolution;
     }
 
     mOrientedRanges.toolMinor = mOrientedRanges.toolMajor;
     mOrientedRanges.toolMinor->axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
-    if (mRawPointerAxes.toolMinor) {
-        mRawPointerAxes.toolMinor->resolution =
-                clampResolution("toolMinor", mRawPointerAxes.toolMinor->resolution);
-        mOrientedRanges.toolMinor->resolution = mRawPointerAxes.toolMinor->resolution;
+    if (mRawPointerAxes.toolMinor.valid) {
+        mRawPointerAxes.toolMinor.resolution =
+                clampResolution("toolMinor", mRawPointerAxes.toolMinor.resolution);
+        mOrientedRanges.toolMinor->resolution = mRawPointerAxes.toolMinor.resolution;
     }
 
     if (mCalibration.sizeCalibration == Calibration::SizeCalibration::GEOMETRIC) {
@@ -704,10 +704,9 @@
         mCalibration.pressureCalibration == Calibration::PressureCalibration::AMPLITUDE) {
         if (mCalibration.pressureScale) {
             mPressureScale = *mCalibration.pressureScale;
-            pressureMax = mPressureScale *
-                    (mRawPointerAxes.pressure ? mRawPointerAxes.pressure->maxValue : 0);
-        } else if (mRawPointerAxes.pressure && mRawPointerAxes.pressure->maxValue != 0) {
-            mPressureScale = 1.0f / mRawPointerAxes.pressure->maxValue;
+            pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue;
+        } else if (mRawPointerAxes.pressure.valid && mRawPointerAxes.pressure.maxValue != 0) {
+            mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue;
         }
     }
 
@@ -726,18 +725,18 @@
     mTiltXScale = 0;
     mTiltYCenter = 0;
     mTiltYScale = 0;
-    mHaveTilt = mRawPointerAxes.tiltX && mRawPointerAxes.tiltY;
+    mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid;
     if (mHaveTilt) {
-        mTiltXCenter = avg(mRawPointerAxes.tiltX->minValue, mRawPointerAxes.tiltX->maxValue);
-        mTiltYCenter = avg(mRawPointerAxes.tiltY->minValue, mRawPointerAxes.tiltY->maxValue);
+        mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue, mRawPointerAxes.tiltX.maxValue);
+        mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue, mRawPointerAxes.tiltY.maxValue);
         mTiltXScale = M_PI / 180;
         mTiltYScale = M_PI / 180;
 
-        if (mRawPointerAxes.tiltX->resolution) {
-            mTiltXScale = 1.0 / mRawPointerAxes.tiltX->resolution;
+        if (mRawPointerAxes.tiltX.resolution) {
+            mTiltXScale = 1.0 / mRawPointerAxes.tiltX.resolution;
         }
-        if (mRawPointerAxes.tiltY->resolution) {
-            mTiltYScale = 1.0 / mRawPointerAxes.tiltY->resolution;
+        if (mRawPointerAxes.tiltY.resolution) {
+            mTiltYScale = 1.0 / mRawPointerAxes.tiltY.resolution;
         }
 
         mOrientedRanges.tilt = InputDeviceInfo::MotionRange{
@@ -767,11 +766,11 @@
     } else if (mCalibration.orientationCalibration != Calibration::OrientationCalibration::NONE) {
         if (mCalibration.orientationCalibration ==
             Calibration::OrientationCalibration::INTERPOLATED) {
-            if (mRawPointerAxes.orientation) {
-                if (mRawPointerAxes.orientation->maxValue > 0) {
-                    mOrientationScale = M_PI_2 / mRawPointerAxes.orientation->maxValue;
-                } else if (mRawPointerAxes.orientation->minValue < 0) {
-                    mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation->minValue;
+            if (mRawPointerAxes.orientation.valid) {
+                if (mRawPointerAxes.orientation.maxValue > 0) {
+                    mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue;
+                } else if (mRawPointerAxes.orientation.minValue < 0) {
+                    mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue;
                 } else {
                     mOrientationScale = 0;
                 }
@@ -796,14 +795,14 @@
             mDistanceScale = mCalibration.distanceScale.value_or(1.0f);
         }
 
-        const bool hasDistance = mRawPointerAxes.distance.has_value();
         mOrientedRanges.distance = InputDeviceInfo::MotionRange{
+
                 .axis = AMOTION_EVENT_AXIS_DISTANCE,
                 .source = mSource,
-                .min = hasDistance ? mRawPointerAxes.distance->minValue * mDistanceScale : 0,
-                .max = hasDistance ? mRawPointerAxes.distance->maxValue * mDistanceScale : 0,
+                .min = mRawPointerAxes.distance.minValue * mDistanceScale,
+                .max = mRawPointerAxes.distance.maxValue * mDistanceScale,
                 .flat = 0,
-                .fuzz = hasDistance ? mRawPointerAxes.distance->fuzz * mDistanceScale : 0,
+                .fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale,
                 .resolution = 0,
         };
     }
@@ -944,7 +943,12 @@
     const std::optional<DisplayViewport> newViewportOpt = findViewport();
 
     // Ensure the device is valid and can be used.
-    if (!newViewportOpt) {
+    if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
+        ALOGW("Touch device '%s' did not report support for X or Y axis!  "
+              "The device will be inoperable.",
+              getDeviceName().c_str());
+        mDeviceMode = DeviceMode::DISABLED;
+    } else if (!newViewportOpt) {
         ALOGI("Touch device '%s' could not query the properties of its associated "
               "display.  The device will be inoperable until the display size "
               "becomes available.",
@@ -1233,7 +1237,7 @@
 
 void TouchInputMapper::resolveCalibration() {
     // Size
-    if (mRawPointerAxes.touchMajor || mRawPointerAxes.toolMajor) {
+    if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) {
         if (mCalibration.sizeCalibration == Calibration::SizeCalibration::DEFAULT) {
             mCalibration.sizeCalibration = Calibration::SizeCalibration::GEOMETRIC;
         }
@@ -1242,7 +1246,7 @@
     }
 
     // Pressure
-    if (mRawPointerAxes.pressure) {
+    if (mRawPointerAxes.pressure.valid) {
         if (mCalibration.pressureCalibration == Calibration::PressureCalibration::DEFAULT) {
             mCalibration.pressureCalibration = Calibration::PressureCalibration::PHYSICAL;
         }
@@ -1251,7 +1255,7 @@
     }
 
     // Orientation
-    if (mRawPointerAxes.orientation) {
+    if (mRawPointerAxes.orientation.valid) {
         if (mCalibration.orientationCalibration == Calibration::OrientationCalibration::DEFAULT) {
             mCalibration.orientationCalibration = Calibration::OrientationCalibration::INTERPOLATED;
         }
@@ -1260,7 +1264,7 @@
     }
 
     // Distance
-    if (mRawPointerAxes.distance) {
+    if (mRawPointerAxes.distance.valid) {
         if (mCalibration.distanceCalibration == Calibration::DistanceCalibration::DEFAULT) {
             mCalibration.distanceCalibration = Calibration::DistanceCalibration::SCALED;
         }
@@ -2247,25 +2251,25 @@
             case Calibration::SizeCalibration::DIAMETER:
             case Calibration::SizeCalibration::BOX:
             case Calibration::SizeCalibration::AREA:
-                if (mRawPointerAxes.touchMajor && mRawPointerAxes.toolMajor) {
+                if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) {
                     touchMajor = in.touchMajor;
-                    touchMinor = mRawPointerAxes.touchMinor ? in.touchMinor : in.touchMajor;
+                    touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
                     toolMajor = in.toolMajor;
-                    toolMinor = mRawPointerAxes.toolMinor ? in.toolMinor : in.toolMajor;
-                    size = mRawPointerAxes.touchMinor ? avg(in.touchMajor, in.touchMinor)
-                                                      : in.touchMajor;
-                } else if (mRawPointerAxes.touchMajor) {
+                    toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor;
+                    size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor)
+                                                            : in.touchMajor;
+                } else if (mRawPointerAxes.touchMajor.valid) {
                     toolMajor = touchMajor = in.touchMajor;
                     toolMinor = touchMinor =
-                            mRawPointerAxes.touchMinor ? in.touchMinor : in.touchMajor;
-                    size = mRawPointerAxes.touchMinor ? avg(in.touchMajor, in.touchMinor)
-                                                      : in.touchMajor;
-                } else if (mRawPointerAxes.toolMajor) {
+                            mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
+                    size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor)
+                                                            : in.touchMajor;
+                } else if (mRawPointerAxes.toolMajor.valid) {
                     touchMajor = toolMajor = in.toolMajor;
                     touchMinor = toolMinor =
-                            mRawPointerAxes.toolMinor ? in.toolMinor : in.toolMajor;
-                    size = mRawPointerAxes.toolMinor ? avg(in.toolMajor, in.toolMinor)
-                                                     : in.toolMajor;
+                            mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor;
+                    size = mRawPointerAxes.toolMinor.valid ? avg(in.toolMajor, in.toolMinor)
+                                                           : in.toolMajor;
                 } else {
                     ALOG_ASSERT(false,
                                 "No touch or tool axes.  "
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 87b72af..a9a0190 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -61,17 +61,17 @@
 struct RawPointerAxes {
     RawAbsoluteAxisInfo x{};
     RawAbsoluteAxisInfo y{};
-    std::optional<RawAbsoluteAxisInfo> pressure{};
-    std::optional<RawAbsoluteAxisInfo> touchMajor{};
-    std::optional<RawAbsoluteAxisInfo> touchMinor{};
-    std::optional<RawAbsoluteAxisInfo> toolMajor{};
-    std::optional<RawAbsoluteAxisInfo> toolMinor{};
-    std::optional<RawAbsoluteAxisInfo> orientation{};
-    std::optional<RawAbsoluteAxisInfo> distance{};
-    std::optional<RawAbsoluteAxisInfo> tiltX{};
-    std::optional<RawAbsoluteAxisInfo> tiltY{};
-    std::optional<RawAbsoluteAxisInfo> trackingId{};
-    std::optional<RawAbsoluteAxisInfo> slot{};
+    RawAbsoluteAxisInfo pressure{};
+    RawAbsoluteAxisInfo touchMajor{};
+    RawAbsoluteAxisInfo touchMinor{};
+    RawAbsoluteAxisInfo toolMajor{};
+    RawAbsoluteAxisInfo toolMinor{};
+    RawAbsoluteAxisInfo orientation{};
+    RawAbsoluteAxisInfo distance{};
+    RawAbsoluteAxisInfo tiltX{};
+    RawAbsoluteAxisInfo tiltY{};
+    RawAbsoluteAxisInfo trackingId{};
+    RawAbsoluteAxisInfo slot{};
 
     inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; }
     inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; }
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 5c5fd3f..128f515 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -240,15 +240,14 @@
         mGestureConverter(*getContext(), deviceContext, getDeviceId()),
         mCapturedEventConverter(*getContext(), deviceContext, mMotionAccumulator, getDeviceId()),
         mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())) {
-    if (std::optional<RawAbsoluteAxisInfo> slotAxis =
-                deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT);
-        slotAxis && slotAxis->maxValue >= 0) {
-        mMotionAccumulator.configure(deviceContext, slotAxis->maxValue + 1, true);
-    } else {
+    RawAbsoluteAxisInfo slotAxisInfo;
+    deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
+    if (!slotAxisInfo.valid || slotAxisInfo.maxValue < 0) {
         LOG(WARNING) << "Touchpad " << deviceContext.getName()
                      << " doesn't have a valid ABS_MT_SLOT axis, and probably won't work properly.";
-        mMotionAccumulator.configure(deviceContext, 1, true);
+        slotAxisInfo.maxValue = 0;
     }
+    mMotionAccumulator.configure(deviceContext, slotAxisInfo.maxValue + 1, true);
 
     mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
     mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 9924d0d..e8e7376 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -66,11 +66,10 @@
                                    const InputDeviceContext& deviceContext, int32_t deviceId)
       : mDeviceId(deviceId),
         mReaderContext(readerContext),
-        mEnableFlingStop(input_flags::enable_touchpad_fling_stop()),
-        // We can safely assume that ABS_MT_POSITION_X and _Y axes will be available, as EventHub
-        // won't classify a device as a touchpad if they're not present.
-        mXAxisInfo(deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X).value()),
-        mYAxisInfo(deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y).value()) {}
+        mEnableFlingStop(input_flags::enable_touchpad_fling_stop()) {
+    deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo);
+    deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo);
+}
 
 std::string GestureConverter::dump() const {
     std::stringstream out;
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp b/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp
index d8a1f50..04655dc 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp
+++ b/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp
@@ -16,8 +16,6 @@
 
 #include "HardwareProperties.h"
 
-#include <optional>
-
 namespace android {
 
 namespace {
@@ -35,34 +33,26 @@
 
 HardwareProperties createHardwareProperties(const InputDeviceContext& context) {
     HardwareProperties props;
-    // We can safely assume that ABS_MT_POSITION_X and _Y axes will be available, as EventHub won't
-    // classify a device as a touchpad if they're not present.
-    RawAbsoluteAxisInfo absMtPositionX = context.getAbsoluteAxisInfo(ABS_MT_POSITION_X).value();
+    RawAbsoluteAxisInfo absMtPositionX;
+    context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX);
     props.left = absMtPositionX.minValue;
     props.right = absMtPositionX.maxValue;
     props.res_x = absMtPositionX.resolution;
 
-    RawAbsoluteAxisInfo absMtPositionY = context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y).value();
+    RawAbsoluteAxisInfo absMtPositionY;
+    context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY);
     props.top = absMtPositionY.minValue;
     props.bottom = absMtPositionY.maxValue;
     props.res_y = absMtPositionY.resolution;
 
-    if (std::optional<RawAbsoluteAxisInfo> absMtOrientation =
-                context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION);
-        absMtOrientation) {
-        props.orientation_minimum = absMtOrientation->minValue;
-        props.orientation_maximum = absMtOrientation->maxValue;
-    } else {
-        props.orientation_minimum = 0;
-        props.orientation_maximum = 0;
-    }
+    RawAbsoluteAxisInfo absMtOrientation;
+    context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation);
+    props.orientation_minimum = absMtOrientation.minValue;
+    props.orientation_maximum = absMtOrientation.maxValue;
 
-    if (std::optional<RawAbsoluteAxisInfo> absMtSlot = context.getAbsoluteAxisInfo(ABS_MT_SLOT);
-        absMtSlot) {
-        props.max_finger_cnt = absMtSlot->maxValue - absMtSlot->minValue + 1;
-    } else {
-        props.max_finger_cnt = 1;
-    }
+    RawAbsoluteAxisInfo absMtSlot;
+    context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot);
+    props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1;
     props.max_touch_cnt = getMaxTouchCount(context);
 
     // T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5
@@ -81,7 +71,9 @@
     // are haptic.
     props.is_haptic_pad = false;
 
-    props.reports_pressure = context.hasAbsoluteAxis(ABS_MT_PRESSURE);
+    RawAbsoluteAxisInfo absMtPressure;
+    context.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &absMtPressure);
+    props.reports_pressure = absMtPressure.valid;
     return props;
 }
 
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index 31fbf20..99db999 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -101,6 +101,7 @@
     Device* device = getDevice(deviceId);
 
     RawAbsoluteAxisInfo info;
+    info.valid = true;
     info.minValue = minValue;
     info.maxValue = maxValue;
     info.flat = flat;
diff --git a/services/inputflinger/tests/HardwareProperties_test.cpp b/services/inputflinger/tests/HardwareProperties_test.cpp
index e87f822..643fab6 100644
--- a/services/inputflinger/tests/HardwareProperties_test.cpp
+++ b/services/inputflinger/tests/HardwareProperties_test.cpp
@@ -50,6 +50,7 @@
     void setupValidAxis(int axis, int32_t min, int32_t max, int32_t resolution) {
         EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis))
                 .WillRepeatedly(Return(std::optional<RawAbsoluteAxisInfo>{{
+                        .valid = true,
                         .minValue = min,
                         .maxValue = max,
                         .flat = 0,
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index e773f58..7e96d5f 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -59,6 +59,7 @@
                                     int32_t resolution) {
     EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis))
             .WillRepeatedly(Return(valid ? std::optional<RawAbsoluteAxisInfo>{{
+                                                   .valid = true,
                                                    .minValue = min,
                                                    .maxValue = max,
                                                    .flat = 0,
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index fea0d9a..bf56d3a 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -129,6 +129,7 @@
         }
         if (mFdp->ConsumeBool()) {
             return std::optional<RawAbsoluteAxisInfo>({
+                    .valid = mFdp->ConsumeBool(),
                     .minValue = mFdp->ConsumeIntegral<int32_t>(),
                     .maxValue = mFdp->ConsumeIntegral<int32_t>(),
                     .flat = mFdp->ConsumeIntegral<int32_t>(),
diff --git a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
index ebbb311..c620032 100644
--- a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
@@ -34,6 +34,7 @@
     if (fdp.ConsumeBool()) {
         eventHub.setAbsoluteAxisInfo(id, axis,
                                      RawAbsoluteAxisInfo{
+                                             .valid = fdp.ConsumeBool(),
                                              .minValue = fdp.ConsumeIntegral<int32_t>(),
                                              .maxValue = fdp.ConsumeIntegral<int32_t>(),
                                              .flat = fdp.ConsumeIntegral<int32_t>(),
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 3fc3901..ee7eda1 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -487,13 +487,13 @@
     return 0ns;
 }
 
-void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime,
-                                  TimePoint lastConfirmedPresentTime) {
+void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime, FrameTime lastSignaledFrameTime) {
     SFTRACE_NAME("VSyncPredictor::onFrameBegin");
     std::lock_guard lock(mMutex);
 
     if (!mDisplayModePtr->getVrrConfig()) return;
 
+    const auto [lastConfirmedPresentTime, lastConfirmedExpectedPresentTime] = lastSignaledFrameTime;
     if (CC_UNLIKELY(mTraceOn)) {
         SFTRACE_FORMAT_INSTANT("vsync is %.2f past last signaled fence",
                                static_cast<float>(expectedPresentTime.ns() -
@@ -519,6 +519,11 @@
         }
     }
 
+    if (lastConfirmedExpectedPresentTime.ns() - lastConfirmedPresentTime.ns() > threshold) {
+        SFTRACE_FORMAT_INSTANT("lastFramePresentedEarly");
+        return;
+    }
+
     const auto phase = ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
     if (phase > 0ns) {
         mMissedVsync = {expectedPresentTime, minFramePeriodLocked()};
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 64e1029..9e1c90b 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -22,6 +22,7 @@
 #include <vector>
 
 #include <android-base/thread_annotations.h>
+#include <scheduler/FrameTime.h>
 #include <scheduler/TimeKeeper.h>
 #include <ui/DisplayId.h>
 
@@ -77,7 +78,7 @@
 
     void setRenderRate(Fps, bool applyImmediately) final EXCLUDES(mMutex);
 
-    void onFrameBegin(TimePoint expectedPresentTime, TimePoint lastConfirmedPresentTime) final
+    void onFrameBegin(TimePoint expectedPresentTime, FrameTime lastSignaledFrameTime) final
             EXCLUDES(mMutex);
     void onFrameMissed(TimePoint expectedPresentTime) final EXCLUDES(mMutex);
 
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 134d28e..3376fad 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -21,6 +21,7 @@
 
 #include <scheduler/Fps.h>
 #include <scheduler/FrameRateMode.h>
+#include <scheduler/FrameTime.h>
 
 #include "VSyncDispatch.h"
 
@@ -112,8 +113,7 @@
      */
     virtual void setRenderRate(Fps, bool applyImmediately) = 0;
 
-    virtual void onFrameBegin(TimePoint expectedPresentTime,
-                              TimePoint lastConfirmedPresentTime) = 0;
+    virtual void onFrameBegin(TimePoint expectedPresentTime, FrameTime lastSignaledFrameTime) = 0;
 
     virtual void onFrameMissed(TimePoint expectedPresentTime) = 0;
 
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
index d37d2dc..38cb446 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -26,6 +26,7 @@
 #include <ui/FenceTime.h>
 
 #include <scheduler/Features.h>
+#include <scheduler/FrameTime.h>
 #include <scheduler/Time.h>
 #include <scheduler/VsyncId.h>
 #include <scheduler/interface/CompositeResult.h>
@@ -57,13 +58,6 @@
     // The time of the VSYNC that preceded this frame. See `presentFenceForPastVsync` for details.
     TimePoint pastVsyncTime(Period minFramePeriod) const;
 
-    // The present fence for the frame that had targeted the most recent VSYNC before this frame.
-    // If the target VSYNC for any given frame is more than `vsyncPeriod` in the future, then the
-    // VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the
-    // `presentFenceForPreviousFrame` if running N VSYNCs ahead, but the one that should have been
-    // signaled by now (unless that frame missed).
-    FenceTimePtr presentFenceForPastVsync(Period minFramePeriod) const;
-
     // Equivalent to `presentFenceForPastVsync` unless running N VSYNCs ahead.
     const FenceTimePtr& presentFenceForPreviousFrame() const {
         return mPresentFences.front().fenceTime;
@@ -72,7 +66,7 @@
     bool isFramePending() const { return mFramePending; }
     bool didMissFrame() const { return mFrameMissed; }
     bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; }
-    TimePoint lastSignaledFrameTime() const { return mLastSignaledFrameTime; };
+    FrameTime lastSignaledFrameTime() const { return mLastSignaledFrameTime; }
 
 protected:
     explicit FrameTarget(const std::string& displayLabel);
@@ -106,10 +100,17 @@
         FenceTimePtr fenceTime = FenceTime::NO_FENCE;
         TimePoint expectedPresentTime = TimePoint();
     };
+
+    // The present fence for the frame that had targeted the most recent VSYNC before this frame.
+    // If the target VSYNC for any given frame is more than `vsyncPeriod` in the future, then the
+    // VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the
+    // `presentFenceForPreviousFrame` if running N VSYNCs ahead, but the one that should have been
+    // signaled by now (unless that frame missed).
+    FenceWithFenceTime presentFenceForPastVsync(Period minFramePeriod) const;
     std::array<FenceWithFenceTime, 2> mPresentFences;
     utils::RingBuffer<FenceWithFenceTime, 5> mFenceWithFenceTimes;
 
-    TimePoint mLastSignaledFrameTime;
+    FrameTime mLastSignaledFrameTime;
 
 private:
     friend class FrameTargeterTestBase;
@@ -120,16 +121,17 @@
         return expectedFrameDuration() > (N - 1) * minFramePeriod;
     }
 
-    const FenceTimePtr pastVsyncTimePtr() const {
-        auto pastFenceTimePtr = FenceTime::NO_FENCE;
+    FenceWithFenceTime pastVsyncTimePtr() const {
+        FenceWithFenceTime pastFenceWithFenceTime;
         for (size_t i = 0; i < mFenceWithFenceTimes.size(); i++) {
-            const auto& [_, fenceTimePtr, expectedPresentTime] = mFenceWithFenceTimes[i];
-            if (expectedPresentTime > mFrameBeginTime) {
-                return pastFenceTimePtr;
+            const auto& fenceWithFenceTime = mFenceWithFenceTimes[i];
+            // TODO(b/354007767) Fix the below condition to avoid frame drop
+            if (fenceWithFenceTime.expectedPresentTime > mFrameBeginTime) {
+                return pastFenceWithFenceTime;
             }
-            pastFenceTimePtr = fenceTimePtr;
+            pastFenceWithFenceTime = fenceWithFenceTime;
         }
-        return pastFenceTimePtr;
+        return pastFenceWithFenceTime;
     }
 };
 
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTime.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTime.h
new file mode 100644
index 0000000..ed5c899
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTime.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2024 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 <scheduler/Time.h>
+
+namespace android::scheduler {
+struct FrameTime {
+    TimePoint signalTime;
+    TimePoint expectedPresentTime;
+};
+} // namespace android::scheduler
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
index 60694b9..1d248fb 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -33,12 +33,12 @@
     return mExpectedPresentTime - Period::fromNs(minFramePeriod.ns() << shift);
 }
 
-FenceTimePtr FrameTarget::presentFenceForPastVsync(Period minFramePeriod) const {
+FrameTarget::FenceWithFenceTime FrameTarget::presentFenceForPastVsync(Period minFramePeriod) const {
     if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
         return pastVsyncTimePtr();
     }
     const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(minFramePeriod));
-    return mPresentFences[i].fenceTime;
+    return mPresentFences[i];
 }
 
 bool FrameTarget::wouldPresentEarly(Period minFramePeriod) const {
@@ -51,7 +51,7 @@
         return true;
     }
 
-    const auto fence = presentFenceForPastVsync(minFramePeriod);
+    const auto fence = presentFenceForPastVsync(minFramePeriod).fenceTime;
     return fence->isValid() && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
 }
 
@@ -93,7 +93,7 @@
                    ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
                    mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)");
 
-    const FenceTimePtr& pastPresentFence = presentFenceForPastVsync(minFramePeriod);
+    FenceWithFenceTime pastPresentFence = presentFenceForPastVsync(minFramePeriod);
 
     // In cases where the present fence is about to fire, give it a small grace period instead of
     // giving up on the frame.
@@ -105,8 +105,8 @@
 
     // Pending frames may trigger backpressure propagation.
     const auto& isFencePending = *isFencePendingFuncPtr;
-    mFramePending = pastPresentFence != FenceTime::NO_FENCE &&
-            isFencePending(pastPresentFence, graceTimeForPresentFenceMs);
+    mFramePending = pastPresentFence.fenceTime != FenceTime::NO_FENCE &&
+            isFencePending(pastPresentFence.fenceTime, graceTimeForPresentFenceMs);
 
     // A frame is missed if the prior frame is still pending. If no longer pending, then we still
     // count the frame as missed if the predicted present time was further in the past than when the
@@ -114,9 +114,10 @@
     // than a typical frame duration, but should not be so small that it reports reasonable drift as
     // a missed frame.
     mFrameMissed = mFramePending || [&] {
-        const nsecs_t pastPresentTime = pastPresentFence->getSignalTime();
+        const nsecs_t pastPresentTime = pastPresentFence.fenceTime->getSignalTime();
         if (pastPresentTime < 0) return false;
-        mLastSignaledFrameTime = TimePoint::fromNs(pastPresentTime);
+        mLastSignaledFrameTime = {.signalTime = TimePoint::fromNs(pastPresentTime),
+                                  .expectedPresentTime = pastPresentFence.expectedPresentTime};
         const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
         return lastScheduledPresentTime.ns() < pastPresentTime - frameMissedSlop;
     }();
diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
index 5448eec..190d062 100644
--- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
@@ -57,6 +57,10 @@
         return target().wouldPresentEarly(minFramePeriod);
     }
 
+    FrameTarget::FenceWithFenceTime presentFenceForPastVsync(Period minFramePeriod) const {
+        return target().presentFenceForPastVsync(minFramePeriod);
+    }
+
     struct Frame {
         Frame(FrameTargeterTestBase* testPtr, VsyncId vsyncId, TimePoint& frameBeginTime,
               Duration frameDuration, Fps refreshRate, Fps peakRefreshRate,
@@ -181,7 +185,7 @@
         const auto fence = frame.end();
 
         EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - kPeriod);
-        EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), fence);
+        EXPECT_EQ(presentFenceForPastVsync(kPeriod).fenceTime, fence);
     }
 }
 
@@ -200,7 +204,7 @@
         const auto fence = frame.end();
 
         EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod);
-        EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), previousFence);
+        EXPECT_EQ(presentFenceForPastVsync(kPeriod).fenceTime, previousFence);
 
         previousFence = fence;
     }
@@ -222,7 +226,7 @@
 
         const auto pastVsyncTime = frameBeginTime + kFrameDuration - 2 * kPeriod;
         EXPECT_EQ(target().pastVsyncTime(kPeriod), pastVsyncTime);
-        EXPECT_EQ(target().presentFenceForPastVsync(kFrameDuration), previousFence);
+        EXPECT_EQ(presentFenceForPastVsync(kFrameDuration).fenceTime, previousFence);
 
         frameBeginTime += kPeriod;
         previousFence = fence;
@@ -248,7 +252,7 @@
         const auto fence = frame.end();
 
         EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod);
-        EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), previousFence);
+        EXPECT_EQ(presentFenceForPastVsync(kPeriod).fenceTime, previousFence);
 
         previousFence = fence;
     }
@@ -274,7 +278,7 @@
 
         const auto pastVsyncTime = frameBeginTime + kFrameDuration - 2 * kPeriod;
         EXPECT_EQ(target().pastVsyncTime(kPeriod), pastVsyncTime);
-        EXPECT_EQ(target().presentFenceForPastVsync(kFrameDuration), previousFence);
+        EXPECT_EQ(presentFenceForPastVsync(kFrameDuration).fenceTime, previousFence);
 
         frameBeginTime += kPeriod;
         previousFence = fence;
@@ -283,7 +287,7 @@
 
 TEST_F(FrameTargeterTest, doesNotDetectEarlyPresentIfNoFence) {
     constexpr Period kPeriod = (60_Hz).getPeriod();
-    EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), FenceTime::NO_FENCE);
+    EXPECT_EQ(presentFenceForPastVsync(kPeriod).fenceTime, FenceTime::NO_FENCE);
     EXPECT_FALSE(wouldPresentEarly(kPeriod));
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index af504ca..b17fd49 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1507,7 +1507,7 @@
 
     bool mPowerHintSessionEnabled;
 
-    bool mLayerLifecycleManagerEnabled = false;
+    bool mLayerLifecycleManagerEnabled = true;
     // Whether a display should be turned on when initialized
     bool mSkipPowerOnForQuiescent;
 
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index cdd77fe..23d3c16 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -15,7 +15,6 @@
  */
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#include "renderengine/ExternalTexture.h"
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 #pragma clang diagnostic ignored "-Wextra"
@@ -31,6 +30,7 @@
 #include <gui/IProducerListener.h>
 #include <gui/LayerMetadata.h>
 #include <log/log.h>
+#include <renderengine/ExternalTexture.h>
 #include <renderengine/mock/FakeExternalTexture.h>
 #include <renderengine/mock/RenderEngine.h>
 #include <system/window.h>
@@ -149,7 +149,6 @@
     sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
             sp<compositionengine::mock::DisplaySurface>::make();
     sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
-    std::vector<sp<Layer>> mAuxiliaryLayers;
 
     sp<GraphicBuffer> mBuffer =
             sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888,
@@ -194,6 +193,7 @@
 template <typename LayerCase>
 void CompositionTest::captureScreenComposition() {
     LayerCase::setupForScreenCapture(this);
+    mFlinger.commit();
 
     const Rect sourceCrop(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
     constexpr bool regionSampling = false;
@@ -204,13 +204,8 @@
                                       RenderArea::Options::CAPTURE_SECURE_LAYERS |
                                               RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION);
 
-    auto traverseLayers = [this](const LayerVector::Visitor& visitor) {
-        return mFlinger.traverseLayersInLayerStack(mDisplay->getLayerStack(),
-                                                   CaptureArgs::UNSET_UID, {}, visitor);
-    };
-
-    // TODO: Use SurfaceFlinger::getLayerSnapshotsForScreenshots instead of this legacy function
-    auto getLayerSnapshotsFn = RenderArea::fromTraverseLayersLambda(traverseLayers);
+    auto getLayerSnapshotsFn = mFlinger.getLayerSnapshotsForScreenshotsFn(mDisplay->getLayerStack(),
+                                                                          CaptureArgs::UNSET_UID);
 
     const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
             GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
@@ -462,7 +457,7 @@
     static constexpr IComposerClient::BlendMode BLENDMODE =
             IComposerClient::BlendMode::PREMULTIPLIED;
 
-    static void setupLatchedBuffer(CompositionTest* test, sp<Layer> layer) {
+    static void setupLatchedBuffer(CompositionTest* test, frontend::RequestedLayerState& layer) {
         Mock::VerifyAndClear(test->mRenderEngine);
 
         const auto buffer = std::make_shared<
@@ -472,21 +467,15 @@
                                                          LayerProperties::FORMAT,
                                                          LayerProperties::USAGE |
                                                                  GraphicBuffer::USAGE_HW_TEXTURE);
-
-        auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
-        layerDrawingState.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
-        layerDrawingState.buffer = buffer;
-        layerDrawingState.acquireFence = Fence::NO_FENCE;
-        layerDrawingState.dataspace = ui::Dataspace::UNKNOWN;
-        layer->setSurfaceDamageRegion(
-                Region(Rect(LayerProperties::HEIGHT, LayerProperties::WIDTH)));
-
-        bool ignoredRecomputeVisibleRegions;
-        layer->latchBuffer(ignoredRecomputeVisibleRegions, 0);
+        layer.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
+        layer.externalTexture = buffer;
+        layer.bufferData->acquireFence = Fence::NO_FENCE;
+        layer.dataspace = ui::Dataspace::UNKNOWN;
+        layer.surfaceDamageRegion = Region(Rect(LayerProperties::HEIGHT, LayerProperties::WIDTH));
         Mock::VerifyAndClear(test->mRenderEngine);
     }
 
-    static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
+    static void setupLayerState(CompositionTest* test, frontend::RequestedLayerState& layer) {
         setupLatchedBuffer(test, layer);
     }
 
@@ -670,14 +659,12 @@
     using Base = BaseLayerProperties<SidebandLayerProperties>;
     static constexpr IComposerClient::BlendMode BLENDMODE = IComposerClient::BlendMode::NONE;
 
-    static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
+    static void setupLayerState(CompositionTest* test, frontend::RequestedLayerState& layer) {
         sp<NativeHandle> stream =
                 NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
                                      false);
-        test->mFlinger.setLayerSidebandStream(layer, stream);
-        auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
-        layerDrawingState.crop =
-                Rect(0, 0, SidebandLayerProperties::HEIGHT, SidebandLayerProperties::WIDTH);
+        layer.sidebandStream = stream;
+        layer.crop = Rect(0, 0, SidebandLayerProperties::HEIGHT, SidebandLayerProperties::WIDTH);
     }
 
     static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) {
@@ -755,17 +742,17 @@
 struct CursorLayerProperties : public BaseLayerProperties<CursorLayerProperties> {
     using Base = BaseLayerProperties<CursorLayerProperties>;
 
-    static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
+    static void setupLayerState(CompositionTest* test, frontend::RequestedLayerState& layer) {
         Base::setupLayerState(test, layer);
-        test->mFlinger.setLayerPotentialCursor(layer, true);
+        layer.potentialCursor = true;
     }
 };
 
 struct NoLayerVariant {
-    using FlingerLayerType = sp<Layer>;
-
-    static FlingerLayerType createLayer(CompositionTest*) { return FlingerLayerType(); }
-    static void injectLayer(CompositionTest*, FlingerLayerType) {}
+    static frontend::RequestedLayerState createLayer(CompositionTest*) {
+        return {LayerCreationArgs()};
+    }
+    static void injectLayer(CompositionTest*, frontend::RequestedLayerState&) {}
     static void cleanupInjectedLayers(CompositionTest*) {}
 
     static void setupCallExpectationsForDirtyGeometry(CompositionTest*) {}
@@ -775,10 +762,10 @@
 template <typename LayerProperties>
 struct BaseLayerVariant {
     template <typename L, typename F>
-    static sp<L> createLayerWithFactory(CompositionTest* test, F factory) {
+    static frontend::RequestedLayerState createLayerWithFactory(CompositionTest* test, F factory) {
         EXPECT_CALL(*test->mFlinger.scheduler(), postMessage(_)).Times(0);
 
-        sp<L> layer = factory();
+        auto layer = factory();
 
         // Layer should be registered with scheduler.
         EXPECT_EQ(1u, test->mFlinger.scheduler()->layerHistorySize());
@@ -792,27 +779,26 @@
         return layer;
     }
 
-    template <typename L>
-    static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) {
-        auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
-        layerDrawingState.layerStack = LAYER_STACK;
-        layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
-                                        LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
-        layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */);
+    static void initLayerDrawingStateAndComputeBounds(CompositionTest* test,
+                                                      frontend::RequestedLayerState& layer) {
+        layer.layerStack = LAYER_STACK;
+        layer.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
+                            LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
     }
 
-    static void injectLayer(CompositionTest* test, sp<Layer> layer) {
+    static void injectLayer(CompositionTest* test, frontend::RequestedLayerState& layer) {
         EXPECT_CALL(*test->mComposer, createLayer(HWC_DISPLAY, _))
                 .WillOnce(DoAll(SetArgPointee<1>(HWC_LAYER), Return(Error::NONE)));
-
+        auto legacyLayer = test->mFlinger.getLegacyLayer(layer.id);
         auto outputLayer = test->mDisplay->getCompositionDisplay()->injectOutputLayerForTest(
-                layer->getCompositionEngineLayerFE());
+                legacyLayer->getCompositionEngineLayerFE({.id = layer.id}));
         outputLayer->editState().visibleRegion = Region(Rect(0, 0, 100, 100));
         outputLayer->editState().outputSpaceVisibleRegion = Region(Rect(0, 0, 100, 100));
 
         Mock::VerifyAndClear(test->mComposer);
 
-        test->mFlinger.mutableDrawingState().layersSortedByZ.add(layer);
+        auto layerCopy = std::make_unique<frontend::RequestedLayerState>(layer);
+        test->mFlinger.addLayer(layerCopy);
         test->mFlinger.mutableVisibleRegionsDirty() = true;
     }
 
@@ -820,10 +806,9 @@
         EXPECT_CALL(*test->mComposer, destroyLayer(HWC_DISPLAY, HWC_LAYER))
                 .WillOnce(Return(Error::NONE));
 
+        test->mFlinger.destroyAllLayerHandles();
         test->mDisplay->getCompositionDisplay()->clearOutputLayers();
-        test->mFlinger.mutableDrawingState().layersSortedByZ.clear();
         test->mFlinger.mutablePreviouslyComposedLayers().clear();
-
         // Layer should be unregistered with scheduler.
         test->mFlinger.commit();
         EXPECT_EQ(0u, test->mFlinger.scheduler()->layerHistorySize());
@@ -833,17 +818,17 @@
 template <typename LayerProperties>
 struct EffectLayerVariant : public BaseLayerVariant<LayerProperties> {
     using Base = BaseLayerVariant<LayerProperties>;
-    using FlingerLayerType = sp<Layer>;
-
-    static FlingerLayerType createLayer(CompositionTest* test) {
-        FlingerLayerType layer = Base::template createLayerWithFactory<Layer>(test, [test]() {
-            return sp<Layer>::make(LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(),
-                                                     "test-layer", LayerProperties::LAYER_FLAGS,
-                                                     LayerMetadata()));
+    static frontend::RequestedLayerState createLayer(CompositionTest* test) {
+        frontend::RequestedLayerState layer = Base::template createLayerWithFactory<
+                frontend::RequestedLayerState>(test, [test]() {
+            auto args = LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(), "test-layer",
+                                          LayerProperties::LAYER_FLAGS, LayerMetadata());
+            auto legacyLayer = sp<Layer>::make(args);
+            test->mFlinger.injectLegacyLayer(legacyLayer);
+            return frontend::RequestedLayerState(args);
         });
 
-        auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
-        layerDrawingState.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
+        layer.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
         return layer;
     }
 
@@ -869,13 +854,15 @@
 template <typename LayerProperties>
 struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> {
     using Base = BaseLayerVariant<LayerProperties>;
-    using FlingerLayerType = sp<Layer>;
 
-    static FlingerLayerType createLayer(CompositionTest* test) {
-        FlingerLayerType layer = Base::template createLayerWithFactory<Layer>(test, [test]() {
+    static frontend::RequestedLayerState createLayer(CompositionTest* test) {
+        frontend::RequestedLayerState layer = Base::template createLayerWithFactory<
+                frontend::RequestedLayerState>(test, [test]() {
             LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer",
                                    LayerProperties::LAYER_FLAGS, LayerMetadata());
-            return sp<Layer>::make(args);
+            auto legacyLayer = sp<Layer>::make(args);
+            test->mFlinger.injectLegacyLayer(legacyLayer);
+            return frontend::RequestedLayerState(args);
         });
 
         LayerProperties::setupLayerState(test, layer);
@@ -917,13 +904,14 @@
 template <typename LayerProperties>
 struct ContainerLayerVariant : public BaseLayerVariant<LayerProperties> {
     using Base = BaseLayerVariant<LayerProperties>;
-    using FlingerLayerType = sp<Layer>;
 
-    static FlingerLayerType createLayer(CompositionTest* test) {
+    static frontend::RequestedLayerState createLayer(CompositionTest* test) {
         LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-container-layer",
                                LayerProperties::LAYER_FLAGS, LayerMetadata());
-        FlingerLayerType layer = sp<Layer>::make(args);
-        Base::template initLayerDrawingStateAndComputeBounds(test, layer);
+        sp<Layer> legacyLayer = sp<Layer>::make(args);
+        test->mFlinger.injectLegacyLayer(legacyLayer);
+        frontend::RequestedLayerState layer(args);
+        Base::initLayerDrawingStateAndComputeBounds(test, layer);
         return layer;
     }
 };
@@ -931,29 +919,19 @@
 template <typename LayerVariant, typename ParentLayerVariant>
 struct ChildLayerVariant : public LayerVariant {
     using Base = LayerVariant;
-    using FlingerLayerType = typename LayerVariant::FlingerLayerType;
     using ParentBase = ParentLayerVariant;
 
-    static FlingerLayerType createLayer(CompositionTest* test) {
+    static frontend::RequestedLayerState createLayer(CompositionTest* test) {
         // Need to create child layer first. Otherwise layer history size will be 2.
-        FlingerLayerType layer = Base::createLayer(test);
-
-        typename ParentBase::FlingerLayerType parentLayer = ParentBase::createLayer(test);
-        parentLayer->addChild(layer);
-        test->mFlinger.setLayerDrawingParent(layer, parentLayer);
-
-        test->mAuxiliaryLayers.push_back(parentLayer);
-
+        frontend::RequestedLayerState layer = Base::createLayer(test);
+        frontend::RequestedLayerState parentLayer = ParentBase::createLayer(test);
+        layer.parentId = parentLayer.id;
+        auto layerCopy = std::make_unique<frontend::RequestedLayerState>(parentLayer);
+        test->mFlinger.addLayer(layerCopy);
         return layer;
     }
 
-    static void cleanupInjectedLayers(CompositionTest* test) {
-        // Clear auxiliary layers first so that child layer can be successfully destroyed in the
-        // following call.
-        test->mAuxiliaryLayers.clear();
-
-        Base::cleanupInjectedLayers(test);
-    }
+    static void cleanupInjectedLayers(CompositionTest* test) { Base::cleanupInjectedLayers(test); }
 };
 
 /* ------------------------------------------------------------------------
@@ -1016,7 +994,7 @@
  */
 
 struct CompositionResultBaseVariant {
-    static void setupLayerState(CompositionTest*, sp<Layer>) {}
+    static void setupLayerState(CompositionTest*, frontend::RequestedLayerState&) {}
 
     template <typename Case>
     static void setupCallExpectationsForDirtyGeometry(CompositionTest* test) {
@@ -1056,9 +1034,8 @@
 };
 
 struct ForcedClientCompositionResultVariant : public CompositionResultBaseVariant {
-    static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
-        const auto outputLayer =
-                TestableSurfaceFlinger::findOutputLayerForDisplay(layer, test->mDisplay);
+    static void setupLayerState(CompositionTest* test, frontend::RequestedLayerState& layer) {
+        const auto outputLayer = test->mFlinger.findOutputLayerForDisplay(layer.id, test->mDisplay);
         LOG_FATAL_IF(!outputLayer);
         outputLayer->editState().forceClientComposition = true;
     }
@@ -1079,7 +1056,7 @@
 };
 
 struct ForcedClientCompositionViaDebugOptionResultVariant : public CompositionResultBaseVariant {
-    static void setupLayerState(CompositionTest* test, sp<Layer>) {
+    static void setupLayerState(CompositionTest* test, frontend::RequestedLayerState&) {
         test->mFlinger.mutableDebugDisableHWC() = true;
     }
 
@@ -1099,7 +1076,7 @@
 };
 
 struct EmptyScreenshotResultVariant {
-    static void setupLayerState(CompositionTest*, sp<Layer>) {}
+    static void setupLayerState(CompositionTest*, frontend::RequestedLayerState&) {}
 
     template <typename Case>
     static void setupCallExpectations(CompositionTest*) {}
@@ -1365,28 +1342,6 @@
  *  Layers with a parent layer with ISurfaceComposerClient::eSecure, on a non-secure display
  */
 
-TEST_F(CompositionTest,
-       HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyGeometry) {
-    displayRefreshCompositionDirtyGeometry<CompositionCase<
-            InsecureDisplaySetupVariant,
-            ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
-                              ContainerLayerVariant<SecureLayerProperties>>,
-            KeepCompositionTypeVariant<
-                    aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
-            ForcedClientCompositionResultVariant>>();
-}
-
-TEST_F(CompositionTest,
-       HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyFrame) {
-    displayRefreshCompositionDirtyFrame<CompositionCase<
-            InsecureDisplaySetupVariant,
-            ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
-                              ContainerLayerVariant<SecureLayerProperties>>,
-            KeepCompositionTypeVariant<
-                    aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
-            ForcedClientCompositionResultVariant>>();
-}
-
 TEST_F(CompositionTest, captureScreenBufferLayerWithSecureParentLayerOnInsecureDisplay) {
     captureScreenComposition<
             CompositionCase<InsecureDisplaySetupVariant,
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index e74f643..c879280 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -706,7 +706,7 @@
     testGpuScenario(config, res);
     EXPECT_EQ(res.gpuDurationNanos, 0L);
     EXPECT_EQ(res.cpuDurationNanos, 0L);
-    EXPECT_GE(res.durationNanos, toNanos(30ms + getErrorMargin()));
+    EXPECT_GE(res.durationNanos, toNanos(29ms + getErrorMargin()));
     EXPECT_LE(res.durationNanos, toNanos(31ms + getErrorMargin()));
 }
 
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index fc54a8b..3df724a 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -34,6 +34,7 @@
 #include "mock/MockSchedulerCallback.h"
 
 #include <FrontEnd/LayerHierarchy.h>
+#include <scheduler/FrameTime.h>
 
 #include <com_android_graphics_surfaceflinger_flags.h>
 #include "FpsOps.h"
@@ -607,7 +608,8 @@
                                              TimePoint::fromNs(2000)));
 
     // Not crossing the min frame period
-    vrrTracker->onFrameBegin(TimePoint::fromNs(2000), TimePoint::fromNs(1500));
+    vrrTracker->onFrameBegin(TimePoint::fromNs(2000),
+                             {TimePoint::fromNs(1500), TimePoint::fromNs(1500)});
     EXPECT_EQ(Fps::fromPeriodNsecs(1000),
               scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
                                              TimePoint::fromNs(2500)));
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp
index ff7612e..d638024 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp
@@ -28,7 +28,6 @@
 class ColorMatrixTest : public CommitAndCompositeTest {};
 
 TEST_F(ColorMatrixTest, colorMatrixChanged) {
-    mFlinger.enableLayerLifecycleManager();
     EXPECT_COLOR_MATRIX_CHANGED(true, true);
     mFlinger.mutableTransactionFlags() |= eTransactionNeeded;
 
@@ -46,7 +45,6 @@
 }
 
 TEST_F(ColorMatrixTest, colorMatrixChangedAfterDisplayTransaction) {
-    mFlinger.enableLayerLifecycleManager();
     EXPECT_COLOR_MATRIX_CHANGED(true, true);
     mFlinger.mutableTransactionFlags() |= eTransactionNeeded;
 
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 4197cbd..e3b2af1 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -326,9 +326,13 @@
 
     auto& mutableStateLock() { return mFlinger->mStateLock; }
 
-    static auto findOutputLayerForDisplay(const sp<Layer>& layer,
-                                          const sp<const DisplayDevice>& display) {
-        return layer->findOutputLayerForDisplay(display.get());
+    compositionengine::OutputLayer* findOutputLayerForDisplay(
+            uint32_t layerId, const sp<const DisplayDevice>& display) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        if (mFlinger->mLegacyLayers.find(layerId) == mFlinger->mLegacyLayers.end()) {
+            return nullptr;
+        }
+        return mFlinger->mLegacyLayers[layerId]->findOutputLayerForDisplay(display.get());
     }
 
     static void setLayerSidebandStream(const sp<Layer>& layer,
@@ -340,17 +344,14 @@
 
     void setLayerCompositionType(const sp<Layer>& layer,
                                  aidl::android::hardware::graphics::composer3::Composition type) {
-        auto outputLayer = findOutputLayerForDisplay(layer, mFlinger->getDefaultDisplayDevice());
+        auto outputLayer = findOutputLayerForDisplay(static_cast<uint32_t>(layer->sequence),
+                                                     mFlinger->getDefaultDisplayDevice());
         LOG_ALWAYS_FATAL_IF(!outputLayer);
         auto& state = outputLayer->editState();
         LOG_ALWAYS_FATAL_IF(!outputLayer->getState().hwc);
         (*state.hwc).hwcCompositionType = type;
     }
 
-    static void setLayerPotentialCursor(const sp<Layer>& layer, bool potentialCursor) {
-        layer->mPotentialCursor = potentialCursor;
-    }
-
     static void setLayerDrawingParent(const sp<Layer>& layer, const sp<Layer>& drawingParent) {
         layer->mDrawingParent = drawingParent;
     }
@@ -395,7 +396,7 @@
             targets.try_emplace(id, &frameTargeter.target());
             targeters.try_emplace(id, &frameTargeter);
         }
-
+        mFlinger->setTransactionFlags(eTransactionFlushNeeded);
         mFlinger->commit(displayId, targets);
 
         if (composite) {
@@ -505,10 +506,9 @@
                                           captureResults, displayState, layers, layerFEs);
     }
 
-    auto traverseLayersInLayerStack(ui::LayerStack layerStack, int32_t uid,
-                                    std::unordered_set<uint32_t> excludeLayerIds,
-                                    const LayerVector::Visitor& visitor) {
-        return mFlinger->traverseLayersInLayerStack(layerStack, uid, excludeLayerIds, visitor);
+    auto getLayerSnapshotsForScreenshotsFn(ui::LayerStack layerStack, uint32_t uid) {
+        return mFlinger->getLayerSnapshotsForScreenshots(layerStack, uid,
+                                                         std::unordered_set<uint32_t>{});
     }
 
     auto getDisplayNativePrimaries(const sp<IBinder>& displayToken,
@@ -620,6 +620,18 @@
         mFlinger->mNewLayers.emplace_back(std::move(layer));
     }
 
+    // Used to add a layer before updateLayerSnapshots is called.
+    // Must have transactionsFlushed enabled for the new layer to be updated.
+    void addLayer(uint32_t layerId) {
+        std::scoped_lock<std::mutex> lock(mFlinger->mCreatedLayersLock);
+        LayerCreationArgs args(std::make_optional(layerId));
+        args.flinger = this->mFlinger.get();
+        auto layer = std::make_unique<frontend::RequestedLayerState>(args);
+        auto legacyLayer = sp<Layer>::make(args);
+        injectLegacyLayer(legacyLayer);
+        mFlinger->mNewLayers.emplace_back(std::move(layer));
+    }
+
     /* ------------------------------------------------------------------------
      * Read-only access to private data to assert post-conditions.
      */
@@ -637,12 +649,24 @@
     void injectLegacyLayer(sp<Layer> layer) {
         FTL_FAKE_GUARD(kMainThreadContext,
                        mFlinger->mLegacyLayers[static_cast<uint32_t>(layer->sequence)] = layer);
-    };
+    }
 
     void releaseLegacyLayer(uint32_t sequence) {
         FTL_FAKE_GUARD(kMainThreadContext, mFlinger->mLegacyLayers.erase(sequence));
+    }
+
+    auto getLegacyLayer(uint32_t layerId) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        return mFlinger->mLegacyLayers[layerId];
     };
 
+    void destroyAllLayerHandles() {
+        ftl::FakeGuard guard(kMainThreadContext);
+        for (auto [layerId, legacyLayer] : mFlinger->mLegacyLayers) {
+            mFlinger->onHandleDestroyed(nullptr, legacyLayer, layerId);
+        }
+    }
+
     auto setLayerHistoryDisplayArea(uint32_t displayArea) {
         return mFlinger->mScheduler->onActiveDisplayAreaChanged(displayArea);
     };
@@ -709,10 +733,6 @@
         return mFlinger->initTransactionTraceWriter();
     }
 
-    // Needed since mLayerLifecycleManagerEnabled is false by default and must
-    // be enabled for tests to go through the new front end path.
-    void enableLayerLifecycleManager() { mFlinger->mLayerLifecycleManagerEnabled = true; }
-
     void notifyExpectedPresentIfRequired(PhysicalDisplayId displayId, Period vsyncPeriod,
                                          TimePoint expectedPresentTime, Fps frameInterval,
                                          std::optional<Period> timeoutOpt) {
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 7fb9247..e13fe49 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -17,6 +17,7 @@
 #undef LOG_TAG
 #define LOG_TAG "TransactionApplicationTest"
 
+#include <binder/Binder.h>
 #include <common/test/FlagUtils.h>
 #include <compositionengine/Display.h>
 #include <compositionengine/mock/DisplaySurface.h>
@@ -26,10 +27,10 @@
 #include <gui/SurfaceComposerClient.h>
 #include <gui/fake/BufferData.h>
 #include <log/log.h>
+#include <renderengine/mock/RenderEngine.h>
 #include <ui/MockFence.h>
 #include <utils/String8.h>
 #include <vector>
-#include <binder/Binder.h>
 
 #include "FrontEnd/TransactionHandler.h"
 #include "TestableSurfaceFlinger.h"
@@ -55,6 +56,7 @@
 
         mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
         mFlinger.setupMockScheduler();
+        mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
         mFlinger.flinger()->addTransactionReadyFilters();
     }
 
@@ -65,6 +67,7 @@
     }
 
     TestableSurfaceFlinger mFlinger;
+    renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
 
     struct TransactionInfo {
         Vector<ComposerState> states;
@@ -323,15 +326,17 @@
     transaction1.states[0].state.bufferData =
             std::make_shared<fake::BufferData>(/* bufferId */ 1, /* width */ 1, /* height */ 1,
                                                /* pixelFormat */ 0, /* outUsage */ 0);
+    mFlinger.addLayer(1);
+    bool out;
+    mFlinger.updateLayerSnapshots(VsyncId{1}, 0, /* transactionsFlushed */ true, out);
     transaction1.states[0].externalTexture =
             std::make_shared<FakeExternalTexture>(*transaction1.states[0].state.bufferData);
-    transaction1.states[0].state.surface =
-            sp<Layer>::make(LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {}))
-                    ->getHandle();
+    transaction1.states[0].state.surface = mFlinger.getLegacyLayer(1)->getHandle();
     auto fence = sp<mock::MockFence>::make();
     EXPECT_CALL(*fence, getStatus()).WillRepeatedly(Return(Fence::Status::Unsignaled));
     transaction1.states[0].state.bufferData->acquireFence = std::move(fence);
     transaction1.states[0].state.bufferData->flags = BufferData::BufferDataChange::fenceChanged;
+    transaction1.states[0].layerId = 1;
     transaction1.isAutoTimestamp = true;
 
     // Transaction 2 should be ready to be applied.
@@ -361,8 +366,7 @@
         }
         mFlinger.getPendingTransactionQueue().clear();
         mFlinger.commitTransactionsLocked(eTransactionMask);
-        mFlinger.mutableCurrentState().layersSortedByZ.clear();
-        mFlinger.mutableDrawingState().layersSortedByZ.clear();
+        mFlinger.destroyAllLayerHandles();
     }
 
     static sp<Fence> fence(Fence::Status status) {
@@ -371,8 +375,7 @@
         return fence;
     }
 
-    ComposerState createComposerState(int layerId, sp<Fence> fence, uint64_t what,
-                                      std::optional<sp<IBinder>> layerHandle = std::nullopt) {
+    ComposerState createComposerState(int layerId, sp<Fence> fence, uint64_t what) {
         ComposerState state;
         state.state.bufferData =
                 std::make_shared<fake::BufferData>(/* bufferId */ 123L, /* width */ 1,
@@ -380,9 +383,6 @@
                                                    /* outUsage */ 0);
         state.state.bufferData->acquireFence = std::move(fence);
         state.state.layerId = layerId;
-        state.state.surface = layerHandle.value_or(
-                sp<Layer>::make(LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {}))
-                        ->getHandle());
         state.state.bufferData->flags = BufferData::BufferDataChange::fenceChanged;
 
         state.state.what = what;
@@ -418,6 +418,19 @@
                               size_t expectedTransactionsPending) {
         EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
         EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+        std::unordered_set<uint32_t> createdLayers;
+        for (auto transaction : transactions) {
+            for (auto& state : transaction.states) {
+                auto layerId = static_cast<uint32_t>(state.state.layerId);
+                if (createdLayers.find(layerId) == createdLayers.end()) {
+                    mFlinger.addLayer(layerId);
+                    createdLayers.insert(layerId);
+                }
+            }
+        }
+        bool unused;
+        bool mustComposite = mFlinger.updateLayerSnapshots(VsyncId{1}, /*frameTimeNs=*/0,
+                                                           /*transactionsFlushed=*/true, unused);
 
         for (auto transaction : transactions) {
             std::vector<ResolvedComposerState> resolvedStates;
@@ -427,6 +440,9 @@
                 resolvedState.state = std::move(state.state);
                 resolvedState.externalTexture =
                         std::make_shared<FakeExternalTexture>(*resolvedState.state.bufferData);
+                resolvedState.layerId = static_cast<uint32_t>(state.state.layerId);
+                resolvedState.state.surface =
+                        mFlinger.getLegacyLayer(resolvedState.layerId)->getHandle();
                 resolvedStates.emplace_back(resolvedState);
             }
 
@@ -458,9 +474,8 @@
 TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSingleSignaledFromTheQueue) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const auto kLayerId = 1;
+    const auto kLayerId = 10;
     const auto kExpectedTransactionsPending = 0u;
-
     const auto signaledTransaction =
             createTransactionInfo(kApplyToken,
                                   {createComposerState(kLayerId, fence(Fence::Status::Signaled),
@@ -773,7 +788,7 @@
 TEST_F(LatchUnsignaledDisabledTest, Flush_RemovesSignaledFromTheQueue) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const auto kLayerId = 1;
+    const auto kLayerId = 10;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto signaledTransaction =
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 3b09554..b63f299 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -19,6 +19,7 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <scheduler/FrameTime.h>
 #include <scheduler/Timer.h>
 
 #include "Scheduler/VSyncDispatchTimerQueue.h"
@@ -51,7 +52,7 @@
     bool isVSyncInPhase(nsecs_t, Fps) final { return false; }
     void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final {}
     void setRenderRate(Fps, bool) final {}
-    void onFrameBegin(TimePoint, TimePoint) final {}
+    void onFrameBegin(TimePoint, scheduler::FrameTime) final {}
     void onFrameMissed(TimePoint) final {}
     void dump(std::string&) const final {}
     bool isCurrentMode(const ftl::NonNull<DisplayModePtr>&) const final { return false; };
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index f36a8a6..8690dba 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -901,17 +901,20 @@
     EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
     EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000));
 
-    vrrTracker.onFrameBegin(TimePoint::fromNs(2000), TimePoint::fromNs(1500));
+    vrrTracker.onFrameBegin(TimePoint::fromNs(2000),
+                            {TimePoint::fromNs(1500), TimePoint::fromNs(1500)});
     EXPECT_EQ(3500, vrrTracker.nextAnticipatedVSyncTimeFrom(2000, 2000));
     EXPECT_EQ(4500, vrrTracker.nextAnticipatedVSyncTimeFrom(3500, 3500));
 
     // Miss when starting 4500 and expect the next vsync will be at 5000 (next one)
-    vrrTracker.onFrameBegin(TimePoint::fromNs(3500), TimePoint::fromNs(2500));
+    vrrTracker.onFrameBegin(TimePoint::fromNs(3500),
+                            {TimePoint::fromNs(2500), TimePoint::fromNs(2500)});
     vrrTracker.onFrameMissed(TimePoint::fromNs(4500));
     EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500));
     EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
 
-    vrrTracker.onFrameBegin(TimePoint::fromNs(7000), TimePoint::fromNs(6500));
+    vrrTracker.onFrameBegin(TimePoint::fromNs(7000),
+                            {TimePoint::fromNs(6500), TimePoint::fromNs(6500)});
     EXPECT_EQ(10500, vrrTracker.nextAnticipatedVSyncTimeFrom(9000, 7000));
 }
 
@@ -943,7 +946,7 @@
 
     // SF starts to catch up
     EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2700));
-    vrrTracker.onFrameBegin(TimePoint::fromNs(3000), TimePoint::fromNs(0));
+    vrrTracker.onFrameBegin(TimePoint::fromNs(3000), {TimePoint::fromNs(0), TimePoint::fromNs(0)});
 
     // SF misses last frame (3000) and observes that when committing (4000)
     EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
@@ -952,17 +955,20 @@
 
     // SF wakes up again instead of the (4000) missed frame
     EXPECT_EQ(4500, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
-    vrrTracker.onFrameBegin(TimePoint::fromNs(4500), TimePoint::fromNs(4500));
+    vrrTracker.onFrameBegin(TimePoint::fromNs(4500),
+                            {TimePoint::fromNs(4500), TimePoint::fromNs(4500)});
 
     // Timeline shifted. The app needs to get the next frame at (7500) as its last frame (6500) will
     // be presented at (7500)
     EXPECT_EQ(7500, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));
     EXPECT_EQ(5500, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500));
-    vrrTracker.onFrameBegin(TimePoint::fromNs(5500), TimePoint::fromNs(4500));
+    vrrTracker.onFrameBegin(TimePoint::fromNs(5500),
+                            {TimePoint::fromNs(4500), TimePoint::fromNs(4500)});
 
     EXPECT_EQ(8500, vrrTracker.nextAnticipatedVSyncTimeFrom(7500, 7500));
     EXPECT_EQ(6500, vrrTracker.nextAnticipatedVSyncTimeFrom(5500, 5500));
-    vrrTracker.onFrameBegin(TimePoint::fromNs(6500), TimePoint::fromNs(5500));
+    vrrTracker.onFrameBegin(TimePoint::fromNs(6500),
+                            {TimePoint::fromNs(5500), TimePoint::fromNs(5500)});
 }
 
 TEST_F(VSyncPredictorTest, renderRateIsPreservedForCommittedVsyncs) {
@@ -1020,6 +1026,35 @@
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(13000));
 }
 
+TEST_F(VSyncPredictorTest, timelineNotAdjustedForEarlyPresent) {
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+    const int32_t kGroup = 0;
+    const auto kResolution = ui::Size(1920, 1080);
+    const auto refreshRate = Fps::fromPeriodNsecs(500);
+    const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+    hal::VrrConfig vrrConfig;
+    vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+    const ftl::NonNull<DisplayModePtr> kMode =
+            ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), refreshRate, kGroup,
+                                                      kResolution, DEFAULT_DISPLAY_ID)
+                                     .setVrrConfig(std::move(vrrConfig))
+                                     .build());
+
+    VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+                              kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+
+    vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false);
+    vrrTracker.addVsyncTimestamp(0);
+    EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
+
+    constexpr auto kLastConfirmedExpectedPresentTime = TimePoint::fromNs(1000);
+    constexpr auto kLastActualSignalTime = TimePoint::fromNs(700); // presented early
+    vrrTracker.onFrameBegin(TimePoint::fromNs(1400),
+                            {kLastActualSignalTime, kLastConfirmedExpectedPresentTime});
+    EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1400, 1000));
+    EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2000, 1000));
+}
 } // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index 4f44d1b..8d6d1d3 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -18,6 +18,8 @@
 
 #include <gmock/gmock.h>
 
+#include <scheduler/FrameTime.h>
+
 #include "Scheduler/VSyncTracker.h"
 
 namespace android::mock {
@@ -37,7 +39,7 @@
     MOCK_METHOD(bool, isVSyncInPhase, (nsecs_t, Fps), (override));
     MOCK_METHOD(void, setDisplayModePtr, (ftl::NonNull<DisplayModePtr>), (override));
     MOCK_METHOD(void, setRenderRate, (Fps, bool), (override));
-    MOCK_METHOD(void, onFrameBegin, (TimePoint, TimePoint), (override));
+    MOCK_METHOD(void, onFrameBegin, (TimePoint, scheduler::FrameTime), (override));
     MOCK_METHOD(void, onFrameMissed, (TimePoint), (override));
     MOCK_METHOD(void, dump, (std::string&), (const, override));
     MOCK_METHOD(bool, isCurrentMode, (const ftl::NonNull<DisplayModePtr>&), (const, override));