Correctly pass screenshot fences to transaction callbacks

In some instances, a screenshot may be captured before a layer has a
release callback registered. This can happen when a new buffer has
not yet been transacted before a screenshot is captured. This causes the
screenshot fence to be dropped and the possibility of tearing when
capturing a screenshot while continuously rendering.

To resolve this, buffer screenshot fences into a list of future fences
when there is no callback registered, and merge those fences when
dispatching the release callback.

Bug: 302703346
Test: SurfaceViewTests#testMovingWhiteSurfaceView 100 times
Change-Id: I91aec3cdb0973092d48cd77e59dd3999e9d9e847
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index c8b1059..219a8e2 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -78,11 +78,13 @@
 #include "SurfaceFlinger.h"
 #include "TimeStats/TimeStats.h"
 #include "TunnelModeEnabledReporter.h"
+#include "Utils/FenceUtils.h"
 
 #define DEBUG_RESIZE 0
 #define EARLY_RELEASE_ENABLED false
 
 namespace android {
+using namespace std::chrono_literals;
 namespace {
 constexpr int kDumpTableRowLength = 159;
 
@@ -2911,7 +2913,8 @@
 }
 
 void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
-                             ui::LayerStack layerStack) {
+                             ui::LayerStack layerStack,
+                             std::function<FenceResult(FenceResult)>&& continuation) {
     // If we are displayed on multiple displays in a single composition cycle then we would
     // need to do careful tracking to enable the use of the mLastClientCompositionFence.
     //  For example we can only use it if all the displays are client comp, and we need
@@ -2946,11 +2949,41 @@
             break;
         }
     }
+
+    if (!FlagManager::getInstance().screenshot_fence_preservation() && continuation) {
+        futureFenceResult = ftl::Future(futureFenceResult).then(std::move(continuation)).share();
+    }
+
     if (ch != nullptr) {
         ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
         ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
         ch->name = mName;
+    } else if (FlagManager::getInstance().screenshot_fence_preservation()) {
+        // If we didn't get a release callback yet, e.g. some scenarios when capturing screenshots
+        // asynchronously, then make sure we don't drop the fence.
+        mAdditionalPreviousReleaseFences.emplace_back(std::move(futureFenceResult),
+                                                      std::move(continuation));
+        std::vector<FenceAndContinuation> mergedFences;
+        sp<Fence> prevFence = nullptr;
+        // For a layer that's frequently screenshotted, try to merge fences to make sure we don't
+        // grow unbounded.
+        for (const auto& futureAndContinution : mAdditionalPreviousReleaseFences) {
+            auto result = futureAndContinution.future.wait_for(0s);
+            if (result != std::future_status::ready) {
+                mergedFences.emplace_back(futureAndContinution);
+                continue;
+            }
+
+            mergeFence(getDebugName(), futureAndContinution.chain().get().value_or(Fence::NO_FENCE),
+                       prevFence);
+        }
+        if (prevFence != nullptr) {
+            mergedFences.emplace_back(ftl::yield(FenceResult(std::move(prevFence))).share());
+        }
+
+        mAdditionalPreviousReleaseFences.swap(mergedFences);
     }
+
     if (mBufferInfo.mBuffer) {
         mPreviouslyPresentedLayerStacks.push_back(layerStack);
     }
@@ -3440,6 +3473,15 @@
             handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
             handle->frameNumber = mDrawingState.frameNumber;
             handle->previousFrameNumber = mDrawingState.previousFrameNumber;
+            if (FlagManager::getInstance().screenshot_fence_preservation() &&
+                mPreviousReleaseBufferEndpoint == handle->listener) {
+                // Add fences from previous screenshots now so that they can be dispatched to the
+                // client.
+                for (const auto& futureAndContinution : mAdditionalPreviousReleaseFences) {
+                    handle->previousReleaseFences.emplace_back(futureAndContinution.chain());
+                }
+                mAdditionalPreviousReleaseFences.clear();
+            }
 
             // Store so latched time and release fence can be set
             mDrawingState.callbackHandles.push_back(handle);