Introduce SurfaceFlinger Queued Transaction

Implements the transaction queue to store the transaction updated from
'setTransactionState', and apply these queued transactions in the main
thread. That would prevent holding the state lock between binder thread
and main thread.

- The setTransactionState won't call 'applyTransactionState' directly.
- The queue is protected by queue lock, apply/get states will still be
  protected by state lock.
- drain and apply transaction queue should be triggered in main thread.
- Sync transaction will wait after the condition broadcast, protected
  by stack lock.

Test: atest libsurfaceflinger_unittest SurfaceFlinger_test libgui_test
Test: atest SurfaceControlTest UiAutomationTest
Test: manual, rotate behavior, seamless rotation, wm-smoke
Bug: 166236811
Bug: 177355824
Change-Id: Ie54a9cc6cdf514df613fba0dff95f6752d1134e2
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a63a3d7..13ac729 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1943,19 +1943,16 @@
 
 bool SurfaceFlinger::handleMessageTransaction() {
     ATRACE_CALL();
+
+    if (getTransactionFlags(eTransactionFlushNeeded)) {
+        flushTransactionQueues();
+    }
     uint32_t transactionFlags = peekTransactionFlags();
-
-    bool flushedATransaction = flushTransactionQueues();
-
     bool runHandleTransaction =
-            (transactionFlags && (transactionFlags != eTransactionFlushNeeded)) ||
-            flushedATransaction ||
-            mForceTraversal;
+            ((transactionFlags & (~eTransactionFlushNeeded)) != 0) || mForceTraversal;
 
     if (runHandleTransaction) {
         handleTransaction(eTransactionMask);
-    } else {
-        getTransactionFlags(eTransactionFlushNeeded);
     }
 
     if (transactionFlushNeeded()) {
@@ -2864,7 +2861,6 @@
         });
     }
 
-    commitInputWindowCommands();
     commitTransaction();
 }
 
@@ -2905,11 +2901,6 @@
                                                                      : nullptr);
 }
 
-void SurfaceFlinger::commitInputWindowCommands() {
-    mInputWindowCommands.merge(mPendingInputWindowCommands);
-    mPendingInputWindowCommands.clear();
-}
-
 void SurfaceFlinger::updateCursorAsync() {
     compositionengine::CompositionRefreshArgs refreshArgs;
     for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
@@ -3272,17 +3263,16 @@
     mForceTraversal = true;
 }
 
-bool SurfaceFlinger::flushTransactionQueues() {
+void SurfaceFlinger::flushTransactionQueues() {
     // to prevent onHandleDestroyed from being called while the lock is held,
     // we must keep a copy of the transactions (specifically the composer
     // states) around outside the scope of the lock
     std::vector<const TransactionState> transactions;
-    bool flushedATransaction = false;
     {
-        Mutex::Autolock _l(mStateLock);
-
-        auto it = mTransactionQueues.begin();
-        while (it != mTransactionQueues.end()) {
+        Mutex::Autolock _l(mQueueLock);
+        // Collect transactions from pending transaction queue.
+        auto it = mPendingTransactionQueues.begin();
+        while (it != mPendingTransactionQueues.end()) {
             auto& [applyToken, transactionQueue] = *it;
 
             while (!transactionQueue.empty()) {
@@ -3294,31 +3284,55 @@
                     break;
                 }
                 transactions.push_back(transaction);
-                applyTransactionState(transaction.frameTimelineInfo, transaction.states,
-                                      transaction.displays, transaction.flags,
-                                      mPendingInputWindowCommands, transaction.desiredPresentTime,
-                                      transaction.isAutoTimestamp, transaction.buffer,
-                                      transaction.postTime, transaction.privileged,
-                                      transaction.hasListenerCallbacks,
-                                      transaction.listenerCallbacks, transaction.originPid,
-                                      transaction.originUid, transaction.id, /*isMainThread*/ true);
                 transactionQueue.pop();
-                flushedATransaction = true;
             }
 
             if (transactionQueue.empty()) {
-                it = mTransactionQueues.erase(it);
-                mTransactionCV.broadcast();
+                it = mPendingTransactionQueues.erase(it);
+                mTransactionQueueCV.broadcast();
             } else {
                 it = std::next(it, 1);
             }
         }
+
+        // Collect transactions from current transaction queue or queue to pending transactions.
+        // Case 1: push to pending when transactionIsReadyToBeApplied is false.
+        // Case 2: push to pending when there exist a pending queue.
+        // Case 3: others are ready to apply.
+        while (!mTransactionQueue.empty()) {
+            const auto& transaction = mTransactionQueue.front();
+            bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) !=
+                    mPendingTransactionQueues.end();
+            // Call transactionIsReadyToBeApplied first in case we need to
+            // incrementPendingBufferCount if the transaction contains a buffer.
+            if (!transactionIsReadyToBeApplied(transaction.isAutoTimestamp,
+                                               transaction.desiredPresentTime, transaction.states,
+                                               true) ||
+                pendingTransactions) {
+                mPendingTransactionQueues[transaction.applyToken].push(transaction);
+            } else {
+                transactions.push_back(transaction);
+            }
+            mTransactionQueue.pop();
+        }
     }
-    return flushedATransaction;
+
+    // Now apply all transactions.
+    Mutex::Autolock _l(mStateLock);
+    for (const auto& transaction : transactions) {
+        applyTransactionState(transaction.frameTimelineInfo, transaction.states,
+                              transaction.displays, transaction.flags,
+                              transaction.inputWindowCommands, transaction.desiredPresentTime,
+                              transaction.isAutoTimestamp, transaction.buffer, transaction.postTime,
+                              transaction.privileged, transaction.hasListenerCallbacks,
+                              transaction.listenerCallbacks, transaction.originPid,
+                              transaction.originUid, transaction.id);
+    }
 }
 
 bool SurfaceFlinger::transactionFlushNeeded() {
-    return !mTransactionQueues.empty();
+    Mutex::Autolock _l(mQueueLock);
+    return !mPendingTransactionQueues.empty();
 }
 
 bool SurfaceFlinger::transactionIsReadyToBeApplied(bool isAutoTimestamp, int64_t desiredPresentTime,
@@ -3339,8 +3353,10 @@
             continue;
         }
         if (s.acquireFence && s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
-          ready = false;
+            ready = false;
         }
+
+        Mutex::Autolock _l(mStateLock);
         sp<Layer> layer = nullptr;
         if (s.surface) {
             layer = fromHandleLocked(s.surface).promote();
@@ -3381,94 +3397,125 @@
         const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
     ATRACE_CALL();
 
-    const int64_t postTime = systemTime();
+    {
+        Mutex::Autolock _l(mQueueLock);
 
-    bool privileged = callingThreadHasUnscopedSurfaceFlingerAccess();
+        const int64_t postTime = systemTime();
+        bool privileged = callingThreadHasUnscopedSurfaceFlingerAccess();
 
-    Mutex::Autolock _l(mStateLock);
+        IPCThreadState* ipc = IPCThreadState::self();
+        const int originPid = ipc->getCallingPid();
+        const int originUid = ipc->getCallingUid();
 
-    // If its TransactionQueue already has a pending TransactionState or if it is pending
-    auto itr = mTransactionQueues.find(applyToken);
-    // if this is an animation frame, wait until prior animation frame has
-    // been applied by SF
-    if (flags & eAnimation) {
-        while (itr != mTransactionQueues.end()) {
-            status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
-            if (CC_UNLIKELY(err != NO_ERROR)) {
-                ALOGW_IF(err == TIMED_OUT,
-                         "setTransactionState timed out "
-                         "waiting for animation frame to apply");
-                break;
+        // If its TransactionQueue already has a pending TransactionState or if it is pending
+        auto itr = mPendingTransactionQueues.find(applyToken);
+        // if this is an animation frame, wait until prior animation frame has
+        // been applied by SF
+        if (flags & eAnimation) {
+            while (itr != mPendingTransactionQueues.end()) {
+                status_t err = mTransactionQueueCV.waitRelative(mQueueLock, s2ns(5));
+                if (CC_UNLIKELY(err != NO_ERROR)) {
+                    ALOGW_IF(err == TIMED_OUT,
+                             "setTransactionState timed out "
+                             "waiting for animation frame to apply");
+                    break;
+                }
+                itr = mPendingTransactionQueues.find(applyToken);
             }
-            itr = mTransactionQueues.find(applyToken);
         }
+
+        const bool pendingTransactions = itr != mPendingTransactionQueues.end();
+        // Expected present time is computed and cached on invalidate, so it may be stale.
+        if (!pendingTransactions) {
+            const auto now = systemTime();
+            const bool nextVsyncPending = now < mExpectedPresentTime.load();
+            const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(now);
+            mExpectedPresentTime = calculateExpectedPresentTime(stats);
+            // The transaction might arrive just before the next vsync but after
+            // invalidate was called. In that case we need to get the next vsync
+            // afterwards.
+            if (nextVsyncPending) {
+                mExpectedPresentTime += stats.vsyncPeriod;
+            }
+        }
+
+        mTransactionQueue.emplace(frameTimelineInfo, states, displays, flags, applyToken,
+                                  inputWindowCommands, desiredPresentTime, isAutoTimestamp,
+                                  uncacheBuffer, postTime, privileged, hasListenerCallbacks,
+                                  listenerCallbacks, originPid, originUid, transactionId);
+
+        if (pendingTransactions ||
+            (!isAutoTimestamp && desiredPresentTime > mExpectedPresentTime.load())) {
+            setTransactionFlags(eTransactionFlushNeeded);
+            return NO_ERROR;
+        }
+
+        // TODO(b/159125966): Remove eEarlyWakeup completely as no client should use this flag
+        if (flags & eEarlyWakeup) {
+            ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
+        }
+
+        if (!privileged && (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
+            ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
+            flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
+        }
+
+        const auto schedule = [](uint32_t flags) {
+            if (flags & eEarlyWakeup) return TransactionSchedule::Early;
+            if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
+            if (flags & eExplicitEarlyWakeupStart) return TransactionSchedule::EarlyStart;
+            return TransactionSchedule::Late;
+        }(flags);
+        setTransactionFlags(eTransactionFlushNeeded, schedule);
     }
 
-    const bool pendingTransactions = itr != mTransactionQueues.end();
-    // Expected present time is computed and cached on invalidate, so it may be stale.
-    if (!pendingTransactions) {
-        const auto now = systemTime();
-        const bool nextVsyncPending = now < mExpectedPresentTime.load();
-        const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(now);
-        mExpectedPresentTime = calculateExpectedPresentTime(stats);
-        // The transaction might arrive just before the next vsync but after
-        // invalidate was called. In that case we need to get the next vsync
-        // afterwards.
-        if (nextVsyncPending) {
-            mExpectedPresentTime += stats.vsyncPeriod;
-        }
-    }
-
-    IPCThreadState* ipc = IPCThreadState::self();
-    const int originPid = ipc->getCallingPid();
-    const int originUid = ipc->getCallingUid();
-
-    // Call transactionIsReadyToBeApplied first in case we need to incrementPendingBufferCount
-    // if the transaction contains a buffer.
-    if (!transactionIsReadyToBeApplied(isAutoTimestamp, desiredPresentTime, states, true) ||
-        pendingTransactions) {
-        mTransactionQueues[applyToken].emplace(frameTimelineInfo, states, displays, flags,
-                                               desiredPresentTime, isAutoTimestamp, uncacheBuffer,
-                                               postTime, privileged, hasListenerCallbacks,
-                                               listenerCallbacks, originPid, originUid,
-                                               transactionId);
-        setTransactionFlags(eTransactionFlushNeeded);
+    // if this is a synchronous transaction, wait for it to take effect
+    // before returning.
+    const bool synchronous = flags & eSynchronous;
+    const bool syncInput = inputWindowCommands.syncInputWindows;
+    if (!synchronous && !syncInput) {
         return NO_ERROR;
     }
 
-    applyTransactionState(frameTimelineInfo, states, displays, flags, inputWindowCommands,
-                          desiredPresentTime, isAutoTimestamp, uncacheBuffer, postTime, privileged,
-                          hasListenerCallbacks, listenerCallbacks, originPid, originUid,
-                          transactionId, /*isMainThread*/ false);
+    Mutex::Autolock _l(mStateLock);
+    if (synchronous) {
+        mTransactionPending = true;
+    }
+    if (syncInput) {
+        mPendingSyncInputWindows = true;
+    }
+
+    // applyTransactionState can be called by either the main SF thread or by
+    // another process through setTransactionState.  While a given process may wish
+    // to wait on synchronous transactions, the main SF thread should never
+    // be blocked.  Therefore, we only wait if isMainThread is false.
+    while (mTransactionPending || mPendingSyncInputWindows) {
+        status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
+        if (CC_UNLIKELY(err != NO_ERROR)) {
+            // just in case something goes wrong in SF, return to the
+            // called after a few seconds.
+            ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out!");
+            mTransactionPending = false;
+            mPendingSyncInputWindows = false;
+            break;
+        }
+    }
+
     return NO_ERROR;
 }
 
-void SurfaceFlinger::applyTransactionState(
-        const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
-        const Vector<DisplayState>& displays, uint32_t flags,
-        const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
-        bool isAutoTimestamp, const client_cache_t& uncacheBuffer, const int64_t postTime,
-        bool privileged, bool hasListenerCallbacks,
-        const std::vector<ListenerCallbacks>& listenerCallbacks, int originPid, int originUid,
-        uint64_t transactionId, bool isMainThread) {
+void SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
+                                           const Vector<ComposerState>& states,
+                                           const Vector<DisplayState>& displays, uint32_t flags,
+                                           const InputWindowCommands& inputWindowCommands,
+                                           const int64_t desiredPresentTime, bool isAutoTimestamp,
+                                           const client_cache_t& uncacheBuffer,
+                                           const int64_t postTime, bool privileged,
+                                           bool hasListenerCallbacks,
+                                           const std::vector<ListenerCallbacks>& listenerCallbacks,
+                                           int originPid, int originUid, uint64_t transactionId) {
     uint32_t transactionFlags = 0;
 
-    if (flags & eAnimation) {
-        // For window updates that are part of an animation we must wait for
-        // previous animation "frames" to be handled.
-        while (!isMainThread && mAnimTransactionPending) {
-            status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
-            if (CC_UNLIKELY(err != NO_ERROR)) {
-                // just in case something goes wrong in SF, return to the
-                // caller after a few seconds.
-                ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out "
-                        "waiting for previous animation frame");
-                mAnimTransactionPending = false;
-                break;
-            }
-        }
-    }
-
     for (const DisplayState& display : displays) {
         transactionFlags |= setDisplayStateLocked(display);
     }
@@ -3527,80 +3574,25 @@
         transactionFlags = eTransactionNeeded;
     }
 
-    // If we are on the main thread, we are about to preform a traversal. Clear the traversal bit
-    // so we don't have to wake up again next frame to preform an uneeded traversal.
-    if (isMainThread && (transactionFlags & eTraversalNeeded)) {
-        transactionFlags = transactionFlags & (~eTraversalNeeded);
-        mForceTraversal = true;
-    }
-
-    const auto schedule = [](uint32_t flags) {
-        if (flags & eEarlyWakeup) return TransactionSchedule::Early;
-        if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
-        if (flags & eExplicitEarlyWakeupStart) return TransactionSchedule::EarlyStart;
-        return TransactionSchedule::Late;
-    }(flags);
-
     if (transactionFlags) {
         if (mInterceptor->isEnabled()) {
             mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags,
                                           originPid, originUid, transactionId);
         }
 
-        // TODO(b/159125966): Remove eEarlyWakeup completly as no client should use this flag
-        if (flags & eEarlyWakeup) {
-            ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
+        // We are on the main thread, we are about to preform a traversal. Clear the traversal bit
+        // so we don't have to wake up again next frame to preform an unnecessary traversal.
+        if (transactionFlags & eTraversalNeeded) {
+            transactionFlags = transactionFlags & (~eTraversalNeeded);
+            mForceTraversal = true;
         }
-
-        if (!privileged && (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
-            ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
-            flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
+        if (transactionFlags) {
+            setTransactionFlags(transactionFlags);
         }
 
-        // this triggers the transaction
-        setTransactionFlags(transactionFlags, schedule);
-
         if (flags & eAnimation) {
             mAnimTransactionPending = true;
         }
-
-        // if this is a synchronous transaction, wait for it to take effect
-        // before returning.
-        const bool synchronous = flags & eSynchronous;
-        const bool syncInput = inputWindowCommands.syncInputWindows;
-        if (!synchronous && !syncInput) {
-            return;
-        }
-
-        if (synchronous) {
-            mTransactionPending = true;
-        }
-        if (syncInput) {
-            mPendingSyncInputWindows = true;
-        }
-
-
-        // applyTransactionState can be called by either the main SF thread or by
-        // another process through setTransactionState.  While a given process may wish
-        // to wait on synchronous transactions, the main SF thread should never
-        // be blocked.  Therefore, we only wait if isMainThread is false.
-        while (!isMainThread && (mTransactionPending || mPendingSyncInputWindows)) {
-            status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
-            if (CC_UNLIKELY(err != NO_ERROR)) {
-                // just in case something goes wrong in SF, return to the
-                // called after a few seconds.
-                ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out!");
-                mTransactionPending = false;
-                mPendingSyncInputWindows = false;
-                break;
-            }
-        }
-    } else {
-        // Update VsyncModulator state machine even if transaction is not needed.
-        if (schedule == TransactionSchedule::EarlyStart ||
-            schedule == TransactionSchedule::EarlyEnd) {
-            modulateVsync(&VsyncModulator::setTransactionSchedule, schedule);
-        }
     }
 }
 
@@ -3999,7 +3991,7 @@
 }
 
 uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& inputWindowCommands) {
-    bool hasChanges = mPendingInputWindowCommands.merge(inputWindowCommands);
+    bool hasChanges = mInputWindowCommands.merge(inputWindowCommands);
     return hasChanges ? eTraversalNeeded : 0;
 }
 
@@ -4264,9 +4256,11 @@
     d.width = 0;
     d.height = 0;
     displays.add(d);
-    setTransactionState(FrameTimelineInfo{}, state, displays, 0, nullptr,
-                        mPendingInputWindowCommands, systemTime(), true, {}, false, {},
-                        0 /* Undefined transactionId */);
+
+    // It should be on the main thread, apply it directly.
+    applyTransactionState(FrameTimelineInfo{}, state, displays, 0, mInputWindowCommands,
+                          systemTime(), true, {}, systemTime(), true, false, {}, getpid(), getuid(),
+                          0 /* Undefined transactionId */);
 
     setPowerModeInternal(display, hal::PowerMode::ON);
     const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ec73b09..b519dcf 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -438,15 +438,18 @@
         TransactionState(const FrameTimelineInfo& frameTimelineInfo,
                          const Vector<ComposerState>& composerStates,
                          const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
-                         int64_t desiredPresentTime, bool isAutoTimestamp,
-                         const client_cache_t& uncacheBuffer, int64_t postTime, bool privileged,
-                         bool hasListenerCallbacks,
+                         const sp<IBinder>& applyToken,
+                         const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+                         bool isAutoTimestamp, const client_cache_t& uncacheBuffer,
+                         int64_t postTime, bool privileged, bool hasListenerCallbacks,
                          std::vector<ListenerCallbacks> listenerCallbacks, int originPid,
                          int originUid, uint64_t transactionId)
               : frameTimelineInfo(frameTimelineInfo),
                 states(composerStates),
                 displays(displayStates),
                 flags(transactionFlags),
+                applyToken(applyToken),
+                inputWindowCommands(inputWindowCommands),
                 desiredPresentTime(desiredPresentTime),
                 isAutoTimestamp(isAutoTimestamp),
                 buffer(uncacheBuffer),
@@ -462,6 +465,8 @@
         Vector<ComposerState> states;
         Vector<DisplayState> displays;
         uint32_t flags;
+        sp<IBinder> applyToken;
+        InputWindowCommands inputWindowCommands;
         const int64_t desiredPresentTime;
         const bool isAutoTimestamp;
         client_cache_t buffer;
@@ -736,10 +741,10 @@
                                const client_cache_t& uncacheBuffer, const int64_t postTime,
                                bool privileged, bool hasListenerCallbacks,
                                const std::vector<ListenerCallbacks>& listenerCallbacks,
-                               int originPid, int originUid, uint64_t transactionId,
-                               bool isMainThread = false) REQUIRES(mStateLock);
-    // Returns true if at least one transaction was flushed
-    bool flushTransactionQueues();
+                               int originPid, int originUid, uint64_t transactionId)
+            REQUIRES(mStateLock);
+    // flush pending transaction that was presented after desiredPresentTime.
+    void flushTransactionQueues();
     // Returns true if there is at least one transaction that needs to be flushed
     bool transactionFlushNeeded();
     uint32_t getTransactionFlags(uint32_t flags);
@@ -757,7 +762,7 @@
     void commitOffscreenLayers();
     bool transactionIsReadyToBeApplied(bool isAutoTimestamp, int64_t desiredPresentTime,
                                        const Vector<ComposerState>& states,
-                                       bool updateTransactionCounters = false) REQUIRES(mStateLock);
+                                       bool updateTransactionCounters = false);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
             REQUIRES(mStateLock);
@@ -1177,8 +1182,11 @@
     uint32_t mTexturePoolSize = 0;
     std::vector<uint32_t> mTexturePool;
 
-    std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> mTransactionQueues;
-
+    mutable Mutex mQueueLock;
+    Condition mTransactionQueueCV;
+    std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
+            mPendingTransactionQueues GUARDED_BY(mQueueLock);
+    std::queue<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock);
     /*
      * Feature prototyping
      */
@@ -1256,7 +1264,6 @@
     const float mEmulatedDisplayDensity;
 
     sp<os::IInputFlinger> mInputFlinger;
-    InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock);
     // Should only be accessed by the main thread.
     InputWindowCommands mInputWindowCommands;
 
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index a361b1e..33823d7 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -57,6 +57,8 @@
                                                       // Sample usage bits from screenrecord
                                                       GRALLOC_USAGE_HW_VIDEO_ENCODER |
                                                               GRALLOC_USAGE_SW_READ_OFTEN);
+                sp<BufferListener> listener = new BufferListener(this);
+                itemConsumer->setFrameAvailableListener(listener);
 
                 vDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"),
                                                                 false /*secure*/);
@@ -68,6 +70,13 @@
                                        Rect(displayState.layerStackSpaceRect), Rect(resolution));
                 t.apply();
                 SurfaceComposerClient::Transaction().apply(true);
+
+                std::unique_lock lock(mMutex);
+                mAvailable = false;
+                // Wait for frame buffer ready.
+                mCondition.wait_for(lock, std::chrono::seconds(2),
+                                    [this]() NO_THREAD_SAFETY_ANALYSIS { return mAvailable; });
+
                 BufferItem item;
                 itemConsumer->acquireBuffer(&item, 0, true);
                 auto sc = std::make_unique<ScreenCapture>(item.mGraphicBuffer);
@@ -80,6 +89,23 @@
 protected:
     LayerTransactionTest* mDelegate;
     RenderPath mRenderPath;
+    std::mutex mMutex;
+    std::condition_variable mCondition;
+    bool mAvailable = false;
+
+    void onFrameAvailable() {
+        std::unique_lock lock(mMutex);
+        mAvailable = true;
+        mCondition.notify_all();
+    }
+
+    class BufferListener : public ConsumerBase::FrameAvailableListener {
+    public:
+        BufferListener(LayerRenderPathTestHarness* owner) : mOwner(owner) {}
+        LayerRenderPathTestHarness* mOwner;
+
+        void onFrameAvailable(const BufferItem& /*item*/) { mOwner->onFrameAvailable(); }
+    };
 };
 
 class LayerTypeTransactionHarness : public LayerTransactionTest {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 3be45e2..6b5109f 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -367,7 +367,8 @@
         return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
     }
 
-    auto& getTransactionQueue() { return mFlinger->mTransactionQueues; }
+    auto& getTransactionQueue() { return mFlinger->mTransactionQueue; }
+    auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
 
     auto setTransactionState(
             const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 6d2f672..0ac5845 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -140,7 +140,6 @@
                                      transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
                                      transaction.id);
 
-        // This transaction should not have been placed on the transaction queue.
         // If transaction is synchronous or syncs input windows, SF
         // applyTransactionState should time out (5s) wating for SF to commit
         // the transaction or to receive a signal that syncInputWindows has
@@ -151,8 +150,9 @@
         } else {
             EXPECT_LE(returnedTime, applicationTime + s2ns(5));
         }
+        // Each transaction should have been placed on the transaction queue
         auto transactionQueue = mFlinger.getTransactionQueue();
-        EXPECT_EQ(0u, transactionQueue.size());
+        EXPECT_EQ(1u, transactionQueue.size());
     }
 
     void PlaceOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
@@ -214,6 +214,8 @@
         // (5s is the timeout period that applyTransactionState waits for SF to
         // commit the transaction)
         EXPECT_LE(systemTime(), applicationSentTime + s2ns(5));
+        // transaction that would goes to pending transaciton queue.
+        mFlinger.flushTransactionQueues();
 
         applicationSentTime = systemTime();
         mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
@@ -233,8 +235,11 @@
             EXPECT_LE(systemTime(), applicationSentTime + s2ns(5));
         }
 
+        // transaction that would goes to pending transaciton queue.
+        mFlinger.flushTransactionQueues();
+
         // check that there is one binder on the pending queue.
-        auto transactionQueue = mFlinger.getTransactionQueue();
+        auto transactionQueue = mFlinger.getPendingTransactionQueue();
         EXPECT_EQ(1u, transactionQueue.size());
 
         auto& [applyToken, transactionStates] = *(transactionQueue.begin());
@@ -273,10 +278,7 @@
     auto& transactionQueue = mFlinger.getTransactionQueue();
     ASSERT_EQ(1u, transactionQueue.size());
 
-    auto& [applyToken, transactionStates] = *(transactionQueue.begin());
-    ASSERT_EQ(1u, transactionStates.size());
-
-    auto& transactionState = transactionStates.front();
+    auto& transactionState = transactionQueue.front();
     checkEqual(transactionA, transactionState);
 
     // because flushing uses the cached expected present time, we send an empty
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index a13f93b..2fefa45 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -84,11 +84,13 @@
     }
 
     void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
+        ASSERT_NE(nullptr, mOutBuffer);
         ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
         TransactionUtils::expectBufferColor(mOutBuffer, mPixels, rect, color, tolerance);
     }
 
     void expectBorder(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
+        ASSERT_NE(nullptr, mOutBuffer);
         ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
         const bool leftBorder = rect.left > 0;
         const bool topBorder = rect.top > 0;
@@ -146,6 +148,7 @@
     }
 
     void checkPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) {
+        ASSERT_NE(nullptr, mOutBuffer);
         ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
         const uint8_t* pixel = mPixels + (4 * (y * mOutBuffer->getStride() + x));
         if (r != pixel[0] || g != pixel[1] || b != pixel[2]) {
@@ -163,10 +166,14 @@
     void expectChildColor(uint32_t x, uint32_t y) { checkPixel(x, y, 200, 200, 200); }
 
     explicit ScreenCapture(const sp<GraphicBuffer>& outBuffer) : mOutBuffer(outBuffer) {
-        mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels));
+        if (mOutBuffer) {
+            mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels));
+        }
     }
 
-    ~ScreenCapture() { mOutBuffer->unlock(); }
+    ~ScreenCapture() {
+        if (mOutBuffer) mOutBuffer->unlock();
+    }
 
 private:
     sp<GraphicBuffer> mOutBuffer;