Merge "SF: Add autorefresh to drawingstate"
diff --git a/include/input/Input.h b/include/input/Input.h
index 54c7114..7cc595a 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -801,8 +801,11 @@
 
     static std::string actionToString(int32_t action);
 
+    // MotionEvent will transform various axes in different ways, based on the source. For
+    // example, the x and y axes will not have any offsets/translations applied if it comes from a
+    // relative mouse device (since SOURCE_RELATIVE_MOUSE is a non-pointer source). These methods
+    // are used to apply these transformations for different axes.
     static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy);
-
     static float calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform&,
                                                const PointerCoords&);
 
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 6f1a7ae..c986b82 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -130,6 +130,19 @@
     return 1; // keep the callback
 }
 
+void DisplayEventDispatcher::populateFrameTimelines(const DisplayEventReceiver::Event& event,
+                                                    VsyncEventData* outVsyncEventData) const {
+    for (size_t i = 0; i < DisplayEventReceiver::kFrameTimelinesLength; i++) {
+        DisplayEventReceiver::Event::VSync::FrameTimeline receiverTimeline =
+                event.vsync.frameTimelines[i];
+        outVsyncEventData->frameTimelines[i] = {.id = receiverTimeline.vsyncId,
+                                                .deadlineTimestamp =
+                                                        receiverTimeline.deadlineTimestamp,
+                                                .expectedPresentTime =
+                                                        receiverTimeline.expectedVSyncTimestamp};
+    }
+}
+
 bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
                                                   PhysicalDisplayId* outDisplayId,
                                                   uint32_t* outCount,
@@ -154,6 +167,9 @@
                     outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp;
                     outVsyncEventData->frameInterval = ev.vsync.frameInterval;
                     outVsyncEventData->expectedPresentTime = ev.vsync.expectedVSyncTimestamp;
+                    outVsyncEventData->preferredFrameTimelineIndex =
+                            ev.vsync.preferredFrameTimelineIndex;
+                    populateFrameTimelines(ev, outVsyncEventData);
                     break;
                 case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
                     dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index f3bd139..92c89b8 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -17,6 +17,7 @@
 #include <gui/DisplayEventReceiver.h>
 #include <utils/Log.h>
 #include <utils/Looper.h>
+#include <array>
 
 namespace android {
 using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
@@ -36,6 +37,26 @@
 
     // The anticipated Vsync present time.
     int64_t expectedPresentTime = 0;
+
+    struct FrameTimeline {
+        // The Vsync Id corresponsing to this vsync event. This will be used to
+        // populate ISurfaceComposer::setFrameTimelineVsync and
+        // SurfaceComposerClient::setFrameTimelineVsync
+        int64_t id = FrameTimelineInfo::INVALID_VSYNC_ID;
+
+        // The deadline in CLOCK_MONOTONIC that the app needs to complete its
+        // frame by (both on the CPU and the GPU)
+        int64_t deadlineTimestamp = std::numeric_limits<int64_t>::max();
+
+        // The anticipated Vsync present time.
+        int64_t expectedPresentTime = 0;
+    };
+
+    // Sorted possible frame timelines.
+    std::array<FrameTimeline, DisplayEventReceiver::kFrameTimelinesLength> frameTimelines;
+
+    // Index into the frameTimelines that represents the platform's preferred frame timeline.
+    size_t preferredFrameTimelineIndex = std::numeric_limits<size_t>::max();
 };
 
 class DisplayEventDispatcher : public LooperCallback {
@@ -77,5 +98,8 @@
 
     bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
                               uint32_t* outCount, VsyncEventData* outVsyncEventData);
+
+    void populateFrameTimelines(const DisplayEventReceiver::Event& event,
+                                VsyncEventData* outVsyncEventData) const;
 };
 } // namespace android
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 0dffbde..ca36843 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -49,6 +49,9 @@
 // ----------------------------------------------------------------------------
 class DisplayEventReceiver {
 public:
+    // Max amount of frame timelines is arbitrarily set to be reasonable.
+    static constexpr int64_t kFrameTimelinesLength = 7;
+
     enum {
         DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
         DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
@@ -77,6 +80,12 @@
             nsecs_t deadlineTimestamp __attribute__((aligned(8)));
             nsecs_t frameInterval __attribute__((aligned(8)));
             int64_t vsyncId;
+            size_t preferredFrameTimelineIndex __attribute__((aligned(8)));
+            struct FrameTimeline {
+                nsecs_t expectedVSyncTimestamp __attribute__((aligned(8)));
+                nsecs_t deadlineTimestamp __attribute__((aligned(8)));
+                int64_t vsyncId;
+            } frameTimelines[kFrameTimelinesLength];
         };
 
         struct Hotplug {
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index d018800..24a7720 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -66,11 +66,21 @@
     return transformedXy - transformedOrigin;
 }
 
-bool shouldDisregardTranslation(uint32_t source) {
+bool isFromSource(uint32_t source, uint32_t test) {
+    return (source & test) == test;
+}
+
+bool shouldDisregardTransformation(uint32_t source) {
+    // Do not apply any transformations to axes from joysticks or touchpads.
+    return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) ||
+            isFromSource(source, AINPUT_SOURCE_CLASS_POSITION);
+}
+
+bool shouldDisregardOffset(uint32_t source) {
     // Pointer events are the only type of events that refer to absolute coordinates on the display,
     // so we should apply the entire window transform. For other types of events, we should make
     // sure to not apply the window translation/offset.
-    return (source & AINPUT_SOURCE_CLASS_POINTER) == 0;
+    return !isFromSource(source, AINPUT_SOURCE_CLASS_POINTER);
 }
 
 } // namespace
@@ -707,7 +717,7 @@
 #endif
 
 bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) {
-    if (source & AINPUT_SOURCE_CLASS_POINTER) {
+    if (isFromSource(source, AINPUT_SOURCE_CLASS_POINTER)) {
         // Specifically excludes HOVER_MOVE and SCROLL.
         switch (action & AMOTION_EVENT_ACTION_MASK) {
         case AMOTION_EVENT_ACTION_DOWN:
@@ -764,17 +774,31 @@
     return android::base::StringPrintf("%" PRId32, action);
 }
 
+// Apply the given transformation to the point without checking whether the entire transform
+// should be disregarded altogether for the provided source.
+static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform,
+                                                   const vec2& xy) {
+    return shouldDisregardOffset(source) ? transformWithoutTranslation(transform, xy)
+                                         : transform.transform(xy);
+}
+
 vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& transform,
                                          const vec2& xy) {
-    return shouldDisregardTranslation(source) ? transformWithoutTranslation(transform, xy)
-                                              : transform.transform(xy);
+    if (shouldDisregardTransformation(source)) {
+        return xy;
+    }
+    return calculateTransformedXYUnchecked(source, transform, xy);
 }
 
 float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source,
                                                  const ui::Transform& transform,
                                                  const PointerCoords& coords) {
+    if (shouldDisregardTransformation(source)) {
+        return coords.getAxisValue(axis);
+    }
+
     if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
-        const vec2 xy = calculateTransformedXY(source, transform, coords.getXYValue());
+        const vec2 xy = calculateTransformedXYUnchecked(source, transform, coords.getXYValue());
         static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
         return xy[axis];
     }
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 1b594f1..a92016b 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -647,9 +647,8 @@
     ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
 }
 
-MotionEvent createTouchDownEvent(float x, float y, float dx, float dy,
-                                 const ui::Transform& transform,
-                                 const ui::Transform& rawTransform) {
+MotionEvent createMotionEvent(int32_t source, uint32_t action, float x, float y, float dx, float dy,
+                              const ui::Transform& transform, const ui::Transform& rawTransform) {
     std::vector<PointerProperties> pointerProperties;
     pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER});
     std::vector<PointerCoords> pointerCoords;
@@ -660,8 +659,8 @@
     pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, dy);
     nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
     MotionEvent event;
-    event.initialize(InputEvent::nextId(), /* deviceId */ 1, AINPUT_SOURCE_TOUCHSCREEN,
-                     /* displayId */ 0, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
+    event.initialize(InputEvent::nextId(), /* deviceId */ 1, source,
+                     /* displayId */ 0, INVALID_HMAC, action,
                      /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
                      /* buttonState */ 0, MotionClassification::NONE, transform,
                      /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
@@ -670,6 +669,13 @@
     return event;
 }
 
+MotionEvent createTouchDownEvent(float x, float y, float dx, float dy,
+                                 const ui::Transform& transform,
+                                 const ui::Transform& rawTransform) {
+    return createMotionEvent(AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_DOWN, x, y, dx, dy,
+                             transform, rawTransform);
+}
+
 TEST_F(MotionEventTest, ApplyTransform) {
     // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
     ui::Transform identity;
@@ -708,16 +714,39 @@
                 changedEvent.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0), 0.001);
 }
 
+TEST_F(MotionEventTest, JoystickAndTouchpadAreNotTransformed) {
+    constexpr static std::array kNonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD,
+                                                                    AMOTION_EVENT_ACTION_DOWN),
+                                                          std::pair(AINPUT_SOURCE_JOYSTICK,
+                                                                    AMOTION_EVENT_ACTION_MOVE)};
+    // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+    ui::Transform transform(ui::Transform::ROT_90, 800, 400);
+    transform.set(transform.tx() + 20, transform.ty() + 40);
+
+    for (const auto& [source, action] : kNonTransformedSources) {
+        const MotionEvent event =
+                createMotionEvent(source, action, 60, 100, 0, 0, transform, transform);
+
+        // These events should not be transformed in any way.
+        ASSERT_EQ(60, event.getX(0));
+        ASSERT_EQ(100, event.getY(0));
+        ASSERT_EQ(event.getRawX(0), event.getX(0));
+        ASSERT_EQ(event.getRawY(0), event.getY(0));
+    }
+}
+
 TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) {
-    constexpr static auto NON_POINTER_SOURCES = {AINPUT_SOURCE_TRACKBALL,
-                                                 AINPUT_SOURCE_MOUSE_RELATIVE,
-                                                 AINPUT_SOURCE_JOYSTICK};
-    for (uint32_t source : NON_POINTER_SOURCES) {
-        // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
-        ui::Transform transform(ui::Transform::ROT_90, 800, 400);
-        transform.set(transform.tx() + 20, transform.ty() + 40);
-        MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, transform);
-        event.setSource(source);
+    constexpr static std::array kNonPointerSources = {std::pair(AINPUT_SOURCE_TRACKBALL,
+                                                                AMOTION_EVENT_ACTION_DOWN),
+                                                      std::pair(AINPUT_SOURCE_MOUSE_RELATIVE,
+                                                                AMOTION_EVENT_ACTION_MOVE)};
+    // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+    ui::Transform transform(ui::Transform::ROT_90, 800, 400);
+    transform.set(transform.tx() + 20, transform.ty() + 40);
+
+    for (const auto& [source, action] : kNonPointerSources) {
+        const MotionEvent event =
+                createMotionEvent(source, action, 60, 100, 42, 96, transform, transform);
 
         // Since this event comes from a non-pointer source, it should include rotation but not
         // translation/offset.
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 79d9b93..fc9680b 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -100,17 +100,10 @@
  * Implementation of AChoreographerFrameCallbackData.
  */
 struct ChoreographerFrameCallbackDataImpl {
-    struct FrameTimeline {
-        int64_t vsyncId{0};
-        int64_t expectedPresentTimeNanos{0};
-        int64_t deadlineNanos{0};
-    };
-
     int64_t frameTimeNanos{0};
 
-    size_t frameTimelinesLength;
-
-    std::vector<FrameTimeline> frameTimelines;
+    std::array<VsyncEventData::FrameTimeline, DisplayEventReceiver::kFrameTimelinesLength>
+            frameTimelines;
 
     size_t preferredFrameTimelineIndex;
 
@@ -456,14 +449,9 @@
 }
 
 ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const {
-    std::vector<ChoreographerFrameCallbackDataImpl::FrameTimeline> frameTimelines;
-    frameTimelines.push_back({.vsyncId = mLastVsyncEventData.id,
-                              .expectedPresentTimeNanos = mLastVsyncEventData.expectedPresentTime,
-                              .deadlineNanos = mLastVsyncEventData.deadlineTimestamp});
     return {.frameTimeNanos = timestamp,
-            .frameTimelinesLength = 1,
-            .preferredFrameTimelineIndex = 0,
-            .frameTimelines = frameTimelines,
+            .preferredFrameTimelineIndex = mLastVsyncEventData.preferredFrameTimelineIndex,
+            .frameTimelines = mLastVsyncEventData.frameTimelines,
             .choreographer = this};
 }
 
@@ -646,7 +634,7 @@
             AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
     LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
                         "Data is only valid in callback");
-    return frameCallbackData->frameTimelinesLength;
+    return frameCallbackData->frameTimelines.size();
 }
 size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
         const AChoreographerFrameCallbackData* data) {
@@ -662,8 +650,8 @@
             AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
     LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
                         "Data is only valid in callback");
-    LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds");
-    return frameCallbackData->frameTimelines[index].vsyncId;
+    LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+    return frameCallbackData->frameTimelines[index].id;
 }
 int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(
         const AChoreographerFrameCallbackData* data, size_t index) {
@@ -671,8 +659,8 @@
             AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
     LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
                         "Data is only valid in callback");
-    LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds");
-    return frameCallbackData->frameTimelines[index].expectedPresentTimeNanos;
+    LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+    return frameCallbackData->frameTimelines[index].expectedPresentTime;
 }
 int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline(
         const AChoreographerFrameCallbackData* data, size_t index) {
@@ -680,8 +668,8 @@
             AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
     LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
                         "Data is only valid in callback");
-    LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelinesLength, "Index out of bounds");
-    return frameCallbackData->frameTimelines[index].deadlineNanos;
+    LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+    return frameCallbackData->frameTimelines[index].deadlineTimestamp;
 }
 
 AChoreographer* AChoreographer_create() {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 176cf89..1b19311 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -342,18 +342,6 @@
 std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
                                                    std::shared_ptr<EventEntry> eventEntry,
                                                    int32_t inputTargetFlags) {
-    if (eventEntry->type == EventEntry::Type::MOTION) {
-        const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
-        if ((motionEntry.source & AINPUT_SOURCE_CLASS_JOYSTICK) ||
-            (motionEntry.source & AINPUT_SOURCE_CLASS_POSITION)) {
-            const ui::Transform identityTransform;
-            // Use identity transform for joystick and position-based (touchpad) events because they
-            // don't depend on the window transform.
-            return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, identityTransform,
-                                                   identityTransform, 1.0f /*globalScaleFactor*/);
-        }
-    }
-
     if (inputTarget.useDefaultPointerTransform()) {
         const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
         return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ba0ce95..d8fd16c 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -3012,59 +3012,6 @@
     EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
 }
 
-TEST_F(InputDispatcherTest, NonPointerMotionEvent_JoystickAndTouchpadNotTransformed) {
-    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
-    sp<FakeWindowHandle> window =
-            new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
-    const std::string name = window->getName();
-
-    // Window gets transformed by offset values.
-    window->setWindowOffset(500.0f, 500.0f);
-
-    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
-    window->setFocusable(true);
-
-    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
-
-    // First, we set focused window so that focusedWindowHandle is not null.
-    setFocusedWindow(window);
-
-    // Second, we consume focus event if it is right or wrong according to onFocusChangedLocked.
-    window->consumeFocusEvent(true);
-
-    constexpr const std::array nonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD,
-                                                                  AMOTION_EVENT_ACTION_DOWN),
-                                                        std::pair(AINPUT_SOURCE_JOYSTICK,
-                                                                  AMOTION_EVENT_ACTION_MOVE)};
-    for (const auto& [source, action] : nonTransformedSources) {
-        const NotifyMotionArgs motionArgs = generateMotionArgs(action, source, ADISPLAY_ID_DEFAULT);
-        mDispatcher->notifyMotion(&motionArgs);
-
-        MotionEvent* event = window->consumeMotion();
-        ASSERT_NE(event, nullptr);
-
-        const MotionEvent& motionEvent = *event;
-        EXPECT_EQ(action, motionEvent.getAction());
-        EXPECT_EQ(motionArgs.pointerCount, motionEvent.getPointerCount());
-
-        float expectedX = motionArgs.pointerCoords[0].getX();
-        float expectedY = motionArgs.pointerCoords[0].getY();
-
-        // Ensure the axis values from the final motion event are not transformed.
-        EXPECT_EQ(expectedX, motionEvent.getX(0))
-                << "expected " << expectedX << " for x coord of " << name.c_str() << ", got "
-                << motionEvent.getX(0);
-        EXPECT_EQ(expectedY, motionEvent.getY(0))
-                << "expected " << expectedY << " for y coord of " << name.c_str() << ", got "
-                << motionEvent.getY(0);
-        // Ensure the raw and transformed axis values for the motion event are the same.
-        EXPECT_EQ(motionEvent.getRawX(0), motionEvent.getX(0))
-                << "expected raw and transformed X-axis values to be equal";
-        EXPECT_EQ(motionEvent.getRawY(0), motionEvent.getY(0))
-                << "expected raw and transformed Y-axis values to be equal";
-    }
-}
-
 /**
  * Ensure that separate calls to sign the same data are generating the same key.
  * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 4f0bbd2..b6cbbb6 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -549,13 +549,19 @@
 }
 
 bool BufferStateLayer::setTransactionCompletedListeners(
-        const std::vector<sp<CallbackHandle>>& handles) {
+        const std::vector<ListenerCallbacks>& listenerCallbacks, const sp<IBinder>& layerHandle) {
     // If there is no handle, we will not send a callback so reset mReleasePreviousBuffer and return
-    if (handles.empty()) {
+    if (listenerCallbacks.empty()) {
         mReleasePreviousBuffer = false;
         return false;
     }
 
+    std::vector<sp<CallbackHandle>> handles;
+    handles.reserve(listenerCallbacks.size());
+    for (auto& [listener, callbackIds] : listenerCallbacks) {
+        handles.emplace_back(new CallbackHandle(listener, callbackIds, layerHandle));
+    }
+
     const bool willPresent = willPresentCurrentTransaction();
 
     for (const auto& handle : handles) {
@@ -571,9 +577,10 @@
             // Store so latched time and release fence can be set
             mDrawingState.callbackHandles.push_back(handle);
 
-        } else { // If this layer will NOT need to be relatched and presented this frame
+        } else {
+            // If this layer will NOT need to be relatched and presented this frame
             // Notify the transaction completed thread this handle is done
-            mFlinger->getTransactionCallbackInvoker().registerUnpresentedCallbackHandle(handle);
+            mFlinger->getTransactionCallbackInvoker().addUnpresentedCallbackHandle(handle);
         }
     }
 
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index eea700c..ceed188 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -65,7 +65,8 @@
     bool setSurfaceDamageRegion(const Region& surfaceDamage) override;
     bool setApi(int32_t api) override;
     bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override;
-    bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override;
+    bool setTransactionCompletedListeners(const std::vector<ListenerCallbacks>& handles,
+                                          const sp<IBinder>& layerHandle) override;
     bool addFrameEvent(const sp<Fence>& acquireFence, nsecs_t postedTime,
                        nsecs_t requestedPresentTime) override;
     bool setPosition(float /*x*/, float /*y*/) override;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 42aeca7..d85e843 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1166,9 +1166,6 @@
 }
 
 bool Layer::setFrameRate(FrameRate frameRate) {
-    if (!mFlinger->useFrameRateApi) {
-        return false;
-    }
     if (mDrawingState.frameRate == frameRate) {
         return false;
     }
@@ -2643,6 +2640,17 @@
     return true;
 }
 
+bool Layer::setTransactionCompletedListeners(
+        const std::vector<ListenerCallbacks>& listenerCallbacks, const sp<IBinder>&) {
+    if (listenerCallbacks.empty()) {
+        return false;
+    }
+    for (auto& listener : listenerCallbacks) {
+        mFlinger->getTransactionCallbackInvoker().addEmptyCallback(listener);
+    }
+    return false;
+}
+
 // ---------------------------------------------------------------------------
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 297ded0..b79903d 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -428,10 +428,8 @@
     virtual bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/) { return false; };
     virtual bool setApi(int32_t /*api*/) { return false; };
     virtual bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/) { return false; };
-    virtual bool setTransactionCompletedListeners(
-            const std::vector<sp<CallbackHandle>>& /*handles*/) {
-        return false;
-    };
+    virtual bool setTransactionCompletedListeners(const std::vector<ListenerCallbacks>& /*handles*/,
+                                                  const sp<IBinder>& /* layerHandle */);
     virtual bool addFrameEvent(const sp<Fence>& /*acquireFence*/, nsecs_t /*postedTime*/,
                                nsecs_t /*requestedPresentTime*/) {
         return false;
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 2bdcaf6..455289f 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -355,14 +355,7 @@
     std::lock_guard<std::mutex> lock(mMutex);
 
     LOG_FATAL_IF(!mVSyncState);
-    const int64_t vsyncId = [&] {
-        if (mTokenManager != nullptr) {
-            return mTokenManager->generateTokenForPredictions(
-                    {timestamp, deadlineTimestamp, expectedVSyncTimestamp});
-        }
-        return FrameTimelineInfo::INVALID_VSYNC_ID;
-    }();
-
+    const int64_t vsyncId = generateToken(timestamp, deadlineTimestamp, expectedVSyncTimestamp);
     mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
                                        expectedVSyncTimestamp, deadlineTimestamp, vsyncId));
     mCondition.notify_all();
@@ -567,12 +560,48 @@
     }
 }
 
+int64_t EventThread::generateToken(nsecs_t timestamp, nsecs_t deadlineTimestamp,
+                                   nsecs_t expectedVSyncTimestamp) const {
+    if (mTokenManager != nullptr) {
+        return mTokenManager->generateTokenForPredictions(
+                {timestamp, deadlineTimestamp, expectedVSyncTimestamp});
+    }
+    return FrameTimelineInfo::INVALID_VSYNC_ID;
+}
+
+void EventThread::generateFrameTimeline(DisplayEventReceiver::Event& event) const {
+    // Add 1 to ensure the preferredFrameTimelineIndex entry (when multiplier == 0) is included.
+    for (int multiplier = -DisplayEventReceiver::kFrameTimelinesLength + 1, currentIndex = 0;
+         currentIndex < DisplayEventReceiver::kFrameTimelinesLength; multiplier++) {
+        nsecs_t deadline = event.vsync.deadlineTimestamp + multiplier * event.vsync.frameInterval;
+        // Valid possible frame timelines must have future values.
+        if (deadline > event.header.timestamp) {
+            if (multiplier == 0) {
+                event.vsync.preferredFrameTimelineIndex = currentIndex;
+                event.vsync.frameTimelines[currentIndex] =
+                        {.vsyncId = event.vsync.vsyncId,
+                         .deadlineTimestamp = event.vsync.deadlineTimestamp,
+                         .expectedVSyncTimestamp = event.vsync.expectedVSyncTimestamp};
+            } else {
+                nsecs_t expectedVSync =
+                        event.vsync.expectedVSyncTimestamp + multiplier * event.vsync.frameInterval;
+                event.vsync.frameTimelines[currentIndex] =
+                        {.vsyncId = generateToken(event.header.timestamp, deadline, expectedVSync),
+                         .deadlineTimestamp = deadline,
+                         .expectedVSyncTimestamp = expectedVSync};
+            }
+            currentIndex++;
+        }
+    }
+}
+
 void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
                                 const DisplayEventConsumers& consumers) {
     for (const auto& consumer : consumers) {
         DisplayEventReceiver::Event copy = event;
         if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
             copy.vsync.frameInterval = mGetVsyncPeriodFunction(consumer->mOwnerUid);
+            generateFrameTimeline(copy);
         }
         switch (consumer->postEvent(copy)) {
             case NO_ERROR:
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 9265a25..de43570 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -204,6 +204,10 @@
     void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp,
                       nsecs_t deadlineTimestamp) override;
 
+    int64_t generateToken(nsecs_t timestamp, nsecs_t deadlineTimestamp,
+                          nsecs_t expectedVSyncTimestamp) const;
+    void generateFrameTimeline(DisplayEventReceiver::Event& event) const;
+
     const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
     frametimeline::TokenManager* const mTokenManager;
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index ac04ecd..c2dcd70 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -331,7 +331,6 @@
 ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
 Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
 ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
-bool SurfaceFlinger::useFrameRateApi;
 bool SurfaceFlinger::enableSdrDimming;
 LatchUnsignaledConfig SurfaceFlinger::enableLatchUnsignaledConfig;
 
@@ -486,8 +485,6 @@
         android::hardware::details::setTrebleTestingOverride(true);
     }
 
-    useFrameRateApi = use_frame_rate_api(true);
-
     mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
 
     // Debug property overrides ro. property
@@ -3806,11 +3803,10 @@
         transactionFlags |= setDisplayStateLocked(display);
     }
 
-    // start and end registration for listeners w/ no surface so they can get their callback.  Note
-    // that listeners with SurfaceControls will start registration during setClientStateLocked
-    // below.
+    // Add listeners w/ surfaces so they can get their callback.  Note that listeners with
+    // SurfaceControls will start registration during setClientStateLocked below.
     for (const auto& listener : listenerCallbacks) {
-        mTransactionCallbackInvoker.addEmptyTransaction(listener);
+        mTransactionCallbackInvoker.addEmptyCallback(listener);
     }
 
     uint32_t clientStateFlags = 0;
@@ -3982,7 +3978,7 @@
     }
     if (layer == nullptr) {
         for (auto& [listener, callbackIds] : s.listeners) {
-            mTransactionCallbackInvoker.registerUnpresentedCallbackHandle(
+            mTransactionCallbackInvoker.addUnpresentedCallbackHandle(
                     new CallbackHandle(listener, callbackIds, s.surface));
         }
         return 0;
@@ -4253,12 +4249,6 @@
             flags |= eTransactionNeeded | eTraversalNeeded;
         }
     }
-    std::vector<sp<CallbackHandle>> callbackHandles;
-    if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) {
-        for (auto& [listener, callbackIds] : filteredListeners) {
-            callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
-        }
-    }
 
     if (what & layer_state_t::eBufferChanged &&
         layer->setBuffer(s.bufferData, postTime, desiredPresentTime, isAutoTimestamp,
@@ -4268,7 +4258,11 @@
         layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
     }
 
-    if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
+    if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) {
+        if (layer->setTransactionCompletedListeners(filteredListeners, s.surface)) {
+            flags |= eTraversalNeeded;
+        }
+    }
     // Do not put anything that updates layer state or modifies flags after
     // setTransactionCompletedListener
     return flags;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c05f6cb..bf628dc 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -248,11 +248,6 @@
     static ui::Dataspace wideColorGamutCompositionDataspace;
     static ui::PixelFormat wideColorGamutCompositionPixelFormat;
 
-    // Whether to use frame rate API when deciding about the refresh rate of the display. This
-    // variable is caches in SF, so that we can check it with each layer creation, and a void the
-    // overhead that is caused by reading from sysprop.
-    static bool useFrameRateApi;
-
     static constexpr SkipInitializationTag SkipInitialization;
 
     // Whether or not SDR layers should be dimmed to the desired SDR white point instead of
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index a8117f7..16f6e31 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -304,14 +304,6 @@
     return defaultValue;
 }
 
-bool use_frame_rate_api(bool defaultValue) {
-    auto temp = SurfaceFlingerProperties::use_frame_rate_api();
-    if (temp.has_value()) {
-        return *temp;
-    }
-    return defaultValue;
-}
-
 bool enable_sdr_dimming(bool defaultValue) {
     return SurfaceFlingerProperties::enable_sdr_dimming().value_or(defaultValue);
 }
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index ed18260..8d0e426 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -88,8 +88,6 @@
 
 bool support_kernel_idle_timer(bool defaultValue);
 
-bool use_frame_rate_api(bool defaultValue);
-
 int32_t display_update_imminent_timeout_ms(int32_t defaultValue);
 
 android::ui::DisplayPrimaries getDisplayNativePrimaries();
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index f3d46ea..418fbc5 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -74,10 +74,10 @@
     }
 }
 
-void TransactionCallbackInvoker::addEmptyTransaction(const ListenerCallbacks& listenerCallbacks) {
+void TransactionCallbackInvoker::addEmptyCallback(const ListenerCallbacks& listenerCallbacks) {
     auto& [listener, callbackIds] = listenerCallbacks;
-    auto& transactionStatsDeque = mCompletedTransactions[listener];
-    transactionStatsDeque.emplace_back(callbackIds);
+    TransactionStats* transactionStats;
+    findOrCreateTransactionStats(listener, callbackIds, &transactionStats);
 }
 
 status_t TransactionCallbackInvoker::addOnCommitCallbackHandles(
@@ -116,7 +116,7 @@
     return NO_ERROR;
 }
 
-status_t TransactionCallbackInvoker::registerUnpresentedCallbackHandle(
+status_t TransactionCallbackInvoker::addUnpresentedCallbackHandle(
         const sp<CallbackHandle>& handle) {
     return addCallbackHandle(handle, std::vector<JankData>());
 }
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index e203d41..6f67947 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -71,8 +71,10 @@
 
     // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
     // presented this frame.
-    status_t registerUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
-    void addEmptyTransaction(const ListenerCallbacks& listenerCallbacks);
+    status_t addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
+    // Adds the callback handles for empty transactions or for non-buffer layer updates which do not
+    // include layer stats.
+    void addEmptyCallback(const ListenerCallbacks& listenerCallbacks);
 
     void addPresentFence(const sp<Fence>& presentFence);
 
@@ -81,14 +83,12 @@
         mCompletedTransactions.clear();
     }
 
-    status_t addCallbackHandle(const sp<CallbackHandle>& handle,
-                               const std::vector<JankData>& jankData);
-
-
 private:
     status_t findOrCreateTransactionStats(const sp<IBinder>& listener,
                                           const std::vector<CallbackId>& callbackIds,
                                           TransactionStats** outTransactionStats);
+    status_t addCallbackHandle(const sp<CallbackHandle>& handle,
+                               const std::vector<JankData>& jankData);
 
     std::unordered_map<sp<IBinder>, std::deque<TransactionStats>, IListenerHash>
         mCompletedTransactions;
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 78f8a2f..7702ea2 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -414,16 +414,6 @@
     prop_name: "ro.surface_flinger.supports_background_blur"
 }
 
-# Indicates whether Scheduler should use frame rate API when adjusting the
-# display refresh rate.
-prop {
-    api_name: "use_frame_rate_api"
-    type: Boolean
-    scope: Public
-    access: Readonly
-    prop_name: "ro.surface_flinger.use_frame_rate_api"
-}
-
 # Sets the timeout used to rate limit DISPLAY_UPDATE_IMMINENT Power HAL notifications.
 # SurfaceFlinger wakeups will trigger this boost whenever they are separated by more than this
 # duration (specified in milliseconds). A value of 0 disables the rate limit, and will result in
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 9c567d6..bf1e7e2 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -152,10 +152,6 @@
     prop_name: "ro.surface_flinger.use_context_priority"
   }
   prop {
-    api_name: "use_frame_rate_api"
-    prop_name: "ro.surface_flinger.use_frame_rate_api"
-  }
-  prop {
     api_name: "use_smart_90_for_video"
     prop_name: "ro.surface_flinger.use_smart_90_for_video"
     deprecated: true
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
index ba60a7d..640b9fb 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
@@ -136,10 +136,6 @@
     prop_name: "ro.surface_flinger.use_context_priority"
   }
   prop {
-    api_name: "use_frame_rate_api"
-    prop_name: "ro.surface_flinger.use_frame_rate_api"
-  }
-  prop {
     api_name: "use_smart_90_for_video"
     prop_name: "ro.surface_flinger.use_smart_90_for_video"
     deprecated: true
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 91a5b52..7beba15 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -1067,7 +1067,7 @@
 }
 
 // b202394221
-TEST_F(LayerCallbackTest, DISABLED_NonBufferLayerStateChanges) {
+TEST_F(LayerCallbackTest, NonBufferLayerStateChanges) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createColorLayer("ColorLayer", Color::RED));
 
@@ -1085,4 +1085,47 @@
     EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
 }
 
+class TimedCallbackHelper {
+public:
+    static void function(void* callbackContext, nsecs_t, const sp<Fence>&,
+                         const std::vector<SurfaceControlStats>&) {
+        if (!callbackContext) {
+            ALOGE("failed to get callback context");
+        }
+        TimedCallbackHelper* helper = static_cast<TimedCallbackHelper*>(callbackContext);
+        std::lock_guard lock(helper->mMutex);
+        helper->mInvokedTime = systemTime();
+        helper->mCv.notify_all();
+    }
+
+    void waitForCallback() {
+        std::unique_lock lock(mMutex);
+        ASSERT_TRUE(mCv.wait_for(lock, std::chrono::seconds(3), [&] { return mInvokedTime != -1; }))
+                << "did not receive callback";
+    }
+    void* getContext() { return static_cast<void*>(this); }
+
+    std::mutex mMutex;
+    std::condition_variable mCv;
+    nsecs_t mInvokedTime = -1;
+};
+
+TEST_F(LayerCallbackTest, EmptyTransactionCallbackOrder) {
+    TimedCallbackHelper onCommitCallback;
+    TimedCallbackHelper onCompleteCallback;
+
+    // Add transaction callback before on commit callback
+    Transaction()
+            .addTransactionCompletedCallback(onCompleteCallback.function,
+                                             onCompleteCallback.getContext())
+            .addTransactionCommittedCallback(onCommitCallback.function,
+                                             onCommitCallback.getContext())
+            .apply();
+
+    EXPECT_NO_FATAL_FAILURE(onCompleteCallback.waitForCallback());
+    EXPECT_NO_FATAL_FAILURE(onCommitCallback.waitForCallback());
+    // verify we get the oncomplete at the same time or after the oncommit callback.
+    EXPECT_GE(onCompleteCallback.mInvokedTime, onCommitCallback.mInvokedTime);
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 28d0222..67a0d7e 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -28,6 +28,7 @@
 
 #include "AsyncCallRecorder.h"
 #include "DisplayHardware/DisplayMode.h"
+#include "FrameTimeline.h"
 #include "Scheduler/EventThread.h"
 
 using namespace std::chrono_literals;
@@ -96,6 +97,8 @@
                                               ConnectionEventRecorder& connectionEventRecorder,
                                               nsecs_t expectedTimestamp, unsigned expectedCount);
     void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
+    void expectVsyncEventFrameTimelinesCorrect(nsecs_t expectedTimestamp,
+                                               nsecs_t preferredDeadline);
     void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
                                                 bool expectedConnected);
     void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
@@ -120,6 +123,7 @@
     std::unique_ptr<impl::EventThread> mThread;
     sp<MockEventThreadConnection> mConnection;
     sp<MockEventThreadConnection> mThrottledConnection;
+    std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
 
     static constexpr uid_t mConnectionUid = 443;
     static constexpr uid_t mThrottledConnectionUid = 177;
@@ -173,8 +177,8 @@
         return VSYNC_PERIOD.count();
     };
 
-    mThread = std::make_unique<impl::EventThread>(std::move(source),
-                                                  /*tokenManager=*/nullptr,
+    mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
+    mThread = std::make_unique<impl::EventThread>(std::move(source), mTokenManager.get(),
                                                   mInterceptVSyncCallRecorder.getInvocable(),
                                                   throttleVsync, getVsyncPeriod);
 
@@ -247,6 +251,45 @@
                                          expectedCount);
 }
 
+void EventThreadTest::expectVsyncEventFrameTimelinesCorrect(nsecs_t expectedTimestamp,
+                                                            nsecs_t preferredDeadline) {
+    auto args = mConnectionEventCallRecorder.waitForCall();
+    ASSERT_TRUE(args.has_value()) << " did not receive an event for timestamp "
+                                  << expectedTimestamp;
+    const auto& event = std::get<0>(args.value());
+    for (int i = 0; i < DisplayEventReceiver::kFrameTimelinesLength; i++) {
+        auto prediction =
+                mTokenManager->getPredictionsForToken(event.vsync.frameTimelines[i].vsyncId);
+        EXPECT_TRUE(prediction.has_value());
+        EXPECT_EQ(prediction.value().endTime, event.vsync.frameTimelines[i].deadlineTimestamp)
+                << "Deadline timestamp does not match cached value";
+        EXPECT_EQ(prediction.value().presentTime,
+                  event.vsync.frameTimelines[i].expectedVSyncTimestamp)
+                << "Expected vsync timestamp does not match cached value";
+
+        if (i > 0) {
+            EXPECT_GT(event.vsync.frameTimelines[i].deadlineTimestamp,
+                      event.vsync.frameTimelines[i - 1].deadlineTimestamp)
+                    << "Deadline timestamp out of order for frame timeline " << i;
+            EXPECT_GT(event.vsync.frameTimelines[i].expectedVSyncTimestamp,
+                      event.vsync.frameTimelines[i - 1].expectedVSyncTimestamp)
+                    << "Expected vsync timestamp out of order for frame timeline " << i;
+        }
+        if (event.vsync.frameTimelines[i].deadlineTimestamp == preferredDeadline) {
+            EXPECT_EQ(i, event.vsync.preferredFrameTimelineIndex)
+                    << "Preferred frame timeline index should be " << i;
+            // For the platform-preferred frame timeline, the vsync ID is 0 because the first frame
+            // timeline is made before the rest.
+            EXPECT_EQ(0, event.vsync.frameTimelines[i].vsyncId)
+                    << "Vsync ID incorrect for frame timeline " << i;
+        } else {
+            // Vsync ID 0 is used for the preferred frame timeline.
+            EXPECT_EQ(i + 1, event.vsync.frameTimelines[i].vsyncId)
+                    << "Vsync ID incorrect for frame timeline " << i;
+        }
+    }
+}
+
 void EventThreadTest::expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
                                                              bool expectedConnected) {
     auto args = mConnectionEventCallRecorder.waitForCall();
@@ -344,6 +387,19 @@
     expectVSyncSetEnabledCallReceived(false);
 }
 
+TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesCorrect) {
+    // Signal that we want the next vsync event to be posted to the connection
+    mThread->requestNextVsync(mConnection);
+
+    expectVSyncSetEnabledCallReceived(true);
+
+    // Use the received callback to signal a vsync event.
+    // The interceptor should receive the event, as well as the connection.
+    mCallback->onVSyncEvent(123, 456, 789);
+    expectInterceptCallReceived(123);
+    expectVsyncEventFrameTimelinesCorrect(123, 789);
+}
+
 TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) {
     // Create a first connection, register it, and request a vsync rate of zero.
     ConnectionEventRecorder firstConnectionEventRecorder{0};
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 3b40965..d021178 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -122,8 +122,6 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-    mFlinger.mutableUseFrameRateApi() = true;
-
     setupScheduler();
 
     mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 9832372..8cca6af 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -446,7 +446,6 @@
     auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
     auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
     auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; }
-    auto& mutableUseFrameRateApi() { return mFlinger->useFrameRateApi; }
     auto& mutableActiveDisplayToken() { return mFlinger->mActiveDisplayToken; }
 
     auto fromHandle(const sp<IBinder>& handle) {