Introduce release buffer callback for BufferStateLayer

Currently BLAST clients use the TransactionCompleted callbacks to
determine when to release buffers. The TransactionCompleted callback
is overloaded. For transactions without buffers, the callback is
called when the transaction is applied on the server. If the
Transaction contains one or more buffers, the callback is called when
all the buffers are latched and ready to be presented. If we have
multiple buffers on multiple transactions, where one or more buffers
maybe dropped, the pending callbacks are called together. This may
delay signaling the client when a buffer can be released.

To fix this, we introduce a new buffer release callback that is
called as soon as a buffer is dropped by the server or when a new
buffer has been latched and the buffer will no longer be presented.
This new callback provides a graphic bufferid to identify the buffer
that can be released and a release fence to wait on.

BlastBufferQueue has been switched to use this new callback. Other
BLAST users continue to use the existing callback.

Test: go/wm-smoke
Test: atest ReleaseBufferCallbackTest
Bug: 178385281

Change-Id: Idd88e4994e543443198a5a8cfa0e3f5f67d5d482
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 96a0c3c..89dfb6f 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -41,6 +41,16 @@
 namespace android {
 
 using PresentState = frametimeline::SurfaceFrame::PresentState;
+namespace {
+void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
+                               const sp<GraphicBuffer>& buffer, const sp<Fence>& releaseFence) {
+    if (!listener) {
+        return;
+    }
+    listener->onReleaseBuffer(buffer->getId(), releaseFence ? releaseFence : Fence::NO_FENCE);
+}
+} // namespace
+
 // clang-format off
 const std::array<float, 16> BufferStateLayer::IDENTITY_MATRIX{
         1, 0, 0, 0,
@@ -65,7 +75,10 @@
         // RenderEngine may have been using the buffer as an external texture
         // after the client uncached the buffer.
         auto& engine(mFlinger->getRenderEngine());
-        engine.unbindExternalTextureBuffer(mBufferInfo.mBuffer->getId());
+        const uint64_t bufferId = mBufferInfo.mBuffer->getId();
+        engine.unbindExternalTextureBuffer(bufferId);
+        callReleaseBufferCallback(mDrawingState.releaseBufferListener, mBufferInfo.mBuffer,
+                                  mBufferInfo.mFence);
     }
 }
 
@@ -74,6 +87,7 @@
     if (ch == nullptr) {
         return OK;
     }
+    ch->previousBufferId = mPreviousBufferId;
     if (!ch->previousReleaseFence.get()) {
         ch->previousReleaseFence = fence;
         return OK;
@@ -190,6 +204,19 @@
         handle->dequeueReadyTime = dequeueReadyTime;
     }
 
+    // If there are multiple transactions in this frame, set the previous id on the earliest
+    // transacton. We don't need to pass in the released buffer id to multiple transactions.
+    // The buffer id does not have to correspond to any particular transaction as long as the
+    // listening end point is the same but the client expects the first transaction callback that
+    // replaces the presented buffer to contain the release fence. This follows the same logic.
+    // see BufferStateLayer::onLayerDisplayed.
+    for (auto& handle : mDrawingState.callbackHandles) {
+        if (handle->releasePreviousBuffer) {
+            handle->previousBufferId = mPreviousBufferId;
+            break;
+        }
+    }
+
     std::vector<JankData> jankData;
     jankData.reserve(mPendingJankClassifications.size());
     while (!mPendingJankClassifications.empty()
@@ -344,8 +371,8 @@
 bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
                                  nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp,
                                  const client_cache_t& clientCacheId, uint64_t frameNumber,
-                                 std::optional<nsecs_t> dequeueTime,
-                                 const FrameTimelineInfo& info) {
+                                 std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info,
+                                 const sp<ITransactionCompletedListener>& releaseBufferListener) {
     ATRACE_CALL();
 
     if (mCurrentState.buffer) {
@@ -353,7 +380,10 @@
         if (mCurrentState.buffer != mDrawingState.buffer) {
             // If mCurrentState has a buffer, and we are about to update again
             // before swapping to drawing state, then the first buffer will be
-            // dropped and we should decrement the pending buffer count.
+            // dropped and we should decrement the pending buffer count and
+            // call any release buffer callbacks if set.
+            callReleaseBufferCallback(mCurrentState.releaseBufferListener, mCurrentState.buffer,
+                                      mCurrentState.acquireFence);
             decrementPendingBufferCount();
             if (mCurrentState.bufferSurfaceFrameTX != nullptr) {
                 addSurfaceFrameDroppedForBuffer(mCurrentState.bufferSurfaceFrameTX);
@@ -361,9 +391,8 @@
             }
         }
     }
-
     mCurrentState.frameNumber = frameNumber;
-
+    mCurrentState.releaseBufferListener = releaseBufferListener;
     mCurrentState.buffer = buffer;
     mCurrentState.clientCacheId = clientCacheId;
     mCurrentState.modified = true;
@@ -889,15 +918,16 @@
     ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers);
 }
 
-uint32_t BufferStateLayer::doTransaction(uint32_t flags) {
-    if (mDrawingState.buffer != nullptr && mDrawingState.buffer != mBufferInfo.mBuffer) {
+void BufferStateLayer::bufferMayChange(sp<GraphicBuffer>& newBuffer) {
+    if (mDrawingState.buffer != nullptr && mDrawingState.buffer != mBufferInfo.mBuffer &&
+        newBuffer != mDrawingState.buffer) {
         // If we are about to update mDrawingState.buffer but it has not yet latched
-        // then we will drop a buffer and should decrement the pending buffer count.
-        // This logic may not work perfectly in the face of a BufferStateLayer being the
-        // deferred side of a deferred transaction, but we don't expect this use case.
+        // then we will drop a buffer and should decrement the pending buffer count and
+        // call any release buffer callbacks if set.
+        callReleaseBufferCallback(mDrawingState.releaseBufferListener, mDrawingState.buffer,
+                                  mDrawingState.acquireFence);
         decrementPendingBufferCount();
     }
-    return Layer::doTransaction(flags);
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index ebf40cb..036e8d2 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -70,7 +70,8 @@
     bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime,
                    nsecs_t desiredPresentTime, bool isAutoTimestamp,
                    const client_cache_t& clientCacheId, uint64_t frameNumber,
-                   std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info) override;
+                   std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info,
+                   const sp<ITransactionCompletedListener>& transactionListener) override;
     bool setAcquireFence(const sp<Fence>& fence) override;
     bool setDataspace(ui::Dataspace dataspace) override;
     bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
@@ -111,7 +112,7 @@
 
     // See mPendingBufferTransactions
     void decrementPendingBufferCount();
-    uint32_t doTransaction(uint32_t flags) override;
+    void bufferMayChange(sp<GraphicBuffer>& newBuffer) override;
     std::atomic<int32_t>* getPendingBufferCounter() override { return &mPendingBufferTransactions; }
     std::string getPendingBufferCounterName() override { return mBlastTransactionName; }
 
@@ -170,6 +171,9 @@
 
     mutable bool mCurrentStateModified = false;
     bool mReleasePreviousBuffer = false;
+
+    // Stores the last set acquire fence signal time used to populate the callback handle's acquire
+    // time.
     nsecs_t mCallbackHandleAcquireTime = -1;
 
     std::deque<std::shared_ptr<android::frametimeline::SurfaceFrame>> mPendingJankClassifications;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 0015bf2..cd3e8ad 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1048,6 +1048,9 @@
         c.callbackHandles.push_back(handle);
     }
 
+    // Allow BufferStateLayer to release any unlatched buffers in drawing state.
+    bufferMayChange(c.buffer);
+
     // Commit the transaction
     commitTransaction(c);
     mPendingStatesSnapshot = mPendingStates;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 26d8e74..85ff479 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -312,6 +312,7 @@
         // When the transaction was posted
         nsecs_t postTime;
 
+        sp<ITransactionCompletedListener> releaseBufferListener;
         // SurfaceFrame that tracks the timeline of Transactions that contain a Buffer. Only one
         // such SurfaceFrame exists because only one buffer can be presented on the layer per vsync.
         // If multiple buffers are queued, the prior ones will be dropped, along with the
@@ -466,7 +467,8 @@
                            nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
                            bool /*isAutoTimestamp*/, const client_cache_t& /*clientCacheId*/,
                            uint64_t /* frameNumber */, std::optional<nsecs_t> /* dequeueTime */,
-                           const FrameTimelineInfo& /*info*/) {
+                           const FrameTimelineInfo& /*info*/,
+                           const sp<ITransactionCompletedListener>& /* releaseBufferListener */) {
         return false;
     };
     virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
@@ -774,6 +776,12 @@
     virtual uint32_t doTransaction(uint32_t transactionFlags);
 
     /*
+     * Called before updating the drawing state buffer. Used by BufferStateLayer to release any
+     * unlatched buffers in the drawing state.
+     */
+    virtual void bufferMayChange(sp<GraphicBuffer>& /* newBuffer */){};
+
+    /*
      * Remove relative z for the layer if its relative parent is not part of the
      * provided layer tree.
      */
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index b29c624..1d00cc3 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -241,7 +241,8 @@
     auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame];
     mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
                       mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
-                      std::nullopt /* dequeueTime */, FrameTimelineInfo{});
+                      std::nullopt /* dequeueTime */, FrameTimelineInfo{},
+                      nullptr /* releaseBufferListener */);
 
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
@@ -254,7 +255,8 @@
     auto buffer = buffers[mFrame];
     mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
                       mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
-                      std::nullopt /* dequeueTime */, FrameTimelineInfo{});
+                      std::nullopt /* dequeueTime */, FrameTimelineInfo{},
+                      nullptr /* releaseBufferListener */);
 
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d048380..61cc8a2 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4027,7 +4027,8 @@
                 : layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1;
 
         if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp,
-                             s.cachedBuffer, frameNumber, dequeueBufferTimestamp, info)) {
+                             s.cachedBuffer, frameNumber, dequeueBufferTimestamp, info,
+                             s.releaseBufferListener)) {
             flags |= eTraversalNeeded;
         }
     } else if (info.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index a78510e..3590e76 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -201,7 +201,8 @@
                                           handle->dequeueReadyTime);
         transactionStats->surfaceStats.emplace_back(surfaceControl, handle->acquireTime,
                                                     handle->previousReleaseFence,
-                                                    handle->transformHint, eventStats, jankData);
+                                                    handle->transformHint, eventStats, jankData,
+                                                    handle->previousBufferId);
     }
     return NO_ERROR;
 }
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index a240c82..caa8a4f 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -50,6 +50,7 @@
     nsecs_t refreshStartTime = 0;
     nsecs_t dequeueReadyTime = 0;
     uint64_t frameNumber = 0;
+    uint64_t previousBufferId = 0;
 };
 
 class TransactionCallbackInvoker {
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 78187f7..b96725f 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -44,6 +44,7 @@
         "MultiDisplayLayerBounds_test.cpp",
         "RefreshRateOverlay_test.cpp",
         "RelativeZ_test.cpp",
+        "ReleaseBufferCallback_test.cpp",
         "ScreenCapture_test.cpp",
         "SetFrameRate_test.cpp",
         "SetGeometry_test.cpp",
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index aa1cce2..158801a 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
 #include <sys/epoll.h>
 
 #include <gui/DisplayEventReceiver.h>
@@ -25,6 +21,8 @@
 #include "LayerTransactionTest.h"
 #include "utils/CallbackUtils.h"
 
+using namespace std::chrono_literals;
+
 namespace android {
 
 using android::hardware::graphics::common::V1_1::BufferUsage;
@@ -801,7 +799,7 @@
     }
 
     // Try to present 100ms in the future
-    nsecs_t time = systemTime() + (100 * 1e6);
+    nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
 
     transaction.setDesiredPresentTime(time);
     transaction.apply();
@@ -825,7 +823,7 @@
     }
 
     // Try to present 100ms in the future
-    nsecs_t time = systemTime() + (100 * 1e6);
+    nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
 
     transaction.setDesiredPresentTime(time);
     transaction.apply();
@@ -842,7 +840,7 @@
     }
 
     // Try to present 33ms after the first frame
-    time += (33.3 * 1e6);
+    time += std::chrono::nanoseconds(33ms).count();
 
     transaction.setDesiredPresentTime(time);
     transaction.apply();
@@ -870,7 +868,7 @@
     }
 
     // Try to present 100ms in the future
-    nsecs_t time = systemTime() + (100 * 1e6);
+    nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
 
     transaction.setDesiredPresentTime(time);
     transaction.apply();
@@ -887,7 +885,7 @@
     }
 
     // Try to present 33ms before the previous frame
-    time -= (33.3 * 1e6);
+    time -= std::chrono::nanoseconds(33ms).count();
 
     transaction.setDesiredPresentTime(time);
     transaction.apply();
@@ -914,7 +912,7 @@
     }
 
     // Try to present 100ms in the past
-    nsecs_t time = systemTime() - (100 * 1e6);
+    nsecs_t time = systemTime() - std::chrono::nanoseconds(100ms).count();
 
     transaction.setDesiredPresentTime(time);
     transaction.apply();
@@ -948,6 +946,3 @@
 }
 
 } // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
new file mode 100644
index 0000000..fb7d41c
--- /dev/null
+++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerTransactionTest.h"
+#include "utils/CallbackUtils.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+// b/181132765 - disabled until cuttlefish failures are investigated
+class ReleaseBufferCallbackHelper {
+public:
+    static void function(void* callbackContext, uint64_t graphicsBufferId,
+                         const sp<Fence>& releaseFence) {
+        if (!callbackContext) {
+            FAIL() << "failed to get callback context";
+        }
+        ReleaseBufferCallbackHelper* helper =
+                static_cast<ReleaseBufferCallbackHelper*>(callbackContext);
+        std::lock_guard lock(helper->mMutex);
+        helper->mCallbackDataQueue.emplace(graphicsBufferId, releaseFence);
+        helper->mConditionVariable.notify_all();
+    }
+
+    void getCallbackData(uint64_t* bufferId) {
+        std::unique_lock lock(mMutex);
+        if (mCallbackDataQueue.empty()) {
+            if (!mConditionVariable.wait_for(lock, std::chrono::seconds(3),
+                                             [&] { return !mCallbackDataQueue.empty(); })) {
+                FAIL() << "failed to get releaseBuffer callback";
+            }
+        }
+
+        auto callbackData = mCallbackDataQueue.front();
+        mCallbackDataQueue.pop();
+        *bufferId = callbackData.first;
+    }
+
+    void verifyNoCallbacks() {
+        // Wait to see if there are extra callbacks
+        std::this_thread::sleep_for(300ms);
+
+        std::lock_guard lock(mMutex);
+        EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received";
+        mCallbackDataQueue = {};
+    }
+
+    android::ReleaseBufferCallback getCallback() {
+        return std::bind(function, static_cast<void*>(this) /* callbackContext */,
+                         std::placeholders::_1, std::placeholders::_2);
+    }
+
+    std::mutex mMutex;
+    std::condition_variable mConditionVariable;
+    std::queue<std::pair<uint64_t, sp<Fence>>> mCallbackDataQueue;
+};
+
+class ReleaseBufferCallbackTest : public LayerTransactionTest {
+public:
+    virtual sp<SurfaceControl> createBufferStateLayer() {
+        return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
+    }
+
+    static void submitBuffer(const sp<SurfaceControl>& layer, sp<GraphicBuffer> buffer,
+                             sp<Fence> fence, CallbackHelper& callback,
+                             ReleaseBufferCallbackHelper& releaseCallback) {
+        Transaction t;
+        t.setBuffer(layer, buffer, releaseCallback.getCallback());
+        t.setAcquireFence(layer, fence);
+        t.addTransactionCompletedCallback(callback.function, callback.getContext());
+        t.apply();
+    }
+
+    static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult) {
+        CallbackData callbackData;
+        helper.getCallbackData(&callbackData);
+        expectedResult.verifyCallbackData(callbackData);
+    }
+
+    static void waitForReleaseBufferCallback(ReleaseBufferCallbackHelper& releaseCallback,
+                                             uint64_t expectedReleaseBufferId) {
+        uint64_t actualReleaseBufferId;
+        releaseCallback.getCallbackData(&actualReleaseBufferId);
+        EXPECT_EQ(expectedReleaseBufferId, actualReleaseBufferId);
+        releaseCallback.verifyNoCallbacks();
+    }
+    static ReleaseBufferCallbackHelper* getReleaseBufferCallbackHelper() {
+        static std::vector<ReleaseBufferCallbackHelper*> sCallbacks;
+        sCallbacks.emplace_back(new ReleaseBufferCallbackHelper());
+        return sCallbacks.back();
+    }
+
+    static sp<GraphicBuffer> getBuffer() {
+        return new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                                 BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                         BufferUsage::COMPOSER_OVERLAY,
+                                 "test");
+    }
+};
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_PresentBuffer) {
+    sp<SurfaceControl> layer = createBufferStateLayer();
+    CallbackHelper transactionCallback;
+    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+    // If a buffer is being presented, we should not emit a release callback.
+    sp<GraphicBuffer> firstBuffer = getBuffer();
+    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+    EXPECT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+
+    // if state doesn't change, no release callbacks are expected
+    Transaction t;
+    t.addTransactionCompletedCallback(transactionCallback.function,
+                                      transactionCallback.getContext());
+    t.apply();
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, ExpectedResult()));
+    EXPECT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+
+    // If a presented buffer is replaced, we should emit a release callback for the
+    // previously presented buffer.
+    sp<GraphicBuffer> secondBuffer = getBuffer();
+    submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+    expected = ExpectedResult();
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED,
+                        ExpectedResult::PreviousBuffer::RELEASED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+}
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_OffScreenLayer) {
+    sp<SurfaceControl> layer = createBufferStateLayer();
+
+    CallbackHelper transactionCallback;
+    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+    // If a buffer is being presented, we should not emit a release callback.
+    sp<GraphicBuffer> firstBuffer = getBuffer();
+    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+    releaseCallback->verifyNoCallbacks();
+
+    // If a layer is parented offscreen then it should not emit a callback since sf still owns
+    // the buffer and can render it again.
+    Transaction t;
+    t.reparent(layer, nullptr);
+    t.addTransactionCompletedCallback(transactionCallback.function,
+                                      transactionCallback.getContext());
+    t.apply();
+    expected = ExpectedResult();
+    expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED,
+                        ExpectedResult::PreviousBuffer::NOT_RELEASED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+    ASSERT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+
+    // If a presented buffer is replaced, we should emit a release callback for the
+    // previously presented buffer.
+    sp<GraphicBuffer> secondBuffer = getBuffer();
+    submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+    expected = ExpectedResult();
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED,
+                        ExpectedResult::PreviousBuffer::NOT_RELEASED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+
+    // If continue to submit buffer we continue to get release callbacks
+    sp<GraphicBuffer> thirdBuffer = getBuffer();
+    submitBuffer(layer, thirdBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+    expected = ExpectedResult();
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED,
+                        ExpectedResult::PreviousBuffer::NOT_RELEASED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBuffer->getId()));
+}
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_LayerLifecycle_layerdestroy) {
+    sp<SurfaceControl> layer = createBufferStateLayer();
+    CallbackHelper* transactionCallback = new CallbackHelper();
+    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+    // If a buffer is being presented, we should not emit a release callback.
+    sp<GraphicBuffer> firstBuffer = getBuffer();
+    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, *releaseCallback);
+    {
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::NOT_ACQUIRED);
+        ASSERT_NO_FATAL_FAILURE(waitForCallback(*transactionCallback, expected));
+        ASSERT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+    }
+
+    // Destroying a currently presenting layer emits a callback.
+    Transaction t;
+    t.reparent(layer, nullptr);
+    t.apply();
+    layer = nullptr;
+
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+}
+
+// Destroying a never presented layer emits a callback.
+TEST_F(ReleaseBufferCallbackTest, DISABLED_LayerLifecycle_OffScreenLayerDestroy) {
+    sp<SurfaceControl> layer = createBufferStateLayer();
+
+    // make layer offscreen
+    Transaction t;
+    t.reparent(layer, nullptr);
+    t.apply();
+
+    CallbackHelper* transactionCallback = new CallbackHelper();
+    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+    // Submitting a buffer does not emit a callback.
+    sp<GraphicBuffer> firstBuffer = getBuffer();
+    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, *releaseCallback);
+    {
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::NOT_ACQUIRED);
+        ASSERT_NO_FATAL_FAILURE(waitForCallback(*transactionCallback, expected));
+        ASSERT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+    }
+
+    // Submitting a second buffer will replace the drawing state buffer and emit a callback.
+    sp<GraphicBuffer> secondBuffer = getBuffer();
+    submitBuffer(layer, secondBuffer, Fence::NO_FENCE, *transactionCallback, *releaseCallback);
+    {
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::NOT_ACQUIRED);
+        ASSERT_NO_FATAL_FAILURE(waitForCallback(*transactionCallback, expected));
+        ASSERT_NO_FATAL_FAILURE(
+                waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+    }
+
+    // Destroying the offscreen layer emits a callback.
+    layer = nullptr;
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBuffer->getId()));
+}
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_FrameDropping) {
+    sp<SurfaceControl> layer = createBufferStateLayer();
+    CallbackHelper transactionCallback;
+    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+    // If a buffer is being presented, we should not emit a release callback.
+    sp<GraphicBuffer> firstBuffer = getBuffer();
+
+    // Try to present 100ms in the future
+    nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
+
+    Transaction t;
+    t.setBuffer(layer, firstBuffer, releaseCallback->getCallback());
+    t.setAcquireFence(layer, Fence::NO_FENCE);
+    t.addTransactionCompletedCallback(transactionCallback.function,
+                                      transactionCallback.getContext());
+    t.setDesiredPresentTime(time);
+    t.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+    EXPECT_NO_FATAL_FAILURE(releaseCallback->verifyNoCallbacks());
+
+    // Dropping frames in transaction queue emits a callback
+    sp<GraphicBuffer> secondBuffer = getBuffer();
+    t.setBuffer(layer, secondBuffer, releaseCallback->getCallback());
+    t.setAcquireFence(layer, Fence::NO_FENCE);
+    t.addTransactionCompletedCallback(transactionCallback.function,
+                                      transactionCallback.getContext());
+    t.setDesiredPresentTime(time);
+    t.apply();
+
+    expected = ExpectedResult();
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED,
+                        ExpectedResult::PreviousBuffer::RELEASED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index dbadf75..b5ef0a1 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -123,7 +123,8 @@
                     traceTimestamp(layerId, bufferId, frameNumber, postTime,
                                    FrameTracer::FrameEvent::QUEUE, /*duration*/ 0));
         layer->setBuffer(buffer, fence, postTime, /*desiredPresentTime*/ 30, false, mClientCache,
-                         frameNumber, dequeueTime, FrameTimelineInfo{});
+                         frameNumber, dequeueTime, FrameTimelineInfo{},
+                         nullptr /* releaseBufferCallback */);
 
         commitTransaction(layer.get());
         bool computeVisisbleRegions;
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 623a5e0..c75538f 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -126,7 +126,7 @@
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
         sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         acquireFence->signalForTest(12);
 
         commitTransaction(layer.get());
@@ -151,7 +151,7 @@
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
         sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
         layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
         const auto droppedSurfaceFrame = layer->mCurrentState.bufferSurfaceFrameTX;
@@ -161,7 +161,7 @@
         sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
         nsecs_t start = systemTime();
         layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         nsecs_t end = systemTime();
         acquireFence2->signalForTest(12);
 
@@ -199,7 +199,7 @@
         sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
 
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         acquireFence->signalForTest(12);
 
         EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
@@ -225,7 +225,7 @@
         sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
 
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
 
@@ -255,7 +255,7 @@
         sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
 
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 3, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 3, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         EXPECT_EQ(2u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
         const auto& bufferSurfaceFrameTX = layer->mCurrentState.bufferSurfaceFrameTX;
@@ -353,7 +353,7 @@
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
         sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
         layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
         const auto droppedSurfaceFrame = layer->mCurrentState.bufferSurfaceFrameTX;
 
@@ -361,7 +361,7 @@
         auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
         sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
         layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         acquireFence2->signalForTest(12);
 
         ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
@@ -388,7 +388,7 @@
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
         sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
         layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
         const auto droppedSurfaceFrame1 = layer->mCurrentState.bufferSurfaceFrameTX;
@@ -398,7 +398,8 @@
         sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
         auto dropStartTime1 = systemTime();
         layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0});
+                         {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0},
+                         nullptr /* releaseBufferCallback */);
         auto dropEndTime1 = systemTime();
         EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
@@ -409,7 +410,7 @@
         sp<GraphicBuffer> buffer3{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
         auto dropStartTime2 = systemTime();
         layer->setBuffer(buffer3, fence3, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 2, /*inputEventId*/ 0});
+                         {/*vsyncId*/ 2, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
         auto dropEndTime2 = systemTime();
         acquireFence3->signalForTest(12);
 
@@ -448,7 +449,8 @@
             sp<Fence> fence1(new Fence());
             sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
             layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
-                             {/*vsyncId*/ 1, /*inputEventId*/ 0});
+                             {/*vsyncId*/ 1, /*inputEventId*/ 0},
+                             nullptr /* releaseBufferCallback */);
             layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 2,
                                                                   /*inputEventId*/ 0},
                                                                  10);