Merge "Move drag event to InputDispatcher (5/n)" into sc-dev
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 3184843..cedff0b 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -62,7 +62,7 @@
 
 using std::string;
 
-#define MAX_SYS_FILES 11
+#define MAX_SYS_FILES 12
 
 const char* k_traceTagsProperty = "debug.atrace.tags.enableflags";
 const char* k_userInitiatedTraceProperty = "debug.atrace.user_initiated";
@@ -177,6 +177,7 @@
         { OPT,      "events/power/suspend_resume/enable" },
         { OPT,      "events/cpuhp/cpuhp_enter/enable" },
         { OPT,      "events/cpuhp/cpuhp_exit/enable" },
+        { OPT,      "events/cpuhp/cpuhp_pause/enable" },
     } },
     { "membus",     "Memory Bus Utilization", 0, {
         { REQ,      "events/memory_bus/enable" },
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 006e532..37fc9a9 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -57,6 +57,8 @@
     chmod 0666 /sys/kernel/tracing/events/cpuhp/cpuhp_enter/enable
     chmod 0666 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_exit/enable
     chmod 0666 /sys/kernel/tracing/events/cpuhp/cpuhp_exit/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_pause/enable
+    chmod 0666 /sys/kernel/tracing/events/cpuhp/cpuhp_pause/enable
     chmod 0666 /sys/kernel/debug/tracing/events/power/gpu_frequency/enable
     chmod 0666 /sys/kernel/tracing/events/power/gpu_frequency/enable
     chmod 0666 /sys/kernel/debug/tracing/events/power/suspend_resume/enable
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 3c53c02..bbbe6f7 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -495,7 +495,7 @@
 
     Rect r = Rect(cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
             cc.rectangle().bottom());
-    t.setCrop_legacy(mLayers[id], r);
+    t.setCrop(mLayers[id], r);
 }
 
 void Replayer::setCornerRadius(SurfaceComposerClient::Transaction& t,
diff --git a/include/android/input.h b/include/android/input.h
index 6fe95c0..bb98beb 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -678,7 +678,7 @@
     /**
      * Axis constant: The movement of y position of a motion event.
      *
-     * Same as {@link RELATIVE_X}, but for y position.
+     * Same as {@link AMOTION_EVENT_AXIS_RELATIVE_X}, but for y position.
      */
     AMOTION_EVENT_AXIS_RELATIVE_Y = 28,
     /**
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 6447844..92b79c7 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -448,12 +448,14 @@
             float           data[16];
             ASensorVector   vector;
             ASensorVector   acceleration;
+            ASensorVector   gyro;
             ASensorVector   magnetic;
             float           temperature;
             float           distance;
             float           light;
             float           pressure;
             float           relative_humidity;
+            AUncalibratedEvent uncalibrated_acceleration;
             AUncalibratedEvent uncalibrated_gyro;
             AUncalibratedEvent uncalibrated_magnetic;
             AMetaDataEvent meta_data;
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index b836581..c17f822 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -467,3 +467,5 @@
 __END_DECLS
 
 #endif // ANDROID_SURFACE_CONTROL_H
+
+/** @} */
diff --git a/include/android/surface_texture.h b/include/android/surface_texture.h
index b227b32..4757254 100644
--- a/include/android/surface_texture.h
+++ b/include/android/surface_texture.h
@@ -176,3 +176,5 @@
 __END_DECLS
 
 #endif /* ANDROID_NATIVE_SURFACE_TEXTURE_H */
+
+/** @} */
diff --git a/include/android/surface_texture_jni.h b/include/android/surface_texture_jni.h
index e40686d..8448d8c 100644
--- a/include/android/surface_texture_jni.h
+++ b/include/android/surface_texture_jni.h
@@ -53,3 +53,5 @@
 __END_DECLS
 
 #endif /* ANDROID_NATIVE_SURFACE_TEXTURE_JNI_H */
+
+/** @} */
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index f1085cf..0386d66 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -138,7 +138,11 @@
         OP_RECORD_AUDIO_HOTWORD = 102,
         // Ops 103-105 are currently unused in native, and intentionally omitted
         OP_RECORD_AUDIO_OUTPUT = 106,
-        _NUM_OP = 107
+        OP_SCHEDULE_EXACT_ALARM = 107,
+        OP_FINE_LOCATION_SOURCE = 108,
+        OP_COARSE_LOCATION_SOURCE = 109,
+        OP_MANAGE_MEDIA = 110,
+        _NUM_OP = 111
     };
 
     AppOpsManager();
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 05eb64b..6c44726 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -82,7 +82,10 @@
      */
     template <class T, class... Args>
     static std::shared_ptr<T> make(Args&&... args) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
         T* t = new T(std::forward<Args>(args)...);
+#pragma clang diagnostic pop
         // warning: Potential leak of memory pointed to by 't' [clang-analyzer-unix.Malloc]
         return t->template ref<T>();  // NOLINT(clang-analyzer-unix.Malloc)
     }
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/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 989abd9..bb8124b 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -935,7 +935,8 @@
         return NO_ERROR;
     }
 
-    status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) override {
+    status_t setDisplayBrightness(const sp<IBinder>& displayToken,
+                                  const gui::DisplayBrightness& brightness) override {
         Parcel data, reply;
         status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         if (error != NO_ERROR) {
@@ -947,7 +948,7 @@
             ALOGE("setDisplayBrightness: failed to write display token: %d", error);
             return error;
         }
-        error = data.writeFloat(brightness);
+        error = data.writeParcelable(brightness);
         if (error != NO_ERROR) {
             ALOGE("setDisplayBrightness: failed to write brightness: %d", error);
             return error;
@@ -1832,8 +1833,8 @@
                 ALOGE("setDisplayBrightness: failed to read display token: %d", error);
                 return error;
             }
-            float brightness = -1.0f;
-            error = data.readFloat(&brightness);
+            gui::DisplayBrightness brightness;
+            error = data.readParcelable(&brightness);
             if (error != NO_ERROR) {
                 ALOGE("setDisplayBrightness: failed to read brightness: %d", error);
                 return error;
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 288bf92..7a18ca6 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -41,7 +41,6 @@
         flags(0),
         mask(0),
         reserved(0),
-        crop_legacy(Rect::INVALID_RECT),
         cornerRadius(0.0f),
         backgroundBlurRadius(0),
         barrierFrameNumber(0),
@@ -64,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;
@@ -85,7 +85,7 @@
     SAFE_PARCEL(output.writeUint32, flags);
     SAFE_PARCEL(output.writeUint32, mask);
     SAFE_PARCEL(matrix.write, output);
-    SAFE_PARCEL(output.write, crop_legacy);
+    SAFE_PARCEL(output.write, crop);
     SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, barrierSurfaceControl_legacy);
     SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, reparentSurfaceControl);
     SAFE_PARCEL(output.writeUint64, barrierFrameNumber);
@@ -153,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) {
@@ -191,7 +192,7 @@
     SAFE_PARCEL(input.readUint32, &mask);
 
     SAFE_PARCEL(matrix.read, input);
-    SAFE_PARCEL(input.read, crop_legacy);
+    SAFE_PARCEL(input.read, crop);
     SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &barrierSurfaceControl_legacy);
     SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &reparentSurfaceControl);
     SAFE_PARCEL(input.readUint64, &barrierFrameNumber);
@@ -276,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();
@@ -407,10 +414,6 @@
         what |= eLayerStackChanged;
         layerStack = other.layerStack;
     }
-    if (other.what & eCropChanged_legacy) {
-        what |= eCropChanged_legacy;
-        crop_legacy = other.crop_legacy;
-    }
     if (other.what & eCornerRadiusChanged) {
         what |= eCornerRadiusChanged;
         cornerRadius = other.cornerRadius;
@@ -548,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 5e8ab92..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);
@@ -1048,15 +1099,15 @@
     return *this;
 }
 
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop_legacy(
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop(
         const sp<SurfaceControl>& sc, const Rect& crop) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->what |= layer_state_t::eCropChanged_legacy;
-    s->crop_legacy = crop;
+    s->what |= layer_state_t::eCropChanged;
+    s->crop = crop;
 
     registerSurfaceControlForCallback(sc);
     return *this;
@@ -1204,20 +1255,6 @@
     return *this;
 }
 
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop(
-        const sp<SurfaceControl>& sc, const Rect& crop) {
-    layer_state_t* s = getLayerState(sc);
-    if (!s) {
-        mStatus = BAD_INDEX;
-        return *this;
-    }
-    s->what |= layer_state_t::eCropChanged;
-    s->crop = crop;
-
-    registerSurfaceControlForCallback(sc);
-    return *this;
-}
-
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrame(
         const sp<SurfaceControl>& sc, const Rect& frame) {
     layer_state_t* s = getLayerState(sc);
@@ -1233,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);
 
@@ -1251,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);
@@ -1458,7 +1526,7 @@
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeometry(
         const sp<SurfaceControl>& sc, const Rect& source, const Rect& dst, int transform) {
-    setCrop_legacy(sc, source);
+    setCrop(sc, source);
 
     int x = dst.left;
     int y = dst.top;
@@ -1998,7 +2066,7 @@
 }
 
 status_t SurfaceComposerClient::setDisplayBrightness(const sp<IBinder>& displayToken,
-                                                     float brightness) {
+                                                     const gui::DisplayBrightness& brightness) {
     return ComposerService::getComposerService()->setDisplayBrightness(displayToken, brightness);
 }
 
diff --git a/libs/gui/aidl/android/gui/DisplayBrightness.aidl b/libs/gui/aidl/android/gui/DisplayBrightness.aidl
new file mode 100644
index 0000000..bdb8c63
--- /dev/null
+++ b/libs/gui/aidl/android/gui/DisplayBrightness.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright 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.
+ */
+
+package android.gui;
+
+/** @hide */
+parcelable DisplayBrightness {
+    // Range 0-1f, the desired sdr white point brightness
+    float sdrWhitePoint = 0f;
+
+    // The SDR white point in nits. -1 if unknown
+    float sdrWhitePointNits = -1f;
+
+    // Range 0-1f, the desired brightness of the display itself. -1f to turn the backlight off
+    float displayBrightness = 0f;
+
+    // The desired brightness of the display in nits. -1 if unknown
+    float displayBrightnessNits = -1f;
+}
\ No newline at end of file
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/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 88cfe4b..2a9e3f7 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <android/gui/DisplayBrightness.h>
 #include <android/gui/IFpsListener.h>
 #include <android/gui/IScreenCaptureListener.h>
 #include <android/gui/ITransactionTraceListener.h>
@@ -415,15 +416,15 @@
      * displayToken
      *      The token of the display whose brightness is set.
      * brightness
-     *      A number between 0.0f (minimum brightness) and 1.0 (maximum brightness), or -1.0f to
-     *      turn the backlight off.
+     *      The DisplayBrightness info to set on the desired display.
      *
      * Returns NO_ERROR upon success. Otherwise,
      *      NAME_NOT_FOUND    if the display is invalid, or
      *      BAD_VALUE         if the brightness is invalid, or
      *      INVALID_OPERATION if brightness operations are not supported.
      */
-    virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) = 0;
+    virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken,
+                                          const gui::DisplayBrightness& brightness) = 0;
 
     /*
      * Sends a power boost to the composer. This function is asynchronous.
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 b273805..d68a9cf 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -98,9 +98,9 @@
         eTransparentRegionChanged = 0x00000020,
         eFlagsChanged = 0x00000040,
         eLayerStackChanged = 0x00000080,
-        eCropChanged_legacy = 0x00000100,
+        /* 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, */
@@ -167,7 +167,6 @@
     uint32_t mask;
     uint8_t reserved;
     matrix22_t matrix;
-    Rect crop_legacy;
     float cornerRadius;
     uint32_t backgroundBlurRadius;
     sp<SurfaceControl> barrierSurfaceControl_legacy;
@@ -249,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 de88943..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*/,
@@ -209,7 +212,8 @@
      *      BAD_VALUE         if the brightness value is invalid, or
      *      INVALID_OPERATION if brightness operaetions are not supported.
      */
-    static status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness);
+    static status_t setDisplayBrightness(const sp<IBinder>& displayToken,
+                                         const gui::DisplayBrightness& brightness);
 
     /*
      * Sends a power boost to the composer. This function is asynchronous.
@@ -387,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();
@@ -436,7 +442,7 @@
                 float alpha);
         Transaction& setMatrix(const sp<SurfaceControl>& sc,
                 float dsdx, float dtdx, float dtdy, float dsdy);
-        Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop);
+        Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
         Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
         Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc,
                                              int backgroundBlurRadius);
@@ -469,9 +475,9 @@
         Transaction& setTransform(const sp<SurfaceControl>& sc, uint32_t transform);
         Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
                                                   bool transformToDisplayInverse);
-        Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
         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 fb07a19..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);
@@ -458,7 +483,7 @@
     ASSERT_NE(nullptr, bg.get());
     Transaction t;
     t.setLayerStack(bg, 0)
-            .setCrop_legacy(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+            .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
             .setColor(bg, half3{0, 0, 0})
             .setLayer(bg, 0)
             .apply();
@@ -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;
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index d65a40b..59e5c13 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -195,7 +195,7 @@
         t.setInputWindowInfo(mSurfaceControl, mInputInfo);
         t.setLayer(mSurfaceControl, LAYER_BASE);
         t.setPosition(mSurfaceControl, x, y);
-        t.setCrop_legacy(mSurfaceControl, crop);
+        t.setCrop(mSurfaceControl, crop);
         t.setAlpha(mSurfaceControl, 1);
         t.apply(true);
     }
@@ -670,7 +670,7 @@
     parentSurface->showAt(100, 100);
 
     nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
-        t.setCrop_legacy(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
+        t.setCrop(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
         t.reparent(sc, parentSurface->mSurfaceControl);
     });
 
@@ -694,7 +694,7 @@
     parentSurface->showAt(50, 50);
 
     nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
-        t.setCrop_legacy(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
+        t.setCrop(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
         t.reparent(sc, parentSurface->mSurfaceControl);
     });
 
diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp
index 5c1bebb..0cd150d 100644
--- a/libs/gui/tests/SamplingDemo.cpp
+++ b/libs/gui/tests/SamplingDemo.cpp
@@ -46,8 +46,7 @@
 
         SurfaceComposerClient::Transaction{}
                 .setLayer(mButton, 0x7fffffff)
-                .setCrop_legacy(mButton,
-                                {0, 0, width - 2 * BUTTON_PADDING, height - 2 * BUTTON_PADDING})
+                .setCrop(mButton, {0, 0, width - 2 * BUTTON_PADDING, height - 2 * BUTTON_PADDING})
                 .setPosition(mButton, samplingArea.left + BUTTON_PADDING,
                              samplingArea.top + BUTTON_PADDING)
                 .setColor(mButton, half3{1, 1, 1})
@@ -59,9 +58,8 @@
 
         SurfaceComposerClient::Transaction{}
                 .setLayer(mButtonBlend, 0x7ffffffe)
-                .setCrop_legacy(mButtonBlend,
-                                {0, 0, width - 2 * SAMPLE_AREA_PADDING,
-                                 height - 2 * SAMPLE_AREA_PADDING})
+                .setCrop(mButtonBlend,
+                         {0, 0, width - 2 * SAMPLE_AREA_PADDING, height - 2 * SAMPLE_AREA_PADDING})
                 .setPosition(mButtonBlend, samplingArea.left + SAMPLE_AREA_PADDING,
                              samplingArea.top + SAMPLE_AREA_PADDING)
                 .setColor(mButtonBlend, half3{1, 1, 1})
@@ -77,7 +75,7 @@
 
             SurfaceComposerClient::Transaction{}
                     .setLayer(mSamplingArea, 0x7ffffffd)
-                    .setCrop_legacy(mSamplingArea, {0, 0, 100, 32})
+                    .setCrop(mSamplingArea, {0, 0, 100, 32})
                     .setPosition(mSamplingArea, 490, 1606)
                     .setColor(mSamplingArea, half3{0, 1, 0})
                     .setAlpha(mSamplingArea, 0.1)
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index e8fb71d..8d7f8c9 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -811,7 +811,7 @@
         return NO_ERROR;
     }
     status_t setDisplayBrightness(const sp<IBinder>& /*displayToken*/,
-                                  float /*brightness*/) override {
+                                  const gui::DisplayBrightness& /*brightness*/) override {
         return NO_ERROR;
     }
 
diff --git a/libs/input/tests/Flags_test.cpp b/libs/input/tests/Flags_test.cpp
index 0dbb4cf..6de030f 100644
--- a/libs/input/tests/Flags_test.cpp
+++ b/libs/input/tests/Flags_test.cpp
@@ -148,6 +148,11 @@
     ASSERT_NE(flags1, flags2);
 }
 
+TEST(Flags, GetValue) {
+    Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+    ASSERT_EQ(flags.get(), 0x3);
+}
+
 TEST(Flags, String_NoFlags) {
     Flags<TestFlags> flags;
     ASSERT_EQ(flags.string(), "0x0");
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 66efb09..f76bfa2 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -651,9 +651,8 @@
     if (mBlurFilter) {
         bool requiresCompositionLayer = false;
         for (const auto& layer : layers) {
-            if (layer->backgroundBlurRadius > 0) {
-                // when skbug.com/11208 and b/176903027 are resolved we can add the additional
-                // restriction for layer->backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius
+            if (layer->backgroundBlurRadius > 0 &&
+                layer->backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius) {
                 requiresCompositionLayer = true;
             }
             for (auto region : layer->blurRegions) {
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 8a2828c..1af70a4 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -68,6 +68,7 @@
         "libcutils",
         "libinput",
         "liblog",
+        "libstatslog",
         "libui",
         "libutils",
     ],
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 1a7fcd5..07011f5 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -37,13 +37,14 @@
 
 // #define LOG_NDEBUG 0
 #include <android-base/file.h>
-#include <android-base/strings.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <cutils/properties.h>
 #include <input/KeyCharacterMap.h>
 #include <input/KeyLayoutMap.h>
 #include <input/VirtualKeyMap.h>
 #include <openssl/sha.h>
+#include <statslog.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
 #include <utils/Timers.h>
@@ -1461,7 +1462,7 @@
             for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
                  it++) {
                 std::unique_ptr<TouchVideoDevice>& videoDevice = *it;
-                if (tryAddVideoDevice(*device, videoDevice)) {
+                if (tryAddVideoDeviceLocked(*device, videoDevice)) {
                     // videoDevice was transferred to 'device'
                     it = mUnattachedVideoDevices.erase(it);
                     break;
@@ -1771,6 +1772,13 @@
     }
 }
 
+void EventHub::reportDeviceAddedForStatisticsLocked(const InputDeviceIdentifier& identifier,
+                                                    Flags<InputDeviceClass> classes) {
+    android::util::stats_write(android::util::INPUTDEVICE_REGISTERED, identifier.name.c_str(),
+                               identifier.vendor, identifier.product, identifier.version,
+                               identifier.bus, identifier.uniqueId.c_str(), classes.get());
+}
+
 void EventHub::openDeviceLocked(const std::string& devicePath) {
     // If an input device happens to register around the time when EventHub's constructor runs, it
     // is possible that the same input event node (for example, /dev/input/event3) will be noticed
@@ -2076,7 +2084,7 @@
     }
     // Transfer ownership of this video device to a matching input device
     for (const auto& [id, device] : mDevices) {
-        if (tryAddVideoDevice(*device, videoDevice)) {
+        if (tryAddVideoDeviceLocked(*device, videoDevice)) {
             return; // 'device' now owns 'videoDevice'
         }
     }
@@ -2088,8 +2096,8 @@
     mUnattachedVideoDevices.push_back(std::move(videoDevice));
 }
 
-bool EventHub::tryAddVideoDevice(EventHub::Device& device,
-                                 std::unique_ptr<TouchVideoDevice>& videoDevice) {
+bool EventHub::tryAddVideoDeviceLocked(EventHub::Device& device,
+                                       std::unique_ptr<TouchVideoDevice>& videoDevice) {
     if (videoDevice->getName() != device.identifier.name) {
         return false;
     }
@@ -2163,6 +2171,7 @@
 }
 
 void EventHub::addDeviceLocked(std::unique_ptr<Device> device) {
+    reportDeviceAddedForStatisticsLocked(device->identifier, device->classes);
     mOpeningDevices.push_back(std::move(device));
 }
 
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 5e5f85e..2afaa85 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -570,8 +570,8 @@
     /**
      * Create a new device for the provided path.
      */
-    void openDeviceLocked(const std::string& devicePath);
-    void openVideoDeviceLocked(const std::string& devicePath);
+    void openDeviceLocked(const std::string& devicePath) REQUIRES(mLock);
+    void openVideoDeviceLocked(const std::string& devicePath) REQUIRES(mLock);
     /**
      * Try to associate a video device with an input device. If the association succeeds,
      * the videoDevice is moved into the input device. 'videoDevice' will become null if this
@@ -579,39 +579,42 @@
      * Return true if the association succeeds.
      * Return false otherwise.
      */
-    bool tryAddVideoDevice(Device& device, std::unique_ptr<TouchVideoDevice>& videoDevice);
-    void createVirtualKeyboardLocked();
-    void addDeviceLocked(std::unique_ptr<Device> device);
-    void assignDescriptorLocked(InputDeviceIdentifier& identifier);
+    bool tryAddVideoDeviceLocked(Device& device, std::unique_ptr<TouchVideoDevice>& videoDevice)
+            REQUIRES(mLock);
+    void createVirtualKeyboardLocked() REQUIRES(mLock);
+    void addDeviceLocked(std::unique_ptr<Device> device) REQUIRES(mLock);
+    void assignDescriptorLocked(InputDeviceIdentifier& identifier) REQUIRES(mLock);
 
-    void closeDeviceByPathLocked(const std::string& devicePath);
-    void closeVideoDeviceByPathLocked(const std::string& devicePath);
-    void closeDeviceLocked(Device& device);
-    void closeAllDevicesLocked();
+    void closeDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock);
+    void closeVideoDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock);
+    void closeDeviceLocked(Device& device) REQUIRES(mLock);
+    void closeAllDevicesLocked() REQUIRES(mLock);
 
     status_t registerFdForEpoll(int fd);
     status_t unregisterFdFromEpoll(int fd);
-    status_t registerDeviceForEpollLocked(Device& device);
-    void registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice);
-    status_t unregisterDeviceFromEpollLocked(Device& device);
-    void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice);
+    status_t registerDeviceForEpollLocked(Device& device) REQUIRES(mLock);
+    void registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice) REQUIRES(mLock);
+    status_t unregisterDeviceFromEpollLocked(Device& device) REQUIRES(mLock);
+    void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice) REQUIRES(mLock);
 
-    status_t scanDirLocked(const std::string& dirname);
-    status_t scanVideoDirLocked(const std::string& dirname);
-    void scanDevicesLocked();
-    status_t readNotifyLocked();
+    status_t scanDirLocked(const std::string& dirname) REQUIRES(mLock);
+    status_t scanVideoDirLocked(const std::string& dirname) REQUIRES(mLock);
+    void scanDevicesLocked() REQUIRES(mLock);
+    status_t readNotifyLocked() REQUIRES(mLock);
 
-    Device* getDeviceByDescriptorLocked(const std::string& descriptor) const;
-    Device* getDeviceLocked(int32_t deviceId) const;
-    Device* getDeviceByPathLocked(const std::string& devicePath) const;
+    Device* getDeviceByDescriptorLocked(const std::string& descriptor) const REQUIRES(mLock);
+    Device* getDeviceLocked(int32_t deviceId) const REQUIRES(mLock);
+    Device* getDeviceByPathLocked(const std::string& devicePath) const REQUIRES(mLock);
     /**
      * Look through all available fd's (both for input devices and for video devices),
      * and return the device pointer.
      */
-    Device* getDeviceByFdLocked(int fd) const;
+    Device* getDeviceByFdLocked(int fd) const REQUIRES(mLock);
 
-    int32_t getNextControllerNumberLocked(const std::string& name);
-    void releaseControllerNumberLocked(int32_t num);
+    int32_t getNextControllerNumberLocked(const std::string& name) REQUIRES(mLock);
+    void releaseControllerNumberLocked(int32_t num) REQUIRES(mLock);
+    void reportDeviceAddedForStatisticsLocked(const InputDeviceIdentifier& identifier,
+                                              Flags<InputDeviceClass> classes) REQUIRES(mLock);
 
     // Protect all internal state.
     mutable std::mutex mLock;
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 93fb2cd..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;
@@ -89,7 +90,6 @@
                    bool /*allowNonRectPreservingTransforms*/) override {
         return false;
     }
-    bool setCrop_legacy(const Rect& /*crop*/) override { return false; }
     void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/,
                                       uint64_t /*frameNumber*/) override {}
     void deferTransactionUntil_legacy(const sp<Layer>& /*barrierLayer*/,
@@ -112,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; }
 
@@ -171,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/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 5f834be..a3e84e2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -89,6 +89,7 @@
         sp<GraphicBuffer> buffer = nullptr;
         sp<Fence> acquireFence = nullptr;
         Rect displayFrame = {};
+        ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
     } overrideInfo;
 
     /*
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index fa87fb8..b0e42b7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -62,6 +62,7 @@
     size_t getAge() const { return mAge; }
     const sp<GraphicBuffer>& getBuffer() const { return mTexture.getBuffer(); }
     const sp<Fence>& getDrawFence() const { return mDrawFence; }
+    ui::Dataspace getOutputDataspace() const { return mOutputDataspace; }
 
     NonBufferHash getNonBufferHash() const;
 
@@ -80,6 +81,7 @@
     void setLastUpdate(std::chrono::steady_clock::time_point now) { mLastUpdate = now; }
     void append(const CachedSet& other) {
         mTexture.setBuffer(nullptr, nullptr);
+        mOutputDataspace = ui::Dataspace::UNKNOWN;
         mDrawFence = nullptr;
 
         mLayers.insert(mLayers.end(), other.mLayers.cbegin(), other.mLayers.cend());
@@ -90,7 +92,8 @@
     }
     void incrementAge() { ++mAge; }
 
-    void render(renderengine::RenderEngine&);
+    // Renders the cached set with the supplied output dataspace.
+    void render(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
 
     void dump(std::string& result) const;
 
@@ -129,6 +132,7 @@
 
     Texture mTexture;
     sp<Fence> mDrawFence;
+    ui::Dataspace mOutputDataspace;
 
     static const bool sDebugHighlighLayers;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 582723d..5b9a9f0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -42,7 +42,8 @@
     NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash,
                                 std::chrono::steady_clock::time_point now);
 
-    void renderCachedSets(renderengine::RenderEngine&);
+    // Renders the newest cached sets with the supplied output dataspace
+    void renderCachedSets(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
 
     void reset();
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index 7f8cb4e..a3beadc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -278,8 +278,8 @@
                            }};
 
     using DataspaceState = OutputLayerState<ui::Dataspace, LayerStateField::Dataspace>;
-    DataspaceState mDataspace{[](auto layer) { return layer->getState().dataspace; },
-                              DataspaceState::getHalToStrings()};
+    DataspaceState mOutputDataspace{[](auto layer) { return layer->getState().dataspace; },
+                                    DataspaceState::getHalToStrings()};
 
     // TODO(b/180638831): Buffer format
 
@@ -341,7 +341,7 @@
     std::array<const StateInterface*, 13> getNonUniqueFields() const {
         return {
                 &mDisplayFrame,   &mSourceCrop,      &mZOrder,         &mBufferTransform,
-                &mBlendMode,      &mAlpha,           &mVisibleRegion,  &mDataspace,
+                &mBlendMode,      &mAlpha,           &mVisibleRegion,  &mOutputDataspace,
                 &mColorTransform, &mCompositionType, &mSidebandStream, &mBuffer,
                 &mSolidColor,
         };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index e96abb7..89de34d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -59,7 +59,7 @@
             compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers);
 
     // The planner will call to the Flattener to render any pending cached set
-    void renderCachedSets(renderengine::RenderEngine&);
+    void renderCachedSets(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
 
     void dump(const Vector<String16>& args, std::string&);
 
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index dc1aacc..ded2dcc 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1252,7 +1252,7 @@
 
 void Output::renderCachedSets() {
     if (mPlanner) {
-        mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine());
+        mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine(), getState().dataspace);
     }
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 54784a2..b364649 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -681,6 +681,7 @@
                                               .buffer = getState().overrideInfo.buffer,
                                               .fence = getState().overrideInfo.acquireFence,
                                       }};
+    settings.sourceDataspace = getState().overrideInfo.dataspace;
     settings.alpha = 1.0f;
 
     return {static_cast<LayerFE::LayerSettings>(settings)};
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index 165e320..45dce98 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -68,6 +68,10 @@
     dumpVal(out, "bufferTransform", toString(bufferTransform), bufferTransform);
     dumpVal(out, "dataspace", toString(dataspace), dataspace);
     dumpVal(out, "z-index", z);
+    dumpVal(out, "override buffer", overrideInfo.buffer.get());
+    dumpVal(out, "override acquire fence", overrideInfo.acquireFence.get());
+    dumpVal(out, "override display frame", overrideInfo.displayFrame);
+    dumpVal(out, "override dataspace", toString(overrideInfo.dataspace), overrideInfo.dataspace);
 
     if (hwc) {
         dumpHwc(*hwc, out);
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index ba03655..137697b 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -79,10 +79,11 @@
         return mFingerprint;
     }
 
-    // TODO(b/181192080): Add all fields which contribute to geometry of override layer (e.g.,
-    // dataspace)
+    // TODO(b/182614524): We sometimes match this with LayerState hashes. Determine if that is
+    // necessary (and therefore we need to match implementations).
     size_t hash = 0;
     android::hashCombineSingle(hash, mBounds);
+    android::hashCombineSingle(hash, mOutputDataspace);
     return hash;
 }
 
@@ -148,10 +149,11 @@
     }
 }
 
-void CachedSet::render(renderengine::RenderEngine& renderEngine) {
+void CachedSet::render(renderengine::RenderEngine& renderEngine, ui::Dataspace outputDataspace) {
     renderengine::DisplaySettings displaySettings{
             .physicalDisplay = Rect(0, 0, mBounds.getWidth(), mBounds.getHeight()),
             .clip = mBounds,
+            .outputDataspace = outputDataspace,
     };
 
     Region clearRegion = Region::INVALID_REGION;
@@ -163,8 +165,7 @@
             .supportsProtectedContent = false,
             .clearRegion = clearRegion,
             .viewport = viewport,
-            // TODO(181192086): Propagate the Output's dataspace instead of using UNKNOWN
-            .dataspace = ui::Dataspace::UNKNOWN,
+            .dataspace = outputDataspace,
             .realContentIsVisible = true,
             .clearContent = false,
             .disableBlurs = false,
@@ -216,6 +217,7 @@
     if (result == NO_ERROR) {
         mTexture.setBuffer(buffer, &renderEngine);
         mDrawFence = new Fence(drawFence.release());
+        mOutputDataspace = outputDataspace;
     }
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index d304c9f..30b5761 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -51,12 +51,13 @@
     return hash;
 }
 
-void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine) {
+void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine,
+                                 ui::Dataspace outputDataspace) {
     if (!mNewCachedSet) {
         return;
     }
 
-    mNewCachedSet->render(renderEngine);
+    mNewCachedSet->render(renderEngine, outputDataspace);
 }
 
 void Flattener::reset() {
@@ -223,6 +224,7 @@
                                 .buffer = mNewCachedSet->getBuffer(),
                                 .acquireFence = mNewCachedSet->getDrawFence(),
                                 .displayFrame = mNewCachedSet->getBounds(),
+                                .dataspace = mNewCachedSet->getOutputDataspace(),
                         };
                         ++incomingLayerIter;
                     }
@@ -253,6 +255,7 @@
                         .buffer = currentLayerIter->getBuffer(),
                         .acquireFence = currentLayerIter->getDrawFence(),
                         .displayFrame = currentLayerIter->getBounds(),
+                        .dataspace = currentLayerIter->getOutputDataspace(),
                 };
                 ++incomingLayerIter;
             }
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index f3746de..222b2be 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -158,7 +158,8 @@
             lhs.mSourceCrop == rhs.mSourceCrop && lhs.mZOrder == rhs.mZOrder &&
             lhs.mBufferTransform == rhs.mBufferTransform && lhs.mBlendMode == rhs.mBlendMode &&
             lhs.mAlpha == rhs.mAlpha && lhs.mVisibleRegion == rhs.mVisibleRegion &&
-            lhs.mDataspace == rhs.mDataspace && lhs.mColorTransform == rhs.mColorTransform &&
+            lhs.mOutputDataspace == rhs.mOutputDataspace &&
+            lhs.mColorTransform == rhs.mColorTransform &&
             lhs.mCompositionType == rhs.mCompositionType &&
             lhs.mSidebandStream == rhs.mSidebandStream && lhs.mBuffer == rhs.mBuffer &&
             (lhs.mCompositionType.get() != hal::Composition::SOLID_COLOR ||
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 4570253..87721c7 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -133,8 +133,9 @@
                             finalPlan);
 }
 
-void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine) {
-    mFlattener.renderCachedSets(renderEngine);
+void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine,
+                               ui::Dataspace outputDataspace) {
+    mFlattener.renderCachedSets(renderEngine, outputDataspace);
 }
 
 void Planner::dump(const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index c33828f..377f817 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -303,6 +303,7 @@
         EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.clip);
         EXPECT_EQ(0.5f, layers[0]->alpha);
         EXPECT_EQ(0.75f, layers[1]->alpha);
+        EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
 
         return NO_ERROR;
     };
@@ -311,7 +312,7 @@
     EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
     EXPECT_CALL(mRenderEngine, cacheExternalTextureBuffer(_));
-    cachedSet.render(mRenderEngine);
+    cachedSet.render(mRenderEngine, ui::Dataspace::SRGB);
     expectReadyBuffer(cachedSet);
 
     // Now check that appending a new cached set properly cleans up RenderEngine resources.
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index c4bd5b3..bd77559 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -134,13 +134,13 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine);
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
 
     // same geometry, update the internal layer stack
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine);
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
 }
 
 void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
@@ -150,7 +150,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine);
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
 
     for (const auto layer : layers) {
         EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -160,7 +160,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine);
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
 
     const auto buffer = layers[0]->getOutputLayer()->getState().overrideInfo.buffer;
     EXPECT_NE(nullptr, buffer);
@@ -195,7 +195,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine);
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
 }
 
 TEST_F(FlattenerTest, flattenLayers_basicFlatten) {
@@ -241,7 +241,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine);
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -276,7 +276,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine);
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -313,7 +313,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine);
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -322,7 +322,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine);
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_NE(nullptr, overrideBuffer2);
@@ -335,7 +335,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine);
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_NE(nullptr, overrideBuffer2);
@@ -344,7 +344,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine);
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -386,7 +386,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine);
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
 
     EXPECT_EQ(nullptr, overrideBuffer1);
     EXPECT_EQ(nullptr, overrideBuffer2);
@@ -399,7 +399,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine);
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -411,7 +411,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine);
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -426,7 +426,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine);
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -437,7 +437,7 @@
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
-    mFlattener->renderCachedSets(mRenderEngine);
+    mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
 
     EXPECT_NE(nullptr, overrideBuffer1);
     EXPECT_EQ(overrideBuffer1, overrideBuffer2);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 44f1a70..cd3e8ad 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -102,8 +102,8 @@
     mCurrentState.active_legacy.h = args.h;
     mCurrentState.flags = layerFlags;
     mCurrentState.active_legacy.transform.set(0, 0);
-    mCurrentState.crop_legacy.makeInvalid();
-    mCurrentState.requestedCrop_legacy = mCurrentState.crop_legacy;
+    mCurrentState.crop.makeInvalid();
+    mCurrentState.requestedCrop = mCurrentState.crop;
     mCurrentState.z = 0;
     mCurrentState.color.a = 1.0f;
     mCurrentState.layerStack = 0;
@@ -949,13 +949,12 @@
                  "            requested={ wh={%4u,%4u} }}\n",
                  this, getName().c_str(), getBufferTransform(), getEffectiveScalingMode(),
                  stateToCommit->active_legacy.w, stateToCommit->active_legacy.h,
-                 stateToCommit->crop_legacy.left, stateToCommit->crop_legacy.top,
-                 stateToCommit->crop_legacy.right, stateToCommit->crop_legacy.bottom,
-                 stateToCommit->crop_legacy.getWidth(), stateToCommit->crop_legacy.getHeight(),
-                 stateToCommit->requested_legacy.w, stateToCommit->requested_legacy.h,
-                 s.active_legacy.w, s.active_legacy.h, s.crop_legacy.left, s.crop_legacy.top,
-                 s.crop_legacy.right, s.crop_legacy.bottom, s.crop_legacy.getWidth(),
-                 s.crop_legacy.getHeight(), s.requested_legacy.w, s.requested_legacy.h);
+                 stateToCommit->crop.left, stateToCommit->crop.top, stateToCommit->crop.right,
+                 stateToCommit->crop.bottom, stateToCommit->crop.getWidth(),
+                 stateToCommit->crop.getHeight(), stateToCommit->requested_legacy.w,
+                 stateToCommit->requested_legacy.h, s.active_legacy.w, s.active_legacy.h,
+                 s.crop.left, s.crop.top, s.crop.right, s.crop.bottom, s.crop.getWidth(),
+                 s.crop.getHeight(), s.requested_legacy.w, s.requested_legacy.h);
     }
 
     // Don't let Layer::doTransaction update the drawing state
@@ -1049,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;
@@ -1335,11 +1337,11 @@
     return true;
 }
 
-bool Layer::setCrop_legacy(const Rect& crop) {
-    if (mCurrentState.requestedCrop_legacy == crop) return false;
+bool Layer::setCrop(const Rect& crop) {
+    if (mCurrentState.requestedCrop == crop) return false;
     mCurrentState.sequence++;
-    mCurrentState.requestedCrop_legacy = crop;
-    mCurrentState.crop_legacy = crop;
+    mCurrentState.requestedCrop = crop;
+    mCurrentState.crop = crop;
 
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
@@ -1744,7 +1746,7 @@
     info.mZ = ds.z;
     info.mWidth = ds.active_legacy.w;
     info.mHeight = ds.active_legacy.h;
-    info.mCrop = ds.crop_legacy;
+    info.mCrop = ds.crop;
     info.mColor = ds.color;
     info.mFlags = ds.flags;
     info.mPixelFormat = getPixelFormat();
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 4a114e2..85ff479 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -214,8 +214,8 @@
         bool modified;
 
         // Crop is expressed in layer space coordinate.
-        Rect crop_legacy;
-        Rect requestedCrop_legacy;
+        Rect crop;
+        Rect requestedCrop;
 
         // If set, defers this state update until the identified Layer
         // receives a frame with the given frameNumber
@@ -257,7 +257,6 @@
         uint32_t bufferTransform;
         bool transformToDisplayInverse;
 
-        Rect crop;
         Region transparentRegionHint;
 
         sp<GraphicBuffer> buffer;
@@ -313,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
@@ -423,7 +423,7 @@
     // space for top-level layers.
     virtual bool setPosition(float x, float y);
     // Buffer space
-    virtual bool setCrop_legacy(const Rect& crop);
+    virtual bool setCrop(const Rect& crop);
 
     // TODO(b/38182121): Could we eliminate the various latching modes by
     // using the layer hierarchy?
@@ -462,13 +462,13 @@
     // Used only to set BufferStateLayer state
     virtual bool setTransform(uint32_t /*transform*/) { return false; };
     virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
-    virtual bool setCrop(const Rect& /*crop*/) { return false; };
     virtual bool setFrame(const Rect& /*frame*/) { return false; };
     virtual 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*/) {
+                           const FrameTimelineInfo& /*info*/,
+                           const sp<ITransactionCompletedListener>& /* releaseBufferListener */) {
         return false;
     };
     virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
@@ -549,7 +549,7 @@
     virtual Region getActiveTransparentRegion(const Layer::State& s) const {
         return s.activeTransparentRegion_legacy;
     }
-    virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
+    virtual Rect getCrop(const Layer::State& s) const { return s.crop; }
     virtual bool needsFiltering(const DisplayDevice*) const { return false; }
 
     // True if this layer requires filtering
@@ -776,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/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
index 053b7f7..1c0263b 100644
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -80,24 +80,23 @@
             // recompute visible region
             mRecomputeVisibleRegions = true;
 
-            if (mFront.crop_legacy != mFront.requestedCrop_legacy) {
-                mFront.crop_legacy = mFront.requestedCrop_legacy;
-                mCurrent.crop_legacy = mFront.requestedCrop_legacy;
+            if (mFront.crop != mFront.requestedCrop) {
+                mFront.crop = mFront.requestedCrop;
+                mCurrent.crop = mFront.requestedCrop;
                 mRecomputeVisibleRegions = true;
             }
         }
 
         ALOGD_IF(DEBUG_RESIZE,
                  "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
-                 "  drawing={ active_legacy   ={ wh={%4u,%4u} crop_legacy={%4d,%4d,%4d,%4d} "
+                 "  drawing={ active_legacy   ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} "
                  "(%4d,%4d) "
                  "}\n"
                  "            requested_legacy={ wh={%4u,%4u} }}\n",
                  mName.c_str(), bufWidth, bufHeight, item.mTransform, item.mScalingMode,
-                 mFront.active_legacy.w, mFront.active_legacy.h, mFront.crop_legacy.left,
-                 mFront.crop_legacy.top, mFront.crop_legacy.right, mFront.crop_legacy.bottom,
-                 mFront.crop_legacy.getWidth(), mFront.crop_legacy.getHeight(),
-                 mFront.requested_legacy.w, mFront.requested_legacy.h);
+                 mFront.active_legacy.w, mFront.active_legacy.h, mFront.crop.left, mFront.crop.top,
+                 mFront.crop.right, mFront.crop.bottom, mFront.crop.getWidth(),
+                 mFront.crop.getHeight(), mFront.requested_legacy.w, mFront.requested_legacy.h);
     }
 
     if (!isFixedSize && !mStickyTransformSet) {
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 7d75657..61cc8a2 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -914,11 +914,6 @@
     }
 
     info->activeDisplayModeId = static_cast<int32_t>(display->getActiveMode()->getId().value());
-    if (display->isPrimary()) {
-        if (const auto mode = getDesiredActiveMode()) {
-            info->activeDisplayModeId = static_cast<int32_t>(mode->modeId.value());
-        }
-    }
 
     const auto& supportedModes = display->getSupportedModes();
     info->supportedDisplayModes.clear();
@@ -1467,14 +1462,16 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) {
+status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken,
+                                              const gui::DisplayBrightness& brightness) {
     if (!displayToken) {
         return BAD_VALUE;
     }
 
     return ftl::chain(schedule([=]() MAIN_THREAD {
                if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
-                   return getHwComposer().setDisplayBrightness(*displayId, brightness);
+                   return getHwComposer().setDisplayBrightness(*displayId,
+                                                               brightness.displayBrightness);
                } else {
                    ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
                    return ftl::yield<status_t>(NAME_NOT_FOUND);
@@ -3854,9 +3851,6 @@
         if (layer->setFlags(s.flags, s.mask))
             flags |= eTraversalNeeded;
     }
-    if (what & layer_state_t::eCropChanged_legacy) {
-        if (layer->setCrop_legacy(s.crop_legacy)) flags |= eTraversalNeeded;
-    }
     if (what & layer_state_t::eCornerRadiusChanged) {
         if (layer->setCornerRadius(s.cornerRadius))
             flags |= eTraversalNeeded;
@@ -4033,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/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 3787b9d..a5b06df 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -680,7 +680,8 @@
                                         float* outAppRequestRefreshRateMax) override;
     status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
                                          bool* outSupport) const override;
-    status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) override;
+    status_t setDisplayBrightness(const sp<IBinder>& displayToken,
+                                  const gui::DisplayBrightness& brightness) override;
     status_t notifyPowerBoost(int32_t boostId) override;
     status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
                                      float lightPosY, float lightPosZ, float lightRadius) override;
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index b0413f1..8a3be9f 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -137,7 +137,7 @@
     addTransparentRegionLocked(transaction, layerId,
                                layer->mCurrentState.activeTransparentRegion_legacy);
     addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack);
-    addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy);
+    addCropLocked(transaction, layerId, layer->mCurrentState.crop);
     addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius);
     addBackgroundBlurRadiusLocked(transaction, layerId, layer->mCurrentState.backgroundBlurRadius);
     addBlurRegionsLocked(transaction, layerId, layer->mCurrentState.blurRegions);
@@ -459,8 +459,8 @@
     if (state.what & layer_state_t::eLayerStackChanged) {
         addLayerStackLocked(transaction, layerId, state.layerStack);
     }
-    if (state.what & layer_state_t::eCropChanged_legacy) {
-        addCropLocked(transaction, layerId, state.crop_legacy);
+    if (state.what & layer_state_t::eCropChanged) {
+        addCropLocked(transaction, layerId, state.crop);
     }
     if (state.what & layer_state_t::eCornerRadiusChanged) {
         addCornerRadiusLocked(transaction, layerId, state.cornerRadius);
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/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index 7a3c45d..f470eda 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -55,7 +55,7 @@
 
     EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
     asTransaction([&](Transaction& t) {
-        t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+        t.setCrop(effectLayer, Rect(0, 0, 400, 400));
         t.show(effectLayer);
     });
 
@@ -76,7 +76,7 @@
 
     EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
     asTransaction([&](Transaction& t) {
-        t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+        t.setCrop(effectLayer, Rect(0, 0, 400, 400));
         t.show(effectLayer);
     });
 
@@ -97,7 +97,7 @@
 
     EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
     asTransaction([&](Transaction& t) {
-        t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+        t.setCrop(effectLayer, Rect(0, 0, 400, 400));
         t.setColor(effectLayer,
                    half3{Color::GREEN.r / 255.0f, Color::GREEN.g / 255.0f,
                          Color::GREEN.b / 255.0f});
@@ -127,10 +127,10 @@
     asTransaction([&](Transaction& t) {
         t.setLayer(leftLayer, mLayerZBase + 1);
         t.reparent(leftLayer, mParentLayer);
-        t.setCrop_legacy(leftLayer, leftRect);
+        t.setCrop(leftLayer, leftRect);
         t.setLayer(rightLayer, mLayerZBase + 2);
         t.reparent(rightLayer, mParentLayer);
-        t.setCrop_legacy(rightLayer, rightRect);
+        t.setCrop(rightLayer, rightRect);
         t.show(leftLayer);
         t.show(rightLayer);
     });
@@ -148,7 +148,7 @@
         t.setLayer(blurLayer, mLayerZBase + 3);
         t.reparent(blurLayer, mParentLayer);
         t.setBackgroundBlurRadius(blurLayer, blurRadius);
-        t.setCrop_legacy(blurLayer, blurRect);
+        t.setCrop(blurLayer, blurRect);
         t.setFrame(blurLayer, blurRect);
         t.setAlpha(blurLayer, 0.0f);
         t.show(blurLayer);
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/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index b35eaa9..7505e6e 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -515,7 +515,7 @@
                                                 ISurfaceComposerClient::eFXSurfaceEffect));
 
     Transaction()
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setCrop(colorLayer, Rect(0, 0, 32, 32))
             .setLayer(colorLayer, mLayerZBase + 1)
             .apply();
 
@@ -554,7 +554,7 @@
         case ISurfaceComposerClient::eFXSurfaceEffect:
             ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 0, 0, layerType));
             Transaction()
-                    .setCrop_legacy(layer, Rect(0, 0, width, height))
+                    .setCrop(layer, Rect(0, 0, width, height))
                     .setColor(layer, half3(1.0f, 0, 0))
                     .apply();
             expectedColor = fillColor;
@@ -565,7 +565,7 @@
                 ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, fillColor, width, height));
                 expectedColor = fillColor;
             }
-            Transaction().setCrop_legacy(layer, Rect(0, 0, width, height)).apply();
+            Transaction().setCrop(layer, Rect(0, 0, width, height)).apply();
             break;
         case ISurfaceComposerClient::eFXSurfaceBufferState:
             ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType));
@@ -727,7 +727,7 @@
                                     createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
                                                 ISurfaceComposerClient::eFXSurfaceEffect));
     Transaction()
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setCrop(colorLayer, Rect(0, 0, 32, 32))
             .setColor(colorLayer, half3(2.0f, 0.0f, 0.0f))
             .apply();
 
@@ -741,7 +741,7 @@
                                     createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
                                                 ISurfaceComposerClient::eFXSurfaceEffect));
     Transaction()
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setCrop(colorLayer, Rect(0, 0, 32, 32))
             .setColor(colorLayer, half3(1.0f, -1.0f, 0.5f))
             .apply();
 
@@ -756,7 +756,7 @@
     ASSERT_NO_FATAL_FAILURE(colorLayer =
                                     createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
                                                 ISurfaceComposerClient::eFXSurfaceEffect));
-    Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
+    Transaction().setCrop(colorLayer, Rect(0, 0, 32, 32)).apply();
 
     const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
     const float alpha = 0.25f;
@@ -783,7 +783,7 @@
     ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer("childWithColor", 0 /* buffer width */,
                                                      0 /* buffer height */,
                                                      ISurfaceComposerClient::eFXSurfaceEffect));
-    Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
+    Transaction().setCrop(colorLayer, Rect(0, 0, 32, 32)).apply();
     const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
     const float alpha = 0.25f;
     const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
@@ -940,7 +940,7 @@
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
     const Rect crop(8, 8, 24, 24);
 
-    Transaction().setCrop_legacy(layer, crop).apply();
+    Transaction().setCrop(layer, crop).apply();
     auto shot = getScreenCapture();
     shot->expectColor(crop, Color::RED);
     shot->expectBorder(crop, Color::BLACK);
@@ -966,13 +966,13 @@
 
     {
         SCOPED_TRACE("empty rect");
-        Transaction().setCrop_legacy(layer, Rect(8, 8, 8, 8)).apply();
+        Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply();
         getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 
     {
         SCOPED_TRACE("negative rect");
-        Transaction().setCrop_legacy(layer, Rect(8, 8, 0, 0)).apply();
+        Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply();
         getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 }
@@ -1001,7 +1001,7 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
-    Transaction().setCrop_legacy(layer, Rect(-128, -64, 128, 64)).apply();
+    Transaction().setCrop(layer, Rect(-128, -64, 128, 64)).apply();
     auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
     shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
@@ -1056,7 +1056,7 @@
 
     const Point position(32, 32);
     const Rect crop(8, 8, 24, 24);
-    Transaction().setPosition(layer, position.x, position.y).setCrop_legacy(layer, crop).apply();
+    Transaction().setPosition(layer, position.x, position.y).setCrop(layer, crop).apply();
     auto shot = getScreenCapture();
     shot->expectColor(crop + position, Color::RED);
     shot->expectBorder(crop + position, Color::BLACK);
@@ -1081,10 +1081,10 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
-    // crop_legacy is affected by matrix
+    // crop is affected by matrix
     Transaction()
             .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
-            .setCrop_legacy(layer, Rect(8, 8, 24, 24))
+            .setCrop(layer, Rect(8, 8, 24, 24))
             .apply();
     auto shot = getScreenCapture();
     shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
@@ -1096,8 +1096,8 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
-    // setCrop_legacy is applied immediately by default, with or without resize pending
-    Transaction().setCrop_legacy(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
+    // setCrop is applied immediately by default, with or without resize pending
+    Transaction().setCrop(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
     {
         SCOPED_TRACE("resize pending");
         auto shot = getScreenCapture();
@@ -1596,7 +1596,7 @@
                                     createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
                                                 ISurfaceComposerClient::eFXSurfaceEffect));
     Transaction()
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setCrop(colorLayer, Rect(0, 0, 32, 32))
             .setLayer(colorLayer, mLayerZBase + 1)
             .apply();
     {
@@ -1653,8 +1653,8 @@
                                      ISurfaceComposerClient::eFXSurfaceEffect, parentLayer.get()));
 
     Transaction()
-            .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setCrop(parentLayer, Rect(0, 0, 100, 100))
+            .setCrop(colorLayer, Rect(0, 0, 32, 32))
             .setLayer(parentLayer, mLayerZBase + 1)
             .apply();
     {
@@ -1714,8 +1714,8 @@
                                      ISurfaceComposerClient::eFXSurfaceEffect, parentLayer.get()));
 
     Transaction()
-            .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setCrop(parentLayer, Rect(0, 0, 100, 100))
+            .setCrop(colorLayer, Rect(0, 0, 32, 32))
             .setLayer(parentLayer, mLayerZBase + 1)
             .apply();
     {
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index be6665b..87c7b7d 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -298,7 +298,7 @@
         // set layer stack (b/68888219)
         Transaction t;
         t.setDisplayLayerStack(mDisplay, mDisplayLayerStack);
-        t.setCrop_legacy(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight));
+        t.setCrop(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight));
         t.setLayerStack(mBlackBgSurface, mDisplayLayerStack);
         t.setColor(mBlackBgSurface, half3{0, 0, 0});
         t.setLayer(mBlackBgSurface, mLayerZBase);
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 67db717..ac5e297 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -145,7 +145,7 @@
     sp<SurfaceControl> parent =
             LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
                                               ISurfaceComposerClient::eFXSurfaceContainer);
-    Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
+    Transaction().setCrop(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
     ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
@@ -199,7 +199,7 @@
     if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
         Transaction()
                 .setCornerRadius(layer, cornerRadius)
-                .setCrop_legacy(layer, Rect(0, 0, size, size))
+                .setCrop(layer, Rect(0, 0, size, size))
                 .apply();
     } else {
         Transaction()
@@ -236,13 +236,13 @@
 
     auto transaction = Transaction()
                                .setCornerRadius(parent, cornerRadius)
-                               .setCrop_legacy(parent, Rect(0, 0, size, size))
+                               .setCrop(parent, Rect(0, 0, size, size))
                                .reparent(child, parent)
                                .setPosition(child, 0, size)
                                // Rotate by half PI
                                .setMatrix(child, 0.0f, -1.0f, 1.0f, 0.0f);
     if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
-        transaction.setCrop_legacy(parent, Rect(0, 0, size, size));
+        transaction.setCrop(parent, Rect(0, 0, size, size));
     } else {
         transaction.setFrame(parent, Rect(0, 0, size, size));
     }
@@ -278,7 +278,7 @@
     if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
         Transaction()
                 .setCornerRadius(parent, cornerRadius)
-                .setCrop_legacy(parent, Rect(0, 0, size, size))
+                .setCrop(parent, Rect(0, 0, size, size))
                 .reparent(child, parent)
                 .setPosition(child, 0, size / 2)
                 .apply();
@@ -351,7 +351,7 @@
     Transaction()
             .setLayer(blurLayer, mLayerZBase + 3)
             .setBackgroundBlurRadius(blurLayer, blurRadius)
-            .setCrop_legacy(blurLayer, blurRect)
+            .setCrop(blurLayer, blurRect)
             .setFrame(blurLayer, blurRect)
             .setSize(blurLayer, blurRect.getWidth(), blurRect.getHeight())
             .setAlpha(blurLayer, 0.0f)
@@ -516,7 +516,7 @@
             .setLayer(layer, INT32_MAX - 1)
             .show(layer)
             .setLayerStack(behindLayer, mDisplayLayerStack)
-            .setCrop_legacy(behindLayer, crop)
+            .setCrop(behindLayer, crop)
             .setLayer(behindLayer, INT32_MAX - 2)
             .show(behindLayer)
             .apply();
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
index f8a0bc1..4753362 100644
--- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -43,7 +43,7 @@
     sp<SurfaceControl> parent =
             LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
                                               ISurfaceComposerClient::eFXSurfaceContainer);
-    Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
+    Transaction().setCrop(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
     sp<SurfaceControl> layerR;
     sp<SurfaceControl> layerG;
     sp<SurfaceControl> layerB;
@@ -84,8 +84,8 @@
             .setColor(parent, half3{0.0f, 0.0f, 0.0f})
             .show(childLayer)
             .show(parent)
-            .setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight))
-            .setCrop_legacy(childLayer, Rect(0, 0, 20, 30))
+            .setCrop(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+            .setCrop(childLayer, Rect(0, 0, 20, 30))
             .apply();
 
     Transaction().setRelativeLayer(childLayer, parent, -1).setLayer(childLayer, 1).apply();
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index ec826ae..e4a1f66 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -131,7 +131,7 @@
         asTransaction([&](Transaction& t) {
             t.setSize(mFGSurfaceControl, 64, 64);
             t.setPosition(mFGSurfaceControl, 64, 64);
-            t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
+            t.setCrop(mFGSurfaceControl, Rect(0, 0, 64, 64));
         });
 
         EXPECT_INITIAL_STATE("After restoring initial state");
@@ -225,7 +225,7 @@
                                                    PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
     TransactionUtils::fillSurfaceRGBA8(childBuffer, 200, 200, 200);
     SurfaceComposerClient::Transaction{}
-            .setCrop_legacy(childNoBuffer, Rect(0, 0, 10, 10))
+            .setCrop(childNoBuffer, Rect(0, 0, 10, 10))
             .show(childNoBuffer)
             .show(childBuffer)
             .apply(true);
@@ -234,9 +234,7 @@
         sc->expectChildColor(73, 73);
         sc->expectFGColor(74, 74);
     }
-    SurfaceComposerClient::Transaction{}
-            .setCrop_legacy(childNoBuffer, Rect(0, 0, 20, 20))
-            .apply(true);
+    SurfaceComposerClient::Transaction{}.setCrop(childNoBuffer, Rect(0, 0, 20, 20)).apply(true);
     {
         ScreenCapture::captureScreen(&sc);
         sc->expectChildColor(73, 73);
@@ -351,7 +349,7 @@
         t.show(mChild);
         t.setPosition(mChild, 0, 0);
         t.setPosition(mFGSurfaceControl, 0, 0);
-        t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5));
+        t.setCrop(mFGSurfaceControl, Rect(0, 0, 5, 5));
     });
 
     {
@@ -947,7 +945,7 @@
                           ISurfaceComposerClient::eFXSurfaceEffect, cropLayer.get());
     ASSERT_TRUE(colorLayer->isValid());
     asTransaction([&](Transaction& t) {
-        t.setCrop_legacy(cropLayer, Rect(5, 5, 10, 10));
+        t.setCrop(cropLayer, Rect(5, 5, 10, 10));
         t.setColor(colorLayer, half3{0, 0, 0});
         t.show(cropLayer);
         t.show(colorLayer);
@@ -1007,7 +1005,7 @@
         t.show(boundlessLayerRightShift);
         t.setPosition(boundlessLayerDownShift, 0, 32);
         t.show(boundlessLayerDownShift);
-        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        t.setCrop(colorLayer, Rect(0, 0, 64, 64));
         t.setColor(colorLayer, half3{0, 0, 0});
         t.show(colorLayer);
     });
@@ -1043,7 +1041,7 @@
         // expect the child layer to be cropped.
         t.setPosition(boundlessLayer, 32, 32);
         t.show(boundlessLayer);
-        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        t.setCrop(colorLayer, Rect(0, 0, 64, 64));
         // undo shift by parent
         t.setPosition(colorLayer, -32, -32);
         t.setColor(colorLayer, half3{0, 0, 0});
@@ -1074,7 +1072,7 @@
         t.setLayer(rootBoundlessLayer, INT32_MAX - 1);
         t.setPosition(rootBoundlessLayer, 32, 32);
         t.show(rootBoundlessLayer);
-        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        t.setCrop(colorLayer, Rect(0, 0, 64, 64));
         t.setColor(colorLayer, half3{0, 0, 0});
         t.show(colorLayer);
         t.hide(mFGSurfaceControl);
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index 16826c1..613b21e 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -36,7 +36,7 @@
         asTransaction([&](Transaction& t) {
             t.setDisplayLayerStack(display, 0);
             t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
-            t.setCrop_legacy(mChildLayer, Rect(0, 0, 400, 400)).show(mChildLayer);
+            t.setCrop(mChildLayer, Rect(0, 0, 400, 400)).show(mChildLayer);
             t.setPosition(mChildLayer, 50, 50);
             t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
             t.setFlags(mChildLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
@@ -58,7 +58,7 @@
             createColorLayer("Grandchild layer", Color::BLUE, mChildLayer.get());
     Transaction()
             .setFlags(grandchild, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque)
-            .setCrop_legacy(grandchild, Rect(0, 0, 200, 200))
+            .setCrop(grandchild, Rect(0, 0, 200, 200))
             .show(grandchild)
             .apply();
 
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index eaf54e3..08de01c 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -71,7 +71,7 @@
         ASSERT_TRUE(mColorLayer->isValid());
         asTransaction([&](Transaction& t) {
             t.setLayerStack(mColorLayer, layerStack);
-            t.setCrop_legacy(mColorLayer, Rect(0, 0, 30, 40));
+            t.setCrop(mColorLayer, Rect(0, 0, 30, 40));
             t.setLayer(mColorLayer, INT32_MAX - 2);
             t.setColor(mColorLayer,
                        half3{mExpectedColor.r / 255.0f, mExpectedColor.g / 255.0f,
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/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 4598f9d..b0753c8 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -328,7 +328,7 @@
 TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) {
     sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
     Rect layerCrop(0, 0, 10, 10);
-    SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true);
+    SurfaceComposerClient::Transaction().setCrop(child, layerCrop).show(child).apply(true);
 
     LayerCaptureArgs captureArgs;
     captureArgs.layerHandle = child->getHandle();
@@ -623,7 +623,7 @@
             .setLayerStack(layer, 0)
             .setLayer(layer, INT32_MAX)
             .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
-            .setCrop_legacy(layer, bounds)
+            .setCrop(layer, bounds)
             .apply();
 
     DisplayCaptureArgs captureArgs;
@@ -671,8 +671,8 @@
             .setLayer(layer, INT32_MAX)
             .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
             .setColor(childLayer, {childColor.r / 255, childColor.g / 255, childColor.b / 255})
-            .setCrop_legacy(layer, bounds)
-            .setCrop_legacy(childLayer, childBounds)
+            .setCrop(layer, bounds)
+            .setCrop(childLayer, childBounds)
             .apply();
 
     DisplayCaptureArgs captureArgs;
@@ -853,9 +853,7 @@
 }
 
 TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
-    SurfaceComposerClient::Transaction()
-            .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1))
-            .apply(true);
+    SurfaceComposerClient::Transaction().setCrop(mFGSurfaceControl, Rect(0, 0, 1, 1)).apply(true);
 
     // Even though the parent is cropped out we should still capture the child.
 
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index a20d5c6..d9cab42 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -370,7 +370,7 @@
 }
 
 void SurfaceInterceptorTest::cropUpdate(Transaction& t) {
-    t.setCrop_legacy(mBGSurfaceControl, CROP_UPDATE);
+    t.setCrop(mBGSurfaceControl, CROP_UPDATE);
 }
 
 void SurfaceInterceptorTest::matrixUpdate(Transaction& t) {
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 11bd9eb..820f248 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -1324,7 +1324,7 @@
         {
             TransactionScope ts(*sFakeComposer);
             Rect cropRect(16, 16, 32, 32);
-            ts.setCrop_legacy(mFGSurfaceControl, cropRect);
+            ts.setCrop(mFGSurfaceControl, cropRect);
         }
         ASSERT_EQ(2, sFakeComposer->getFrameCount());
 
@@ -1692,7 +1692,7 @@
             ts.show(mChild);
             ts.setPosition(mChild, 0, 0);
             ts.setPosition(Base::mFGSurfaceControl, 0, 0);
-            ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 5, 5));
+            ts.setCrop(Base::mFGSurfaceControl, Rect(0, 0, 5, 5));
         }
         // NOTE: The foreground surface would be occluded by the child
         // now, but is included in the stack because the child is
@@ -1915,7 +1915,7 @@
             TransactionScope ts(*Base::sFakeComposer);
             ts.setColor(Base::mChild,
                         {LIGHT_GRAY.r / 255.0f, LIGHT_GRAY.g / 255.0f, LIGHT_GRAY.b / 255.0f});
-            ts.setCrop_legacy(Base::mChild, Rect(0, 0, 10, 10));
+            ts.setCrop(Base::mChild, Rect(0, 0, 10, 10));
         }
 
         Base::sFakeComposer->runVSyncAndWait();
@@ -2010,7 +2010,7 @@
         TransactionScope ts(*Base::sFakeComposer);
         ts.setSize(Base::mFGSurfaceControl, 64, 64);
         ts.setPosition(Base::mFGSurfaceControl, 64, 64);
-        ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 64, 64));
+        ts.setCrop(Base::mFGSurfaceControl, Rect(0, 0, 64, 64));
     }
 
     void Test_SurfacePositionLatching() {
@@ -2043,7 +2043,7 @@
         {
             TransactionScope ts(*Base::sFakeComposer);
             ts.setSize(Base::mFGSurfaceControl, 128, 128);
-            ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 63, 63));
+            ts.setCrop(Base::mFGSurfaceControl, Rect(0, 0, 63, 63));
         }
 
         auto referenceFrame1 = Base::mBaseFrame;
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index c3946cb..4e1c0c7 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -884,7 +884,7 @@
         });
 
         auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
-        layerDrawingState.crop_legacy = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
+        layerDrawingState.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
         return layer;
     }
 
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);
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index a513239..438e5dd 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -842,6 +842,8 @@
   bool ret = true;
   switch (device->properties.apiVersion ^
           VK_VERSION_PATCH(device->properties.apiVersion)) {
+    case VK_API_VERSION_1_2:
+      FALLTHROUGH_INTENDED;
     case VK_API_VERSION_1_1:
       ret &=
           visitor->Visit("subgroupProperties", &device->subgroup_properties) &&
@@ -896,6 +898,8 @@
 inline bool Iterate(Visitor* visitor, VkJsonInstance* instance) {
   bool ret = true;
   switch (instance->api_version ^ VK_VERSION_PATCH(instance->api_version)) {
+    case VK_API_VERSION_1_2:
+      FALLTHROUGH_INTENDED;
     case VK_API_VERSION_1_1:
       ret &= visitor->Visit("deviceGroups", &instance->device_groups);
       FALLTHROUGH_INTENDED;