Latch Unsignaled when only a single layer is being updated.

Three conditions are introduced.
DISABLED (Default for now): This is when latch unsignaled is completely disabled.
AUTO: This is when we will latch for the single layer update. Further refinements will be done in b/200284381
ALWAYS: This will latch unsignaled no matter what the change is.

BUG: 198189193
Test: Did the manual test and
atest libsurfaceflinger_unittest
atest MockFence_test
atest libgui_test

Change-Id: I0c0b475ba4a093275fac23a986fc610ea462f73e
diff --git a/libs/ui/include/ui/Fence.h b/libs/ui/include/ui/Fence.h
index 7634007..9aae145 100644
--- a/libs/ui/include/ui/Fence.h
+++ b/libs/ui/include/ui/Fence.h
@@ -124,7 +124,7 @@
     // getStatus() returns whether the fence has signaled yet. Prefer this to
     // getSignalTime() or wait() if all you care about is whether the fence has
     // signaled.
-    inline Status getStatus() {
+    virtual inline Status getStatus() {
         // The sync_wait call underlying wait() has been measured to be
         // significantly faster than the sync_fence_info call underlying
         // getSignalTime(), which might otherwise appear to be the more obvious
diff --git a/libs/ui/include_mock/ui/MockFence.h b/libs/ui/include_mock/ui/MockFence.h
index 162ec02..71adee4 100644
--- a/libs/ui/include_mock/ui/MockFence.h
+++ b/libs/ui/include_mock/ui/MockFence.h
@@ -27,6 +27,7 @@
     virtual ~MockFence() = default;
 
     MOCK_METHOD(nsecs_t, getSignalTime, (), (const, override));
+    MOCK_METHOD(Status, getStatus, (), (override));
 };
 
 }; // namespace android::mock
diff --git a/libs/ui/tests/MockFence_test.cpp b/libs/ui/tests/MockFence_test.cpp
index 6e520b1..40dddc3 100644
--- a/libs/ui/tests/MockFence_test.cpp
+++ b/libs/ui/tests/MockFence_test.cpp
@@ -42,4 +42,16 @@
     EXPECT_EQ(1234, fence->getSignalTime());
 }
 
+TEST_F(MockFenceTest, getStatus) {
+    sp<Fence> fence = getFenceForTesting();
+
+    EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Unsignaled));
+    EXPECT_EQ(Fence::Status::Unsignaled, fence->getStatus());
+
+    EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Signaled));
+    EXPECT_EQ(Fence::Status::Signaled, fence->getStatus());
+
+    EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Invalid));
+    EXPECT_EQ(Fence::Status::Invalid, fence->getStatus());
+}
 } // namespace android::ui
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 4e5d2d0..8aecec1 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -118,7 +118,7 @@
 bool BufferQueueLayer::fenceHasSignaled() const {
     Mutex::Autolock lock(mQueueItemLock);
 
-    if (SurfaceFlinger::enableLatchUnsignaled) {
+    if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
         return true;
     }
 
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index c0753f9..f7f96ab 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -630,7 +630,7 @@
 // Interface implementation for BufferLayer
 // -----------------------------------------------------------------------
 bool BufferStateLayer::fenceHasSignaled() const {
-    if (SurfaceFlinger::enableLatchUnsignaled) {
+    if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
         return true;
     }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 8d7221c..9b16774 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -341,7 +341,7 @@
 ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
 bool SurfaceFlinger::useFrameRateApi;
 bool SurfaceFlinger::enableSdrDimming;
-bool SurfaceFlinger::enableLatchUnsignaled;
+LatchUnsignaledConfig SurfaceFlinger::enableLatchUnsignaledConfig;
 
 std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
     switch(displayColorSetting) {
@@ -501,7 +501,17 @@
     // Debug property overrides ro. property
     enableSdrDimming = property_get_bool("debug.sf.enable_sdr_dimming", enable_sdr_dimming(false));
 
-    enableLatchUnsignaled = base::GetBoolProperty("debug.sf.latch_unsignaled"s, false);
+    enableLatchUnsignaledConfig = getLatchUnsignaledConfig();
+}
+
+LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
+    if (base::GetBoolProperty("debug.sf.latch_unsignaled"s, false)) {
+        return LatchUnsignaledConfig::Always;
+    } else if (base::GetBoolProperty("debug.sf.auto_latch_unsignaled"s, false)) {
+        return LatchUnsignaledConfig::Auto;
+    } else {
+        return LatchUnsignaledConfig::Disabled;
+    }
 }
 
 SurfaceFlinger::~SurfaceFlinger() = default;
@@ -3421,29 +3431,34 @@
 }
 
 bool SurfaceFlinger::flushTransactionQueues() {
-    bool needsTraversal = false;
     // 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;
+    std::vector<TransactionState> transactions;
     // Layer handles that have transactions with buffers that are ready to be applied.
     std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> bufferLayersReadyToPresent;
     {
         Mutex::Autolock _l(mStateLock);
         {
             Mutex::Autolock _l(mQueueLock);
+            // allowLatchUnsignaled acts as a filter condition when latch unsignaled is either auto
+            // or always. auto: in this case we let buffer latch unsignaled if we have only one
+            // applyToken and if only first transaction is latch unsignaled. If more than one
+            // applyToken we don't latch unsignaled.
+            bool allowLatchUnsignaled = allowedLatchUnsignaled();
+            bool isFirstUnsignaledTransactionApplied = false;
             // Collect transactions from pending transaction queue.
             auto it = mPendingTransactionQueues.begin();
             while (it != mPendingTransactionQueues.end()) {
                 auto& [applyToken, transactionQueue] = *it;
-
                 while (!transactionQueue.empty()) {
                     auto& transaction = transactionQueue.front();
                     if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
                                                        transaction.isAutoTimestamp,
                                                        transaction.desiredPresentTime,
                                                        transaction.originUid, transaction.states,
-                                                       bufferLayersReadyToPresent)) {
+                                                       bufferLayersReadyToPresent,
+                                                       allowLatchUnsignaled)) {
                         setTransactionFlags(eTransactionFlushNeeded);
                         break;
                     }
@@ -3452,6 +3467,14 @@
                     });
                     transactions.emplace_back(std::move(transaction));
                     transactionQueue.pop();
+                    if (allowLatchUnsignaled &&
+                        enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto) {
+                        // if allowLatchUnsignaled && we are in LatchUnsignaledConfig::Auto
+                        // then we should have only one applyToken for processing.
+                        // so we can stop further transactions on this applyToken.
+                        isFirstUnsignaledTransactionApplied = true;
+                        break;
+                    }
                 }
 
                 if (transactionQueue.empty()) {
@@ -3463,52 +3486,115 @@
             }
 
             // Collect transactions from current transaction queue or queue to pending transactions.
-            // Case 1: push to pending when transactionIsReadyToBeApplied is false.
+            // Case 1: push to pending when transactionIsReadyToBeApplied is false
+            // or the first transaction was unsignaled.
             // Case 2: push to pending when there exist a pending queue.
-            // Case 3: others are ready to apply.
+            // Case 3: others are the transactions that are ready to apply.
             while (!mTransactionQueue.empty()) {
                 auto& transaction = mTransactionQueue.front();
                 bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) !=
                         mPendingTransactionQueues.end();
-                if (pendingTransactions ||
+                if (isFirstUnsignaledTransactionApplied || pendingTransactions ||
                     !transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
                                                    transaction.isAutoTimestamp,
                                                    transaction.desiredPresentTime,
                                                    transaction.originUid, transaction.states,
-                                                   bufferLayersReadyToPresent)) {
+                                                   bufferLayersReadyToPresent,
+                                                   allowLatchUnsignaled)) {
                     mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
                 } else {
                     transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
                         bufferLayersReadyToPresent.insert(state.surface);
                     });
                     transactions.emplace_back(std::move(transaction));
+                    if (allowLatchUnsignaled &&
+                        enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto) {
+                        isFirstUnsignaledTransactionApplied = true;
+                    }
                 }
-                mTransactionQueue.pop();
+                mTransactionQueue.pop_front();
                 ATRACE_INT("TransactionQueue", mTransactionQueue.size());
             }
-        }
 
-        // Now apply all transactions.
-        for (const auto& transaction : transactions) {
-            needsTraversal |=
-                    applyTransactionState(transaction.frameTimelineInfo, transaction.states,
-                                          transaction.displays, transaction.flags,
-                                          transaction.inputWindowCommands,
-                                          transaction.desiredPresentTime,
-                                          transaction.isAutoTimestamp, transaction.buffer,
-                                          transaction.postTime, transaction.permissions,
-                                          transaction.hasListenerCallbacks,
-                                          transaction.listenerCallbacks, transaction.originPid,
-                                          transaction.originUid, transaction.id);
-            if (transaction.transactionCommittedSignal) {
-                mTransactionCommittedSignals.emplace_back(
-                        std::move(transaction.transactionCommittedSignal));
-            }
+            return applyTransactions(transactions);
         }
-    } // unlock mStateLock
+    }
+}
+
+bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions) {
+    bool needsTraversal = false;
+    // Now apply all transactions.
+    for (const auto& transaction : transactions) {
+        needsTraversal |=
+                applyTransactionState(transaction.frameTimelineInfo, transaction.states,
+                                      transaction.displays, transaction.flags,
+                                      transaction.inputWindowCommands,
+                                      transaction.desiredPresentTime, transaction.isAutoTimestamp,
+                                      transaction.buffer, transaction.postTime,
+                                      transaction.permissions, transaction.hasListenerCallbacks,
+                                      transaction.listenerCallbacks, transaction.originPid,
+                                      transaction.originUid, transaction.id);
+        if (transaction.transactionCommittedSignal) {
+            mTransactionCommittedSignals.emplace_back(
+                    std::move(transaction.transactionCommittedSignal));
+        }
+    }
     return needsTraversal;
 }
 
+bool SurfaceFlinger::allowedLatchUnsignaled() {
+    if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) {
+        return false;
+    }
+    // Always mode matches the current latch unsignaled behavior.
+    // This behavior is currently used by the partners and we would like
+    // to keep it until we are completely migrated to Auto mode successfully
+    // and we we have our fallback based implementation in place.
+    if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Always) {
+        return true;
+    }
+
+    //  if enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto
+    //  we don't latch unsignaled if more than one applyToken, as it can backpressure
+    //  the other transactions.
+    if (mPendingTransactionQueues.size() > 1) {
+        return false;
+    }
+    std::optional<sp<IBinder>> applyToken = std::nullopt;
+    bool isPendingTransactionQueuesItem = false;
+    if (!mPendingTransactionQueues.empty()) {
+        applyToken = mPendingTransactionQueues.begin()->first;
+        isPendingTransactionQueuesItem = true;
+    }
+
+    for (const auto& item : mTransactionQueue) {
+        if (!applyToken.has_value()) {
+            applyToken = item.applyToken;
+        } else if (applyToken.has_value() && applyToken != item.applyToken) {
+            return false;
+        }
+    }
+
+    if (isPendingTransactionQueuesItem) {
+        return checkTransactionCanLatchUnsignaled(
+                mPendingTransactionQueues.begin()->second.front());
+    } else if (applyToken.has_value()) {
+        return checkTransactionCanLatchUnsignaled((mTransactionQueue.front()));
+    }
+    return false;
+}
+
+bool SurfaceFlinger::checkTransactionCanLatchUnsignaled(const TransactionState& transaction) {
+    if (transaction.states.size() == 1) {
+        const auto& state = transaction.states.begin()->state;
+        return (state.flags & ~layer_state_t::eBufferChanged) == 0 &&
+                state.bufferData.flags.test(BufferData::BufferDataChange::fenceChanged) &&
+                state.bufferData.acquireFence &&
+                state.bufferData.acquireFence->getStatus() == Fence::Status::Unsignaled;
+    }
+    return false;
+}
+
 bool SurfaceFlinger::transactionFlushNeeded() {
     Mutex::Autolock _l(mQueueLock);
     return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty();
@@ -3540,7 +3626,8 @@
         const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
         uid_t originUid, const Vector<ComposerState>& states,
         const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
-                bufferLayersReadyToPresent) const {
+                bufferLayersReadyToPresent,
+        bool allowLatchUnsignaled) const {
     ATRACE_CALL();
     const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
     // Do not present if the desiredPresentTime has not passed unless it is more than one second
@@ -3567,7 +3654,7 @@
         const layer_state_t& s = state.state;
         const bool acquireFenceChanged =
                 s.bufferData.flags.test(BufferData::BufferDataChange::fenceChanged);
-        if (acquireFenceChanged && s.bufferData.acquireFence && !enableLatchUnsignaled &&
+        if (acquireFenceChanged && s.bufferData.acquireFence && !allowLatchUnsignaled &&
             s.bufferData.acquireFence->getStatus() == Fence::Status::Unsignaled) {
             ATRACE_NAME("fence unsignaled");
             return false;
@@ -3628,7 +3715,7 @@
                          : CountDownLatch::eSyncTransaction));
     }
 
-    mTransactionQueue.emplace(state);
+    mTransactionQueue.emplace_back(state);
     ATRACE_INT("TransactionQueue", mTransactionQueue.size());
 
     const auto schedule = [](uint32_t flags) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 276c7f6..97fddf2 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -135,6 +135,8 @@
     eTransactionMask = 0x1f,
 };
 
+enum class LatchUnsignaledConfig { Always, Auto, Disabled };
+
 using DisplayColorSetting = compositionengine::OutputColorSetting;
 
 struct SurfaceFlingerBE {
@@ -257,7 +259,7 @@
     // being treated as native display brightness
     static bool enableSdrDimming;
 
-    static bool enableLatchUnsignaled;
+    static LatchUnsignaledConfig enableLatchUnsignaledConfig;
 
     // must be called before clients can connect
     void init() ANDROID_API;
@@ -751,7 +753,14 @@
             const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
             uid_t originUid, const Vector<ComposerState>& states,
             const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
-                    bufferLayersReadyToPresent) const REQUIRES(mStateLock);
+                    bufferLayersReadyToPresent,
+            bool allowLatchUnsignaled) const REQUIRES(mStateLock);
+    static LatchUnsignaledConfig getLatchUnsignaledConfig();
+    bool latchUnsignaledIsAllowed(std::vector<TransactionState>& transactions) REQUIRES(mStateLock);
+    bool allowedLatchUnsignaled() REQUIRES(mQueueLock, mStateLock);
+    bool checkTransactionCanLatchUnsignaled(const TransactionState& transaction)
+            REQUIRES(mStateLock);
+    bool applyTransactions(std::vector<TransactionState>& transactions) REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
             REQUIRES(mStateLock);
@@ -1242,7 +1251,7 @@
     Condition mTransactionQueueCV;
     std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
             mPendingTransactionQueues GUARDED_BY(mQueueLock);
-    std::queue<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock);
+    std::deque<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock);
     /*
      * Feature prototyping
      */
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index c23fcc7..9832372 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -375,6 +375,7 @@
 
     auto& getTransactionQueue() { return mFlinger->mTransactionQueue; }
     auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
+    auto& getTransactionCommittedSignals() { return mFlinger->mTransactionCommittedSignals; }
 
     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 05551b4..8caadfb 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -24,8 +24,8 @@
 #include <gtest/gtest.h>
 #include <gui/SurfaceComposerClient.h>
 #include <log/log.h>
+#include <ui/MockFence.h>
 #include <utils/String8.h>
-
 #include "TestableScheduler.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/MockEventThread.h"
@@ -74,6 +74,13 @@
         EXPECT_CALL(*mVSyncTracker, currentPeriod())
                 .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
 
+        EXPECT_CALL(*mFenceUnsignaled, getStatus())
+                .WillRepeatedly(Return(Fence::Status::Unsignaled));
+        EXPECT_CALL(*mFenceUnsignaled2, getStatus())
+                .WillRepeatedly(Return(Fence::Status::Unsignaled));
+        EXPECT_CALL(*mFenceSignaled, getStatus()).WillRepeatedly(Return(Fence::Status::Signaled));
+        EXPECT_CALL(*mFenceSignaled2, getStatus()).WillRepeatedly(Return(Fence::Status::Signaled));
+
         mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
         mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController),
                                 std::unique_ptr<mock::VSyncTracker>(mVSyncTracker),
@@ -88,6 +95,10 @@
     mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
     mock::VsyncController* mVsyncController = new mock::VsyncController();
     mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker();
+    mock::MockFence* mFenceUnsignaled = new mock::MockFence();
+    mock::MockFence* mFenceSignaled = new mock::MockFence();
+    mock::MockFence* mFenceUnsignaled2 = new mock::MockFence();
+    mock::MockFence* mFenceSignaled2 = new mock::MockFence();
 
     struct TransactionInfo {
         Vector<ComposerState> states;
@@ -124,6 +135,15 @@
         transaction.frameTimelineInfo = frameTimelineInfo;
     }
 
+    void setupSingleWithComposer(TransactionInfo& transaction, uint32_t flags,
+                                 bool syncInputWindows, int64_t desiredPresentTime,
+                                 bool isAutoTimestamp, const FrameTimelineInfo& frameTimelineInfo,
+                                 const Vector<ComposerState>* states) {
+        setupSingle(transaction, flags, syncInputWindows, desiredPresentTime, isAutoTimestamp,
+                    frameTimelineInfo);
+        transaction.states = *states;
+    }
+
     void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
         ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
         EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
@@ -245,6 +265,188 @@
         EXPECT_EQ(0u, transactionQueue.size());
     }
 
+    void Flush_removesUnsignaledFromTheQueue(Vector<ComposerState> state1,
+                                             Vector<ComposerState> state2,
+                                             bool updateApplyToken = true) {
+        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+
+        TransactionInfo transactionA;
+        setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous,
+                                /*syncInputWindows*/ false,
+                                /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+                                FrameTimelineInfo{}, &state1);
+
+        mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+                                     transactionA.displays, transactionA.flags,
+                                     transactionA.applyToken, transactionA.inputWindowCommands,
+                                     transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
+                                     transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionA.id);
+
+        TransactionInfo transactionB;
+        if (updateApplyToken) {
+            transactionB.applyToken = sp<IBinder>();
+        }
+        setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous,
+                                /*syncInputWindows*/ false,
+                                /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+                                FrameTimelineInfo{}, &state2);
+        mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
+                                     transactionB.displays, transactionB.flags,
+                                     transactionB.applyToken, transactionB.inputWindowCommands,
+                                     transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
+                                     transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionB.id);
+
+        mFlinger.flushTransactionQueues();
+        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+        EXPECT_EQ(2ul, mFlinger.getTransactionCommittedSignals().size());
+    }
+
+    void Flush_removesFromTheQueue(const Vector<ComposerState>& state) {
+        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+
+        TransactionInfo transaction;
+        setupSingleWithComposer(transaction, ISurfaceComposer::eSynchronous,
+                                /*syncInputWindows*/ false,
+                                /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+                                FrameTimelineInfo{}, &state);
+
+        mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
+                                     transaction.displays, transaction.flags,
+                                     transaction.applyToken, transaction.inputWindowCommands,
+                                     transaction.desiredPresentTime, transaction.isAutoTimestamp,
+                                     transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transaction.id);
+
+        mFlinger.flushTransactionQueues();
+        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+        EXPECT_EQ(1u, mFlinger.getTransactionCommittedSignals().size());
+    }
+
+    void Flush_keepsInTheQueue(const Vector<ComposerState>& state) {
+        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+
+        TransactionInfo transaction;
+        setupSingleWithComposer(transaction, ISurfaceComposer::eSynchronous,
+                                /*syncInputWindows*/ false,
+                                /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+                                FrameTimelineInfo{}, &state);
+
+        mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
+                                     transaction.displays, transaction.flags,
+                                     transaction.applyToken, transaction.inputWindowCommands,
+                                     transaction.desiredPresentTime, transaction.isAutoTimestamp,
+                                     transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transaction.id);
+
+        mFlinger.flushTransactionQueues();
+        EXPECT_EQ(1u, mFlinger.getPendingTransactionQueue().size());
+        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+        EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size());
+    }
+
+    void Flush_KeepsUnsignaledInTheQueue(const Vector<ComposerState>& state1,
+                                         const Vector<ComposerState>& state2,
+                                         bool updateApplyToken = true,
+                                         uint32_t pendingTransactionQueueSize = 1u) {
+        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+        auto time = systemTime();
+        TransactionInfo transactionA;
+        TransactionInfo transactionB;
+        setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous,
+                                /*syncInputWindows*/ false,
+                                /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
+                                FrameTimelineInfo{}, &state1);
+        setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous,
+                                /*syncInputWindows*/ false,
+                                /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
+                                FrameTimelineInfo{}, &state2);
+        mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+                                     transactionA.displays, transactionA.flags,
+                                     transactionA.applyToken, transactionA.inputWindowCommands,
+                                     transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
+                                     transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionA.id);
+        if (updateApplyToken) {
+            transactionB.applyToken = sp<IBinder>();
+        }
+        mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
+                                     transactionB.displays, transactionB.flags,
+                                     transactionB.applyToken, transactionB.inputWindowCommands,
+                                     transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
+                                     transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionB.id);
+
+        mFlinger.flushTransactionQueues();
+        EXPECT_EQ(pendingTransactionQueueSize, mFlinger.getPendingTransactionQueue().size());
+        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+    }
+
+    void Flush_removesSignaledFromTheQueue(const Vector<ComposerState>& state1,
+                                           const Vector<ComposerState>& state2) {
+        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+
+        auto time = systemTime();
+        TransactionInfo transactionA;
+        TransactionInfo transactionB;
+        setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous,
+                                /*syncInputWindows*/ false,
+                                /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
+                                FrameTimelineInfo{}, &state1);
+        setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous,
+                                /*syncInputWindows*/ false,
+                                /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
+                                FrameTimelineInfo{}, &state2);
+        mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+                                     transactionA.displays, transactionA.flags,
+                                     transactionA.applyToken, transactionA.inputWindowCommands,
+                                     transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
+                                     transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionA.id);
+        mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
+                                     transactionB.displays, transactionB.flags,
+                                     transactionB.applyToken, transactionB.inputWindowCommands,
+                                     transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
+                                     transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionB.id);
+
+        mFlinger.flushTransactionQueues();
+        EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+        EXPECT_EQ(2ul, mFlinger.getTransactionCommittedSignals().size());
+    }
+
+    static Vector<ComposerState> createComposerStateVector(const ComposerState& state1,
+                                                           const ComposerState& state2) {
+        Vector<ComposerState> states;
+        states.push_back(state1);
+        states.push_back(state2);
+        return states;
+    }
+
+    static Vector<ComposerState> createComposerStateVector(const ComposerState& state) {
+        Vector<ComposerState> states;
+        states.push_back(state);
+        return states;
+    }
+
+    static ComposerState createComposerState(int layerId, sp<Fence> fence,
+                                             uint32_t stateFlags = layer_state_t::eBufferChanged) {
+        ComposerState composer_state;
+        composer_state.state.bufferData.acquireFence = std::move(fence);
+        composer_state.state.layerId = layerId;
+        composer_state.state.bufferData.flags = BufferData::BufferDataChange::fenceChanged;
+        composer_state.state.flags = stateFlags;
+        return composer_state;
+    }
+
     bool mHasListenerCallbacks = false;
     std::vector<ListenerCallbacks> mCallbacks;
     int mTransactionNumber = 0;
@@ -327,4 +529,216 @@
     auto ret = mFlinger.fromHandle(badHandle);
     EXPECT_EQ(nullptr, ret.promote().get());
 }
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSingleSignaledFromTheQueue_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_removesFromTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSingleUnSignaledFromTheQueue_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_removesFromTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest,
+       Flush_KeepsUnSignaledInTheQueue_NonBufferCropChange_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_keepsInTheQueue(createComposerStateVector(
+            createComposerState(/*layerId*/ 1, mFenceUnsignaled, layer_state_t::eCropChanged)));
+}
+
+TEST_F(TransactionApplicationTest,
+       Flush_KeepsUnSignaledInTheQueue_NonBufferChangeClubed_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_keepsInTheQueue(createComposerStateVector(
+            createComposerState(/*layerId*/ 1, mFenceUnsignaled,
+                                layer_state_t::eCropChanged | layer_state_t::eBufferChanged)));
+}
+
+TEST_F(TransactionApplicationTest,
+       Flush_KeepsInTheQueueSameApplyTokenMultiState_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_keepsInTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+                                      createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueue_MultipleStateTransaction_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_keepsInTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+                                      createComposerState(/*layerId*/ 2, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_removesSignaledFromTheQueue(createComposerStateVector(
+                                              createComposerState(/*layerId*/ 1, mFenceSignaled)),
+                                      createComposerStateVector(
+                                              createComposerState(/*layerId*/ 2, mFenceSignaled2)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemoveSignaledWithUnsignaledIntact_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+                                            createComposerState(/*layerId*/ 1, mFenceSignaled)),
+                                    createComposerStateVector(
+                                            createComposerState(/*layerId*/ 2, mFenceUnsignaled)));
+    EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest,
+       Flush_KeepsTransactionInTheQueueSameApplyToken_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+                                            createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
+                                    createComposerStateVector(
+                                            createComposerState(/*layerId*/ 2, mFenceSignaled)),
+                                    /*updateApplyToken*/ false);
+    EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsTransactionInTheQueue_LatchUnsignaled_Auto) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+                                            createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
+                                    createComposerStateVector(
+                                            createComposerState(/*layerId*/ 2, mFenceUnsignaled)),
+                                    /*updateApplyToken*/ true,
+                                    /*pendingTransactionQueueSize*/ 2u);
+    EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Disabled) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+    Flush_removesFromTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueue_LatchUnsignaled_Disabled) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+    Flush_keepsInTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueueSameLayerId_LatchUnsignaled_Disabled) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+    Flush_keepsInTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+                                      createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueueDifferentLayerId_LatchUnsignaled_Disabled) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+    Flush_keepsInTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+                                      createComposerState(/*layerId*/ 2, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnSignaled_Disabled) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+    Flush_removesSignaledFromTheQueue(createComposerStateVector(
+                                              createComposerState(/*layerId*/ 1, mFenceSignaled)),
+                                      createComposerStateVector(
+                                              createComposerState(/*layerId*/ 2, mFenceSignaled2)));
+}
+
+TEST_F(TransactionApplicationTest,
+       Flush_KeepInTheQueueDifferentApplyToken_LatchUnsignaled_Disabled) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+                                            createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
+                                    createComposerStateVector(
+                                            createComposerState(/*layerId*/ 2, mFenceSignaled)));
+    EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepInTheQueueSameApplyToken_LatchUnsignaled_Disabled) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+                                            createComposerState(/*layerId*/ 1, mFenceSignaled)),
+                                    createComposerStateVector(
+                                            createComposerState(/*layerId*/ 2, mFenceUnsignaled)),
+                                    /*updateApplyToken*/ false);
+    EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepInTheUnsignaledTheQueue_LatchUnsignaled_Disabled) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+    Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+                                            createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
+                                    createComposerStateVector(
+                                            createComposerState(/*layerId*/ 2, mFenceUnsignaled)),
+                                    /*updateApplyToken*/ false);
+    EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Always) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+    Flush_removesFromTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesFromTheQueue_LatchUnsignaled_Always) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+    Flush_removesFromTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesFromTheQueueSameLayerId_LatchUnsignaled_Always) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+    Flush_removesFromTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+                                      createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest,
+       Flush_RemovesFromTheQueueDifferentLayerId_LatchUnsignaled_Always) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+    Flush_removesFromTheQueue(
+            createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+                                      createComposerState(/*layerId*/ 2, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnSignaled_Always) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+    Flush_removesSignaledFromTheQueue(createComposerStateVector(
+                                              createComposerState(/*layerId*/ 1, mFenceSignaled)),
+                                      createComposerStateVector(
+                                              createComposerState(/*layerId*/ 2, mFenceSignaled2)));
+}
+
+TEST_F(TransactionApplicationTest,
+       Flush_RemovesFromTheQueueDifferentApplyToken_LatchUnsignaled_Always) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+    Flush_removesUnsignaledFromTheQueue(createComposerStateVector(
+                                                createComposerState(/*layerId*/ 1, mFenceSignaled)),
+                                        createComposerStateVector(
+                                                createComposerState(/*layerId*/ 2,
+                                                                    mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest,
+       Flush_RemovesUnsignaledFromTheQueueSameApplyToken_LatchUnsignaled_Always) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+    Flush_removesUnsignaledFromTheQueue(createComposerStateVector(
+                                                createComposerState(/*layerId*/ 1,
+                                                                    mFenceUnsignaled)),
+                                        createComposerStateVector(
+                                                createComposerState(/*layerId*/ 2, mFenceSignaled)),
+                                        /*updateApplyToken*/ false);
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesUnsignaledFromTheQueue_LatchUnsignaled_Always) {
+    SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+    Flush_removesUnsignaledFromTheQueue(createComposerStateVector(
+                                                createComposerState(/*layerId*/ 1,
+                                                                    mFenceUnsignaled)),
+                                        createComposerStateVector(
+                                                createComposerState(/*layerId*/ 2,
+                                                                    mFenceUnsignaled)));
+}
+
 } // namespace android