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/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 82c9268..f778232 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -169,8 +169,6 @@
 
     mNumAcquired = 0;
     mNumFrameAvailable = 0;
-    mPendingReleaseItem.item = BufferItem();
-    mPendingReleaseItem.releaseFence = nullptr;
 }
 
 BLASTBufferQueue::~BLASTBufferQueue() {
@@ -242,7 +240,6 @@
         std::unique_lock _lock{mMutex};
         ATRACE_CALL();
         BQA_LOGV("transactionCallback");
-        mInitialCallbackReceived = true;
 
         if (!stats.empty()) {
             mTransformHint = stats[0].transformHint;
@@ -255,38 +252,20 @@
                                             stats[0].frameEventStats.compositorTiming,
                                             stats[0].latchTime,
                                             stats[0].frameEventStats.dequeueReadyTime);
-        }
-        if (mPendingReleaseItem.item.mGraphicBuffer != nullptr) {
-            if (!stats.empty()) {
-                mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
-            } else {
-                BQA_LOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
-                mPendingReleaseItem.releaseFence = nullptr;
+            currFrameNumber = stats[0].frameEventStats.frameNumber;
+
+            if (mTransactionCompleteCallback &&
+                currFrameNumber >= mTransactionCompleteFrameNumber) {
+                if (currFrameNumber > mTransactionCompleteFrameNumber) {
+                    BQA_LOGE("transactionCallback received for a newer framenumber=%" PRIu64
+                             " than expected=%" PRIu64,
+                             currFrameNumber, mTransactionCompleteFrameNumber);
+                }
+                transactionCompleteCallback = std::move(mTransactionCompleteCallback);
+                mTransactionCompleteFrameNumber = 0;
             }
-            mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item,
-                                               mPendingReleaseItem.releaseFence
-                                                       ? mPendingReleaseItem.releaseFence
-                                                       : Fence::NO_FENCE);
-            mNumAcquired--;
-            mPendingReleaseItem.item = BufferItem();
-            mPendingReleaseItem.releaseFence = nullptr;
         }
 
-        if (mSubmitted.empty()) {
-            BQA_LOGE("ERROR: callback with no corresponding submitted buffer item");
-        }
-        mPendingReleaseItem.item = std::move(mSubmitted.front());
-        mSubmitted.pop();
-
-        processNextBufferLocked(false /* useNextTransaction */);
-
-        currFrameNumber = mPendingReleaseItem.item.mFrameNumber;
-        if (mTransactionCompleteCallback && mTransactionCompleteFrameNumber == currFrameNumber) {
-            transactionCompleteCallback = std::move(mTransactionCompleteCallback);
-            mTransactionCompleteFrameNumber = 0;
-        }
-
-        mCallbackCV.notify_all();
         decStrong((void*)transactionCallbackThunk);
     }
 
@@ -295,15 +274,46 @@
     }
 }
 
+// Unlike transactionCallbackThunk the release buffer callback does not extend the life of the
+// BBQ. This is because if the BBQ is destroyed, then the buffers will be released by the client.
+// So we pass in a weak pointer to the BBQ and if it still alive, then we release the buffer.
+// Otherwise, this is a no-op.
+static void releaseBufferCallbackThunk(wp<BLASTBufferQueue> context, uint64_t graphicBufferId,
+                                       const sp<Fence>& releaseFence) {
+    sp<BLASTBufferQueue> blastBufferQueue = context.promote();
+    ALOGV("releaseBufferCallbackThunk graphicBufferId=%" PRIu64 " blastBufferQueue=%s",
+          graphicBufferId, blastBufferQueue ? "alive" : "dead");
+    if (blastBufferQueue) {
+        blastBufferQueue->releaseBufferCallback(graphicBufferId, releaseFence);
+    }
+}
+
+void BLASTBufferQueue::releaseBufferCallback(uint64_t graphicBufferId,
+                                             const sp<Fence>& releaseFence) {
+    ATRACE_CALL();
+    std::unique_lock _lock{mMutex};
+    BQA_LOGV("releaseBufferCallback graphicBufferId=%" PRIu64, graphicBufferId);
+
+    auto it = mSubmitted.find(graphicBufferId);
+    if (it == mSubmitted.end()) {
+        BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %" PRIu64,
+                 graphicBufferId);
+        return;
+    }
+
+    mBufferItemConsumer->releaseBuffer(it->second, releaseFence);
+    mSubmitted.erase(it);
+    mNumAcquired--;
+    processNextBufferLocked(false /* useNextTransaction */);
+    mCallbackCV.notify_all();
+}
+
 void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
     ATRACE_CALL();
-    BQA_LOGV("processNextBufferLocked useNextTransaction=%s", toString(useNextTransaction));
-
     // If the next transaction is set, we want to guarantee the our acquire will not fail, so don't
     // include the extra buffer when checking if we can acquire the next buffer.
     const bool includeExtraAcquire = !useNextTransaction;
     if (mNumFrameAvailable == 0 || maxBuffersAcquired(includeExtraAcquire)) {
-        BQA_LOGV("processNextBufferLocked waiting for frame available or callback");
         mCallbackCV.notify_all();
         return;
     }
@@ -353,7 +363,7 @@
     }
 
     mNumAcquired++;
-    mSubmitted.push(bufferItem);
+    mSubmitted[buffer->getId()] = bufferItem;
 
     bool needsDisconnect = false;
     mBufferItemConsumer->getConnectionEvents(bufferItem.mFrameNumber, &needsDisconnect);
@@ -369,7 +379,10 @@
     mLastBufferScalingMode = bufferItem.mScalingMode;
     mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
 
-    t->setBuffer(mSurfaceControl, buffer);
+    auto releaseBufferCallback =
+            std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
+                      std::placeholders::_1, std::placeholders::_2);
+    t->setBuffer(mSurfaceControl, buffer, releaseBufferCallback);
     t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
     t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
     t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage);
@@ -427,9 +440,12 @@
     }
 
     BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
-             " applyTransaction=%s mTimestamp=%" PRId64 " mPendingTransactions.size=%d",
+             " applyTransaction=%s mTimestamp=%" PRId64 "%s mPendingTransactions.size=%d"
+             " graphicBufferId=%" PRIu64,
              mSize.width, mSize.height, bufferItem.mFrameNumber, toString(applyTransaction),
-             bufferItem.mTimestamp, static_cast<uint32_t>(mPendingTransactions.size()));
+             bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp ? "(auto)" : "",
+             static_cast<uint32_t>(mPendingTransactions.size()),
+             bufferItem.mGraphicBuffer->getId());
 }
 
 Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
@@ -444,18 +460,17 @@
     std::unique_lock _lock{mMutex};
 
     const bool nextTransactionSet = mNextTransaction != nullptr;
-    BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s mFlushShadowQueue=%s",
-             item.mFrameNumber, toString(nextTransactionSet), toString(mFlushShadowQueue));
-
-    if (nextTransactionSet || mFlushShadowQueue) {
+    if (nextTransactionSet) {
         while (mNumFrameAvailable > 0 || maxBuffersAcquired(false /* includeExtraAcquire */)) {
             BQA_LOGV("waiting in onFrameAvailable...");
             mCallbackCV.wait(_lock);
         }
     }
-    mFlushShadowQueue = false;
     // add to shadow queue
     mNumFrameAvailable++;
+
+    BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber,
+             toString(nextTransactionSet));
     processNextBufferLocked(nextTransactionSet /* useNextTransaction */);
 }
 
@@ -514,14 +529,12 @@
 }
 
 // Check if we have acquired the maximum number of buffers.
-// As a special case, we wait for the first callback before acquiring the second buffer so we
-// can ensure the first buffer is presented if multiple buffers are queued in succession.
 // Consumer can acquire an additional buffer if that buffer is not droppable. Set
 // includeExtraAcquire is true to include this buffer to the count. Since this depends on the state
 // of the buffer, the next acquire may return with NO_BUFFER_AVAILABLE.
 bool BLASTBufferQueue::maxBuffersAcquired(bool includeExtraAcquire) const {
     int maxAcquiredBuffers = MAX_ACQUIRED_BUFFERS + (includeExtraAcquire ? 2 : 1);
-    return mNumAcquired == maxAcquiredBuffers || (!mInitialCallbackReceived && mNumAcquired == 1);
+    return mNumAcquired == maxAcquiredBuffers;
 }
 
 class BBQSurface : public Surface {
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 0ded936..9b5be1f 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -27,7 +27,8 @@
 
 enum class Tag : uint32_t {
     ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
-    LAST = ON_TRANSACTION_COMPLETED,
+    ON_RELEASE_BUFFER,
+    LAST = ON_RELEASE_BUFFER,
 };
 
 } // Anonymous namespace
@@ -122,6 +123,7 @@
     for (const auto& data : jankData) {
         SAFE_PARCEL(output->writeParcelable, data);
     }
+    SAFE_PARCEL(output->writeUint64, previousBufferId);
     return NO_ERROR;
 }
 
@@ -144,6 +146,7 @@
         SAFE_PARCEL(input->readParcelable, &data);
         jankData.push_back(data);
     }
+    SAFE_PARCEL(input->readUint64, &previousBufferId);
     return NO_ERROR;
 }
 
@@ -245,6 +248,12 @@
                                          onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED,
                                                                   stats);
     }
+
+    void onReleaseBuffer(uint64_t graphicBufferId, sp<Fence> releaseFence) override {
+        callRemoteAsync<decltype(
+                &ITransactionCompletedListener::onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER,
+                                                                  graphicBufferId, releaseFence);
+    }
 };
 
 // Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
@@ -263,6 +272,8 @@
         case Tag::ON_TRANSACTION_COMPLETED:
             return callLocalAsync(data, reply,
                                   &ITransactionCompletedListener::onTransactionCompleted);
+        case Tag::ON_RELEASE_BUFFER:
+            return callLocalAsync(data, reply, &ITransactionCompletedListener::onReleaseBuffer);
     }
 }
 
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 6bb8bf2..7a18ca6 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -63,7 +63,8 @@
         fixedTransformHint(ui::Transform::ROT_INVALID),
         frameNumber(0),
         frameTimelineInfo(),
-        autoRefresh(false) {
+        autoRefresh(false),
+        releaseBufferListener(nullptr) {
     matrix.dsdx = matrix.dtdy = 1.0f;
     matrix.dsdy = matrix.dtdx = 0.0f;
     hdrMetadata.validTypes = 0;
@@ -152,6 +153,7 @@
     SAFE_PARCEL(output.writeUint64, frameNumber);
     SAFE_PARCEL(frameTimelineInfo.write, output);
     SAFE_PARCEL(output.writeBool, autoRefresh);
+    SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(releaseBufferListener));
 
     SAFE_PARCEL(output.writeUint32, blurRegions.size());
     for (auto region : blurRegions) {
@@ -275,6 +277,12 @@
     SAFE_PARCEL(frameTimelineInfo.read, input);
     SAFE_PARCEL(input.readBool, &autoRefresh);
 
+    tmpBinder = nullptr;
+    SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
+    if (tmpBinder) {
+        releaseBufferListener = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
+    }
+
     uint32_t numRegions = 0;
     SAFE_PARCEL(input.readUint32, &numRegions);
     blurRegions.clear();
@@ -543,6 +551,13 @@
         what |= eAutoRefreshChanged;
         autoRefresh = other.autoRefresh;
     }
+    if (other.what & eReleaseBufferListenerChanged) {
+        if (releaseBufferListener) {
+            ALOGW("Overriding releaseBufferListener");
+        }
+        what |= eReleaseBufferListenerChanged;
+        releaseBufferListener = other.releaseBufferListener;
+    }
     if (other.what & eStretchChanged) {
         what |= eStretchChanged;
         stretchEffect = other.stretchEffect;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index c16a98f..11fe490 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -195,6 +195,17 @@
     }
 }
 
+void TransactionCompletedListener::setReleaseBufferCallback(uint64_t graphicBufferId,
+                                                            ReleaseBufferCallback listener) {
+    std::scoped_lock<std::mutex> lock(mMutex);
+    mReleaseBufferCallbacks[graphicBufferId] = listener;
+}
+
+void TransactionCompletedListener::removeReleaseBufferCallback(uint64_t graphicBufferId) {
+    std::scoped_lock<std::mutex> lock(mMutex);
+    mReleaseBufferCallbacks.erase(graphicBufferId);
+}
+
 void TransactionCompletedListener::addSurfaceStatsListener(void* context, void* cookie,
         sp<SurfaceControl> surfaceControl, SurfaceStatsCallback listener) {
     std::lock_guard<std::mutex> lock(mMutex);
@@ -275,6 +286,20 @@
                             .surfaceControls[surfaceStats.surfaceControl]
                             ->setTransformHint(surfaceStats.transformHint);
                 }
+                // If there is buffer id set, we look up any pending client release buffer callbacks
+                // and call them. This is a performance optimization when we have a transaction
+                // callback and a release buffer callback happening at the same time to avoid an
+                // additional ipc call from the server.
+                if (surfaceStats.previousBufferId) {
+                    ReleaseBufferCallback callback =
+                            popReleaseBufferCallbackLocked(surfaceStats.previousBufferId);
+                    if (callback) {
+                        callback(surfaceStats.previousBufferId,
+                                 surfaceStats.previousReleaseFence
+                                         ? surfaceStats.previousReleaseFence
+                                         : Fence::NO_FENCE);
+                    }
+                }
             }
 
             callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
@@ -297,6 +322,32 @@
     }
 }
 
+void TransactionCompletedListener::onReleaseBuffer(uint64_t graphicBufferId,
+                                                   sp<Fence> releaseFence) {
+    ReleaseBufferCallback callback;
+    {
+        std::scoped_lock<std::mutex> lock(mMutex);
+        callback = popReleaseBufferCallbackLocked(graphicBufferId);
+    }
+    if (!callback) {
+        ALOGE("Could not call release buffer callback, buffer not found %" PRIu64, graphicBufferId);
+        return;
+    }
+    callback(graphicBufferId, releaseFence);
+}
+
+ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked(
+        uint64_t graphicBufferId) {
+    ReleaseBufferCallback callback;
+    auto itr = mReleaseBufferCallbacks.find(graphicBufferId);
+    if (itr == mReleaseBufferCallbacks.end()) {
+        return nullptr;
+    }
+    callback = itr->second;
+    mReleaseBufferCallbacks.erase(itr);
+    return callback;
+}
+
 // ---------------------------------------------------------------------------
 
 void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId);
@@ -1219,17 +1270,20 @@
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
-        const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer) {
+        const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
+        ReleaseBufferCallback callback) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
+    removeReleaseBufferCallback(s);
     s->what |= layer_state_t::eBufferChanged;
     s->buffer = buffer;
     if (mIsAutoTimestamp) {
         mDesiredPresentTime = systemTime();
     }
+    setReleaseBufferCallback(s, callback);
 
     registerSurfaceControlForCallback(sc);
 
@@ -1237,6 +1291,34 @@
     return *this;
 }
 
+void SurfaceComposerClient::Transaction::removeReleaseBufferCallback(layer_state_t* s) {
+    if (!s->releaseBufferListener) {
+        return;
+    }
+
+    s->what &= ~static_cast<uint64_t>(layer_state_t::eReleaseBufferListenerChanged);
+    s->releaseBufferListener = nullptr;
+    TransactionCompletedListener::getInstance()->removeReleaseBufferCallback(s->buffer->getId());
+}
+
+void SurfaceComposerClient::Transaction::setReleaseBufferCallback(layer_state_t* s,
+                                                                  ReleaseBufferCallback callback) {
+    if (!callback) {
+        return;
+    }
+
+    if (!s->buffer) {
+        ALOGW("Transaction::setReleaseBufferCallback"
+              "ignored trying to set a callback on a null buffer.");
+        return;
+    }
+
+    s->what |= layer_state_t::eReleaseBufferListenerChanged;
+    s->releaseBufferListener = TransactionCompletedListener::getIInstance();
+    auto listener = TransactionCompletedListener::getInstance();
+    listener->setReleaseBufferCallback(s->buffer->getId(), callback);
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAcquireFence(
         const sp<SurfaceControl>& sc, const sp<Fence>& fence) {
     layer_state_t* s = getLayerState(sc);
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index dd8e714..0173ffd 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -89,13 +89,14 @@
 
     void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
             const std::vector<SurfaceControlStats>& stats);
+    void releaseBufferCallback(uint64_t graphicBufferId, const sp<Fence>& releaseFence);
     void setNextTransaction(SurfaceComposerClient::Transaction *t);
     void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
     void setTransactionCompleteCallback(uint64_t frameNumber,
                                         std::function<void(int64_t)>&& transactionCompleteCallback);
 
     void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format);
-    void flushShadowQueue() { mFlushShadowQueue = true; }
+    void flushShadowQueue() {}
 
     status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
     status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
@@ -132,16 +133,10 @@
 
     int32_t mNumFrameAvailable GUARDED_BY(mMutex);
     int32_t mNumAcquired GUARDED_BY(mMutex);
-    bool mInitialCallbackReceived GUARDED_BY(mMutex) = false;
-    struct PendingReleaseItem {
-        BufferItem item;
-        sp<Fence> releaseFence;
-    };
 
-    std::queue<const BufferItem> mSubmitted GUARDED_BY(mMutex);
-    // Keep a reference to the currently presented buffer so we can release it when the next buffer
-    // is ready to be presented.
-    PendingReleaseItem mPendingReleaseItem GUARDED_BY(mMutex);
+    // Keep a reference to the submitted buffers so we can release when surfaceflinger drops the
+    // buffer or the buffer has been presented and a new buffer is ready to be presented.
+    std::unordered_map<uint64_t /* bufferId */, BufferItem> mSubmitted GUARDED_BY(mMutex);
 
     ui::Size mSize GUARDED_BY(mMutex);
     ui::Size mRequestedSize GUARDED_BY(mMutex);
@@ -157,9 +152,6 @@
     std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
             mPendingTransactions GUARDED_BY(mMutex);
 
-    // If set to true, the next queue buffer will wait until the shadow queue has been processed by
-    // the adapter.
-    bool mFlushShadowQueue = false;
     // Last requested auto refresh state set by the producer. The state indicates that the consumer
     // should acquire the next frame as soon as it can and not wait for a frame to become available.
     // This is only relevant for shared buffer mode.
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index cb17cee..098760e 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -87,13 +87,14 @@
     SurfaceStats() = default;
     SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence,
                  uint32_t hint, FrameEventHistoryStats frameEventStats,
-                 std::vector<JankData> jankData)
+                 std::vector<JankData> jankData, uint64_t previousBufferId)
           : surfaceControl(sc),
             acquireTime(time),
             previousReleaseFence(prevReleaseFence),
             transformHint(hint),
             eventStats(frameEventStats),
-            jankData(std::move(jankData)) {}
+            jankData(std::move(jankData)),
+            previousBufferId(previousBufferId) {}
 
     sp<IBinder> surfaceControl;
     nsecs_t acquireTime = -1;
@@ -101,6 +102,7 @@
     uint32_t transformHint = 0;
     FrameEventHistoryStats eventStats;
     std::vector<JankData> jankData;
+    uint64_t previousBufferId;
 };
 
 class TransactionStats : public Parcelable {
@@ -139,6 +141,8 @@
     DECLARE_META_INTERFACE(TransactionCompletedListener)
 
     virtual void onTransactionCompleted(ListenerStats stats) = 0;
+
+    virtual void onReleaseBuffer(uint64_t graphicBufferId, sp<Fence> releaseFence) = 0;
 };
 
 class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 183ec40..d68a9cf 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -100,7 +100,7 @@
         eLayerStackChanged = 0x00000080,
         /* was eCropChanged_legacy, now available 0x00000100, */
         eDeferTransaction_legacy = 0x00000200,
-        /* was ScalingModeChanged, now available 0x00000400, */
+        eReleaseBufferListenerChanged = 0x00000400,
         eShadowRadiusChanged = 0x00000800,
         eReparentChildren = 0x00001000,
         /* was eDetachChildren, now available 0x00002000, */
@@ -248,6 +248,11 @@
 
     // Stretch effect to be applied to this layer
     StretchEffect stretchEffect;
+
+    // Listens to when the buffer is safe to be released. This is used for blast
+    // layers only. The callback includes a release fence as well as the graphic
+    // buffer id to identify the buffer.
+    sp<ITransactionCompletedListener> releaseBufferListener;
 };
 
 struct ComposerState {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 88484c0..f29983c 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -80,6 +80,9 @@
 using TransactionCompletedCallback =
         std::function<void(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
                            const std::vector<SurfaceControlStats>& /*stats*/)>;
+using ReleaseBufferCallback =
+        std::function<void(uint64_t /* graphicsBufferId */, const sp<Fence>& /*releaseFence*/)>;
+
 using SurfaceStatsCallback =
         std::function<void(void* /*context*/, nsecs_t /*latchTime*/,
                            const sp<Fence>& /*presentFence*/,
@@ -388,6 +391,8 @@
 
         void cacheBuffers();
         void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
+        void setReleaseBufferCallback(layer_state_t* state, ReleaseBufferCallback callback);
+        void removeReleaseBufferCallback(layer_state_t* state);
 
     public:
         Transaction();
@@ -471,7 +476,8 @@
         Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
                                                   bool transformToDisplayInverse);
         Transaction& setFrame(const sp<SurfaceControl>& sc, const Rect& frame);
-        Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer);
+        Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
+                               ReleaseBufferCallback callback = nullptr);
         Transaction& setCachedBuffer(const sp<SurfaceControl>& sc, int32_t bufferId);
         Transaction& setAcquireFence(const sp<SurfaceControl>& sc, const sp<Fence>& fence);
         Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
@@ -650,6 +656,8 @@
 
     std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex);
     std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex);
+    std::unordered_map<uint64_t /* graphicsBufferId */, ReleaseBufferCallback>
+            mReleaseBufferCallbacks GUARDED_BY(mMutex);
     std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry>
                 mSurfaceStatsListeners GUARDED_BY(mMutex);
 
@@ -683,8 +691,15 @@
                 SurfaceStatsCallback listener);
     void removeSurfaceStatsListener(void* context, void* cookie);
 
-    // Overrides BnTransactionCompletedListener's onTransactionCompleted
+    void setReleaseBufferCallback(uint64_t /* graphicsBufferId */, ReleaseBufferCallback);
+    void removeReleaseBufferCallback(uint64_t /* graphicsBufferId */);
+
+    // BnTransactionCompletedListener overrides
     void onTransactionCompleted(ListenerStats stats) override;
+    void onReleaseBuffer(uint64_t /* graphicsBufferId */, sp<Fence> releaseFence) override;
+
+private:
+    ReleaseBufferCallback popReleaseBufferCallbackLocked(uint64_t /* graphicsBufferId */);
 };
 
 } // namespace android
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 7895e99..fe48d88 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -73,13 +73,34 @@
 
     void waitForCallbacks() {
         std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
-        while (mBlastBufferQueueAdapter->mSubmitted.size() > 0) {
+        // Wait until all but one of the submitted buffers have been released.
+        while (mBlastBufferQueueAdapter->mSubmitted.size() > 1) {
             mBlastBufferQueueAdapter->mCallbackCV.wait(lock);
         }
     }
 
+    void setTransactionCompleteCallback(int64_t frameNumber) {
+        mBlastBufferQueueAdapter->setTransactionCompleteCallback(frameNumber, [&](int64_t frame) {
+            std::unique_lock lock{mMutex};
+            mLastTransactionCompleteFrameNumber = frame;
+            mCallbackCV.notify_all();
+        });
+    }
+
+    void waitForCallback(int64_t frameNumber) {
+        std::unique_lock lock{mMutex};
+        // Wait until all but one of the submitted buffers have been released.
+        while (mLastTransactionCompleteFrameNumber < frameNumber) {
+            mCallbackCV.wait(lock);
+        }
+    }
+
 private:
     sp<BLASTBufferQueue> mBlastBufferQueueAdapter;
+
+    std::mutex mMutex;
+    std::condition_variable mCallbackCV;
+    int64_t mLastTransactionCompleteFrameNumber = -1;
 };
 
 class BLASTBufferQueueTest : public ::testing::Test {
@@ -128,7 +149,7 @@
         mCaptureArgs.dataspace = ui::Dataspace::V0_SRGB;
     }
 
-    void setUpProducer(BLASTBufferQueueHelper adapter, sp<IGraphicBufferProducer>& producer) {
+    void setUpProducer(BLASTBufferQueueHelper& adapter, sp<IGraphicBufferProducer>& producer) {
         producer = adapter.getIGraphicBufferProducer();
         setUpProducer(producer);
     }
@@ -205,10 +226,10 @@
                     EXPECT_GE(epsilon, abs(g - *(pixel + 1)));
                     EXPECT_GE(epsilon, abs(b - *(pixel + 2)));
                 }
+                ASSERT_EQ(false, ::testing::Test::HasFailure());
             }
         }
         captureBuf->unlock();
-        ASSERT_EQ(false, ::testing::Test::HasFailure());
     }
 
     static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
@@ -315,7 +336,8 @@
 
     nsecs_t desiredPresentTime = systemTime() + nsecs_t(5 * 1e8);
     IGraphicBufferProducer::QueueBufferOutput qbOutput;
-    IGraphicBufferProducer::QueueBufferInput input(desiredPresentTime, false, HAL_DATASPACE_UNKNOWN,
+    IGraphicBufferProducer::QueueBufferInput input(desiredPresentTime, true /* autotimestamp */,
+                                                   HAL_DATASPACE_UNKNOWN,
                                                    Rect(mDisplayWidth, mDisplayHeight),
                                                    NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
                                                    Fence::NO_FENCE);
@@ -351,7 +373,8 @@
     buf->unlock();
 
     IGraphicBufferProducer::QueueBufferOutput qbOutput;
-    IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+    IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+                                                   HAL_DATASPACE_UNKNOWN,
                                                    Rect(mDisplayWidth, mDisplayHeight),
                                                    NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
                                                    Fence::NO_FENCE);
@@ -396,7 +419,8 @@
                                               nullptr, nullptr);
         ASSERT_EQ(NO_ERROR, ret);
         IGraphicBufferProducer::QueueBufferOutput qbOutput;
-        IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+        IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+                                                       HAL_DATASPACE_UNKNOWN,
                                                        Rect(mDisplayWidth, mDisplayHeight),
                                                        NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
                                                        Fence::NO_FENCE);
@@ -429,7 +453,8 @@
     buf->unlock();
 
     IGraphicBufferProducer::QueueBufferOutput qbOutput;
-    IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+    IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+                                                   HAL_DATASPACE_UNKNOWN,
                                                    Rect(mDisplayWidth, mDisplayHeight / 2),
                                                    NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
                                                    Fence::NO_FENCE);
@@ -486,7 +511,8 @@
     buf->unlock();
 
     IGraphicBufferProducer::QueueBufferOutput qbOutput;
-    IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+    IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+                                                   HAL_DATASPACE_UNKNOWN,
                                                    Rect(bufferSideLength, finalCropSideLength),
                                                    NATIVE_WINDOW_SCALING_MODE_SCALE_CROP, 0,
                                                    Fence::NO_FENCE);
@@ -537,7 +563,8 @@
         ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
         ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
         IGraphicBufferProducer::QueueBufferOutput qbOutput;
-        IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+        IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+                                                       HAL_DATASPACE_UNKNOWN,
                                                        Rect(mDisplayWidth, mDisplayHeight),
                                                        NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
                                                        Fence::NO_FENCE);
@@ -577,7 +604,7 @@
     sp<IGraphicBufferProducer> slowIgbProducer;
     setUpProducer(slowAdapter, slowIgbProducer);
     nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
-    queueBuffer(slowIgbProducer, 0 /* r */, 0 /* g */, 0 /* b */, presentTimeDelay);
+    queueBuffer(slowIgbProducer, 0 /* r */, 255 /* g */, 0 /* b */, presentTimeDelay);
 
     BLASTBufferQueueHelper fastAdapter(bgSurface, mDisplayWidth, mDisplayHeight);
     sp<IGraphicBufferProducer> fastIgbProducer;
@@ -617,7 +644,8 @@
         fillQuadrants(buf);
 
         IGraphicBufferProducer::QueueBufferOutput qbOutput;
-        IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+        IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+                                                       HAL_DATASPACE_UNKNOWN,
                                                        Rect(bufWidth, bufHeight),
                                                        NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
                                                        tr, Fence::NO_FENCE);
@@ -838,6 +866,7 @@
     IGraphicBufferProducer::QueueBufferOutput qbOutput;
     nsecs_t requestedPresentTimeA = 0;
     nsecs_t postedTimeA = 0;
+    adapter.setTransactionCompleteCallback(1);
     setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true);
     history.applyDelta(qbOutput.frameTimestamps);
 
@@ -848,7 +877,7 @@
     ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
     ASSERT_GE(events->postedTime, postedTimeA);
 
-    adapter.waitForCallbacks();
+    adapter.waitForCallback(1);
 
     // queue another buffer so we query for frame event deltas
     nsecs_t requestedPresentTimeB = 0;