Merge "SurfaceFlinger: add transactions to FrameTimeline"
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 2f113ee..964195d 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -69,14 +69,15 @@
     }
 
     virtual status_t setTransactionState(
-            const Vector<ComposerState>& state, const Vector<DisplayState>& displays,
-            uint32_t flags, const sp<IBinder>& applyToken, const InputWindowCommands& commands,
-            int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
-            bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
-            uint64_t transactionId) {
+            int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
+            const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+            const InputWindowCommands& commands, int64_t desiredPresentTime,
+            const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+            const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
 
+        SAFE_PARCEL(data.writeInt64, frameTimelineVsyncId);
         SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(state.size()));
         for (const auto& s : state) {
             SAFE_PARCEL(s.write, data);
@@ -1234,6 +1235,8 @@
         case SET_TRANSACTION_STATE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
 
+            int64_t frameTimelineVsyncId;
+            SAFE_PARCEL(data.readInt64, &frameTimelineVsyncId);
             uint32_t count = 0;
             SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
             Vector<ComposerState> state;
@@ -1285,9 +1288,10 @@
             uint64_t transactionId = -1;
             SAFE_PARCEL(data.readUint64, &transactionId);
 
-            return setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands,
-                                       desiredPresentTime, uncachedBuffer, hasListenerCallbacks,
-                                       listenerCallbacks, transactionId);
+            return setTransactionState(frameTimelineVsyncId, state, displays, stateFlags,
+                                       applyToken, inputWindowCommands, desiredPresentTime,
+                                       uncachedBuffer, hasListenerCallbacks, listenerCallbacks,
+                                       transactionId);
         }
         case BOOT_FINISHED: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index ca98843..2f6a34b 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -365,7 +365,8 @@
         mExplicitEarlyWakeupStart(other.mExplicitEarlyWakeupStart),
         mExplicitEarlyWakeupEnd(other.mExplicitEarlyWakeupEnd),
         mContainsBuffer(other.mContainsBuffer),
-        mDesiredPresentTime(other.mDesiredPresentTime) {
+        mDesiredPresentTime(other.mDesiredPresentTime),
+        mFrameTimelineVsyncId(other.mFrameTimelineVsyncId) {
     mDisplayStates = other.mDisplayStates;
     mComposerStates = other.mComposerStates;
     mInputWindowCommands = other.mInputWindowCommands;
@@ -394,6 +395,7 @@
     const bool explicitEarlyWakeupEnd = parcel->readBool();
     const bool containsBuffer = parcel->readBool();
     const int64_t desiredPresentTime = parcel->readInt64();
+    const int64_t frameTimelineVsyncId = parcel->readInt64();
 
     size_t count = static_cast<size_t>(parcel->readUint32());
     if (count > parcel->dataSize()) {
@@ -466,6 +468,7 @@
     mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd;
     mContainsBuffer = containsBuffer;
     mDesiredPresentTime = desiredPresentTime;
+    mFrameTimelineVsyncId = frameTimelineVsyncId;
     mDisplayStates = displayStates;
     mListenerCallbacks = listenerCallbacks;
     mComposerStates = composerStates;
@@ -495,6 +498,7 @@
     parcel->writeBool(mExplicitEarlyWakeupEnd);
     parcel->writeBool(mContainsBuffer);
     parcel->writeInt64(mDesiredPresentTime);
+    parcel->writeInt64(mFrameTimelineVsyncId);
     parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
     for (auto const& displayState : mDisplayStates) {
         displayState.write(*parcel);
@@ -570,6 +574,15 @@
     mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup;
     mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart;
     mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd;
+
+    // When merging vsync Ids we take the oldest one
+    if (mFrameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID &&
+        other.mFrameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID) {
+        mFrameTimelineVsyncId = std::max(mFrameTimelineVsyncId, other.mFrameTimelineVsyncId);
+    } else if (mFrameTimelineVsyncId == ISurfaceComposer::INVALID_VSYNC_ID) {
+        mFrameTimelineVsyncId = other.mFrameTimelineVsyncId;
+    }
+
     other.clear();
     return *this;
 }
@@ -587,6 +600,7 @@
     mExplicitEarlyWakeupStart = false;
     mExplicitEarlyWakeupEnd = false;
     mDesiredPresentTime = -1;
+    mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
 }
 
 void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -597,8 +611,8 @@
     uncacheBuffer.id = cacheId;
 
     sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, false, {},
-                            0 /* Undefined transactionId */);
+    sf->setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, {}, {}, 0, applyToken, {}, -1,
+                            uncacheBuffer, false, {}, 0 /* Undefined transactionId */);
 }
 
 void SurfaceComposerClient::Transaction::cacheBuffers() {
@@ -729,8 +743,8 @@
     mId = generateId();
 
     sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
-                            mDesiredPresentTime,
+    sf->setTransactionState(mFrameTimelineVsyncId, composerStates, displayStates, flags, applyToken,
+                            mInputWindowCommands, mDesiredPresentTime,
                             {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
                             hasListenerCallbacks, listenerCallbacks, transactionId);
     mInputWindowCommands.clear();
@@ -1538,6 +1552,12 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineVsync(
+        int64_t frameTimelineVsyncId) {
+    mFrameTimelineVsyncId = frameTimelineVsyncId;
+    return *this;
+}
+
 // ---------------------------------------------------------------------------
 
 DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 3e3570d..a416147 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -109,6 +109,9 @@
 
     enum ConfigChanged { eConfigChangedSuppress = 0, eConfigChangedDispatch = 1 };
 
+    // Needs to be in sync with android.graphics.FrameInfo.INVALID_VSYNC_ID in java
+    static constexpr int64_t INVALID_VSYNC_ID = -1;
+
     /*
      * Create a connection with SurfaceFlinger.
      */
@@ -153,8 +156,8 @@
 
     /* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
     virtual status_t setTransactionState(
-            const Vector<ComposerState>& state, const Vector<DisplayState>& displays,
-            uint32_t flags, const sp<IBinder>& applyToken,
+            int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
+            const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
             const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
             const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
             const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) = 0;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index a04fbc1..cc82502 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -373,6 +373,9 @@
         // The desired present time does not affect this ordering.
         int64_t mDesiredPresentTime = -1;
 
+        // The vsync Id provided by Choreographer.getVsyncId
+        int64_t mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
+
         InputWindowCommands mInputWindowCommands;
         int mStatus = NO_ERROR;
 
@@ -537,6 +540,10 @@
         // a buffer of a different size.
         Transaction& setFixedTransformHint(const sp<SurfaceControl>& sc, int32_t transformHint);
 
+        // Sets the frame timeline vsync id received from choreographer that corresponds
+        // to the transaction.
+        Transaction& setFrameTimelineVsync(int64_t frameTimelineVsyncId);
+
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
 
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index b72c6b4..5a376da 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -695,7 +695,8 @@
     void destroyDisplay(const sp<IBinder>& /*display */) override {}
     std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override { return {}; }
     sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId) const override { return nullptr; }
-    status_t setTransactionState(const Vector<ComposerState>& /*state*/,
+    status_t setTransactionState(int64_t /*frameTimelineVsyncId*/,
+                                 const Vector<ComposerState>& /*state*/,
                                  const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
                                  const sp<IBinder>& /*applyToken*/,
                                  const InputWindowCommands& /*inputWindowCommands*/,
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 33126ab..a574549 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -381,6 +381,10 @@
     return NO_ERROR;
 }
 
+void BufferQueueLayer::setFrameTimelineVsyncForBuffer(int64_t frameTimelineVsyncId) {
+    mFrameTimelineVsyncId = frameTimelineVsyncId;
+}
+
 // -----------------------------------------------------------------------
 // Interface implementation for BufferLayerConsumer::ContentsChangedListener
 // -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index fc992f7..fb8a0c2 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -103,6 +103,7 @@
 
     status_t updateActiveBuffer() override;
     status_t updateFrameNumber(nsecs_t latchTime) override;
+    void setFrameTimelineVsyncForBuffer(int64_t frameTimelineVsyncId) override;
 
     sp<Layer> createClone() override;
 
@@ -146,6 +147,11 @@
     std::atomic<bool> mSidebandStreamChanged{false};
 
     sp<ContentsChangedListener> mContentsChangedListener;
+
+    // The last vsync id received on this layer. This will be used when we get
+    // a buffer to correlate the buffer with the vsync id. Can only be accessed
+    // with the SF state lock held.
+    std::optional<int64_t> mFrameTimelineVsyncId;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 9f99fee..2033f8e 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -161,6 +161,10 @@
 bool BufferStateLayer::applyPendingStates(Layer::State* stateToCommit) {
     mCurrentStateModified = mCurrentState.modified;
     bool stateUpdateAvailable = Layer::applyPendingStates(stateToCommit);
+    if (stateUpdateAvailable && mCallbackHandleAcquireTime != -1) {
+        // Update the actual end time if we have a buffer
+        mSurfaceFrame->setActualEndTime(mCallbackHandleAcquireTime);
+    }
     mCurrentStateModified = stateUpdateAvailable && mCurrentStateModified;
     mCurrentState.modified = false;
     return stateUpdateAvailable;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index a42c32c..2ca1e23 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -19,6 +19,7 @@
 #include <deque>
 #include <mutex>
 
+#include <gui/ISurfaceComposer.h>
 #include <ui/FenceTime.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
@@ -128,7 +129,7 @@
 
 class TokenManager : public android::frametimeline::TokenManager {
 public:
-    TokenManager() : mCurrentToken(0) {}
+    TokenManager() : mCurrentToken(ISurfaceComposer::INVALID_VSYNC_ID + 1) {}
     ~TokenManager() = default;
 
     int64_t generateTokenForPredictions(TimelineItem&& predictions) override;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 58291d4..df2ef80 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -62,6 +62,7 @@
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWComposer.h"
 #include "EffectLayer.h"
+#include "FrameTimeline.h"
 #include "FrameTracer/FrameTracer.h"
 #include "LayerProtoHelper.h"
 #include "LayerRejecter.h"
@@ -76,6 +77,7 @@
 
 using base::StringAppendF;
 using namespace android::flag_operators;
+using PresentState = frametimeline::SurfaceFrame::PresentState;
 
 std::atomic<int32_t> Layer::sSequence{1};
 
@@ -124,6 +126,8 @@
     mCurrentState.shadowRadius = 0.f;
     mCurrentState.treeHasFrameRateVote = false;
     mCurrentState.fixedTransformHint = ui::Transform::ROT_INVALID;
+    mCurrentState.frameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
+    mCurrentState.postTime = -1;
 
     if (args.flags & ISurfaceComposerClient::eNoColorFill) {
         // Set an invalid color so there is no color fill.
@@ -823,8 +827,8 @@
 
 void Layer::popPendingState(State* stateToCommit) {
     ATRACE_CALL();
-    *stateToCommit = mPendingStates[0];
 
+    *stateToCommit = mPendingStates[0];
     mPendingStates.removeAt(0);
     ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
 }
@@ -881,6 +885,20 @@
         mFlinger->setTraversalNeeded();
     }
 
+    if (stateUpdateAvailable) {
+        const auto vsyncId =
+                stateToCommit->frameTimelineVsyncId == ISurfaceComposer::INVALID_VSYNC_ID
+                ? std::nullopt
+                : std::make_optional(stateToCommit->frameTimelineVsyncId);
+
+        auto surfaceFrame =
+                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mTransactionName, vsyncId);
+        surfaceFrame->setActualQueueTime(stateToCommit->postTime);
+        surfaceFrame->setActualEndTime(stateToCommit->postTime);
+
+        mSurfaceFrame = std::move(surfaceFrame);
+    }
+
     mCurrentState.modified = false;
     return stateUpdateAvailable;
 }
@@ -1018,6 +1036,7 @@
 
 void Layer::commitTransaction(const State& stateToCommit) {
     mDrawingState = stateToCommit;
+    mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mSurfaceFrame), PresentState::Presented);
 }
 
 uint32_t Layer::getTransactionFlags(uint32_t flags) {
@@ -1434,8 +1453,12 @@
     return true;
 }
 
-void Layer::setFrameTimelineVsync(int64_t frameTimelineVsyncId) {
-    mFrameTimelineVsyncId = frameTimelineVsyncId;
+void Layer::setFrameTimelineVsyncForTransaction(int64_t frameTimelineVsyncId, nsecs_t postTime) {
+    mCurrentState.sequence++;
+    mCurrentState.frameTimelineVsyncId = frameTimelineVsyncId;
+    mCurrentState.postTime = postTime;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
 }
 
 Layer::FrameRate Layer::getFrameRateForLayerTree() const {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index bdc4f84..02593d5 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -71,6 +71,10 @@
 class SurfaceInterceptor;
 }
 
+namespace frametimeline {
+class SurfaceFrame;
+} // namespace frametimeline
+
 struct LayerCreationArgs {
     LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t w, uint32_t h,
                       uint32_t flags, LayerMetadata);
@@ -267,6 +271,12 @@
         // a buffer of a different size. ui::Transform::ROT_INVALID means the
         // a fixed transform hint is not set.
         ui::Transform::RotationFlags fixedTransformHint;
+
+        // The vsync id that was used to start the transaction
+        int64_t frameTimelineVsyncId;
+
+        // When the transaction was posted
+        nsecs_t postTime;
     };
 
     /*
@@ -823,7 +833,8 @@
 
     bool setFrameRate(FrameRate);
 
-    void setFrameTimelineVsync(int64_t frameTimelineVsyncId);
+    virtual void setFrameTimelineVsyncForBuffer(int64_t /*frameTimelineVsyncId*/) {}
+    void setFrameTimelineVsyncForTransaction(int64_t frameTimelineVsyncId, nsecs_t postTime);
 
     // Creates a new handle each time, so we only expect
     // this to be called once.
@@ -1023,12 +1034,12 @@
     // Can only be accessed with the SF state lock held.
     bool mChildrenChanged{false};
 
-    // Can only be accessed with the SF state lock held.
-    std::optional<int64_t> mFrameTimelineVsyncId;
-
     // Window types from WindowManager.LayoutParams
     const InputWindowInfo::Type mWindowType;
 
+    // Can only be accessed with the SF state lock held.
+    std::unique_ptr<frametimeline::SurfaceFrame> mSurfaceFrame;
+
 private:
     virtual void setTransformHint(ui::Transform::RotationFlags) {}
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 83fa8ec..152d872 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3195,7 +3195,8 @@
                     break;
                 }
                 transactions.push_back(transaction);
-                applyTransactionState(transaction.states, transaction.displays, transaction.flags,
+                applyTransactionState(transaction.frameTimelineVsyncId, transaction.states,
+                                      transaction.displays, transaction.flags,
                                       mPendingInputWindowCommands, transaction.desiredPresentTime,
                                       transaction.buffer, transaction.postTime,
                                       transaction.privileged, transaction.hasListenerCallbacks,
@@ -3245,9 +3246,10 @@
 }
 
 status_t SurfaceFlinger::setTransactionState(
-        const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags,
-        const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
-        int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+        int64_t frameTimelineVsyncId, const Vector<ComposerState>& states,
+        const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+        const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+        const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
         const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
     ATRACE_CALL();
 
@@ -3285,22 +3287,24 @@
     const int originUid = ipc->getCallingUid();
 
     if (pendingTransactions || !transactionIsReadyToBeApplied(desiredPresentTime, states)) {
-        mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime,
-                                               uncacheBuffer, postTime, privileged,
-                                               hasListenerCallbacks, listenerCallbacks, originPid,
-                                               originUid, transactionId);
+        mTransactionQueues[applyToken].emplace(frameTimelineVsyncId, states, displays, flags,
+                                               desiredPresentTime, uncacheBuffer, postTime,
+                                               privileged, hasListenerCallbacks, listenerCallbacks,
+                                               originPid, originUid, transactionId);
         setTransactionFlags(eTransactionFlushNeeded);
         return NO_ERROR;
     }
 
-    applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime,
-                          uncacheBuffer, postTime, privileged, hasListenerCallbacks,
-                          listenerCallbacks, originPid, originUid, /*isMainThread*/ false);
+    applyTransactionState(frameTimelineVsyncId, states, displays, flags, inputWindowCommands,
+                          desiredPresentTime, uncacheBuffer, postTime, privileged,
+                          hasListenerCallbacks, listenerCallbacks, originPid, originUid,
+                          /*isMainThread*/ false);
     return NO_ERROR;
 }
 
 void SurfaceFlinger::applyTransactionState(
-        const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags,
+        int64_t frameTimelineVsyncId, const Vector<ComposerState>& states,
+        const Vector<DisplayState>& displays, uint32_t flags,
         const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
         const client_cache_t& uncacheBuffer, const int64_t postTime, bool privileged,
         bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
@@ -3338,8 +3342,9 @@
     std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> listenerCallbacksWithSurfaces;
     uint32_t clientStateFlags = 0;
     for (const ComposerState& state : states) {
-        clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged,
-                                                 listenerCallbacksWithSurfaces);
+        clientStateFlags |=
+                setClientStateLocked(frameTimelineVsyncId, state, desiredPresentTime, postTime,
+                                     privileged, listenerCallbacksWithSurfaces);
         if ((flags & eAnimation) && state.state.surface) {
             if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) {
                 mScheduler->recordLayerHistory(layer.get(), desiredPresentTime,
@@ -3516,8 +3521,8 @@
 }
 
 uint32_t SurfaceFlinger::setClientStateLocked(
-        const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime,
-        bool privileged,
+        int64_t frameTimelineVsyncId, const ComposerState& composerState,
+        int64_t desiredPresentTime, int64_t postTime, bool privileged,
         std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) {
     const layer_state_t& s = composerState.state;
 
@@ -3828,6 +3833,9 @@
             flags |= eTraversalNeeded;
         }
     }
+
+    layer->setFrameTimelineVsyncForTransaction(frameTimelineVsyncId, postTime);
+
     if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
     // Do not put anything that updates layer state or modifies flags after
     // setTransactionCompletedListener
@@ -4094,7 +4102,8 @@
     d.width = 0;
     d.height = 0;
     displays.add(d);
-    setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, false, {},
+    setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, state, displays, 0, nullptr,
+                        mPendingInputWindowCommands, -1, {}, false, {},
                         0 /* Undefined transactionId */);
 
     setPowerModeInternal(display, hal::PowerMode::ON);
@@ -6200,7 +6209,7 @@
         return BAD_VALUE;
     }
 
-    layer->setFrameTimelineVsync(frameTimelineVsyncId);
+    layer->setFrameTimelineVsyncForBuffer(frameTimelineVsyncId);
     return NO_ERROR;
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b991b61..9a552d8 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -339,8 +339,8 @@
     virtual ~SurfaceFlinger();
 
     virtual uint32_t setClientStateLocked(
-            const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime,
-            bool privileged,
+            int64_t frameTimelineVsyncId, const ComposerState& composerState,
+            int64_t desiredPresentTime, int64_t postTime, bool privileged,
             std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
             REQUIRES(mStateLock);
     virtual void commitTransactionLocked();
@@ -431,13 +431,14 @@
     };
 
     struct TransactionState {
-        TransactionState(const Vector<ComposerState>& composerStates,
+        TransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& composerStates,
                          const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
                          int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
                          int64_t postTime, bool privileged, bool hasListenerCallbacks,
                          std::vector<ListenerCallbacks> listenerCallbacks, int originPid,
                          int originUid, uint64_t transactionId)
-              : states(composerStates),
+              : frameTimelineVsyncId(frameTimelineVsyncId),
+                states(composerStates),
                 displays(displayStates),
                 flags(transactionFlags),
                 desiredPresentTime(desiredPresentTime),
@@ -450,6 +451,7 @@
                 originUid(originUid),
                 id(transactionId) {}
 
+        int64_t frameTimelineVsyncId;
         Vector<ComposerState> states;
         Vector<DisplayState> displays;
         uint32_t flags;
@@ -510,7 +512,7 @@
     void destroyDisplay(const sp<IBinder>& displayToken) override;
     std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override;
     sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override;
-    status_t setTransactionState(const Vector<ComposerState>& state,
+    status_t setTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
                                  const Vector<DisplayState>& displays, uint32_t flags,
                                  const sp<IBinder>& applyToken,
                                  const InputWindowCommands& inputWindowCommands,
@@ -710,7 +712,7 @@
     /*
      * Transactions
      */
-    void applyTransactionState(const Vector<ComposerState>& state,
+    void applyTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
                                const Vector<DisplayState>& displays, uint32_t flags,
                                const InputWindowCommands& inputWindowCommands,
                                const int64_t desiredPresentTime,
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index 409f90d..2c8178e 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -118,7 +118,11 @@
 }
 
 void RefreshRateSelectionTest::commitTransaction(Layer* layer) {
-    layer->commitTransaction(layer->getCurrentState());
+    layer->pushPendingState();
+    auto c = layer->getCurrentState();
+    if (layer->applyPendingStates(&c)) {
+        layer->commitTransaction(c);
+    }
 }
 
 void RefreshRateSelectionTest::setupScheduler() {
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index d4591fc..efee826 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -157,7 +157,11 @@
 
 void SetFrameRateTest::commitTransaction() {
     for (auto layer : mLayers) {
-        layer.get()->commitTransaction(layer.get()->getCurrentState());
+        layer->pushPendingState();
+        auto c = layer->getCurrentState();
+        if (layer->applyPendingStates(&c)) {
+            layer->commitTransaction(c);
+        }
     }
 }
 
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 37f159c..8c12e94 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -348,7 +348,7 @@
 
     auto& getTransactionQueue() { return mFlinger->mTransactionQueues; }
 
-    auto setTransactionState(const Vector<ComposerState>& states,
+    auto setTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& states,
                              const Vector<DisplayState>& displays, uint32_t flags,
                              const sp<IBinder>& applyToken,
                              const InputWindowCommands& inputWindowCommands,
@@ -356,9 +356,9 @@
                              bool hasListenerCallbacks,
                              std::vector<ListenerCallbacks>& listenerCallbacks,
                              uint64_t transactionId) {
-        return mFlinger->setTransactionState(states, displays, flags, applyToken,
-                                             inputWindowCommands, desiredPresentTime, uncacheBuffer,
-                                             hasListenerCallbacks, listenerCallbacks,
+        return mFlinger->setTransactionState(frameTimelineVsyncId, states, displays, flags,
+                                             applyToken, inputWindowCommands, desiredPresentTime,
+                                             uncacheBuffer, hasListenerCallbacks, listenerCallbacks,
                                              transactionId);
     }
 
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 7151a52..760bf65 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -100,6 +100,7 @@
         sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
         InputWindowCommands inputWindowCommands;
         int64_t desiredPresentTime = -1;
+        int64_t frameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
         client_cache_t uncacheBuffer;
         int64_t id = -1;
     };
@@ -115,11 +116,12 @@
     }
 
     void setupSingle(TransactionInfo& transaction, uint32_t flags, bool syncInputWindows,
-                     int64_t desiredPresentTime) {
+                     int64_t desiredPresentTime, int64_t frameTimelineVsyncId) {
         mTransactionNumber++;
         transaction.flags |= flags; // ISurfaceComposer::eSynchronous;
         transaction.inputWindowCommands.syncInputWindows = syncInputWindows;
         transaction.desiredPresentTime = desiredPresentTime;
+        transaction.frameTimelineVsyncId = frameTimelineVsyncId;
     }
 
     void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
@@ -129,9 +131,10 @@
         EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillOnce(Return(systemTime()));
         TransactionInfo transaction;
         setupSingle(transaction, flags, syncInputWindows,
-                    /*desiredPresentTime*/ -1);
+                    /*desiredPresentTime*/ -1, ISurfaceComposer::INVALID_VSYNC_ID);
         nsecs_t applicationTime = systemTime();
-        mFlinger.setTransactionState(transaction.states, transaction.displays, transaction.flags,
+        mFlinger.setTransactionState(transaction.frameTimelineVsyncId, transaction.states,
+                                     transaction.displays, transaction.flags,
                                      transaction.applyToken, transaction.inputWindowCommands,
                                      transaction.desiredPresentTime, transaction.uncacheBuffer,
                                      mHasListenerCallbacks, mCallbacks, transaction.id);
@@ -163,9 +166,10 @@
                 .WillOnce(Return(time + nsecs_t(5 * 1e8)));
         TransactionInfo transaction;
         setupSingle(transaction, flags, syncInputWindows,
-                    /*desiredPresentTime*/ time + s2ns(1));
+                    /*desiredPresentTime*/ time + s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
         nsecs_t applicationSentTime = systemTime();
-        mFlinger.setTransactionState(transaction.states, transaction.displays, transaction.flags,
+        mFlinger.setTransactionState(transaction.frameTimelineVsyncId, transaction.states,
+                                     transaction.displays, transaction.flags,
                                      transaction.applyToken, transaction.inputWindowCommands,
                                      transaction.desiredPresentTime, transaction.uncacheBuffer,
                                      mHasListenerCallbacks, mCallbacks, transaction.id);
@@ -187,16 +191,17 @@
         // transaction that should go on the pending thread
         TransactionInfo transactionA;
         setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
-                    /*desiredPresentTime*/ time + s2ns(1));
+                    /*desiredPresentTime*/ time + s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
 
         // transaction that would not have gone on the pending thread if not
         // blocked
         TransactionInfo transactionB;
         setupSingle(transactionB, flags, syncInputWindows,
-                    /*desiredPresentTime*/ -1);
+                    /*desiredPresentTime*/ -1, ISurfaceComposer::INVALID_VSYNC_ID);
 
         nsecs_t applicationSentTime = systemTime();
-        mFlinger.setTransactionState(transactionA.states, transactionA.displays, transactionA.flags,
+        mFlinger.setTransactionState(transactionA.frameTimelineVsyncId, transactionA.states,
+                                     transactionA.displays, transactionA.flags,
                                      transactionA.applyToken, transactionA.inputWindowCommands,
                                      transactionA.desiredPresentTime, transactionA.uncacheBuffer,
                                      mHasListenerCallbacks, mCallbacks, transactionA.id);
@@ -207,7 +212,8 @@
         EXPECT_LE(systemTime(), applicationSentTime + s2ns(5));
 
         applicationSentTime = systemTime();
-        mFlinger.setTransactionState(transactionB.states, transactionB.displays, transactionB.flags,
+        mFlinger.setTransactionState(transactionB.frameTimelineVsyncId, transactionB.states,
+                                     transactionB.displays, transactionB.flags,
                                      transactionB.applyToken, transactionB.inputWindowCommands,
                                      transactionB.desiredPresentTime, transactionB.uncacheBuffer,
                                      mHasListenerCallbacks, mCallbacks, transactionB.id);
@@ -252,11 +258,12 @@
             .WillOnce(Return(s2ns(2)));
     TransactionInfo transactionA; // transaction to go on pending queue
     setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
-                /*desiredPresentTime*/ s2ns(1));
-    mFlinger.setTransactionState(transactionA.states, transactionA.displays, transactionA.flags,
-                                 transactionA.applyToken, transactionA.inputWindowCommands,
-                                 transactionA.desiredPresentTime, transactionA.uncacheBuffer,
-                                 mHasListenerCallbacks, mCallbacks, transactionA.id);
+                /*desiredPresentTime*/ s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
+    mFlinger.setTransactionState(transactionA.frameTimelineVsyncId, transactionA.states,
+                                 transactionA.displays, transactionA.flags, transactionA.applyToken,
+                                 transactionA.inputWindowCommands, transactionA.desiredPresentTime,
+                                 transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                 transactionA.id);
 
     auto& transactionQueue = mFlinger.getTransactionQueue();
     ASSERT_EQ(1, transactionQueue.size());
@@ -272,9 +279,10 @@
     // different process) to re-query and reset the cached expected present time
     TransactionInfo empty;
     empty.applyToken = sp<IBinder>();
-    mFlinger.setTransactionState(empty.states, empty.displays, empty.flags, empty.applyToken,
-                                 empty.inputWindowCommands, empty.desiredPresentTime,
-                                 empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks, empty.id);
+    mFlinger.setTransactionState(empty.frameTimelineVsyncId, empty.states, empty.displays,
+                                 empty.flags, empty.applyToken, empty.inputWindowCommands,
+                                 empty.desiredPresentTime, empty.uncacheBuffer,
+                                 mHasListenerCallbacks, mCallbacks, empty.id);
 
     // flush transaction queue should flush as desiredPresentTime has
     // passed