SurfaceFlinger: add transactions to FrameTimeline

Allow transaction clients to specify the vsyncId that started the
transaction and plumb this data to FrameTimeline to be able
to track jank associated with transactions

Bug: 166302754
Test: manually see transactions in frame timeline

Change-Id: Id05e0d0a73039204943d93b666cb67e3e7515a69
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 9b6272d..d4a9cc1 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()) {
@@ -464,6 +466,7 @@
     mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd;
     mContainsBuffer = containsBuffer;
     mDesiredPresentTime = desiredPresentTime;
+    mFrameTimelineVsyncId = frameTimelineVsyncId;
     mDisplayStates = displayStates;
     mListenerCallbacks = listenerCallbacks;
     mComposerStates = composerStates;
@@ -493,6 +496,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);
@@ -568,6 +572,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;
 }
@@ -585,6 +598,7 @@
     mExplicitEarlyWakeupStart = false;
     mExplicitEarlyWakeupEnd = false;
     mDesiredPresentTime = -1;
+    mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
 }
 
 void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -595,8 +609,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() {
@@ -727,8 +741,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();
@@ -1517,6 +1531,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 bbe3893..519b26e 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;
 
@@ -538,6 +541,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 ea1f78c..f6c3cef 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 85046a4..193804c 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 88ece50..f903758 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;
     };
 
     /*
@@ -821,7 +831,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.
@@ -1021,12 +1032,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 9bfc63a..b4605aa 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;
 
@@ -3823,6 +3828,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
@@ -4087,7 +4095,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);
@@ -6193,7 +6202,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 0fd17d1..06ea473 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