Merge tag 'android-16.0.0_r1' of https://android.googlesource.com/platform/frameworks/native into HEAD

Android 16.0.0 release 1

Change-Id: Iacaf25353d3020d7edcd8b9b6c484a94d875105f
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 22ebfe0..bda2756 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -93,6 +93,7 @@
         "android/gui/StalledTransactionInfo.aidl",
         "android/**/TouchOcclusionMode.aidl",
         "android/gui/TrustedOverlay.aidl",
+        "android/gui/BorderSettings.aidl",
     ],
 }
 
@@ -266,8 +267,6 @@
         "FenceMonitor.cpp",
         "Flags.cpp",
         "GLConsumer.cpp",
-        "IConsumerListener.cpp",
-        "IGraphicBufferConsumer.cpp",
         "IGraphicBufferProducer.cpp",
         "IProducerListener.cpp",
         "ISurfaceComposer.cpp",
@@ -283,6 +282,7 @@
         "SurfaceControl.cpp",
         "SurfaceComposerClient.cpp",
         "SyncFeatures.cpp",
+        "TransactionState.cpp",
         "VsyncEventData.cpp",
         "view/Surface.cpp",
         "WindowInfosListenerReporter.cpp",
@@ -296,7 +296,6 @@
 cc_defaults {
     name: "libgui-defaults",
     defaults: ["libgui_bufferqueue-defaults"],
-    srcs: [":libgui-sources"],
     static_libs: [
         "libgui_aidl_static",
         "libgui_window_info_static",
@@ -320,6 +319,10 @@
         "libgui-defaults",
     ],
 
+    srcs: [
+        ":libgui-sources",
+    ],
+
     export_static_lib_headers: [
         "libgui_aidl_static",
         "libgui_window_info_static",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 7aee903..5b0f21d 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -197,15 +197,15 @@
         mUpdateDestinationFrame(updateDestinationFrame) {
     createBufferQueue(&mProducer, &mConsumer);
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
-    mBufferItemConsumer = new BLASTBufferItemConsumer(mProducer, mConsumer,
-                                                      GraphicBuffer::USAGE_HW_COMPOSER |
-                                                              GraphicBuffer::USAGE_HW_TEXTURE,
-                                                      1, false, this);
+    mBufferItemConsumer = sp<BLASTBufferItemConsumer>::make(mProducer, mConsumer,
+                                                            GraphicBuffer::USAGE_HW_COMPOSER |
+                                                                    GraphicBuffer::USAGE_HW_TEXTURE,
+                                                            1, false, this);
 #else
-    mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
-                                                      GraphicBuffer::USAGE_HW_COMPOSER |
-                                                              GraphicBuffer::USAGE_HW_TEXTURE,
-                                                      1, false, this);
+    mBufferItemConsumer = sp<BLASTBufferItemConsumer>::make(mConsumer,
+                                                            GraphicBuffer::USAGE_HW_COMPOSER |
+                                                                    GraphicBuffer::USAGE_HW_TEXTURE,
+                                                            1, false, this);
 #endif //  COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
     // since the adapter is in the client process, set dequeue timeout
     // explicitly so that dequeueBuffer will block
@@ -222,8 +222,6 @@
     ComposerServiceAIDL::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
     mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
     mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers;
-    mNumAcquired = 0;
-    mNumFrameAvailable = 0;
 
     TransactionCompletedListener::getInstance()->addQueueStallListener(
             [&](const std::string& reason) {
@@ -244,12 +242,6 @@
     BQA_LOGV("BLASTBufferQueue created");
 }
 
-BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface,
-                                   int width, int height, int32_t format)
-      : BLASTBufferQueue(name) {
-    update(surface, width, height, format);
-}
-
 BLASTBufferQueue::~BLASTBufferQueue() {
     TransactionCompletedListener::getInstance()->removeQueueStallListener(this);
     if (mPendingTransactions.empty()) {
@@ -421,14 +413,12 @@
                                                     stat.frameEventStats.dequeueReadyTime);
                 }
                 auto currFrameNumber = stat.frameEventStats.frameNumber;
-                std::vector<ReleaseCallbackId> staleReleases;
-                for (const auto& [key, value]: mSubmitted) {
-                    if (currFrameNumber > key.framenumber) {
-                        staleReleases.push_back(key);
+                // Release stale buffers.
+                for (const auto& [key, _] : mSubmitted) {
+                    if (currFrameNumber <= key.framenumber) {
+                        continue; // not stale.
                     }
-                }
-                for (const auto& staleRelease : staleReleases) {
-                    releaseBufferCallbackLocked(staleRelease,
+                    releaseBufferCallbackLocked(key,
                                                 stat.previousReleaseFence
                                                         ? stat.previousReleaseFence
                                                         : Fence::NO_FENCE,
@@ -447,7 +437,7 @@
 
 void BLASTBufferQueue::flushShadowQueue() {
     BQA_LOGV("flushShadowQueue");
-    int numFramesToFlush = mNumFrameAvailable;
+    int32_t numFramesToFlush = mNumFrameAvailable;
     while (numFramesToFlush > 0) {
         acquireNextBufferLocked(std::nullopt);
         numFramesToFlush--;
@@ -624,7 +614,7 @@
     mNumAcquired++;
     mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
     ReleaseCallbackId releaseCallbackId(buffer->getId(), mLastAcquiredFrameNumber);
-    mSubmitted[releaseCallbackId] = bufferItem;
+    mSubmitted.emplace_or_replace(releaseCallbackId, bufferItem);
 
     bool needsDisconnect = false;
     mBufferItemConsumer->getConnectionEvents(bufferItem.mFrameNumber, &needsDisconnect);
@@ -647,7 +637,8 @@
                            bufferItem.mScalingMode, crop);
 
     auto releaseBufferCallback = makeReleaseBufferCallbackThunk();
-    sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE;
+    sp<Fence> fence =
+            bufferItem.mFence ? sp<Fence>::make(bufferItem.mFence->dup()) : Fence::NO_FENCE;
 
     nsecs_t dequeueTime = -1;
     {
@@ -857,7 +848,7 @@
 
 void BLASTBufferQueue::onFrameDequeued(const uint64_t bufferId) {
     std::lock_guard _lock{mTimestampMutex};
-    mDequeueTimestamps[bufferId] = systemTime();
+    mDequeueTimestamps.emplace_or_replace(bufferId, systemTime());
 };
 
 void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) {
@@ -946,15 +937,22 @@
           : Surface(igbp, controlledByApp, scHandle), mBbq(bbq) {}
 
     void allocateBuffers() override {
+        ATRACE_CALL();
         uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth;
         uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight;
         auto gbp = getIGraphicBufferProducer();
-        std::thread ([reqWidth, reqHeight, gbp=getIGraphicBufferProducer(),
-                      reqFormat=mReqFormat, reqUsage=mReqUsage] () {
+        std::thread allocateThread([reqWidth, reqHeight, gbp = getIGraphicBufferProducer(),
+                                    reqFormat = mReqFormat, reqUsage = mReqUsage]() {
+            if (com_android_graphics_libgui_flags_allocate_buffer_priority()) {
+                androidSetThreadName("allocateBuffers");
+                pid_t tid = gettid();
+                androidSetThreadPriority(tid, ANDROID_PRIORITY_DISPLAY);
+            }
+
             gbp->allocateBuffers(reqWidth, reqHeight,
                                  reqFormat, reqUsage);
-
-        }).detach();
+        });
+        allocateThread.detach();
     }
 
     status_t setFrameRate(float frameRate, int8_t compatibility,
@@ -1024,7 +1022,7 @@
     if (includeSurfaceControlHandle && mSurfaceControl) {
         scHandle = mSurfaceControl->getHandle();
     }
-    return new BBQSurface(mProducer, true, scHandle, this);
+    return sp<BBQSurface>::make(mProducer, true, scHandle, this);
 }
 
 void BLASTBufferQueue::mergeWithNextTransaction(SurfaceComposerClient::Transaction* t,
@@ -1032,9 +1030,9 @@
     std::lock_guard _lock{mMutex};
     if (mLastAcquiredFrameNumber >= frameNumber) {
         // Apply the transaction since we have already acquired the desired frame.
-        t->apply();
+        t->setApplyToken(mApplyToken).apply();
     } else {
-        mPendingTransactions.emplace_back(frameNumber, *t);
+        mPendingTransactions.emplace_back(frameNumber, std::move(*t));
         // Clear the transaction so it can't be applied elsewhere.
         t->clear();
     }
@@ -1052,8 +1050,8 @@
 void BLASTBufferQueue::mergePendingTransactions(SurfaceComposerClient::Transaction* t,
                                                 uint64_t frameNumber) {
     auto mergeTransaction =
-            [&t, currentFrameNumber = frameNumber](
-                    std::tuple<uint64_t, SurfaceComposerClient::Transaction> pendingTransaction) {
+            [t, currentFrameNumber = frameNumber](
+                    std::pair<uint64_t, SurfaceComposerClient::Transaction>& pendingTransaction) {
                 auto& [targetFrameNumber, transaction] = pendingTransaction;
                 if (currentFrameNumber < targetFrameNumber) {
                     return false;
@@ -1130,10 +1128,10 @@
 class AsyncProducerListener : public BnProducerListener {
 private:
     const sp<IProducerListener> mListener;
+    AsyncProducerListener(const sp<IProducerListener>& listener) : mListener(listener) {}
+    friend class sp<AsyncProducerListener>;
 
 public:
-    AsyncProducerListener(const sp<IProducerListener>& listener) : mListener(listener) {}
-
     void onBufferReleased() override {
         AsyncWorker::getInstance().post([listener = mListener]() { listener->onBufferReleased(); });
     }
@@ -1187,7 +1185,7 @@
             return BufferQueueProducer::connect(listener, api, producerControlledByApp, output);
         }
 
-        return BufferQueueProducer::connect(new AsyncProducerListener(listener), api,
+        return BufferQueueProducer::connect(sp<AsyncProducerListener>::make(listener), api,
                                             producerControlledByApp, output);
     }
 
@@ -1227,6 +1225,7 @@
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
     status_t waitForBufferRelease(std::unique_lock<std::mutex>& bufferQueueLock,
                                   nsecs_t timeout) const override {
+        const auto startTime = std::chrono::steady_clock::now();
         sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
         if (!bbq) {
             return OK;
@@ -1253,6 +1252,14 @@
         }
 
         bbq->releaseBufferCallback(id, fence, maxAcquiredBufferCount);
+        const nsecs_t durationNanos = std::chrono::duration_cast<std::chrono::nanoseconds>(
+                                              std::chrono::steady_clock::now() - startTime)
+                                              .count();
+        // Provide a callback for Choreographer to start buffer stuffing recovery when blocked
+        // on buffer release.
+        std::function<void(const nsecs_t)> callbackCopy = bbq->getWaitForBufferReleaseCallback();
+        if (callbackCopy) callbackCopy(durationNanos);
+
         return OK;
     }
 #endif
@@ -1344,6 +1351,17 @@
     mApplyToken = std::move(applyToken);
 }
 
+void BLASTBufferQueue::setWaitForBufferReleaseCallback(
+        std::function<void(const nsecs_t)> callback) {
+    std::lock_guard _lock{mWaitForBufferReleaseMutex};
+    mWaitForBufferReleaseCallback = std::move(callback);
+}
+
+std::function<void(const nsecs_t)> BLASTBufferQueue::getWaitForBufferReleaseCallback() const {
+    std::lock_guard _lock{mWaitForBufferReleaseMutex};
+    return mWaitForBufferReleaseCallback;
+}
+
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
 
 void BLASTBufferQueue::updateBufferReleaseProducer() {
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 3b2d337..9dcd5dc 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -215,14 +215,14 @@
     FlattenableUtils::read(buffer, size, flags);
 
     if (flags & 1) {
-        mGraphicBuffer = new GraphicBuffer();
+        mGraphicBuffer = sp<GraphicBuffer>::make();
         status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count);
         if (err) return err;
         size -= FlattenableUtils::align<4>(buffer);
     }
 
     if (flags & 2) {
-        mFence = new Fence();
+        mFence = sp<Fence>::make();
         status_t err = mFence->unflatten(buffer, size, fds, count);
         if (err) return err;
         size -= FlattenableUtils::align<4>(buffer);
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index 8566419..4926ceb 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -17,6 +17,7 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "BufferItemConsumer"
 //#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Errors.h>
 #include <utils/Log.h>
 
 #include <inttypes.h>
@@ -24,6 +25,7 @@
 #include <com_android_graphics_libgui_flags.h>
 #include <gui/BufferItem.h>
 #include <gui/BufferItemConsumer.h>
+#include <gui/Surface.h>
 #include <ui/BufferQueueDefs.h>
 #include <ui/GraphicBuffer.h>
 
@@ -35,6 +37,30 @@
 
 namespace android {
 
+std::tuple<sp<BufferItemConsumer>, sp<Surface>> BufferItemConsumer::create(
+        uint64_t consumerUsage, int bufferCount, bool controlledByApp,
+        bool isConsumerSurfaceFlinger) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+    sp<BufferItemConsumer> bufferItemConsumer =
+            sp<BufferItemConsumer>::make(consumerUsage, bufferCount, controlledByApp,
+                                         isConsumerSurfaceFlinger);
+    return {bufferItemConsumer, bufferItemConsumer->getSurface()};
+#else
+    sp<IGraphicBufferProducer> igbp;
+    sp<IGraphicBufferConsumer> igbc;
+    BufferQueue::createBufferQueue(&igbp, &igbc, isConsumerSurfaceFlinger);
+    sp<BufferItemConsumer> bufferItemConsumer =
+            sp<BufferItemConsumer>::make(igbc, consumerUsage, bufferCount, controlledByApp);
+    return {bufferItemConsumer, sp<Surface>::make(igbp, controlledByApp)};
+#endif
+}
+
+sp<BufferItemConsumer> BufferItemConsumer::create(const sp<IGraphicBufferConsumer>& consumer,
+                                                  uint64_t consumerUsage, int bufferCount,
+                                                  bool controlledByApp) {
+    return sp<BufferItemConsumer>::make(consumer, consumerUsage, bufferCount, controlledByApp);
+}
+
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 BufferItemConsumer::BufferItemConsumer(uint64_t consumerUsage, int bufferCount,
                                        bool controlledByApp, bool isConsumerSurfaceFlinger)
@@ -107,13 +133,36 @@
     return OK;
 }
 
+status_t BufferItemConsumer::attachBuffer(const sp<GraphicBuffer>& buffer) {
+    if (!buffer) {
+        BI_LOGE("BufferItemConsumer::attachBuffer no input buffer specified.");
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock _l(mMutex);
+
+    int slot = INVALID_BUFFER_SLOT;
+    status_t status = mConsumer->attachBuffer(&slot, buffer);
+    if (status != OK) {
+        BI_LOGE("BufferItemConsumer::attachBuffer unable to attach buffer %d", status);
+        return status;
+    }
+
+    mSlots[slot] = {
+            .mGraphicBuffer = buffer,
+            .mFence = nullptr,
+            .mFrameNumber = 0,
+    };
+
+    return OK;
+}
+
 status_t BufferItemConsumer::releaseBuffer(const BufferItem &item,
         const sp<Fence>& releaseFence) {
     Mutex::Autolock _l(mMutex);
     return releaseBufferSlotLocked(item.mSlot, item.mGraphicBuffer, releaseFence);
 }
 
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
 status_t BufferItemConsumer::releaseBuffer(const sp<GraphicBuffer>& buffer,
                                            const sp<Fence>& releaseFence) {
     Mutex::Autolock _l(mMutex);
@@ -129,7 +178,6 @@
 
     return releaseBufferSlotLocked(slotIndex, buffer, releaseFence);
 }
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
 
 status_t BufferItemConsumer::releaseBufferSlotLocked(int slotIndex, const sp<GraphicBuffer>& buffer,
                                                      const sp<Fence>& releaseFence) {
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index b0f6e69..f21ac18 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -108,6 +108,15 @@
 }
 #endif
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+void BufferQueue::ProxyConsumerListener::onSlotCountChanged(int slotCount) {
+    sp<ConsumerListener> listener(mConsumerListener.promote());
+    if (listener != nullptr) {
+        listener->onSlotCountChanged(slotCount);
+    }
+}
+#endif
+
 void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
         sp<IGraphicBufferConsumer>* outConsumer,
         bool consumerIsSurfaceFlinger) {
@@ -116,15 +125,16 @@
     LOG_ALWAYS_FATAL_IF(outConsumer == nullptr,
             "BufferQueue: outConsumer must not be NULL");
 
-    sp<BufferQueueCore> core(new BufferQueueCore());
+    sp<BufferQueueCore> core = sp<BufferQueueCore>::make();
     LOG_ALWAYS_FATAL_IF(core == nullptr,
             "BufferQueue: failed to create BufferQueueCore");
 
-    sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger));
+    sp<IGraphicBufferProducer> producer =
+            sp<BufferQueueProducer>::make(core, consumerIsSurfaceFlinger);
     LOG_ALWAYS_FATAL_IF(producer == nullptr,
             "BufferQueue: failed to create BufferQueueProducer");
 
-    sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
+    sp<IGraphicBufferConsumer> consumer = sp<BufferQueueConsumer>::make(core);
     LOG_ALWAYS_FATAL_IF(consumer == nullptr,
             "BufferQueue: failed to create BufferQueueConsumer");
 
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 9855b5b..4681c9e 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-#include <inttypes.h>
-#include <pwd.h>
-#include <sys/types.h>
-
 #define LOG_TAG "BufferQueueConsumer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 //#define LOG_NDEBUG 0
@@ -48,6 +44,11 @@
 
 #include <com_android_graphics_libgui_flags.h>
 
+#include <inttypes.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <optional>
+
 namespace android {
 
 // Macros for include BufferQueueCore information in log messages
@@ -341,9 +342,9 @@
             return BAD_VALUE;
         }
 
-        if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
-            BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
-                    slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+        const int totalSlotCount = mCore->getTotalSlotCountLocked();
+        if (slot < 0 || slot >= totalSlotCount) {
+            BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
             return BAD_VALUE;
         } else if (!mSlots[slot].mBufferState.isAcquired()) {
             BQ_LOGE("detachBuffer: slot %d is not owned by the consumer "
@@ -477,44 +478,38 @@
     return NO_ERROR;
 }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
+status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
+                                            const sp<Fence>& releaseFence) {
+#else
 status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
         const sp<Fence>& releaseFence, EGLDisplay eglDisplay,
         EGLSyncKHR eglFence) {
+#endif
     ATRACE_CALL();
     ATRACE_BUFFER_INDEX(slot);
 
-    if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS ||
-            releaseFence == nullptr) {
-        BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot,
-                releaseFence.get());
+    const int totalSlotCount = mCore->getTotalSlotCountLocked();
+    if (slot < 0 || slot >= totalSlotCount) {
+        BQ_LOGE("releaseBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
         return BAD_VALUE;
     }
-
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
-    if (eglFence != EGL_NO_SYNC_KHR) {
-        // Most platforms will be using native fences, so it's unlikely that we'll ever have to
-        // process an eglFence. Ideally we can remove this code eventually. In the mean time, do our
-        // best to wait for it so the buffer stays valid, otherwise return an error to the caller.
-        //
-        // EGL_SYNC_FLUSH_COMMANDS_BIT_KHR so that we don't wait forever on a fence that hasn't
-        // shown up on the GPU yet.
-        EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
-                                             1000000000);
-        if (result == EGL_FALSE) {
-            BQ_LOGE("releaseBuffer: error %#x waiting for fence", eglGetError());
-            return UNKNOWN_ERROR;
-        } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
-            BQ_LOGE("releaseBuffer: timeout waiting for fence");
-            return UNKNOWN_ERROR;
-        }
-        eglDestroySyncKHR(eglDisplay, eglFence);
+    if (releaseFence == nullptr) {
+        BQ_LOGE("releaseBuffer: slot %d fence %p NULL", slot, releaseFence.get());
+        return BAD_VALUE;
     }
-#endif
 
     sp<IProducerListener> listener;
     { // Autolock scope
         std::lock_guard<std::mutex> lock(mCore->mMutex);
 
+        const int totalSlotCount = mCore->getTotalSlotCountLocked();
+        if (slot < 0 || slot >= totalSlotCount || releaseFence == nullptr) {
+            BQ_LOGE("releaseBuffer: slot %d out of range [0, %d) or fence %p NULL", slot,
+                    totalSlotCount, releaseFence.get());
+            return BAD_VALUE;
+        }
+
         // If the frame number has changed because the buffer has been reallocated,
         // we can ignore this releaseBuffer for the old buffer.
         // Ignore this for the shared buffer where the frame number can easily
@@ -661,6 +656,43 @@
     return NO_ERROR;
 }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t BufferQueueConsumer::getReleasedBuffersExtended(std::vector<bool>* outSlotMask) {
+    ATRACE_CALL();
+
+    if (outSlotMask == nullptr) {
+        BQ_LOGE("getReleasedBuffersExtended: outSlotMask may not be NULL");
+        return BAD_VALUE;
+    }
+
+    std::lock_guard<std::mutex> lock(mCore->mMutex);
+
+    if (mCore->mIsAbandoned) {
+        BQ_LOGE("getReleasedBuffersExtended: BufferQueue has been abandoned");
+        return NO_INIT;
+    }
+
+    const int totalSlotCount = mCore->getTotalSlotCountLocked();
+    outSlotMask->resize(totalSlotCount);
+    for (int s = 0; s < totalSlotCount; ++s) {
+        (*outSlotMask)[s] = !mSlots[s].mAcquireCalled;
+    }
+
+    // Remove from the mask queued buffers for which acquire has been called,
+    // since the consumer will not receive their buffer addresses and so must
+    // retain their cached information
+    BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
+    while (current != mCore->mQueue.end()) {
+        if (current->mAcquireCalled) {
+            (*outSlotMask)[current->mSlot] = false;
+        }
+        ++current;
+    }
+
+    return NO_ERROR;
+}
+#endif
+
 status_t BufferQueueConsumer::setDefaultBufferSize(uint32_t width,
         uint32_t height) {
     ATRACE_CALL();
@@ -679,6 +711,28 @@
     return NO_ERROR;
 }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t BufferQueueConsumer::allowUnlimitedSlots(bool allowUnlimitedSlots) {
+    ATRACE_CALL();
+    BQ_LOGV("allowUnlimitedSlots: %d", allowUnlimitedSlots);
+    std::lock_guard<std::mutex> lock(mCore->mMutex);
+
+    if (mCore->mIsAbandoned) {
+        BQ_LOGE("allowUnlimitedSlots: BufferQueue has been abandoned");
+        return NO_INIT;
+    }
+
+    if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
+        BQ_LOGE("allowUnlimitedSlots: BufferQueue already connected");
+        return INVALID_OPERATION;
+    }
+
+    mCore->mAllowExtendedSlotCount = allowUnlimitedSlots;
+
+    return OK;
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+
 status_t BufferQueueConsumer::setMaxBufferCount(int bufferCount) {
     ATRACE_CALL();
 
@@ -714,20 +768,31 @@
     return NO_ERROR;
 }
 
+status_t BufferQueueConsumer::setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
+    return setMaxAcquiredBufferCount(maxAcquiredBuffers, std::nullopt);
+}
+
 status_t BufferQueueConsumer::setMaxAcquiredBufferCount(
-        int maxAcquiredBuffers) {
+        int maxAcquiredBuffers, std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) {
     ATRACE_FORMAT("%s(%d)", __func__, maxAcquiredBuffers);
 
-    if (maxAcquiredBuffers < 1 ||
-            maxAcquiredBuffers > BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) {
-        BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d",
-                maxAcquiredBuffers);
-        return BAD_VALUE;
-    }
-
-    sp<IConsumerListener> listener;
+    std::optional<OnBufferReleasedCallback> callback;
     { // Autolock scope
         std::unique_lock<std::mutex> lock(mCore->mMutex);
+
+        // We reserve two slots in order to guarantee that the producer and
+        // consumer can run asynchronously.
+        int maxMaxAcquiredBuffers =
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+                mCore->getTotalSlotCountLocked() - 2;
+#else
+                BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS;
+#endif
+        if (maxAcquiredBuffers < 1 || maxAcquiredBuffers > maxMaxAcquiredBuffers) {
+            BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d", maxAcquiredBuffers);
+            return BAD_VALUE;
+        }
+
         mCore->waitWhileAllocatingLocked(lock);
 
         if (mCore->mIsAbandoned) {
@@ -773,13 +838,20 @@
         BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers);
         mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
         VALIDATE_CONSISTENCY();
-        if (delta < 0 && mCore->mBufferReleasedCbEnabled) {
-            listener = mCore->mConsumerListener;
+        if (delta < 0) {
+            if (onBuffersReleasedCallback) {
+                callback = std::move(onBuffersReleasedCallback);
+            } else if (mCore->mBufferReleasedCbEnabled) {
+                callback = [listener = mCore->mConsumerListener]() {
+                    listener->onBuffersReleased();
+                };
+            }
         }
     }
+
     // Call back without lock held
-    if (listener != nullptr) {
-        listener->onBuffersReleased();
+    if (callback) {
+        (*callback)();
     }
 
     return NO_ERROR;
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 5a09399..6c79904 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -38,6 +38,8 @@
 
 #include <system/window.h>
 
+#include <ui/BufferQueueDefs.h>
+
 namespace android {
 
 // Macros for include BufferQueueCore information in log messages
@@ -97,7 +99,11 @@
         mConnectedProducerListener(),
         mBufferReleasedCbEnabled(false),
         mBufferAttachedCbEnabled(false),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+        mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#else
         mSlots(),
+#endif
         mQueue(),
         mFreeSlots(),
         mFreeBuffers(),
@@ -111,6 +117,9 @@
         mDefaultWidth(1),
         mDefaultHeight(1),
         mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+        mAllowExtendedSlotCount(false),
+#endif
         mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
         mMaxAcquiredBufferCount(1),
         mMaxDequeuedBufferCount(1),
@@ -221,6 +230,14 @@
     }
 }
 
+int BufferQueueCore::getTotalSlotCountLocked() const {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    return mAllowExtendedSlotCount ? mMaxBufferCount : BufferQueueDefs::NUM_BUFFER_SLOTS;
+#else
+    return BufferQueueDefs::NUM_BUFFER_SLOTS;
+#endif
+}
+
 int BufferQueueCore::getMinUndequeuedBufferCountLocked() const {
     // If dequeueBuffer is allowed to error out, we don't have to add an
     // extra buffer.
@@ -253,6 +270,26 @@
     return maxBufferCount;
 }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t BufferQueueCore::extendSlotCountLocked(int size) {
+    int previousSize = (int)mSlots.size();
+    if (previousSize > size) {
+        return BAD_VALUE;
+    }
+    if (previousSize == size) {
+        return NO_ERROR;
+    }
+
+    mSlots.resize(size);
+    for (int i = previousSize; i < size; i++) {
+        mUnusedSlots.push_back(i);
+    }
+
+    mMaxBufferCount = size;
+    return NO_ERROR;
+}
+#endif
+
 void BufferQueueCore::clearBufferSlotLocked(int slot) {
     BQ_LOGV("clearBufferSlotLocked: slot %d", slot);
 
@@ -383,7 +420,7 @@
 void BufferQueueCore::validateConsistencyLocked() const {
     static const useconds_t PAUSE_TIME = 0;
     int allocatedSlots = 0;
-    for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
+    for (int slot = 0; slot < getTotalSlotCountLocked(); ++slot) {
         bool isInFreeSlots = mFreeSlots.count(slot) != 0;
         bool isInFreeBuffers =
                 std::find(mFreeBuffers.cbegin(), mFreeBuffers.cend(), slot) !=
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index bc23f49..c08552e 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -40,6 +40,7 @@
 #include <gui/TraceUtils.h>
 #include <private/gui/BufferQueueThreadState.h>
 
+#include <utils/Errors.h>
 #include <utils/Log.h>
 #include <utils/Trace.h>
 
@@ -108,9 +109,9 @@
         return NO_INIT;
     }
 
-    if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
-        BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)",
-                slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+    int maxSlot = mCore->getTotalSlotCountLocked();
+    if (slot < 0 || slot >= maxSlot) {
+        BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)", slot, maxSlot);
         return BAD_VALUE;
     } else if (!mSlots[slot].mBufferState.isDequeued()) {
         BQ_LOGE("requestBuffer: slot %d is not owned by the producer "
@@ -123,6 +124,49 @@
     return NO_ERROR;
 }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t BufferQueueProducer::extendSlotCount(int size) {
+    ATRACE_CALL();
+
+    sp<IConsumerListener> listener;
+    {
+        std::lock_guard<std::mutex> lock(mCore->mMutex);
+        BQ_LOGV("extendSlotCount: size %d", size);
+
+        if (mCore->mIsAbandoned) {
+            BQ_LOGE("extendSlotCount: BufferQueue has been abandoned");
+            return NO_INIT;
+        }
+
+        if (!mCore->mAllowExtendedSlotCount) {
+            BQ_LOGE("extendSlotCount: Consumer did not allow unlimited slots");
+            return INVALID_OPERATION;
+        }
+
+        int maxBeforeExtension = mCore->mMaxBufferCount;
+
+        if (size == maxBeforeExtension) {
+            return NO_ERROR;
+        }
+
+        if (size < maxBeforeExtension) {
+            return BAD_VALUE;
+        }
+
+        if (status_t ret = mCore->extendSlotCountLocked(size); ret != OK) {
+            return ret;
+        }
+        listener = mCore->mConsumerListener;
+    }
+
+    if (listener) {
+        listener->onSlotCountChanged(size);
+    }
+
+    return NO_ERROR;
+}
+#endif
+
 status_t BufferQueueProducer::setMaxDequeuedBufferCount(
         int maxDequeuedBuffers) {
     int maxBufferCount;
@@ -170,11 +214,13 @@
         int minUndequedBufferCount = mCore->getMinUndequeuedBufferCountLocked();
         int bufferCount = minUndequedBufferCount + maxDequeuedBuffers;
 
-        if (bufferCount > BufferQueueDefs::NUM_BUFFER_SLOTS) {
+        if (bufferCount > mCore->getTotalSlotCountLocked()) {
             BQ_LOGE("setMaxDequeuedBufferCount: bufferCount %d too large "
-                    "(max %d)", bufferCount, BufferQueueDefs::NUM_BUFFER_SLOTS);
-            bufferCount = BufferQueueDefs::NUM_BUFFER_SLOTS;
-            maxDequeuedBuffers = bufferCount - minUndequedBufferCount;
+                    "(max %d)",
+                    bufferCount, mCore->getTotalSlotCountLocked());
+                    bufferCount = mCore->getTotalSlotCountLocked();
+                    maxDequeuedBuffers = bufferCount - minUndequedBufferCount;
+            return BAD_VALUE;
         }
 
         const int minBufferSlots = mCore->getMinMaxBufferCountLocked();
@@ -320,8 +366,10 @@
         // Producers are not allowed to dequeue more than
         // mMaxDequeuedBufferCount buffers.
         // This check is only done if a buffer has already been queued
-        if (mCore->mBufferHasBeenQueued &&
-                dequeuedCount >= mCore->mMaxDequeuedBufferCount) {
+        using namespace com::android::graphics::libgui::flags;
+        bool flagGatedBufferHasBeenQueued =
+                bq_always_use_max_dequeued_buffer_count() || mCore->mBufferHasBeenQueued;
+        if (flagGatedBufferHasBeenQueued && dequeuedCount >= mCore->mMaxDequeuedBufferCount) {
             // Supress error logs when timeout is non-negative.
             if (mDequeueTimeout < 0) {
                 BQ_LOGE("%s: attempting to exceed the max dequeued buffer "
@@ -649,11 +697,11 @@
                 .requestorName = {mConsumerName.c_str(), mConsumerName.size()},
                 .extras = std::move(tempOptions),
         };
-        sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest);
+        sp<GraphicBuffer> graphicBuffer = sp<GraphicBuffer>::make(allocRequest);
 #else
         sp<GraphicBuffer> graphicBuffer =
-                new GraphicBuffer(width, height, format, BQ_LAYER_COUNT, usage,
-                                  {mConsumerName.c_str(), mConsumerName.size()});
+                sp<GraphicBuffer>::make(width, height, format, BQ_LAYER_COUNT, usage,
+                                        std::string{mConsumerName.c_str(), mConsumerName.size()});
 #endif
 
         status_t error = graphicBuffer->initCheck();
@@ -757,9 +805,9 @@
             return BAD_VALUE;
         }
 
-        if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
-            BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
-                    slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+        const int totalSlotCount = mCore->getTotalSlotCountLocked();
+        if (slot < 0 || slot >= totalSlotCount) {
+            BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
             return BAD_VALUE;
         } else if (!mSlots[slot].mBufferState.isDequeued()) {
             // TODO(http://b/140581935): This message is BQ_LOGW because it
@@ -994,9 +1042,9 @@
             return NO_INIT;
         }
 
-        if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
-            BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)",
-                    slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+        const int totalSlotCount = mCore->getTotalSlotCountLocked();
+        if (slot < 0 || slot >= totalSlotCount) {
+            BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
             return BAD_VALUE;
         } else if (!mSlots[slot].mBufferState.isDequeued()) {
             BQ_LOGE("queueBuffer: slot %d is not owned by the producer "
@@ -1240,9 +1288,9 @@
             return BAD_VALUE;
         }
 
-        if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
-            BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot,
-                    BufferQueueDefs::NUM_BUFFER_SLOTS);
+        const int totalSlotCount = mCore->getTotalSlotCountLocked();
+        if (slot < 0 || slot >= totalSlotCount) {
+            BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount);
             return BAD_VALUE;
         } else if (!mSlots[slot].mBufferState.isDequeued()) {
             BQ_LOGE("cancelBuffer: slot %d is not owned by the producer "
@@ -1410,6 +1458,9 @@
             output->nextFrameNumber = mCore->mFrameCounter + 1;
             output->bufferReplaced = false;
             output->maxBufferCount = mCore->mMaxBufferCount;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+            output->isSlotExpansionAllowed = mCore->mAllowExtendedSlotCount;
+#endif
 
             if (listener != nullptr) {
                 // Set up a death notification so that we can disconnect
@@ -1417,7 +1468,7 @@
 #ifndef NO_BINDER
                 if (IInterface::asBinder(listener)->remoteBinder() != nullptr) {
                     status = IInterface::asBinder(listener)->linkToDeath(
-                            static_cast<IBinder::DeathRecipient*>(this));
+                            sp<IBinder::DeathRecipient>::fromExisting(this));
                     if (status != NO_ERROR) {
                         BQ_LOGE("connect: linkToDeath failed: %s (%d)",
                                 strerror(-status), status);
@@ -1506,8 +1557,7 @@
                                 IInterface::asBinder(mCore->mLinkedToDeath);
                         // This can fail if we're here because of the death
                         // notification, but we just ignore it
-                        token->unlinkToDeath(
-                                static_cast<IBinder::DeathRecipient*>(this));
+                        token->unlinkToDeath(static_cast<IBinder::DeathRecipient*>(this));
                     }
 #endif
                     mCore->mSharedBufferSlot =
@@ -1638,11 +1688,11 @@
 #endif
 
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
-        sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest);
+        sp<GraphicBuffer> graphicBuffer = sp<GraphicBuffer>::make(allocRequest);
 #else
-        sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
-                allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
-                allocUsage, allocName);
+        sp<GraphicBuffer> graphicBuffer =
+                sp<GraphicBuffer>::make(allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
+                                        allocUsage, allocName);
 #endif
 
         status_t result = graphicBuffer->initCheck();
diff --git a/libs/gui/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp
index e9cb013..4f495d0 100644
--- a/libs/gui/BufferReleaseChannel.cpp
+++ b/libs/gui/BufferReleaseChannel.cpp
@@ -108,7 +108,7 @@
 
 status_t BufferReleaseChannel::Message::unflatten(void const*& buffer, size_t& size,
                                                   int const*& fds, size_t& count) {
-    releaseFence = new Fence();
+    releaseFence = sp<Fence>::make();
     if (status_t err = releaseFence->unflatten(buffer, size, fds, count); err != OK) {
         return err;
     }
@@ -344,4 +344,4 @@
     return STATUS_OK;
 }
 
-} // namespace android::gui
\ No newline at end of file
+} // namespace android::gui
diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp
index ba50bf8..80a3543 100644
--- a/libs/gui/Choreographer.cpp
+++ b/libs/gui/Choreographer.cpp
@@ -20,6 +20,7 @@
 #include <gui/Choreographer.h>
 #include <gui/TraceUtils.h>
 #include <jni.h>
+#include <utils/Looper.h>
 
 #undef LOG_TAG
 #define LOG_TAG "AChoreographer"
@@ -69,7 +70,7 @@
 
 Choreographer::Context Choreographer::gChoreographers;
 
-static thread_local Choreographer* gChoreographer;
+static thread_local sp<Choreographer> gChoreographer;
 
 void Choreographer::initJVM(JNIEnv* env) {
     env->GetJavaVM(&gJni.jvm);
@@ -86,14 +87,14 @@
                              "()V");
 }
 
-Choreographer* Choreographer::getForThread() {
+sp<Choreographer> Choreographer::getForThread() {
     if (gChoreographer == nullptr) {
         sp<Looper> looper = Looper::getForThread();
         if (!looper.get()) {
             ALOGW("No looper prepared for thread");
             return nullptr;
         }
-        gChoreographer = new Choreographer(looper);
+        gChoreographer = sp<Choreographer>::make(looper);
         status_t result = gChoreographer->initialize();
         if (result != OK) {
             ALOGW("Failed to initialize");
@@ -238,7 +239,7 @@
         // socket should be atomic across processes.
         DisplayEventReceiver::Event event;
         event.header =
-                DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
+                DisplayEventReceiver::Event::Header{DisplayEventType::DISPLAY_EVENT_NULL,
                                                     PhysicalDisplayId::fromPort(0), systemTime()};
         injectEvent(event);
     }
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 602bba8..1a975de 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -37,6 +37,8 @@
 
 #include <private/gui/ComposerService.h>
 
+#include <ui/BufferQueueDefs.h>
+
 #include <log/log.h>
 #include <utils/Log.h>
 #include <utils/String8.h>
@@ -59,7 +61,11 @@
     return android_atomic_inc(&globalCounter);
 }
 
-ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :
+ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp)
+      :
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+        mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
         mAbandoned(false),
         mConsumer(bufferQueue),
         mPrevFinalReleaseFence(Fence::NO_FENCE) {
@@ -68,7 +74,12 @@
 
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 ConsumerBase::ConsumerBase(bool controlledByApp, bool consumerIsSurfaceFlinger)
-      : mAbandoned(false), mPrevFinalReleaseFence(Fence::NO_FENCE) {
+      :
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+        mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
+        mAbandoned(false),
+        mPrevFinalReleaseFence(Fence::NO_FENCE) {
     sp<IGraphicBufferProducer> producer;
     BufferQueue::createBufferQueue(&producer, &mConsumer, consumerIsSurfaceFlinger);
     mSurface = sp<Surface>::make(producer, controlledByApp);
@@ -77,7 +88,11 @@
 
 ConsumerBase::ConsumerBase(const sp<IGraphicBufferProducer>& producer,
                            const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp)
-      : mAbandoned(false),
+      :
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+        mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
+        mAbandoned(false),
         mConsumer(consumer),
         mSurface(sp<Surface>::make(producer, controlledByApp)),
         mPrevFinalReleaseFence(Fence::NO_FENCE) {
@@ -101,9 +116,16 @@
     if (err != NO_ERROR) {
         CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)",
                 strerror(-err), err);
-    } else {
-        mConsumer->setConsumerName(mName);
+        return;
     }
+
+    mConsumer->setConsumerName(mName);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    if (err = mConsumer->allowUnlimitedSlots(true); err != NO_ERROR) {
+        CB_LOGE("ConsumerBase: error marking as allowed to have unlimited slots: %s (%d)",
+                strerror(-err), err);
+    }
+#endif
 }
 
 ConsumerBase::~ConsumerBase() {
@@ -130,7 +152,11 @@
     }
 
     uint64_t id = buffer->getId();
-    for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    for (int i = 0; i < (int)mSlots.size(); ++i) {
+#else
+    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+#endif
         auto& slot = mSlots[i];
         if (slot.mGraphicBuffer && slot.mGraphicBuffer->getId() == id) {
             return i;
@@ -234,7 +260,10 @@
 
 void ConsumerBase::onBuffersReleased() {
     Mutex::Autolock lock(mMutex);
+    onBuffersReleasedLocked();
+}
 
+void ConsumerBase::onBuffersReleasedLocked() {
     CB_LOGV("onBuffersReleased");
 
     if (mAbandoned) {
@@ -242,6 +271,15 @@
         return;
     }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    std::vector<bool> mask;
+    mConsumer->getReleasedBuffersExtended(&mask);
+    for (size_t i = 0; i < mSlots.size(); i++) {
+        if (mask[i]) {
+            freeBufferLocked(i);
+        }
+    }
+#else
     uint64_t mask = 0;
     mConsumer->getReleasedBuffers(&mask);
     for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
@@ -249,11 +287,23 @@
             freeBufferLocked(i);
         }
     }
+#endif
 }
 
 void ConsumerBase::onSidebandStreamChanged() {
 }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+void ConsumerBase::onSlotCountChanged(int slotCount) {
+    CB_LOGV("onSlotCountChanged: %d", slotCount);
+    Mutex::Autolock lock(mMutex);
+
+    if (slotCount > (int)mSlots.size()) {
+        mSlots.resize(slotCount);
+    }
+}
+#endif
+
 void ConsumerBase::abandon() {
     CB_LOGV("abandon");
     Mutex::Autolock lock(mMutex);
@@ -270,7 +320,11 @@
         CB_LOGE("abandonLocked: ConsumerBase is abandoned!");
         return;
     }
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    for (int i = 0; i < (int)mSlots.size(); ++i) {
+#else
     for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+#endif
         freeBufferLocked(i);
     }
     // disconnect from the BufferQueue
@@ -334,6 +388,26 @@
 }
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
 
+status_t ConsumerBase::addReleaseFence(const sp<GraphicBuffer> buffer, const sp<Fence>& fence) {
+    CB_LOGV("addReleaseFence");
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        CB_LOGE("addReleaseFence: ConsumerBase is abandoned!");
+        return NO_INIT;
+    }
+    if (buffer == nullptr) {
+        return BAD_VALUE;
+    }
+
+    int slotIndex = getSlotForBufferLocked(buffer);
+    if (slotIndex == BufferQueue::INVALID_BUFFER_SLOT) {
+        return BAD_VALUE;
+    }
+
+    return addReleaseFenceLocked(slotIndex, buffer, fence);
+}
+
 status_t ConsumerBase::setDefaultBufferSize(uint32_t width, uint32_t height) {
     Mutex::Autolock _l(mMutex);
     if (mAbandoned) {
@@ -387,6 +461,15 @@
         CB_LOGE("setMaxBufferCount: ConsumerBase is abandoned!");
         return NO_INIT;
     }
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    if (status_t err = mConsumer->allowUnlimitedSlots(false); err != NO_ERROR) {
+        CB_LOGE("ConsumerBase: error marking as not allowed to have unlimited slots: %s (%d)",
+                strerror(-err), err);
+        return err;
+    }
+#endif
+
     return mConsumer->setMaxBufferCount(bufferCount);
 }
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
@@ -397,10 +480,10 @@
         CB_LOGE("setMaxAcquiredBufferCount: ConsumerBase is abandoned!");
         return NO_INIT;
     }
-    return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers);
+    return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers,
+                                                {[this]() { onBuffersReleasedLocked(); }});
 }
 
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 status_t ConsumerBase::setConsumerIsProtected(bool isProtected) {
     Mutex::Autolock lock(mMutex);
     if (mAbandoned) {
@@ -409,7 +492,6 @@
     }
     return mConsumer->setConsumerIsProtected(isProtected);
 }
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 
 sp<NativeHandle> ConsumerBase::getSidebandStream() const {
     Mutex::Autolock _l(mMutex);
@@ -448,6 +530,15 @@
     if (err != OK) {
         return err;
     }
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    std::vector<bool> mask;
+    mConsumer->getReleasedBuffersExtended(&mask);
+    for (int i = 0; i < (int)mSlots.size(); i++) {
+        if (mask[i]) {
+            freeBufferLocked(i);
+        }
+    }
+#else
     uint64_t mask;
     mConsumer->getReleasedBuffers(&mask);
     for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
@@ -455,6 +546,8 @@
             freeBufferLocked(i);
         }
     }
+#endif
+
     return OK;
 }
 
@@ -585,9 +678,13 @@
     return OK;
 }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
+status_t ConsumerBase::releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer) {
+#else
 status_t ConsumerBase::releaseBufferLocked(
         int slot, const sp<GraphicBuffer> graphicBuffer,
         EGLDisplay display, EGLSyncKHR eglFence) {
+#endif
     if (mAbandoned) {
         CB_LOGE("releaseBufferLocked: ConsumerBase is abandoned!");
         return NO_INIT;
@@ -596,13 +693,20 @@
     // buffer on the same slot), the buffer producer is definitely no longer
     // tracking it.
     if (!stillTracking(slot, graphicBuffer)) {
+        CB_LOGV("releaseBufferLocked: Not tracking, exiting without calling releaseBuffer for "
+                "slot=%d/%" PRIu64,
+                slot, mSlots[slot].mFrameNumber);
         return OK;
     }
 
     CB_LOGV("releaseBufferLocked: slot=%d/%" PRIu64,
             slot, mSlots[slot].mFrameNumber);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
+    status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber, mSlots[slot].mFence);
+#else
     status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber,
             display, eglFence, mSlots[slot].mFence);
+#endif
     if (err == IGraphicBufferConsumer::STALE_BUFFER_SLOT) {
         freeBufferLocked(slot);
     }
@@ -615,7 +719,11 @@
 
 bool ConsumerBase::stillTracking(int slot,
         const sp<GraphicBuffer> graphicBuffer) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    if (slot < 0 || slot >= (int)mSlots.size()) {
+#else
     if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) {
+#endif
         return false;
     }
     return (mSlots[slot].mGraphicBuffer != nullptr &&
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index 23b432e..ecbceb7 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -20,7 +20,11 @@
 
 #include <com_android_graphics_libgui_flags.h>
 #include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
 #include <gui/CpuConsumer.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
 #include <utils/Log.h>
 
 #define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
@@ -31,6 +35,28 @@
 
 namespace android {
 
+std::tuple<sp<CpuConsumer>, sp<Surface>> CpuConsumer::create(size_t maxLockedBuffers,
+                                                             bool controlledByApp,
+                                                             bool isConsumerSurfaceFlinger) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+    sp<CpuConsumer> consumer =
+            sp<CpuConsumer>::make(maxLockedBuffers, controlledByApp, isConsumerSurfaceFlinger);
+    return {consumer, consumer->getSurface()};
+#else
+    sp<IGraphicBufferProducer> igbp;
+    sp<IGraphicBufferConsumer> igbc;
+    BufferQueue::createBufferQueue(&igbp, &igbc, isConsumerSurfaceFlinger);
+
+    return {sp<CpuConsumer>::make(igbc, maxLockedBuffers, controlledByApp),
+            sp<Surface>::make(igbp, controlledByApp)};
+#endif
+}
+
+sp<CpuConsumer> CpuConsumer::create(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers,
+                                    bool controlledByApp) {
+    return sp<CpuConsumer>::make(bq, maxLockedBuffers, controlledByApp);
+}
+
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 CpuConsumer::CpuConsumer(size_t maxLockedBuffers, bool controlledByApp,
                          bool isConsumerSurfaceFlinger)
@@ -230,7 +256,7 @@
         return err;
     }
 
-    sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+    sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE);
     addReleaseFenceLocked(ab.mSlot, ab.mGraphicBuffer, fence);
     releaseBufferLocked(ab.mSlot, ab.mGraphicBuffer);
 
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 68f10f4..6f23885 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -167,7 +167,7 @@
         for (ssize_t i = 0; i < n; i++) {
             const DisplayEventReceiver::Event& ev = buf[i];
             switch (ev.header.type) {
-                case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+                case DisplayEventType::DISPLAY_EVENT_VSYNC:
                     // Later vsync events will just overwrite the info from earlier
                     // ones. That's fine, we only care about the most recent.
                     gotVsync = true;
@@ -183,7 +183,7 @@
                         ATRACE_INT("RenderRate", fps);
                     }
                     break;
-                case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+                case DisplayEventType::DISPLAY_EVENT_HOTPLUG:
                     if (ev.hotplug.connectionError == 0) {
                         dispatchHotplug(ev.header.timestamp, ev.header.displayId,
                                         ev.hotplug.connected);
@@ -192,31 +192,28 @@
                                                        ev.hotplug.connectionError);
                     }
                     break;
-                case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE:
+                case DisplayEventType::DISPLAY_EVENT_MODE_CHANGE:
                     dispatchModeChanged(ev.header.timestamp, ev.header.displayId,
                                         ev.modeChange.modeId, ev.modeChange.vsyncPeriod);
                     break;
-                case DisplayEventReceiver::DISPLAY_EVENT_NULL:
+                case DisplayEventType::DISPLAY_EVENT_NULL:
                     dispatchNullEvent(ev.header.timestamp, ev.header.displayId);
                     break;
-                case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
+                case DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
                     mFrameRateOverrides.emplace_back(ev.frameRateOverride);
                     break;
-                case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH:
+                case DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH:
                     dispatchFrameRateOverrides(ev.header.timestamp, ev.header.displayId,
                                                std::move(mFrameRateOverrides));
                     break;
-                case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
+                case DisplayEventType::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
                     dispatchHdcpLevelsChanged(ev.header.displayId,
                                               ev.hdcpLevelsChange.connectedLevel,
                                               ev.hdcpLevelsChange.maxLevel);
                     break;
-                case DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION:
+                case DisplayEventType::DISPLAY_EVENT_MODE_REJECTION:
                     dispatchModeRejected(ev.header.displayId, ev.modeRejection.modeId);
                     break;
-                default:
-                    ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
-                    break;
             }
         }
     }
diff --git a/libs/gui/Flags.cpp b/libs/gui/Flags.cpp
index 85ee2cd..ee2802f 100644
--- a/libs/gui/Flags.cpp
+++ b/libs/gui/Flags.cpp
@@ -29,6 +29,14 @@
 #endif
 }
 
+ParcelableSurfaceType surfaceToParcelableSurfaceType(const sp<Surface>& surface) {
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+    return view::Surface::fromSurface(surface);
+#else
+    return surface->getIGraphicBufferProducer();
+#endif
+}
+
 sp<IGraphicBufferProducer> surfaceTypeToIGBP(const sp<SurfaceType>& surface) {
 #if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
     return surface->getIGraphicBufferProducer();
diff --git a/libs/gui/FrameRateUtils.cpp b/libs/gui/FrameRateUtils.cpp
index 5c4879c..1b2354e 100644
--- a/libs/gui/FrameRateUtils.cpp
+++ b/libs/gui/FrameRateUtils.cpp
@@ -42,7 +42,7 @@
 
     if (compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT &&
         compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE &&
-        compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE &&
+        compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST &&
         (!privileged ||
          (compatibility != ANATIVEWINDOW_FRAME_RATE_EXACT &&
           compatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE))) {
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index f2173cd..2c5770d 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -37,6 +37,7 @@
 #include <gui/DebugEGLImageTracker.h>
 #include <gui/GLConsumer.h>
 #include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 
 #include <private/gui/ComposerService.h>
@@ -101,6 +102,50 @@
     return hasIt;
 }
 
+std::tuple<sp<GLConsumer>, sp<Surface>> GLConsumer::create(uint32_t tex, uint32_t textureTarget,
+                                                           bool useFenceSync,
+                                                           bool isControlledByApp) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+    sp<GLConsumer> consumer =
+            sp<GLConsumer>::make(tex, textureTarget, useFenceSync, isControlledByApp);
+    return {consumer, consumer->getSurface()};
+#else
+    sp<IGraphicBufferProducer> igbp;
+    sp<IGraphicBufferConsumer> igbc;
+    BufferQueue::createBufferQueue(&igbp, &igbc);
+
+    return {sp<GLConsumer>::make(igbc, tex, textureTarget, useFenceSync, isControlledByApp),
+            sp<Surface>::make(igbp, isControlledByApp)};
+#endif
+}
+
+std::tuple<sp<GLConsumer>, sp<Surface>> GLConsumer::create(uint32_t textureTarget,
+                                                           bool useFenceSync,
+                                                           bool isControlledByApp) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+    sp<GLConsumer> consumer = sp<GLConsumer>::make(textureTarget, useFenceSync, isControlledByApp);
+    return {consumer, consumer->getSurface()};
+#else
+    sp<IGraphicBufferProducer> igbp;
+    sp<IGraphicBufferConsumer> igbc;
+    BufferQueue::createBufferQueue(&igbp, &igbc);
+
+    return {sp<GLConsumer>::make(igbc, textureTarget, useFenceSync, isControlledByApp),
+            sp<Surface>::make(igbp, isControlledByApp)};
+#endif
+}
+
+sp<GLConsumer> GLConsumer::create(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
+                                  uint32_t textureTarget, bool useFenceSync,
+                                  bool isControlledByApp) {
+    return sp<GLConsumer>::make(bq, tex, textureTarget, useFenceSync, isControlledByApp);
+}
+
+sp<GLConsumer> GLConsumer::create(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget,
+                                  bool useFenceSync, bool isControlledByApp) {
+    return sp<GLConsumer>::make(bq, textureTarget, useFenceSync, isControlledByApp);
+}
+
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 GLConsumer::GLConsumer(uint32_t tex, uint32_t texTarget, bool useFenceSync, bool isControlledByApp)
       : ConsumerBase(isControlledByApp, /* isConsumerSurfaceFlinger */ false),
@@ -119,6 +164,9 @@
         mTexTarget(texTarget),
         mEglDisplay(EGL_NO_DISPLAY),
         mEglContext(EGL_NO_CONTEXT),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+        mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
         mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
         mAttached(true) {
     GLC_LOGV("GLConsumer");
@@ -129,27 +177,29 @@
 }
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 
-GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
-        uint32_t texTarget, bool useFenceSync, bool isControlledByApp) :
-    ConsumerBase(bq, isControlledByApp),
-    mCurrentCrop(Rect::EMPTY_RECT),
-    mCurrentTransform(0),
-    mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
-    mCurrentFence(Fence::NO_FENCE),
-    mCurrentTimestamp(0),
-    mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
-    mCurrentFrameNumber(0),
-    mDefaultWidth(1),
-    mDefaultHeight(1),
-    mFilteringEnabled(true),
-    mTexName(tex),
-    mUseFenceSync(useFenceSync),
-    mTexTarget(texTarget),
-    mEglDisplay(EGL_NO_DISPLAY),
-    mEglContext(EGL_NO_CONTEXT),
-    mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
-    mAttached(true)
-{
+GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t texTarget,
+                       bool useFenceSync, bool isControlledByApp)
+      : ConsumerBase(bq, isControlledByApp),
+        mCurrentCrop(Rect::EMPTY_RECT),
+        mCurrentTransform(0),
+        mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+        mCurrentFence(Fence::NO_FENCE),
+        mCurrentTimestamp(0),
+        mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+        mCurrentFrameNumber(0),
+        mDefaultWidth(1),
+        mDefaultHeight(1),
+        mFilteringEnabled(true),
+        mTexName(tex),
+        mUseFenceSync(useFenceSync),
+        mTexTarget(texTarget),
+        mEglDisplay(EGL_NO_DISPLAY),
+        mEglContext(EGL_NO_CONTEXT),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+        mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
+        mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+        mAttached(true) {
     GLC_LOGV("GLConsumer");
 
     memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(),
@@ -176,6 +226,9 @@
         mTexTarget(texTarget),
         mEglDisplay(EGL_NO_DISPLAY),
         mEglContext(EGL_NO_CONTEXT),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+        mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
         mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
         mAttached(false) {
     GLC_LOGV("GLConsumer");
@@ -204,6 +257,9 @@
         mTexTarget(texTarget),
         mEglDisplay(EGL_NO_DISPLAY),
         mEglContext(EGL_NO_CONTEXT),
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+        mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS),
+#endif
         mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
         mAttached(false) {
     GLC_LOGV("GLConsumer");
@@ -322,7 +378,7 @@
         }
 
         if (mReleasedTexImage == nullptr) {
-            mReleasedTexImage = new EglImage(getDebugTexImageBuffer());
+            mReleasedTexImage = sp<EglImage>::make(getDebugTexImageBuffer());
         }
 
         mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
@@ -354,10 +410,10 @@
     if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) {
         // The first time, create the debug texture in case the application
         // continues to use it.
-        sp<GraphicBuffer> buffer = new GraphicBuffer(
-                kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888,
-                DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY,
-                "[GLConsumer debug texture]");
+        sp<GraphicBuffer> buffer =
+                sp<GraphicBuffer>::make(kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888,
+                                        DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY,
+                                        "[GLConsumer debug texture]");
         uint32_t* bits;
         buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits));
         uint32_t stride = buffer->getStride();
@@ -389,24 +445,35 @@
     // replaces any old EglImage with a new one (using the new buffer).
     if (item->mGraphicBuffer != nullptr) {
         int slot = item->mSlot;
-        mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer);
+        mEglSlots[slot].mEglImage = sp<EglImage>::make(item->mGraphicBuffer);
     }
 
     return NO_ERROR;
 }
 
-status_t GLConsumer::releaseBufferLocked(int buf,
-        sp<GraphicBuffer> graphicBuffer,
-        EGLDisplay display, EGLSyncKHR eglFence) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+void GLConsumer::onSlotCountChanged(int slotCount) {
+    ConsumerBase::onSlotCountChanged(slotCount);
+
+    Mutex::Autolock lock(mMutex);
+    if (slotCount > (int)mEglSlots.size()) {
+        mEglSlots.resize(slotCount);
+    }
+}
+#endif
+
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
+status_t GLConsumer::releaseBufferLocked(int buf, sp<GraphicBuffer> graphicBuffer,
+                                         EGLDisplay display, EGLSyncKHR eglFence) {
     // release the buffer if it hasn't already been discarded by the
     // BufferQueue. This can happen, for example, when the producer of this
     // buffer has reallocated the original buffer slot after this buffer
     // was acquired.
-    status_t err = ConsumerBase::releaseBufferLocked(
-            buf, graphicBuffer, display, eglFence);
+    status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence);
     mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
     return err;
 }
+#endif
 
 status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item,
         PendingRelease* pendingRelease)
@@ -468,9 +535,14 @@
     // release old buffer
     if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
         if (pendingRelease == nullptr) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
+            status_t status =
+                    releaseBufferLocked(mCurrentTexture, mCurrentTextureImage->graphicBuffer());
+#else
             status_t status = releaseBufferLocked(
                     mCurrentTexture, mCurrentTextureImage->graphicBuffer(),
                     mEglDisplay, mEglSlots[mCurrentTexture].mEglFence);
+#endif
             if (status < NO_ERROR) {
                 GLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)",
                         strerror(-status), status);
@@ -479,10 +551,7 @@
             }
         } else {
             pendingRelease->currentTexture = mCurrentTexture;
-            pendingRelease->graphicBuffer =
-                    mCurrentTextureImage->graphicBuffer();
-            pendingRelease->display = mEglDisplay;
-            pendingRelease->fence = mEglSlots[mCurrentTexture].mEglFence;
+            pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer();
             pendingRelease->isPending = true;
         }
     }
@@ -713,7 +782,7 @@
                         "fd: %#x", eglGetError());
                 return UNKNOWN_ERROR;
             }
-            sp<Fence> fence(new Fence(fenceFd));
+            sp<Fence> fence = sp<Fence>::make(fenceFd);
             status_t err = addReleaseFenceLocked(mCurrentTexture,
                     mCurrentTextureImage->graphicBuffer(), fence);
             if (err != OK) {
@@ -722,6 +791,11 @@
                 return err;
             }
         } else if (mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
+            // Basically all clients are using native fence syncs. If they aren't, we lose nothing
+            // by waiting here, because the alternative can cause deadlocks (b/339705065).
+            glFinish();
+#else
             EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence;
             if (fence != EGL_NO_SYNC_KHR) {
                 // There is already a fence for the current slot.  We need to
@@ -751,6 +825,7 @@
             }
             glFlush();
             mEglSlots[mCurrentTexture].mEglFence = fence;
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
         }
     }
 
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
deleted file mode 100644
index f3bd90c..0000000
--- a/libs/gui/IConsumerListener.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2013 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 <gui/IConsumerListener.h>
-
-#include <gui/BufferItem.h>
-
-namespace android {
-
-namespace { // Anonymous
-
-enum class Tag : uint32_t {
-    ON_DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
-    ON_FRAME_AVAILABLE,
-    ON_FRAME_REPLACED,
-    ON_BUFFERS_RELEASED,
-    ON_SIDEBAND_STREAM_CHANGED,
-    ON_FRAME_DEQUEUED,
-    ON_FRAME_CANCELLED,
-    ON_FRAME_DETACHED,
-    LAST = ON_FRAME_DETACHED,
-};
-
-} // Anonymous namespace
-
-class BpConsumerListener : public SafeBpInterface<IConsumerListener> {
-public:
-    explicit BpConsumerListener(const sp<IBinder>& impl)
-          : SafeBpInterface<IConsumerListener>(impl, "BpConsumerListener") {}
-
-    ~BpConsumerListener() override;
-
-    void onDisconnect() override {
-        callRemoteAsync<decltype(&IConsumerListener::onDisconnect)>(Tag::ON_DISCONNECT);
-    }
-
-    void onFrameDequeued(const uint64_t bufferId) override {
-        callRemoteAsync<decltype(&IConsumerListener::onFrameDequeued)>(Tag::ON_FRAME_DEQUEUED,
-                                                                       bufferId);
-    }
-
-    void onFrameDetached(const uint64_t bufferId) override {
-        callRemoteAsync<decltype(&IConsumerListener::onFrameDetached)>(Tag::ON_FRAME_DETACHED,
-                                                                       bufferId);
-    }
-
-    void onFrameCancelled(const uint64_t bufferId) override {
-        callRemoteAsync<decltype(&IConsumerListener::onFrameCancelled)>(Tag::ON_FRAME_CANCELLED,
-                                                                        bufferId);
-    }
-
-    void onFrameAvailable(const BufferItem& item) override {
-        callRemoteAsync<decltype(&IConsumerListener::onFrameAvailable)>(Tag::ON_FRAME_AVAILABLE,
-                                                                        item);
-    }
-
-    void onFrameReplaced(const BufferItem& item) override {
-        callRemoteAsync<decltype(&IConsumerListener::onFrameReplaced)>(Tag::ON_FRAME_REPLACED,
-                                                                       item);
-    }
-
-    void onBuffersReleased() override {
-        callRemoteAsync<decltype(&IConsumerListener::onBuffersReleased)>(Tag::ON_BUFFERS_RELEASED);
-    }
-
-    void onSidebandStreamChanged() override {
-        callRemoteAsync<decltype(&IConsumerListener::onSidebandStreamChanged)>(
-                Tag::ON_SIDEBAND_STREAM_CHANGED);
-    }
-
-    void addAndGetFrameTimestamps(const NewFrameEventsEntry* /*newTimestamps*/,
-                                  FrameEventHistoryDelta* /*outDelta*/) override {
-        LOG_ALWAYS_FATAL("IConsumerListener::addAndGetFrameTimestamps cannot be proxied");
-    }
-};
-
-// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
-// clang warning -Wweak-vtables)
-BpConsumerListener::~BpConsumerListener() = default;
-
-IMPLEMENT_META_INTERFACE(ConsumerListener, "android.gui.IConsumerListener");
-
-status_t BnConsumerListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-                                        uint32_t flags) {
-    if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
-        return BBinder::onTransact(code, data, reply, flags);
-    }
-    auto tag = static_cast<Tag>(code);
-    switch (tag) {
-        case Tag::ON_DISCONNECT:
-            return callLocalAsync(data, reply, &IConsumerListener::onDisconnect);
-        case Tag::ON_FRAME_AVAILABLE:
-            return callLocalAsync(data, reply, &IConsumerListener::onFrameAvailable);
-        case Tag::ON_FRAME_REPLACED:
-            return callLocalAsync(data, reply, &IConsumerListener::onFrameReplaced);
-        case Tag::ON_BUFFERS_RELEASED:
-            return callLocalAsync(data, reply, &IConsumerListener::onBuffersReleased);
-        case Tag::ON_SIDEBAND_STREAM_CHANGED:
-            return callLocalAsync(data, reply, &IConsumerListener::onSidebandStreamChanged);
-        case Tag::ON_FRAME_DEQUEUED:
-            return callLocalAsync(data, reply, &IConsumerListener::onFrameDequeued);
-        case Tag::ON_FRAME_CANCELLED:
-            return callLocalAsync(data, reply, &IConsumerListener::onFrameCancelled);
-        case Tag::ON_FRAME_DETACHED:
-            return callLocalAsync(data, reply, &IConsumerListener::onFrameDetached);
-    }
-}
-
-} // namespace android
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
deleted file mode 100644
index 282957b..0000000
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2013 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 <gui/IGraphicBufferConsumer.h>
-
-#include <gui/BufferItem.h>
-#include <gui/IConsumerListener.h>
-
-#include <binder/Parcel.h>
-
-#include <ui/Fence.h>
-#include <ui/GraphicBuffer.h>
-
-#include <utils/NativeHandle.h>
-#include <utils/String8.h>
-#include <cstdint>
-
-namespace android {
-
-namespace { // Anonymous namespace
-
-enum class Tag : uint32_t {
-    ACQUIRE_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
-    DETACH_BUFFER,
-    ATTACH_BUFFER,
-    RELEASE_BUFFER,
-    CONSUMER_CONNECT,
-    CONSUMER_DISCONNECT,
-    GET_RELEASED_BUFFERS,
-    SET_DEFAULT_BUFFER_SIZE,
-    SET_MAX_BUFFER_COUNT,
-    SET_MAX_ACQUIRED_BUFFER_COUNT,
-    SET_CONSUMER_NAME,
-    SET_DEFAULT_BUFFER_FORMAT,
-    SET_DEFAULT_BUFFER_DATA_SPACE,
-    SET_CONSUMER_USAGE_BITS,
-    SET_CONSUMER_IS_PROTECTED,
-    SET_TRANSFORM_HINT,
-    GET_SIDEBAND_STREAM,
-    GET_OCCUPANCY_HISTORY,
-    DISCARD_FREE_BUFFERS,
-    DUMP_STATE,
-    LAST = DUMP_STATE,
-};
-
-} // Anonymous namespace
-
-class BpGraphicBufferConsumer : public SafeBpInterface<IGraphicBufferConsumer> {
-public:
-    explicit BpGraphicBufferConsumer(const sp<IBinder>& impl)
-          : SafeBpInterface<IGraphicBufferConsumer>(impl, "BpGraphicBufferConsumer") {}
-
-    ~BpGraphicBufferConsumer() override;
-
-    status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen,
-                           uint64_t maxFrameNumber) override {
-        using Signature = decltype(&IGraphicBufferConsumer::acquireBuffer);
-        return callRemote<Signature>(Tag::ACQUIRE_BUFFER, buffer, presentWhen, maxFrameNumber);
-    }
-
-    status_t detachBuffer(int slot) override {
-        using Signature = decltype(&IGraphicBufferConsumer::detachBuffer);
-        return callRemote<Signature>(Tag::DETACH_BUFFER, slot);
-    }
-
-    status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) override {
-        using Signature = decltype(&IGraphicBufferConsumer::attachBuffer);
-        return callRemote<Signature>(Tag::ATTACH_BUFFER, slot, buffer);
-    }
-
-    status_t releaseBuffer(int buf, uint64_t frameNumber,
-                           EGLDisplay display __attribute__((unused)),
-                           EGLSyncKHR fence __attribute__((unused)),
-                           const sp<Fence>& releaseFence) override {
-        using Signature = status_t (IGraphicBufferConsumer::*)(int, uint64_t, const sp<Fence>&);
-        return callRemote<Signature>(Tag::RELEASE_BUFFER, buf, frameNumber, releaseFence);
-    }
-
-    status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) override {
-        using Signature = decltype(&IGraphicBufferConsumer::consumerConnect);
-        return callRemote<Signature>(Tag::CONSUMER_CONNECT, consumer, controlledByApp);
-    }
-
-    status_t consumerDisconnect() override {
-        return callRemote<decltype(&IGraphicBufferConsumer::consumerDisconnect)>(
-                Tag::CONSUMER_DISCONNECT);
-    }
-
-    status_t getReleasedBuffers(uint64_t* slotMask) override {
-        using Signature = decltype(&IGraphicBufferConsumer::getReleasedBuffers);
-        return callRemote<Signature>(Tag::GET_RELEASED_BUFFERS, slotMask);
-    }
-
-    status_t setDefaultBufferSize(uint32_t width, uint32_t height) override {
-        using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferSize);
-        return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_SIZE, width, height);
-    }
-
-    status_t setMaxBufferCount(int bufferCount) override {
-        using Signature = decltype(&IGraphicBufferConsumer::setMaxBufferCount);
-        return callRemote<Signature>(Tag::SET_MAX_BUFFER_COUNT, bufferCount);
-    }
-
-    status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) override {
-        using Signature = decltype(&IGraphicBufferConsumer::setMaxAcquiredBufferCount);
-        return callRemote<Signature>(Tag::SET_MAX_ACQUIRED_BUFFER_COUNT, maxAcquiredBuffers);
-    }
-
-    status_t setConsumerName(const String8& name) override {
-        using Signature = decltype(&IGraphicBufferConsumer::setConsumerName);
-        return callRemote<Signature>(Tag::SET_CONSUMER_NAME, name);
-    }
-
-    status_t setDefaultBufferFormat(PixelFormat defaultFormat) override {
-        using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferFormat);
-        return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_FORMAT, defaultFormat);
-    }
-
-    status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) override {
-        using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferDataSpace);
-        return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_DATA_SPACE, defaultDataSpace);
-    }
-
-    status_t setConsumerUsageBits(uint64_t usage) override {
-        using Signature = decltype(&IGraphicBufferConsumer::setConsumerUsageBits);
-        return callRemote<Signature>(Tag::SET_CONSUMER_USAGE_BITS, usage);
-    }
-
-    status_t setConsumerIsProtected(bool isProtected) override {
-        using Signature = decltype(&IGraphicBufferConsumer::setConsumerIsProtected);
-        return callRemote<Signature>(Tag::SET_CONSUMER_IS_PROTECTED, isProtected);
-    }
-
-    status_t setTransformHint(uint32_t hint) override {
-        using Signature = decltype(&IGraphicBufferConsumer::setTransformHint);
-        return callRemote<Signature>(Tag::SET_TRANSFORM_HINT, hint);
-    }
-
-    status_t getSidebandStream(sp<NativeHandle>* outStream) const override {
-        using Signature = decltype(&IGraphicBufferConsumer::getSidebandStream);
-        return callRemote<Signature>(Tag::GET_SIDEBAND_STREAM, outStream);
-    }
-
-    status_t getOccupancyHistory(bool forceFlush,
-                                 std::vector<OccupancyTracker::Segment>* outHistory) override {
-        using Signature = decltype(&IGraphicBufferConsumer::getOccupancyHistory);
-        return callRemote<Signature>(Tag::GET_OCCUPANCY_HISTORY, forceFlush, outHistory);
-    }
-
-    status_t discardFreeBuffers() override {
-        return callRemote<decltype(&IGraphicBufferConsumer::discardFreeBuffers)>(
-                Tag::DISCARD_FREE_BUFFERS);
-    }
-
-    status_t dumpState(const String8& prefix, String8* outResult) const override {
-        using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const;
-        return callRemote<Signature>(Tag::DUMP_STATE, prefix, outResult);
-    }
-};
-
-// Out-of-line virtual method definition to trigger vtable emission in this translation unit
-// (see clang warning -Wweak-vtables)
-BpGraphicBufferConsumer::~BpGraphicBufferConsumer() = default;
-
-IMPLEMENT_META_INTERFACE(GraphicBufferConsumer, "android.gui.IGraphicBufferConsumer");
-
-status_t BnGraphicBufferConsumer::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-                                             uint32_t flags) {
-    if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
-        return BBinder::onTransact(code, data, reply, flags);
-    }
-    auto tag = static_cast<Tag>(code);
-    switch (tag) {
-        case Tag::ACQUIRE_BUFFER:
-            return callLocal(data, reply, &IGraphicBufferConsumer::acquireBuffer);
-        case Tag::DETACH_BUFFER:
-            return callLocal(data, reply, &IGraphicBufferConsumer::detachBuffer);
-        case Tag::ATTACH_BUFFER:
-            return callLocal(data, reply, &IGraphicBufferConsumer::attachBuffer);
-        case Tag::RELEASE_BUFFER: {
-            using Signature = status_t (IGraphicBufferConsumer::*)(int, uint64_t, const sp<Fence>&);
-            return callLocal<Signature>(data, reply, &IGraphicBufferConsumer::releaseBuffer);
-        }
-        case Tag::CONSUMER_CONNECT:
-            return callLocal(data, reply, &IGraphicBufferConsumer::consumerConnect);
-        case Tag::CONSUMER_DISCONNECT:
-            return callLocal(data, reply, &IGraphicBufferConsumer::consumerDisconnect);
-        case Tag::GET_RELEASED_BUFFERS:
-            return callLocal(data, reply, &IGraphicBufferConsumer::getReleasedBuffers);
-        case Tag::SET_DEFAULT_BUFFER_SIZE:
-            return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferSize);
-        case Tag::SET_MAX_BUFFER_COUNT:
-            return callLocal(data, reply, &IGraphicBufferConsumer::setMaxBufferCount);
-        case Tag::SET_MAX_ACQUIRED_BUFFER_COUNT:
-            return callLocal(data, reply, &IGraphicBufferConsumer::setMaxAcquiredBufferCount);
-        case Tag::SET_CONSUMER_NAME:
-            return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerName);
-        case Tag::SET_DEFAULT_BUFFER_FORMAT:
-            return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferFormat);
-        case Tag::SET_DEFAULT_BUFFER_DATA_SPACE:
-            return callLocal(data, reply, &IGraphicBufferConsumer::setDefaultBufferDataSpace);
-        case Tag::SET_CONSUMER_USAGE_BITS:
-            return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerUsageBits);
-        case Tag::SET_CONSUMER_IS_PROTECTED:
-            return callLocal(data, reply, &IGraphicBufferConsumer::setConsumerIsProtected);
-        case Tag::SET_TRANSFORM_HINT:
-            return callLocal(data, reply, &IGraphicBufferConsumer::setTransformHint);
-        case Tag::GET_SIDEBAND_STREAM:
-            return callLocal(data, reply, &IGraphicBufferConsumer::getSidebandStream);
-        case Tag::GET_OCCUPANCY_HISTORY:
-            return callLocal(data, reply, &IGraphicBufferConsumer::getOccupancyHistory);
-        case Tag::DISCARD_FREE_BUFFERS:
-            return callLocal(data, reply, &IGraphicBufferConsumer::discardFreeBuffers);
-        case Tag::DUMP_STATE: {
-            using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const;
-            return callLocal<Signature>(data, reply, &IGraphicBufferConsumer::dumpState);
-        }
-    }
-}
-
-} // namespace android
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 0914480..1d1910e 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -81,6 +81,7 @@
     GET_LAST_QUEUED_BUFFER2,
     SET_FRAME_RATE,
     SET_ADDITIONAL_OPTIONS,
+    SET_MAX_BUFER_COUNT_EXTENDED,
 };
 
 class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -103,7 +104,7 @@
         }
         bool nonNull = reply.readInt32();
         if (nonNull) {
-            *buf = new GraphicBuffer();
+            *buf = sp<GraphicBuffer>::make();
             result = reply.read(**buf);
             if(result != NO_ERROR) {
                 (*buf).clear();
@@ -149,6 +150,20 @@
         return result;
     }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    status_t extendSlotCount(int size) override {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        data.writeInt32(size);
+        status_t result = remote()->transact(SET_MAX_BUFER_COUNT_EXTENDED, data, &reply);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.readInt32();
+        return result;
+    }
+#endif
+
     virtual status_t setAsyncMode(bool async) {
         Parcel data, reply;
         data.writeInterfaceToken(
@@ -182,7 +197,7 @@
         }
 
         *buf = reply.readInt32();
-        *fence = new Fence();
+        *fence = sp<Fence>::make();
         result = reply.read(**fence);
         if (result != NO_ERROR) {
             fence->clear();
@@ -278,7 +293,7 @@
         if (result == NO_ERROR) {
             bool nonNull = reply.readInt32();
             if (nonNull) {
-                *outBuffer = new GraphicBuffer;
+                *outBuffer = sp<GraphicBuffer>::make();
                 result = reply.read(**outBuffer);
                 if (result != NO_ERROR) {
                     outBuffer->clear();
@@ -287,7 +302,7 @@
             }
             nonNull = reply.readInt32();
             if (nonNull) {
-                *outFence = new Fence;
+                *outFence = sp<Fence>::make();
                 result = reply.read(**outFence);
                 if (result != NO_ERROR) {
                     outBuffer->clear();
@@ -625,7 +640,7 @@
         bool hasBuffer = reply.readBool();
         sp<GraphicBuffer> buffer;
         if (hasBuffer) {
-            buffer = new GraphicBuffer();
+            buffer = sp<GraphicBuffer>::make();
             result = reply.read(*buffer);
             if (result == NO_ERROR) {
                 result = reply.read(outTransformMatrix, sizeof(float) * 16);
@@ -635,7 +650,7 @@
             ALOGE("getLastQueuedBuffer failed to read buffer: %d", result);
             return result;
         }
-        sp<Fence> fence(new Fence);
+        sp<Fence> fence = sp<Fence>::make();
         result = reply.read(*fence);
         if (result != NO_ERROR) {
             ALOGE("getLastQueuedBuffer failed to read fence: %d", result);
@@ -672,7 +687,7 @@
         }
         sp<GraphicBuffer> buffer;
         if (hasBuffer) {
-            buffer = new GraphicBuffer();
+            buffer = sp<GraphicBuffer>::make();
             result = reply.read(*buffer);
             if (result == NO_ERROR) {
                 result = reply.read(*outRect);
@@ -685,7 +700,7 @@
             ALOGE("getLastQueuedBuffer failed to read buffer: %d", result);
             return result;
         }
-        sp<Fence> fence(new Fence);
+        sp<Fence> fence = sp<Fence>::make();
         result = reply.read(*fence);
         if (result != NO_ERROR) {
             ALOGE("getLastQueuedBuffer failed to read fence: %d", result);
@@ -981,6 +996,14 @@
 
 // ----------------------------------------------------------------------
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+status_t IGraphicBufferProducer::extendSlotCount(int size) {
+    // No-op for IGBP other than BufferQueue.
+    (void)size;
+    return INVALID_OPERATION;
+}
+#endif
+
 status_t IGraphicBufferProducer::setLegacyBufferDrop(bool drop) {
     // No-op for IGBP other than BufferQueue.
     (void) drop;
@@ -1209,7 +1232,7 @@
         }
         case ATTACH_BUFFER: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
-            sp<GraphicBuffer> buffer = new GraphicBuffer();
+            sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make();
             status_t result = data.read(*buffer.get());
             int slot = 0;
             if (result == NO_ERROR) {
@@ -1227,7 +1250,7 @@
                 return result;
             }
             for (sp<GraphicBuffer>& buffer : buffers) {
-                buffer = new GraphicBuffer();
+                buffer = sp<GraphicBuffer>::make();
                 result = data.read(*buffer.get());
                 if (result != NO_ERROR) {
                     return result;
@@ -1283,7 +1306,7 @@
         case CANCEL_BUFFER: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             int buf = data.readInt32();
-            sp<Fence> fence = new Fence();
+            sp<Fence> fence = sp<Fence>::make();
             status_t result = data.read(*fence.get());
             if (result == NO_ERROR) {
                 result = cancelBuffer(buf, fence);
@@ -1582,6 +1605,15 @@
             return NO_ERROR;
         }
 #endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+        case SET_MAX_BUFER_COUNT_EXTENDED: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            int size = data.readInt32();
+            status_t result = extendSlotCount(size);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
+#endif
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp
index 4e92a39..393e1c3 100644
--- a/libs/gui/IGraphicBufferProducerFlattenables.cpp
+++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp
@@ -105,7 +105,7 @@
             : std::nullopt;
 #endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES
 
-    fence = new Fence();
+    fence = sp<Fence>::make();
     status_t result = fence->unflatten(buffer, size, fds, count);
     if (result != NO_ERROR) {
         return result;
@@ -128,7 +128,7 @@
 constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
     return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) +
             sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount) +
-            sizeof(result);
+            sizeof(result) + sizeof(isSlotExpansionAllowed);
 }
 size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
     return minFlattenedSize() + frameTimestamps.getFlattenedSize();
@@ -152,6 +152,7 @@
     FlattenableUtils::write(buffer, size, nextFrameNumber);
     FlattenableUtils::write(buffer, size, bufferReplaced);
     FlattenableUtils::write(buffer, size, maxBufferCount);
+    FlattenableUtils::write(buffer, size, isSlotExpansionAllowed);
 
     status_t result = frameTimestamps.flatten(buffer, size, fds, count);
     if (result != NO_ERROR) {
@@ -175,6 +176,7 @@
     FlattenableUtils::read(buffer, size, nextFrameNumber);
     FlattenableUtils::read(buffer, size, bufferReplaced);
     FlattenableUtils::read(buffer, size, maxBufferCount);
+    FlattenableUtils::read(buffer, size, isSlotExpansionAllowed);
 
     status_t result = frameTimestamps.unflatten(buffer, size, fds, count);
     if (result != NO_ERROR) {
@@ -226,7 +228,7 @@
     FlattenableUtils::read(fBuffer, size, result);
     int32_t isBufferNull = 0;
     FlattenableUtils::read(fBuffer, size, isBufferNull);
-    buffer = new GraphicBuffer();
+    buffer = sp<GraphicBuffer>::make();
     if (!isBufferNull) {
         status_t status = buffer->unflatten(fBuffer, size, fds, count);
         if (status != NO_ERROR) {
@@ -321,7 +323,7 @@
     FlattenableUtils::read(buffer, size, slot);
     FlattenableUtils::read(buffer, size, bufferAge);
 
-    fence = new Fence();
+    fence = sp<Fence>::make();
     status_t status = fence->unflatten(buffer, size, fds, count);
     if (status != NO_ERROR) {
         return status;
@@ -393,7 +395,7 @@
 
     FlattenableUtils::read(buffer, size, slot);
 
-    fence = new Fence();
+    fence = sp<Fence>::make();
     return fence->unflatten(buffer, size, fds, count);
 }
 
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 2699368..ae4b74e 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -26,6 +26,7 @@
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerState.h>
 #include <gui/SchedulingPolicy.h>
+#include <gui/TransactionState.h>
 #include <private/gui/ParcelUtils.h>
 #include <stdint.h>
 #include <sys/types.h>
@@ -60,54 +61,12 @@
 
     virtual ~BpSurfaceComposer();
 
-    status_t setTransactionState(
-            const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
-            Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
-            InputWindowCommands commands, int64_t desiredPresentTime, bool isAutoTimestamp,
-            const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
-            const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
-            const std::vector<uint64_t>& mergedTransactionIds) override {
+    status_t setTransactionState(TransactionState&& state) override {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(state.writeToParcel, &data);
 
-        frameTimelineInfo.writeToParcel(&data);
-
-        SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(state.size()));
-        for (const auto& s : state) {
-            SAFE_PARCEL(s.write, data);
-        }
-
-        SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(displays.size()));
-        for (const auto& d : displays) {
-            SAFE_PARCEL(d.write, data);
-        }
-
-        SAFE_PARCEL(data.writeUint32, flags);
-        SAFE_PARCEL(data.writeStrongBinder, applyToken);
-        SAFE_PARCEL(commands.write, data);
-        SAFE_PARCEL(data.writeInt64, desiredPresentTime);
-        SAFE_PARCEL(data.writeBool, isAutoTimestamp);
-        SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(uncacheBuffers.size()));
-        for (const client_cache_t& uncacheBuffer : uncacheBuffers) {
-            SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote());
-            SAFE_PARCEL(data.writeUint64, uncacheBuffer.id);
-        }
-        SAFE_PARCEL(data.writeBool, hasListenerCallbacks);
-
-        SAFE_PARCEL(data.writeVectorSize, listenerCallbacks);
-        for (const auto& [listener, callbackIds] : listenerCallbacks) {
-            SAFE_PARCEL(data.writeStrongBinder, listener);
-            SAFE_PARCEL(data.writeParcelableVector, callbackIds);
-        }
-
-        SAFE_PARCEL(data.writeUint64, transactionId);
-
-        SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(mergedTransactionIds.size()));
-        for (auto mergedTransactionId : mergedTransactionIds) {
-            SAFE_PARCEL(data.writeUint64, mergedTransactionId);
-        }
-
-        if (flags & ISurfaceComposer::eOneWay) {
+        if (state.mFlags & ISurfaceComposer::eOneWay) {
             return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE,
                     data, &reply, IBinder::FLAG_ONEWAY);
         } else {
@@ -132,75 +91,9 @@
         case SET_TRANSACTION_STATE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
 
-            FrameTimelineInfo frameTimelineInfo;
-            frameTimelineInfo.readFromParcel(&data);
-
-            uint32_t count = 0;
-            SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
-            Vector<ComposerState> state;
-            state.setCapacity(count);
-            for (size_t i = 0; i < count; i++) {
-                ComposerState s;
-                SAFE_PARCEL(s.read, data);
-                state.add(s);
-            }
-
-            SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
-            DisplayState d;
-            Vector<DisplayState> displays;
-            displays.setCapacity(count);
-            for (size_t i = 0; i < count; i++) {
-                SAFE_PARCEL(d.read, data);
-                displays.add(d);
-            }
-
-            uint32_t stateFlags = 0;
-            SAFE_PARCEL(data.readUint32, &stateFlags);
-            sp<IBinder> applyToken;
-            SAFE_PARCEL(data.readStrongBinder, &applyToken);
-            InputWindowCommands inputWindowCommands;
-            SAFE_PARCEL(inputWindowCommands.read, data);
-
-            int64_t desiredPresentTime = 0;
-            bool isAutoTimestamp = true;
-            SAFE_PARCEL(data.readInt64, &desiredPresentTime);
-            SAFE_PARCEL(data.readBool, &isAutoTimestamp);
-
-            SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
-            std::vector<client_cache_t> uncacheBuffers(count);
-            sp<IBinder> tmpBinder;
-            for (size_t i = 0; i < count; i++) {
-                SAFE_PARCEL(data.readNullableStrongBinder, &tmpBinder);
-                uncacheBuffers[i].token = tmpBinder;
-                SAFE_PARCEL(data.readUint64, &uncacheBuffers[i].id);
-            }
-
-            bool hasListenerCallbacks = false;
-            SAFE_PARCEL(data.readBool, &hasListenerCallbacks);
-
-            std::vector<ListenerCallbacks> listenerCallbacks;
-            int32_t listenersSize = 0;
-            SAFE_PARCEL_READ_SIZE(data.readInt32, &listenersSize, data.dataSize());
-            for (int32_t i = 0; i < listenersSize; i++) {
-                SAFE_PARCEL(data.readStrongBinder, &tmpBinder);
-                std::vector<CallbackId> callbackIds;
-                SAFE_PARCEL(data.readParcelableVector, &callbackIds);
-                listenerCallbacks.emplace_back(tmpBinder, callbackIds);
-            }
-
-            uint64_t transactionId = -1;
-            SAFE_PARCEL(data.readUint64, &transactionId);
-
-            SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
-            std::vector<uint64_t> mergedTransactions(count);
-            for (size_t i = 0; i < count; i++) {
-                SAFE_PARCEL(data.readUint64, &mergedTransactions[i]);
-            }
-
-            return setTransactionState(frameTimelineInfo, state, displays, stateFlags, applyToken,
-                                       std::move(inputWindowCommands), desiredPresentTime,
-                                       isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
-                                       listenerCallbacks, transactionId, mergedTransactions);
+            TransactionState state;
+            SAFE_PARCEL(state.readFromParcel, &data);
+            return setTransactionState(std::move(state));
         }
         case GET_SCHEDULING_POLICY: {
             gui::SchedulingPolicy policy;
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 83fc827..ed28e79 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -92,7 +92,7 @@
     if (err != NO_ERROR) return err;
 
     if (hasFence) {
-        gpuCompositionDoneFence = new Fence();
+        gpuCompositionDoneFence = sp<Fence>::make();
         err = input->read(*gpuCompositionDoneFence);
         if (err != NO_ERROR) return err;
     }
@@ -157,7 +157,7 @@
 
     SAFE_PARCEL(input->readBool, &hasFence);
     if (hasFence) {
-        previousReleaseFence = new Fence();
+        previousReleaseFence = sp<Fence>::make();
         SAFE_PARCEL(input->read, *previousReleaseFence);
     }
     bool hasTransformHint = false;
@@ -216,7 +216,7 @@
         return err;
     }
     if (hasFence) {
-        presentFence = new Fence();
+        presentFence = sp<Fence>::make();
         err = input->read(*presentFence);
         if (err != NO_ERROR) {
             return err;
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index c1a03fc..86bc97e 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -55,6 +55,28 @@
 using gui::FocusRequest;
 using gui::WindowInfoHandle;
 
+namespace {
+bool isSameWindowHandle(const sp<WindowInfoHandle>& lhs, const sp<WindowInfoHandle>& rhs) {
+    if (lhs == rhs) {
+        return true;
+    }
+
+    if (!lhs || !rhs) {
+        return false;
+    }
+
+    return *lhs->getInfo() == *rhs->getInfo();
+};
+
+bool isSameSurfaceControl(const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs) {
+    if (lhs == rhs) {
+        return true;
+    }
+
+    return SurfaceControl::isSameSurface(lhs, rhs);
+};
+} // namespace
+
 layer_state_t::layer_state_t()
       : surface(nullptr),
         layerId(-1),
@@ -66,13 +88,13 @@
         mask(0),
         reserved(0),
         cornerRadius(0.0f),
+        clientDrawnCornerRadius(0.0f),
         backgroundBlurRadius(0),
         color(0),
         bufferTransform(0),
         transformToDisplayInverse(false),
         crop({0, 0, -1, -1}),
         dataspace(ui::Dataspace::UNKNOWN),
-        surfaceDamageRegion(),
         api(-1),
         colorTransform(mat4()),
         bgColor(0),
@@ -116,19 +138,21 @@
     SAFE_PARCEL(output.writeFloat, crop.left);
     SAFE_PARCEL(output.writeFloat, crop.bottom);
     SAFE_PARCEL(output.writeFloat, crop.right);
-    SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, relativeLayerSurfaceControl);
-    SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, parentSurfaceControlForChild);
+    SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output,
+                mNotDefCmpState.relativeLayerSurfaceControl);
+    SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output,
+                mNotDefCmpState.parentSurfaceControlForChild);
     SAFE_PARCEL(output.writeFloat, color.r);
     SAFE_PARCEL(output.writeFloat, color.g);
     SAFE_PARCEL(output.writeFloat, color.b);
     SAFE_PARCEL(output.writeFloat, color.a);
-    SAFE_PARCEL(windowInfoHandle->writeToParcel, &output);
-    SAFE_PARCEL(output.write, transparentRegion);
+    SAFE_PARCEL(mNotDefCmpState.windowInfoHandle->writeToParcel, &output);
+    SAFE_PARCEL(output.write, mNotDefCmpState.transparentRegion);
     SAFE_PARCEL(output.writeUint32, bufferTransform);
     SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
     SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace));
     SAFE_PARCEL(output.write, hdrMetadata);
-    SAFE_PARCEL(output.write, surfaceDamageRegion);
+    SAFE_PARCEL(output.write, mNotDefCmpState.surfaceDamageRegion);
     SAFE_PARCEL(output.writeInt32, api);
 
     if (sidebandStream) {
@@ -140,6 +164,7 @@
 
     SAFE_PARCEL(output.write, colorTransform.asArray(), 16 * sizeof(float));
     SAFE_PARCEL(output.writeFloat, cornerRadius);
+    SAFE_PARCEL(output.writeFloat, clientDrawnCornerRadius);
     SAFE_PARCEL(output.writeUint32, backgroundBlurRadius);
     SAFE_PARCEL(output.writeParcelable, metadata);
     SAFE_PARCEL(output.writeFloat, bgColor.r);
@@ -155,6 +180,7 @@
         SAFE_PARCEL(output.writeParcelableVector, listener.callbackIds);
     }
     SAFE_PARCEL(output.writeFloat, shadowRadius);
+    SAFE_PARCEL(output.writeParcelable, borderSettings);
     SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority);
     SAFE_PARCEL(output.writeFloat, frameRate);
     SAFE_PARCEL(output.writeByte, frameRateCompatibility);
@@ -239,8 +265,10 @@
     SAFE_PARCEL(input.readFloat, &crop.bottom);
     SAFE_PARCEL(input.readFloat, &crop.right);
 
-    SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl);
-    SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild);
+    SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input,
+                &mNotDefCmpState.relativeLayerSurfaceControl);
+    SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input,
+                &mNotDefCmpState.parentSurfaceControlForChild);
 
     float tmpFloat = 0;
     SAFE_PARCEL(input.readFloat, &tmpFloat);
@@ -252,9 +280,9 @@
     SAFE_PARCEL(input.readFloat, &tmpFloat);
     color.a = tmpFloat;
 
-    SAFE_PARCEL(windowInfoHandle->readFromParcel, &input);
+    SAFE_PARCEL(mNotDefCmpState.windowInfoHandle->readFromParcel, &input);
 
-    SAFE_PARCEL(input.read, transparentRegion);
+    SAFE_PARCEL(input.read, mNotDefCmpState.transparentRegion);
     SAFE_PARCEL(input.readUint32, &bufferTransform);
     SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
 
@@ -263,7 +291,7 @@
     dataspace = static_cast<ui::Dataspace>(tmpUint32);
 
     SAFE_PARCEL(input.read, hdrMetadata);
-    SAFE_PARCEL(input.read, surfaceDamageRegion);
+    SAFE_PARCEL(input.read, mNotDefCmpState.surfaceDamageRegion);
     SAFE_PARCEL(input.readInt32, &api);
 
     bool tmpBool = false;
@@ -274,6 +302,7 @@
 
     SAFE_PARCEL(input.read, &colorTransform, 16 * sizeof(float));
     SAFE_PARCEL(input.readFloat, &cornerRadius);
+    SAFE_PARCEL(input.readFloat, &clientDrawnCornerRadius);
     SAFE_PARCEL(input.readUint32, &backgroundBlurRadius);
     SAFE_PARCEL(input.readParcelable, &metadata);
 
@@ -300,6 +329,8 @@
         listeners.emplace_back(listener, callbackIds);
     }
     SAFE_PARCEL(input.readFloat, &shadowRadius);
+    SAFE_PARCEL(input.readParcelable, &borderSettings);
+
     SAFE_PARCEL(input.readInt32, &frameRateSelectionPriority);
     SAFE_PARCEL(input.readFloat, &frameRate);
     SAFE_PARCEL(input.readByte, &frameRateCompatibility);
@@ -580,7 +611,7 @@
     }
     if (other.what & eTransparentRegionChanged) {
         what |= eTransparentRegionChanged;
-        transparentRegion = other.transparentRegion;
+        mNotDefCmpState.transparentRegion = other.mNotDefCmpState.transparentRegion;
     }
     if (other.what & eFlagsChanged) {
         what |= eFlagsChanged;
@@ -596,6 +627,10 @@
         what |= eCornerRadiusChanged;
         cornerRadius = other.cornerRadius;
     }
+    if (other.what & eClientDrawnCornerRadiusChanged) {
+        what |= eClientDrawnCornerRadiusChanged;
+        clientDrawnCornerRadius = other.clientDrawnCornerRadius;
+    }
     if (other.what & eBackgroundBlurRadiusChanged) {
         what |= eBackgroundBlurRadiusChanged;
         backgroundBlurRadius = other.backgroundBlurRadius;
@@ -608,11 +643,13 @@
         what |= eRelativeLayerChanged;
         what &= ~eLayerChanged;
         z = other.z;
-        relativeLayerSurfaceControl = other.relativeLayerSurfaceControl;
+        mNotDefCmpState.relativeLayerSurfaceControl =
+                other.mNotDefCmpState.relativeLayerSurfaceControl;
     }
     if (other.what & eReparent) {
         what |= eReparent;
-        parentSurfaceControlForChild = other.parentSurfaceControlForChild;
+        mNotDefCmpState.parentSurfaceControlForChild =
+                other.mNotDefCmpState.parentSurfaceControlForChild;
     }
     if (other.what & eBufferTransformChanged) {
         what |= eBufferTransformChanged;
@@ -658,7 +695,7 @@
     }
     if (other.what & eSurfaceDamageRegionChanged) {
         what |= eSurfaceDamageRegionChanged;
-        surfaceDamageRegion = other.surfaceDamageRegion;
+        mNotDefCmpState.surfaceDamageRegion = other.mNotDefCmpState.surfaceDamageRegion;
     }
     if (other.what & eApiChanged) {
         what |= eApiChanged;
@@ -677,7 +714,8 @@
     }
     if (other.what & eInputInfoChanged) {
         what |= eInputInfoChanged;
-        windowInfoHandle = new WindowInfoHandle(*other.windowInfoHandle);
+        mNotDefCmpState.windowInfoHandle =
+                sp<WindowInfoHandle>::make(*other.mNotDefCmpState.windowInfoHandle);
     }
     if (other.what & eBackgroundColorChanged) {
         what |= eBackgroundColorChanged;
@@ -692,6 +730,10 @@
         what |= eShadowRadiusChanged;
         shadowRadius = other.shadowRadius;
     }
+    if (other.what & eBorderSettingsChanged) {
+        what |= eBorderSettingsChanged;
+        borderSettings = other.borderSettings;
+    }
     if (other.what & eLutsChanged) {
         what |= eLutsChanged;
         luts = other.luts;
@@ -800,7 +842,8 @@
     CHECK_DIFF(diff, eAlphaChanged, other, color.a);
     CHECK_DIFF(diff, eMatrixChanged, other, matrix);
     if (other.what & eTransparentRegionChanged &&
-        (!transparentRegion.hasSameRects(other.transparentRegion))) {
+        (!mNotDefCmpState.transparentRegion.hasSameRects(
+                other.mNotDefCmpState.transparentRegion))) {
         diff |= eTransparentRegionChanged;
     }
     if (other.what & eFlagsChanged) {
@@ -809,6 +852,7 @@
     }
     CHECK_DIFF(diff, eLayerStackChanged, other, layerStack);
     CHECK_DIFF(diff, eCornerRadiusChanged, other, cornerRadius);
+    CHECK_DIFF(diff, eClientDrawnCornerRadiusChanged, other, clientDrawnCornerRadius);
     CHECK_DIFF(diff, eBackgroundBlurRadiusChanged, other, backgroundBlurRadius);
     if (other.what & eBlurRegionsChanged) diff |= eBlurRegionsChanged;
     if (other.what & eRelativeLayerChanged) {
@@ -816,8 +860,8 @@
         diff &= ~eLayerChanged;
     }
     if (other.what & eReparent &&
-        !SurfaceControl::isSameSurface(parentSurfaceControlForChild,
-                                       other.parentSurfaceControlForChild)) {
+        !SurfaceControl::isSameSurface(mNotDefCmpState.parentSurfaceControlForChild,
+                                       other.mNotDefCmpState.parentSurfaceControlForChild)) {
         diff |= eReparent;
     }
     CHECK_DIFF(diff, eBufferTransformChanged, other, bufferTransform);
@@ -831,7 +875,8 @@
     CHECK_DIFF(diff, eCachingHintChanged, other, cachingHint);
     CHECK_DIFF(diff, eHdrMetadataChanged, other, hdrMetadata);
     if (other.what & eSurfaceDamageRegionChanged &&
-        (!surfaceDamageRegion.hasSameRects(other.surfaceDamageRegion))) {
+        (!mNotDefCmpState.surfaceDamageRegion.hasSameRects(
+                other.mNotDefCmpState.surfaceDamageRegion))) {
         diff |= eSurfaceDamageRegionChanged;
     }
     CHECK_DIFF(diff, eApiChanged, other, api);
@@ -843,6 +888,7 @@
     CHECK_DIFF2(diff, eBackgroundColorChanged, other, bgColor, bgColorDataspace);
     if (other.what & eMetadataChanged) diff |= eMetadataChanged;
     CHECK_DIFF(diff, eShadowRadiusChanged, other, shadowRadius);
+    CHECK_DIFF(diff, eBorderSettingsChanged, other, borderSettings);
     CHECK_DIFF(diff, eDefaultFrameRateCompatibilityChanged, other, defaultFrameRateCompatibility);
     CHECK_DIFF(diff, eFrameRateSelectionPriority, other, frameRateSelectionPriority);
     CHECK_DIFF3(diff, eFrameRateChanged, other, frameRate, frameRateCompatibility,
@@ -893,6 +939,38 @@
     SAFE_PARCEL(input.readFloat, &dsdy);
     return NO_ERROR;
 }
+void layer_state_t::updateTransparentRegion(const Region& transparentRegion) {
+    what |= eTransparentRegionChanged;
+    mNotDefCmpState.transparentRegion = transparentRegion;
+}
+void layer_state_t::updateSurfaceDamageRegion(const Region& surfaceDamageRegion) {
+    what |= eSurfaceDamageRegionChanged;
+    mNotDefCmpState.surfaceDamageRegion = surfaceDamageRegion;
+}
+void layer_state_t::updateRelativeLayer(const sp<SurfaceControl>& relativeTo, int32_t z) {
+    what |= layer_state_t::eRelativeLayerChanged;
+    what &= ~layer_state_t::eLayerChanged;
+    mNotDefCmpState.relativeLayerSurfaceControl = relativeTo;
+    this->z = z;
+}
+void layer_state_t::updateParentLayer(const sp<SurfaceControl>& newParent) {
+    what |= layer_state_t::eReparent;
+    mNotDefCmpState.parentSurfaceControlForChild =
+            newParent ? newParent->getParentingLayer() : nullptr;
+}
+void layer_state_t::updateInputWindowInfo(sp<gui::WindowInfoHandle>&& info) {
+    what |= eInputInfoChanged;
+    mNotDefCmpState.windowInfoHandle = std::move(info);
+}
+
+bool layer_state_t::NotDefaultComparableState::operator==(
+        const NotDefaultComparableState& rhs) const {
+    return transparentRegion.hasSameRects(rhs.transparentRegion) &&
+            surfaceDamageRegion.hasSameRects(rhs.surfaceDamageRegion) &&
+            isSameWindowHandle(windowInfoHandle, rhs.windowInfoHandle) &&
+            isSameSurfaceControl(relativeLayerSurfaceControl, rhs.relativeLayerSurfaceControl) &&
+            isSameSurfaceControl(parentSurfaceControlForChild, rhs.parentSurfaceControlForChild);
+}
 
 // ------------------------------- InputWindowCommands ----------------------------------------
 
@@ -993,13 +1071,13 @@
     bool tmpBool = false;
     SAFE_PARCEL(input->readBool, &tmpBool);
     if (tmpBool) {
-        buffer = new GraphicBuffer();
+        buffer = sp<GraphicBuffer>::make();
         SAFE_PARCEL(input->read, *buffer);
     }
 
     SAFE_PARCEL(input->readBool, &tmpBool);
     if (tmpBool) {
-        acquireFence = new Fence();
+        acquireFence = sp<Fence>::make();
         SAFE_PARCEL(input->read, *acquireFence);
     }
 
@@ -1026,8 +1104,8 @@
 }
 
 status_t TrustedPresentationListener::writeToParcel(Parcel* parcel) const {
-    SAFE_PARCEL(parcel->writeStrongBinder, callbackInterface);
-    SAFE_PARCEL(parcel->writeInt32, callbackId);
+    SAFE_PARCEL(parcel->writeStrongBinder, mState.callbackInterface);
+    SAFE_PARCEL(parcel->writeInt32, mState.callbackId);
     return NO_ERROR;
 }
 
@@ -1035,9 +1113,9 @@
     sp<IBinder> tmpBinder = nullptr;
     SAFE_PARCEL(parcel->readNullableStrongBinder, &tmpBinder);
     if (tmpBinder) {
-        callbackInterface = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
+        mState.callbackInterface = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
     }
-    SAFE_PARCEL(parcel->readInt32, &callbackId);
+    SAFE_PARCEL(parcel->readInt32, &mState.callbackId);
     return NO_ERROR;
 }
 
diff --git a/libs/gui/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp
index 2de023e..30b5785 100644
--- a/libs/gui/ScreenCaptureResults.cpp
+++ b/libs/gui/ScreenCaptureResults.cpp
@@ -18,6 +18,7 @@
 
 #include <private/gui/ParcelUtils.h>
 #include <ui/FenceResult.h>
+#include <ui/GraphicBuffer.h>
 
 namespace android::gui {
 
@@ -54,7 +55,7 @@
     bool hasGraphicBuffer;
     SAFE_PARCEL(parcel->readBool, &hasGraphicBuffer);
     if (hasGraphicBuffer) {
-        buffer = new GraphicBuffer();
+        buffer = sp<GraphicBuffer>::make();
         SAFE_PARCEL(parcel->read, *buffer);
     }
 
@@ -79,7 +80,7 @@
     bool hasGainmap;
     SAFE_PARCEL(parcel->readBool, &hasGainmap);
     if (hasGainmap) {
-        optionalGainMap = new GraphicBuffer();
+        optionalGainMap = sp<GraphicBuffer>::make();
         SAFE_PARCEL(parcel->read, *optionalGainMap);
     }
     SAFE_PARCEL(parcel->readFloat, &hdrSdrRatio);
diff --git a/libs/gui/StreamSplitter.cpp b/libs/gui/StreamSplitter.cpp
index 653b91b..848ae7a 100644
--- a/libs/gui/StreamSplitter.cpp
+++ b/libs/gui/StreamSplitter.cpp
@@ -47,7 +47,7 @@
         return BAD_VALUE;
     }
 
-    sp<StreamSplitter> splitter(new StreamSplitter(inputQueue));
+    sp<StreamSplitter> splitter = sp<StreamSplitter>::make(inputQueue);
     status_t status = splitter->mInput->consumerConnect(splitter, false);
     if (status == NO_ERROR) {
         splitter->mInput->setConsumerName(String8("StreamSplitter"));
@@ -82,7 +82,7 @@
     Mutex::Autolock lock(mMutex);
 
     IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
-    sp<OutputListener> listener(new OutputListener(this, outputQueue));
+    sp<OutputListener> listener = sp<OutputListener>::make(this, outputQueue);
     IInterface::asBinder(outputQueue)->linkToDeath(listener);
     status_t status = outputQueue->connect(listener, NATIVE_WINDOW_API_CPU,
             /* producerControlledByApp */ false, &queueBufferOutput);
@@ -140,7 +140,7 @@
 
     // Initialize our reference count for this buffer
     mBuffers.add(bufferItem.mGraphicBuffer->getId(),
-            new BufferTracker(bufferItem.mGraphicBuffer));
+                 sp<BufferTracker>::make(bufferItem.mGraphicBuffer));
 
     IGraphicBufferProducer::QueueBufferInput queueInput(
             bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp,
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index e41f9bb..83c6b57 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -38,6 +38,7 @@
 #include <utils/NativeHandle.h>
 #include <utils/Trace.h>
 
+#include <ui/BufferQueueDefs.h>
 #include <ui/DynamicDisplayInfo.h>
 #include <ui/Fence.h>
 #include <ui/GraphicBuffer.h>
@@ -98,7 +99,10 @@
       : mGraphicBufferProducer(bufferProducer),
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
         mSurfaceDeathListener(nullptr),
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+        mSlots(NUM_BUFFER_SLOTS),
+#endif
         mCrop(Rect::EMPTY_RECT),
         mBufferAge(0),
         mGenerationNumber(0),
@@ -192,7 +196,7 @@
 status_t Surface::allowAllocation(bool allowAllocation) {
     return mGraphicBufferProducer->allowAllocation(allowAllocation);
 }
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+#endif
 
 status_t Surface::setGenerationNumber(uint32_t generation) {
     status_t result = mGraphicBufferProducer->setGenerationNumber(generation);
@@ -505,7 +509,7 @@
     if (result != OK) {
         return result;
     }
-    sp<Fence> fence(new Fence(fenceFd));
+    sp<Fence> fence = sp<Fence>::make(fenceFd);
     int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED");
     if (waitResult != OK) {
         ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d",
@@ -658,7 +662,11 @@
         return result;
     }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    if (buf < 0 || buf >= (int)mSlots.size()) {
+#else
     if (buf < 0 || buf >= NUM_BUFFER_SLOTS) {
+#endif
         ALOGE("dequeueBuffer: IGraphicBufferProducer returned invalid slot number %d", buf);
         android_errorWriteLog(0x534e4554, "36991414"); // SafetyNet logging
         return FAILED_TRANSACTION;
@@ -757,7 +765,11 @@
     Mutex::Autolock lock(mMutex);
 
     uint64_t bufferId = buffer->getId();
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    for (int slot = 0; slot < (int)mSlots.size(); ++slot) {
+#else
     for (int slot = 0; slot < Surface::NUM_BUFFER_SLOTS; ++slot) {
+#endif
         auto& bufferSlot = mSlots[slot];
         if (bufferSlot.buffer != nullptr && bufferSlot.buffer->getId() == bufferId) {
             bufferSlot.buffer = nullptr;
@@ -840,7 +852,11 @@
             return output.result;
         }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+        if (output.slot < 0 || output.slot >= (int)mSlots.size()) {
+#else
         if (output.slot < 0 || output.slot >= NUM_BUFFER_SLOTS) {
+#endif
             mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs);
             ALOGE("%s: IGraphicBufferProducer returned invalid slot number %d",
                     __FUNCTION__, output.slot);
@@ -963,7 +979,7 @@
         }
         return OK;
     }
-    sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+    sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE);
     mGraphicBufferProducer->cancelBuffer(i, fence);
 
     if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == i) {
@@ -1001,7 +1017,7 @@
             ALOGE("%s: cannot find slot number for cancelled buffer", __FUNCTION__);
             badSlotResult = slot;
         } else {
-            sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+            sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE);
             cancelBufferInputs[numBuffersCancelled].slot = slot;
             cancelBufferInputs[numBuffersCancelled++].fence = fence;
         }
@@ -1027,7 +1043,11 @@
         return BAD_VALUE;
     }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    for (int i = 0; i < (int)mSlots.size(); i++) {
+#else
     for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+#endif
         if (mSlots[i].buffer != nullptr &&
                 mSlots[i].buffer->handle == buffer->handle) {
             return i;
@@ -1058,7 +1078,7 @@
     Rect crop(Rect::EMPTY_RECT);
     mCrop.intersect(Rect(buffer->width, buffer->height), &crop);
 
-    sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+    sp<Fence> fence(fenceFd >= 0 ? sp<Fence>::make(fenceFd) : Fence::NO_FENCE);
     IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
             static_cast<android_dataspace>(mDataSpace), crop, mScalingMode,
             mTransform ^ mStickyTransform, fence, mStickyTransform,
@@ -2072,7 +2092,7 @@
 }
 
 int Surface::connect(int api) {
-    static sp<SurfaceListener> listener = new StubSurfaceListener();
+    static sp<SurfaceListener> listener = sp<StubSurfaceListener>::make();
     return connect(api, listener);
 }
 
@@ -2084,7 +2104,7 @@
     mReportRemovedBuffers = reportBufferRemoval;
 
     if (listener != nullptr) {
-        mListenerProxy = new ProducerListenerProxy(this, listener);
+        mListenerProxy = sp<ProducerListenerProxy>::make(this, listener);
     }
 
     int err =
@@ -2094,6 +2114,9 @@
         mDefaultHeight = output.height;
         mNextFrameNumber = output.nextFrameNumber;
         mMaxBufferCount = output.maxBufferCount;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+        mIsSlotExpansionAllowed = output.isSlotExpansionAllowed;
+#endif
 
         // Ignore transform hint if sticky transform is set or transform to display inverse flag is
         // set. Transform hint should be ignored if the client is expected to always submit buffers
@@ -2190,7 +2213,11 @@
         *outFence = Fence::NO_FENCE;
     }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    for (int i = 0; i < (int)mSlots.size(); i++) {
+#else
     for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+#endif
         if (mSlots[i].buffer != nullptr &&
                 mSlots[i].buffer->getId() == buffer->getId()) {
             if (mReportRemovedBuffers) {
@@ -2203,17 +2230,47 @@
     return NO_ERROR;
 }
 
+int Surface::isBufferOwned(const sp<GraphicBuffer>& buffer, bool* outIsOwned) const {
+    ATRACE_CALL();
+
+    if (buffer == nullptr) {
+        ALOGE("%s: Bad input, buffer was null", __FUNCTION__);
+        return BAD_VALUE;
+    }
+    if (outIsOwned == nullptr) {
+        ALOGE("%s: Bad input, output was null", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mMutex);
+
+    int slot = this->getSlotFromBufferLocked(buffer->getNativeBuffer());
+    if (slot == BAD_VALUE) {
+        ALOGV("%s: Buffer %" PRIu64 " is not owned", __FUNCTION__, buffer->getId());
+        *outIsOwned = false;
+        return NO_ERROR;
+    } else if (slot < 0) {
+        ALOGV("%s: Buffer %" PRIu64 " look up failed (%d)", __FUNCTION__, buffer->getId(), slot);
+        *outIsOwned = false;
+        return slot;
+    }
+
+    *outIsOwned = true;
+    return NO_ERROR;
+}
+
 int Surface::attachBuffer(ANativeWindowBuffer* buffer)
 {
     ATRACE_CALL();
-    ALOGV("Surface::attachBuffer");
+    sp<GraphicBuffer> graphicBuffer(static_cast<GraphicBuffer*>(buffer));
+
+    ALOGV("Surface::attachBuffer bufferId=%" PRIu64, graphicBuffer->getId());
 
     Mutex::Autolock lock(mMutex);
     if (mReportRemovedBuffers) {
         mRemovedBuffers.clear();
     }
 
-    sp<GraphicBuffer> graphicBuffer(static_cast<GraphicBuffer*>(buffer));
     uint32_t priorGeneration = graphicBuffer->mGenerationNumber;
     graphicBuffer->mGenerationNumber = mGenerationNumber;
     int32_t attachedSlot = -1;
@@ -2292,8 +2349,35 @@
     ALOGV("Surface::setMaxDequeuedBufferCount");
     Mutex::Autolock lock(mMutex);
 
-    status_t err = mGraphicBufferProducer->setMaxDequeuedBufferCount(
-            maxDequeuedBuffers);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    if (maxDequeuedBuffers > BufferQueueDefs::NUM_BUFFER_SLOTS && !mIsSlotExpansionAllowed) {
+        return BAD_VALUE;
+    }
+
+    int minUndequeuedBuffers = 0;
+    status_t err = mGraphicBufferProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+                                                 &minUndequeuedBuffers);
+    if (err != OK) {
+        ALOGE("IGraphicBufferProducer::query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS) returned %s",
+              strerror(-err));
+        return err;
+    }
+
+    if (maxDequeuedBuffers > (int)mSlots.size()) {
+        int newSlotCount = minUndequeuedBuffers + maxDequeuedBuffers;
+        err = mGraphicBufferProducer->extendSlotCount(newSlotCount);
+        if (err != OK) {
+            ALOGE("IGraphicBufferProducer::extendSlotCount(%d) returned %s", newSlotCount,
+                  strerror(-err));
+            return err;
+        }
+
+        mSlots.resize(newSlotCount);
+    }
+    err = mGraphicBufferProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers);
+#else
+    status_t err = mGraphicBufferProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers);
+#endif
     ALOGE_IF(err, "IGraphicBufferProducer::setMaxDequeuedBufferCount(%d) "
             "returned %s", maxDequeuedBuffers, strerror(-err));
 
@@ -2501,7 +2585,11 @@
         ALOGE("%s: %zu buffers were freed while being dequeued!",
                 __FUNCTION__, mDequeuedSlots.size());
     }
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    for (int i = 0; i < (int)mSlots.size(); i++) {
+#else
     for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+#endif
         mSlots[i].buffer = nullptr;
     }
 }
@@ -2510,7 +2598,11 @@
         std::vector<sp<GraphicBuffer>>* outBuffers) {
     ALOGV("Surface::getAndFlushBuffersFromSlots");
     for (int32_t i : slots) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+        if (i < 0 || i >= (int)mSlots.size()) {
+#else
         if (i < 0 || i >= NUM_BUFFER_SLOTS) {
+#endif
             ALOGE("%s: Invalid slotIndex: %d", __FUNCTION__, i);
             return BAD_VALUE;
         }
@@ -2670,7 +2762,11 @@
             newDirtyRegion.set(bounds);
             mDirtyRegion.clear();
             Mutex::Autolock lock(mMutex);
-            for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+            for (int i = 0; i < (int)mSlots.size(); i++) {
+#else
+            for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+#endif
                 mSlots[i].dirtyRegion.clear();
             }
         }
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index be88b11..786bc06 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -19,6 +19,7 @@
 #include <semaphore.h>
 #include <stdint.h>
 #include <sys/types.h>
+#include <algorithm>
 
 #include <android/gui/BnWindowInfosReportedListener.h>
 #include <android/gui/DisplayState.h>
@@ -122,7 +123,7 @@
         explicit DeathObserver(ComposerService& mgr) : mComposerService(mgr) { }
     };
 
-    mDeathObserver = new DeathObserver(*const_cast<ComposerService*>(this));
+    mDeathObserver = sp<DeathObserver>::make(*const_cast<ComposerService*>(this));
     IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver);
     return true;
 }
@@ -169,7 +170,7 @@
         explicit DeathObserver(ComposerServiceAIDL& mgr) : mComposerService(mgr) {}
     };
 
-    mDeathObserver = new DeathObserver(*const_cast<ComposerServiceAIDL*>(this));
+    mDeathObserver = sp<DeathObserver>::make(*const_cast<ComposerServiceAIDL*>(this));
     IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver);
     return true;
 }
@@ -201,7 +202,7 @@
         DefaultComposerClient& dc = DefaultComposerClient::getInstance();
         Mutex::Autolock _l(dc.mLock);
         if (dc.mClient == nullptr) {
-            dc.mClient = new SurfaceComposerClient;
+            dc.mClient = sp<SurfaceComposerClient>::make();
         }
         return dc.mClient;
     }
@@ -398,7 +399,7 @@
 sp<TransactionCompletedListener> TransactionCompletedListener::getInstance() {
     std::lock_guard<std::mutex> lock(sListenerInstanceMutex);
     if (sInstance == nullptr) {
-        sInstance = new TransactionCompletedListener;
+        sInstance = sp<TransactionCompletedListener>::make();
     }
     return sInstance;
 }
@@ -690,7 +691,7 @@
     std::scoped_lock<std::mutex> lock(mMutex);
     mTrustedPresentationCallbacks[id] =
             std::tuple<TrustedPresentationCallback, void*>(tpc, context);
-    return new SurfaceComposerClient::PresentationCallbackRAII(this, id);
+    return sp<SurfaceComposerClient::PresentationCallbackRAII>::make(this, id);
 }
 
 void TransactionCompletedListener::clearTrustedPresentationCallback(int id) {
@@ -742,7 +743,7 @@
  */
 class BufferCache : public Singleton<BufferCache> {
 public:
-    BufferCache() : token(new BBinder()) {}
+    BufferCache() : token(sp<BBinder>::make()) {}
 
     sp<IBinder> getToken() {
         return IInterface::asBinder(TransactionCompletedListener::getIInstance());
@@ -823,36 +824,24 @@
 // ---------------------------------------------------------------------------
 
 SurfaceComposerClient::Transaction::Transaction() {
-    mId = generateId();
+    mState.mId = generateId();
     mTransactionCompletedListener = TransactionCompletedListener::getInstance();
 }
 
-SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
-      : mId(other.mId),
-        mAnimation(other.mAnimation),
-        mEarlyWakeupStart(other.mEarlyWakeupStart),
-        mEarlyWakeupEnd(other.mEarlyWakeupEnd),
-        mMayContainBuffer(other.mMayContainBuffer),
-        mDesiredPresentTime(other.mDesiredPresentTime),
-        mIsAutoTimestamp(other.mIsAutoTimestamp),
-        mFrameTimelineInfo(other.mFrameTimelineInfo),
-        mApplyToken(other.mApplyToken) {
-    mDisplayStates = other.mDisplayStates;
-    mComposerStates = other.mComposerStates;
-    mInputWindowCommands = other.mInputWindowCommands;
-    mListenerCallbacks = other.mListenerCallbacks;
-    mTransactionCompletedListener = TransactionCompletedListener::getInstance();
-}
+SurfaceComposerClient::Transaction::Transaction(Transaction&& other)
+      : mTransactionCompletedListener(TransactionCompletedListener::getInstance()),
+        mState(std::move(other.mState)),
+        mListenerCallbacks(std::move(other.mListenerCallbacks)) {}
 
 void SurfaceComposerClient::Transaction::sanitize(int pid, int uid) {
     uint32_t permissions = LayerStatePermissions::getTransactionPermissions(pid, uid);
-    for (auto & [handle, composerState] : mComposerStates) {
+    for (auto& composerState : mState.mComposerStates) {
         composerState.state.sanitize(permissions);
     }
-    if (!mInputWindowCommands.empty() &&
+    if (!mState.mInputWindowCommands.empty() &&
         (permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) == 0) {
         ALOGE("Only privileged callers are allowed to send input commands.");
-        mInputWindowCommands.clear();
+        mState.mInputWindowCommands.clear();
     }
 }
 
@@ -867,36 +856,13 @@
 
 
 status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
-    const uint64_t transactionId = parcel->readUint64();
-    const bool animation = parcel->readBool();
-    const bool earlyWakeupStart = parcel->readBool();
-    const bool earlyWakeupEnd = parcel->readBool();
-    const int64_t desiredPresentTime = parcel->readInt64();
-    const bool isAutoTimestamp = parcel->readBool();
-    const bool logCallPoints = parcel->readBool();
-    FrameTimelineInfo frameTimelineInfo;
-    frameTimelineInfo.readFromParcel(parcel);
+    TransactionState tmpState;
+    SAFE_PARCEL(tmpState.readFromParcel, parcel);
 
-    sp<IBinder> applyToken;
-    parcel->readNullableStrongBinder(&applyToken);
     size_t count = static_cast<size_t>(parcel->readUint32());
     if (count > parcel->dataSize()) {
         return BAD_VALUE;
     }
-    SortedVector<DisplayState> displayStates;
-    displayStates.setCapacity(count);
-    for (size_t i = 0; i < count; i++) {
-        DisplayState displayState;
-        if (displayState.read(*parcel) == BAD_VALUE) {
-            return BAD_VALUE;
-        }
-        displayStates.add(displayState);
-    }
-
-    count = static_cast<size_t>(parcel->readUint32());
-    if (count > parcel->dataSize()) {
-        return BAD_VALUE;
-    }
     std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> listenerCallbacks;
     listenerCallbacks.reserve(count);
     for (size_t i = 0; i < count; i++) {
@@ -922,62 +888,8 @@
         }
     }
 
-    count = static_cast<size_t>(parcel->readUint32());
-    if (count > parcel->dataSize()) {
-        return BAD_VALUE;
-    }
-    std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> composerStates;
-    composerStates.reserve(count);
-    for (size_t i = 0; i < count; i++) {
-        sp<IBinder> surfaceControlHandle;
-        SAFE_PARCEL(parcel->readStrongBinder, &surfaceControlHandle);
-
-        ComposerState composerState;
-        if (composerState.read(*parcel) == BAD_VALUE) {
-            return BAD_VALUE;
-        }
-        composerStates[surfaceControlHandle] = composerState;
-    }
-
-    InputWindowCommands inputWindowCommands;
-    inputWindowCommands.read(*parcel);
-
-    count = static_cast<size_t>(parcel->readUint32());
-    if (count > parcel->dataSize()) {
-        return BAD_VALUE;
-    }
-    std::vector<client_cache_t> uncacheBuffers(count);
-    for (size_t i = 0; i < count; i++) {
-        sp<IBinder> tmpBinder;
-        SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
-        uncacheBuffers[i].token = tmpBinder;
-        SAFE_PARCEL(parcel->readUint64, &uncacheBuffers[i].id);
-    }
-
-    count = static_cast<size_t>(parcel->readUint32());
-    if (count > parcel->dataSize()) {
-        return BAD_VALUE;
-    }
-    std::vector<uint64_t> mergedTransactionIds(count);
-    for (size_t i = 0; i < count; i++) {
-        SAFE_PARCEL(parcel->readUint64, &mergedTransactionIds[i]);
-    }
-
-    // Parsing was successful. Update the object.
-    mId = transactionId;
-    mAnimation = animation;
-    mEarlyWakeupStart = earlyWakeupStart;
-    mEarlyWakeupEnd = earlyWakeupEnd;
-    mDesiredPresentTime = desiredPresentTime;
-    mIsAutoTimestamp = isAutoTimestamp;
-    mFrameTimelineInfo = frameTimelineInfo;
-    mDisplayStates = displayStates;
-    mListenerCallbacks = listenerCallbacks;
-    mComposerStates = composerStates;
-    mInputWindowCommands = inputWindowCommands;
-    mApplyToken = applyToken;
-    mUncacheBuffers = std::move(uncacheBuffers);
-    mMergedTransactionIds = std::move(mergedTransactionIds);
+    mState = std::move(tmpState);
+    mListenerCallbacks = std::move(listenerCallbacks);
     return NO_ERROR;
 }
 
@@ -995,19 +907,7 @@
 
     const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers();
 
-    parcel->writeUint64(mId);
-    parcel->writeBool(mAnimation);
-    parcel->writeBool(mEarlyWakeupStart);
-    parcel->writeBool(mEarlyWakeupEnd);
-    parcel->writeInt64(mDesiredPresentTime);
-    parcel->writeBool(mIsAutoTimestamp);
-    parcel->writeBool(mLogCallPoints);
-    mFrameTimelineInfo.writeToParcel(parcel);
-    parcel->writeStrongBinder(mApplyToken);
-    parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
-    for (auto const& displayState : mDisplayStates) {
-        displayState.write(*parcel);
-    }
+    SAFE_PARCEL(mState.writeToParcel, parcel);
 
     parcel->writeUint32(static_cast<uint32_t>(mListenerCallbacks.size()));
     for (auto const& [listener, callbackInfo] : mListenerCallbacks) {
@@ -1022,25 +922,6 @@
         }
     }
 
-    parcel->writeUint32(static_cast<uint32_t>(mComposerStates.size()));
-    for (auto const& [handle, composerState] : mComposerStates) {
-        SAFE_PARCEL(parcel->writeStrongBinder, handle);
-        composerState.write(*parcel);
-    }
-
-    mInputWindowCommands.write(*parcel);
-
-    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mUncacheBuffers.size()));
-    for (const client_cache_t& uncacheBuffer : mUncacheBuffers) {
-        SAFE_PARCEL(parcel->writeStrongBinder, uncacheBuffer.token.promote());
-        SAFE_PARCEL(parcel->writeUint64, uncacheBuffer.id);
-    }
-
-    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mMergedTransactionIds.size()));
-    for (auto mergedTransactionId : mMergedTransactionIds) {
-        SAFE_PARCEL(parcel->writeUint64, mergedTransactionId);
-    }
-
     return NO_ERROR;
 }
 
@@ -1065,42 +946,8 @@
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
-    while (mMergedTransactionIds.size() + other.mMergedTransactionIds.size() >
-                   MAX_MERGE_HISTORY_LENGTH - 1 &&
-           mMergedTransactionIds.size() > 0) {
-        mMergedTransactionIds.pop_back();
-    }
-    if (other.mMergedTransactionIds.size() == MAX_MERGE_HISTORY_LENGTH) {
-        mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
-                                     other.mMergedTransactionIds.begin(),
-                                     other.mMergedTransactionIds.end() - 1);
-    } else if (other.mMergedTransactionIds.size() > 0u) {
-        mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
-                                     other.mMergedTransactionIds.begin(),
-                                     other.mMergedTransactionIds.end());
-    }
-    mMergedTransactionIds.insert(mMergedTransactionIds.begin(), other.mId);
-
-    for (auto const& [handle, composerState] : other.mComposerStates) {
-        if (mComposerStates.count(handle) == 0) {
-            mComposerStates[handle] = composerState;
-        } else {
-            if (composerState.state.what & layer_state_t::eBufferChanged) {
-                releaseBufferIfOverwriting(mComposerStates[handle].state);
-            }
-            mComposerStates[handle].state.merge(composerState.state);
-        }
-    }
-
-    for (auto const& state : other.mDisplayStates) {
-        ssize_t index = mDisplayStates.indexOf(state);
-        if (index < 0) {
-            mDisplayStates.add(state);
-        } else {
-            mDisplayStates.editItemAt(static_cast<size_t>(index)).merge(state);
-        }
-    }
-
+    mState.merge(std::move(other.mState),
+                 std::bind(&Transaction::releaseBufferIfOverwriting, this, std::placeholders::_1));
     for (const auto& [listener, callbackInfo] : other.mListenerCallbacks) {
         auto& [callbackIds, surfaceControls] = callbackInfo;
         mListenerCallbacks[listener].callbackIds.insert(std::make_move_iterator(
@@ -1124,53 +971,21 @@
         }
     }
 
-    for (const auto& cacheId : other.mUncacheBuffers) {
-        mUncacheBuffers.push_back(cacheId);
-    }
-
-    mInputWindowCommands.merge(other.mInputWindowCommands);
-
-    mMayContainBuffer |= other.mMayContainBuffer;
-    mEarlyWakeupStart = mEarlyWakeupStart || other.mEarlyWakeupStart;
-    mEarlyWakeupEnd = mEarlyWakeupEnd || other.mEarlyWakeupEnd;
-    mApplyToken = other.mApplyToken;
-
-    mergeFrameTimelineInfo(mFrameTimelineInfo, other.mFrameTimelineInfo);
-
-    mLogCallPoints |= other.mLogCallPoints;
-    if (mLogCallPoints) {
-        ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY,
-             "Transaction %" PRIu64 " merged with transaction %" PRIu64, other.getId(), mId);
-    }
-
     other.clear();
     return *this;
 }
 
 void SurfaceComposerClient::Transaction::clear() {
-    mComposerStates.clear();
-    mDisplayStates.clear();
+    mState.clear();
     mListenerCallbacks.clear();
-    mInputWindowCommands.clear();
-    mUncacheBuffers.clear();
-    mMayContainBuffer = false;
-    mAnimation = false;
-    mEarlyWakeupStart = false;
-    mEarlyWakeupEnd = false;
-    mDesiredPresentTime = 0;
-    mIsAutoTimestamp = true;
-    mFrameTimelineInfo = {};
-    mApplyToken = nullptr;
-    mMergedTransactionIds.clear();
-    mLogCallPoints = false;
 }
 
-uint64_t SurfaceComposerClient::Transaction::getId() {
-    return mId;
+uint64_t SurfaceComposerClient::Transaction::getId() const {
+    return mState.mId;
 }
 
 std::vector<uint64_t> SurfaceComposerClient::Transaction::getMergedTransactionIds() {
-    return mMergedTransactionIds;
+    return mState.mMergedTransactionIds;
 }
 
 void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -1179,12 +994,13 @@
     client_cache_t uncacheBuffer;
     uncacheBuffer.token = BufferCache::getInstance().getToken();
     uncacheBuffer.id = cacheId;
-    Vector<ComposerState> composerStates;
-    Vector<DisplayState> displayStates;
-    status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, displayStates,
-                                              ISurfaceComposer::eOneWay,
-                                              Transaction::getDefaultApplyToken(), {}, systemTime(),
-                                              true, {uncacheBuffer}, false, {}, generateId(), {});
+    TransactionState state;
+    state.mId = generateId();
+    state.mApplyToken = Transaction::getDefaultApplyToken();
+    state.mUncacheBuffers.emplace_back(std::move(uncacheBuffer));
+    state.mFlags = ISurfaceComposer::eOneWay;
+    state.mDesiredPresentTime = systemTime();
+    status_t status = sf->setTransactionState(std::move(state));
     if (status != NO_ERROR) {
         ALOGE_AND_TRACE("SurfaceComposerClient::doUncacheBufferTransaction - %s",
                         strerror(-status));
@@ -1192,13 +1008,13 @@
 }
 
 void SurfaceComposerClient::Transaction::cacheBuffers() {
-    if (!mMayContainBuffer) {
+    if (!mState.mMayContainBuffer) {
         return;
     }
 
     size_t count = 0;
-    for (auto& [handle, cs] : mComposerStates) {
-        layer_state_t* s = &(mComposerStates[handle].state);
+    for (auto& cs : mState.mComposerStates) {
+        layer_state_t* s = &cs.state;
         if (!(s->what & layer_state_t::eBufferChanged)) {
             continue;
         } else if (s->bufferData &&
@@ -1225,7 +1041,7 @@
             std::optional<client_cache_t> uncacheBuffer;
             cacheId = BufferCache::getInstance().cache(s->bufferData->buffer, uncacheBuffer);
             if (uncacheBuffer) {
-                mUncacheBuffers.push_back(*uncacheBuffer);
+                mState.mUncacheBuffers.emplace_back(*uncacheBuffer);
             }
         }
         s->bufferData->flags |= BufferData::BufferDataChange::cachedBufferChanged;
@@ -1294,8 +1110,7 @@
                                         /*callbackContext=*/nullptr);
     }
 
-    bool hasListenerCallbacks = !mListenerCallbacks.empty();
-    std::vector<ListenerCallbacks> listenerCallbacks;
+    mState.mHasListenerCallbacks = !mListenerCallbacks.empty();
     // For every listener with registered callbacks
     for (const auto& [listener, callbackInfo] : mListenerCallbacks) {
         auto& [callbackIds, surfaceControls] = callbackInfo;
@@ -1304,7 +1119,8 @@
         }
 
         if (surfaceControls.empty()) {
-            listenerCallbacks.emplace_back(IInterface::asBinder(listener), std::move(callbackIds));
+            mState.mListenerCallbacks.emplace_back(IInterface::asBinder(listener),
+                                                   std::move(callbackIds));
         } else {
             // If the listener has any SurfaceControls set on this Transaction update the surface
             // state
@@ -1316,53 +1132,33 @@
                 }
                 std::vector<CallbackId> callbacks(callbackIds.begin(), callbackIds.end());
                 s->what |= layer_state_t::eHasListenerCallbacksChanged;
-                s->listeners.emplace_back(IInterface::asBinder(listener), callbacks);
+                s->listeners.emplace_back(IInterface::asBinder(listener), std::move(callbacks));
             }
         }
     }
 
     cacheBuffers();
 
-    Vector<ComposerState> composerStates;
-    Vector<DisplayState> displayStates;
-    uint32_t flags = 0;
-
-    for (auto const& kv : mComposerStates) {
-        composerStates.add(kv.second);
-    }
-
-    displayStates = std::move(mDisplayStates);
-
-    if (mAnimation) {
-        flags |= ISurfaceComposer::eAnimation;
-    }
     if (oneWay) {
         if (synchronous) {
             ALOGE("Transaction attempted to set synchronous and one way at the same time"
                   " this is an invalid request. Synchronous will win for safety");
         } else {
-            flags |= ISurfaceComposer::eOneWay;
+            mState.mFlags |= ISurfaceComposer::eOneWay;
         }
     }
 
-    // If both mEarlyWakeupStart and mEarlyWakeupEnd are set
+    // If both ISurfaceComposer::eEarlyWakeupStart and ISurfaceComposer::eEarlyWakeupEnd are set
     // it is equivalent for none
-    if (mEarlyWakeupStart && !mEarlyWakeupEnd) {
-        flags |= ISurfaceComposer::eEarlyWakeupStart;
+    uint32_t wakeupFlags = ISurfaceComposer::eEarlyWakeupStart | ISurfaceComposer::eEarlyWakeupEnd;
+    if ((mState.mFlags & wakeupFlags) == wakeupFlags) {
+        mState.mFlags &= ~(wakeupFlags);
     }
-    if (mEarlyWakeupEnd && !mEarlyWakeupStart) {
-        flags |= ISurfaceComposer::eEarlyWakeupEnd;
-    }
-
-    sp<IBinder> applyToken = mApplyToken ? mApplyToken : getDefaultApplyToken();
+    if (!mState.mApplyToken) mState.mApplyToken = getDefaultApplyToken();
 
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-    status_t binderStatus =
-            sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags,
-                                    applyToken, mInputWindowCommands, mDesiredPresentTime,
-                                    mIsAutoTimestamp, mUncacheBuffers, hasListenerCallbacks,
-                                    listenerCallbacks, mId, mMergedTransactionIds);
-    mId = generateId();
+    status_t binderStatus = sf->setTransactionState(std::move(mState));
+    mState.mId = generateId();
 
     // Clear the current states and flags
     clear();
@@ -1371,15 +1167,15 @@
         syncCallback->wait();
     }
 
-    if (mLogCallPoints) {
-        ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", mId);
+    if (mState.mLogCallPoints) {
+        ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", getId());
     }
 
     mStatus = NO_ERROR;
     return binderStatus;
 }
 
-sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = new BBinder();
+sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = sp<BBinder>::make();
 
 std::mutex SurfaceComposerClient::Transaction::sApplyTokenMutex;
 
@@ -1407,17 +1203,22 @@
 }
 
 void SurfaceComposerClient::Transaction::enableDebugLogCallPoints() {
-    mLogCallPoints = true;
+    mState.mLogCallPoints = true;
 }
 
 // ---------------------------------------------------------------------------
 
 sp<IBinder> SurfaceComposerClient::createVirtualDisplay(const std::string& displayName,
-                                                        bool isSecure, const std::string& uniqueId,
+                                                        bool isSecure, bool optimizeForPower,
+                                                        const std::string& uniqueId,
                                                         float requestedRefreshRate) {
+    const gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy = optimizeForPower
+            ? gui::ISurfaceComposer::OptimizationPolicy::optimizeForPower
+            : gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance;
     sp<IBinder> display = nullptr;
     binder::Status status =
             ComposerServiceAIDL::getComposerService()->createVirtualDisplay(displayName, isSecure,
+                                                                            optimizationPolicy,
                                                                             uniqueId,
                                                                             requestedRefreshRate,
                                                                             &display);
@@ -1437,9 +1238,8 @@
             ComposerServiceAIDL::getComposerService()->getPhysicalDisplayIds(&displayIds);
     if (status.isOk()) {
         physicalDisplayIds.reserve(displayIds.size());
-        for (auto item : displayIds) {
-            auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(item));
-            physicalDisplayIds.push_back(*id);
+        for (auto id : displayIds) {
+            physicalDisplayIds.push_back(PhysicalDisplayId::fromValue(static_cast<uint64_t>(id)));
         }
     }
     return physicalDisplayIds;
@@ -1461,31 +1261,19 @@
 }
 
 void SurfaceComposerClient::Transaction::setAnimationTransaction() {
-    mAnimation = true;
+    mState.mFlags |= ISurfaceComposer::eAnimation;
 }
 
 void SurfaceComposerClient::Transaction::setEarlyWakeupStart() {
-    mEarlyWakeupStart = true;
+    mState.mFlags |= ISurfaceComposer::eEarlyWakeupStart;
 }
 
 void SurfaceComposerClient::Transaction::setEarlyWakeupEnd() {
-    mEarlyWakeupEnd = true;
+    mState.mFlags |= ISurfaceComposer::eEarlyWakeupEnd;
 }
 
 layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) {
-    auto handle = sc->getLayerStateHandle();
-
-    if (mComposerStates.count(handle) == 0) {
-        // we don't have it, add an initialized layer_state to our list
-        ComposerState s;
-
-        s.state.surface = handle;
-        s.state.layerId = sc->getLayerId();
-
-        mComposerStates[handle] = s;
-    }
-
-    return &(mComposerStates[handle].state);
+    return mState.getLayerState(sc);
 }
 
 void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback(
@@ -1543,11 +1331,7 @@
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->what |= layer_state_t::eRelativeLayerChanged;
-    s->what &= ~layer_state_t::eLayerChanged;
-    s->relativeLayerSurfaceControl = relativeTo;
-    s->z = z;
-
+    s->updateRelativeLayer(relativeTo, z);
     registerSurfaceControlForCallback(sc);
     return *this;
 }
@@ -1577,9 +1361,7 @@
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->what |= layer_state_t::eTransparentRegionChanged;
-    s->transparentRegion = transparentRegion;
-
+    s->updateTransparentRegion(transparentRegion);
     registerSurfaceControlForCallback(sc);
     return *this;
 }
@@ -1695,6 +1477,18 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setClientDrawnCornerRadius(
+        const sp<SurfaceControl>& sc, float clientDrawnCornerRadius) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eClientDrawnCornerRadiusChanged;
+    s->clientDrawnCornerRadius = clientDrawnCornerRadius;
+    return *this;
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBackgroundBlurRadius(
         const sp<SurfaceControl>& sc, int backgroundBlurRadius) {
     layer_state_t* s = getLayerState(sc);
@@ -1729,9 +1523,7 @@
     if (SurfaceControl::isSameSurface(sc, newParent)) {
         return *this;
     }
-    s->what |= layer_state_t::eReparent;
-    s->parentSurfaceControlForChild = newParent ? newParent->getParentingLayer() : nullptr;
-
+    s->updateParentLayer(newParent);
     registerSurfaceControlForCallback(sc);
     return *this;
 }
@@ -1857,8 +1649,8 @@
         setReleaseBufferCallback(bufferData.get(), callback);
     }
 
-    if (mIsAutoTimestamp) {
-        mDesiredPresentTime = systemTime();
+    if (mState.mIsAutoTimestamp) {
+        mState.mDesiredPresentTime = systemTime();
     }
     s->what |= layer_state_t::eBufferChanged;
     s->bufferData = std::move(bufferData);
@@ -1876,7 +1668,7 @@
                                        const std::vector<SurfaceControlStats>&) {},
                                     nullptr);
 
-    mMayContainBuffer = true;
+    mState.mMayContainBuffer = true;
     return *this;
 }
 
@@ -1961,9 +1753,9 @@
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLuts(
-        const sp<SurfaceControl>& sc, const base::unique_fd& lutFd,
-        const std::vector<int32_t>& offsets, const std::vector<int32_t>& dimensions,
-        const std::vector<int32_t>& sizes, const std::vector<int32_t>& samplingKeys) {
+        const sp<SurfaceControl>& sc, base::unique_fd&& lutFd, const std::vector<int32_t>& offsets,
+        const std::vector<int32_t>& dimensions, const std::vector<int32_t>& sizes,
+        const std::vector<int32_t>& samplingKeys) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
@@ -1972,8 +1764,8 @@
 
     s->what |= layer_state_t::eLutsChanged;
     if (lutFd.ok()) {
-        s->luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(dup(lutFd.get())), offsets,
-                                                     dimensions, sizes, samplingKeys);
+        s->luts = std::make_shared<gui::DisplayLuts>(std::move(lutFd), offsets, dimensions, sizes,
+                                                     samplingKeys);
     } else {
         s->luts = nullptr;
     }
@@ -2017,9 +1809,7 @@
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->what |= layer_state_t::eSurfaceDamageRegionChanged;
-    s->surfaceDamageRegion = surfaceDamageRegion;
-
+    s->updateSurfaceDamageRegion(surfaceDamageRegion);
     registerSurfaceControlForCallback(sc);
     return *this;
 }
@@ -2054,8 +1844,8 @@
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredPresentTime(
         nsecs_t desiredPresentTime) {
-    mDesiredPresentTime = desiredPresentTime;
-    mIsAutoTimestamp = false;
+    mState.mDesiredPresentTime = desiredPresentTime;
+    mState.mIsAutoTimestamp = false;
     return *this;
 }
 
@@ -2138,21 +1928,20 @@
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->windowInfoHandle = std::move(info);
-    s->what |= layer_state_t::eInputInfoChanged;
+    s->updateInputWindowInfo(std::move(info));
     return *this;
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
         const FocusRequest& request) {
-    mInputWindowCommands.focusRequests.push_back(request);
+    mState.mInputWindowCommands.addFocusRequest(request);
     return *this;
 }
 
 SurfaceComposerClient::Transaction&
 SurfaceComposerClient::Transaction::addWindowInfosReportedListener(
         sp<gui::IWindowInfosReportedListener> windowInfosReportedListener) {
-    mInputWindowCommands.windowInfosReportedListeners.insert(windowInfosReportedListener);
+    mState.mInputWindowCommands.addWindowInfosReportedListener(windowInfosReportedListener);
     return *this;
 }
 
@@ -2236,6 +2025,19 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBorderSettings(
+        const sp<SurfaceControl>& sc, gui::BorderSettings settings) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    s->what |= layer_state_t::eBorderSettingsChanged;
+    s->borderSettings = settings;
+    return *this;
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate(
         const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility,
         int8_t changeFrameRateStrategy) {
@@ -2316,7 +2118,7 @@
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineInfo(
         const FrameTimelineInfo& frameTimelineInfo) {
-    mergeFrameTimelineInfo(mFrameTimelineInfo, frameTimelineInfo);
+    mState.mergeFrameTimelineInfo(frameTimelineInfo);
     return *this;
 }
 
@@ -2355,7 +2157,7 @@
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApplyToken(
         const sp<IBinder>& applyToken) {
-    mApplyToken = applyToken;
+    mState.mApplyToken = applyToken;
     return *this;
 }
 
@@ -2372,10 +2174,6 @@
     return *this;
 }
 
-bool SurfaceComposerClient::flagEdgeExtensionEffectUseShader() {
-    return com::android::graphics::libgui::flags::edge_extension_shader();
-}
-
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setEdgeExtensionEffect(
         const sp<SurfaceControl>& sc, const gui::EdgeExtensionParameters& effect) {
     layer_state_t* s = getLayerState(sc);
@@ -2487,15 +2285,7 @@
 // ---------------------------------------------------------------------------
 
 DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
-    DisplayState s;
-    s.token = token;
-    ssize_t index = mDisplayStates.indexOf(s);
-    if (index < 0) {
-        // we don't have it, add an initialized layer_state to our list
-        s.what = 0;
-        index = mDisplayStates.add(s);
-    }
-    return mDisplayStates.editItemAt(static_cast<size_t>(index));
+    return mState.getDisplayState(token);
 }
 
 status_t SurfaceComposerClient::Transaction::setDisplaySurface(const sp<IBinder>& token,
@@ -2548,20 +2338,6 @@
     s.what |= DisplayState::eDisplaySizeChanged;
 }
 
-// copied from FrameTimelineInfo::merge()
-void SurfaceComposerClient::Transaction::mergeFrameTimelineInfo(FrameTimelineInfo& t,
-                                                                const FrameTimelineInfo& other) {
-    // When merging vsync Ids we take the oldest valid one
-    if (t.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID &&
-        other.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
-        if (other.vsyncId > t.vsyncId) {
-            t = other;
-        }
-    } else if (t.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
-        t = other;
-    }
-}
-
 SurfaceComposerClient::Transaction&
 SurfaceComposerClient::Transaction::setTrustedPresentationCallback(
         const sp<SurfaceControl>& sc, TrustedPresentationCallback cb,
@@ -2578,8 +2354,9 @@
     }
     s->what |= layer_state_t::eTrustedPresentationInfoChanged;
     s->trustedPresentationThresholds = thresholds;
-    s->trustedPresentationListener.callbackInterface = TransactionCompletedListener::getIInstance();
-    s->trustedPresentationListener.callbackId = sc->getLayerId();
+    s->trustedPresentationListener.configure(
+            {.callbackInterface = TransactionCompletedListener::getIInstance(),
+             .callbackId = sc->getLayerId()});
 
     return *this;
 }
@@ -2595,8 +2372,7 @@
     }
     s->what |= layer_state_t::eTrustedPresentationInfoChanged;
     s->trustedPresentationThresholds = TrustedPresentationThresholds();
-    s->trustedPresentationListener.callbackInterface = nullptr;
-    s->trustedPresentationListener.callbackId = -1;
+    s->trustedPresentationListener.clear();
 
     return *this;
 }
@@ -2689,9 +2465,9 @@
         }
         ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
         if (err == NO_ERROR) {
-            *outSurface = new SurfaceControl(this, result.handle, result.layerId,
-                                             toString(result.layerName), w, h, format,
-                                             result.transformHint, flags);
+            *outSurface = sp<SurfaceControl>::make(this, result.handle, result.layerId,
+                                                   toString(result.layerName), w, h, format,
+                                                   result.transformHint, flags);
         }
     }
     return err;
@@ -2707,7 +2483,8 @@
     const binder::Status status = mClient->mirrorSurface(mirrorFromHandle, &result);
     const status_t err = statusTFromBinderStatus(status);
     if (err == NO_ERROR) {
-        return new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName));
+        return sp<SurfaceControl>::make(this, result.handle, result.layerId,
+                                        toString(result.layerName));
     }
     return nullptr;
 }
@@ -2717,7 +2494,8 @@
     const binder::Status status = mClient->mirrorDisplay(displayId.value, &result);
     const status_t err = statusTFromBinderStatus(status);
     if (err == NO_ERROR) {
-        return new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName));
+        return sp<SurfaceControl>::make(this, result.handle, result.layerId,
+                                        toString(result.layerName));
     }
     return nullptr;
 }
@@ -2780,6 +2558,7 @@
     if (status.isOk()) {
         // convert gui::StaticDisplayInfo to ui::StaticDisplayInfo
         outInfo->connectionType = static_cast<ui::DisplayConnectionType>(ginfo.connectionType);
+        outInfo->port = ginfo.port;
         outInfo->density = ginfo.density;
         outInfo->secure = ginfo.secure;
         outInfo->installOrientation = static_cast<ui::Rotation>(ginfo.installOrientation);
@@ -3293,10 +3072,17 @@
     return statusTFromBinderStatus(status);
 }
 
-status_t SurfaceComposerClient::setActivePictureListener(
+status_t SurfaceComposerClient::addActivePictureListener(
         const sp<gui::IActivePictureListener>& listener) {
     binder::Status status =
-            ComposerServiceAIDL::getComposerService()->setActivePictureListener(listener);
+            ComposerServiceAIDL::getComposerService()->addActivePictureListener(listener);
+    return statusTFromBinderStatus(status);
+}
+
+status_t SurfaceComposerClient::removeActivePictureListener(
+        const sp<gui::IActivePictureListener>& listener) {
+    binder::Status status =
+            ComposerServiceAIDL::getComposerService()->removeActivePictureListener(listener);
     return statusTFromBinderStatus(status);
 }
 
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index f126c0b..1eb9b87 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -141,7 +141,8 @@
                                  ISurfaceComposerClient::eOpaque);
     mBbqChild = mClient->createSurface(String8::format("[BBQ] %s", mName.c_str()), 0, 0, mFormat,
                                        flags, mHandle, {}, &ignore);
-    mBbq = sp<BLASTBufferQueue>::make("[BBQ]" + mName, mBbqChild, mWidth, mHeight, mFormat);
+    mBbq = sp<BLASTBufferQueue>::make("[BBQ] " + mName, /* updateDestinationFrame */ true);
+    mBbq->update(mBbqChild, mWidth, mHeight, mFormat);
 
     // This surface is always consumed by SurfaceFlinger, so the
     // producerControlledByApp value doesn't matter; using false.
@@ -268,10 +269,11 @@
     SAFE_PARCEL(parcel.readUint32, &format);
 
     // We aren't the original owner of the surface.
-    *outSurfaceControl = new SurfaceControl(new SurfaceComposerClient(
-                                                    interface_cast<ISurfaceComposerClient>(client)),
-                                            handle.get(), layerId, layerName, width, height, format,
-                                            transformHint);
+    *outSurfaceControl =
+            sp<SurfaceControl>::make(sp<SurfaceComposerClient>::make(
+                                             interface_cast<ISurfaceComposerClient>(client)),
+                                     handle, layerId, layerName, width, height, format,
+                                     transformHint);
 
     return NO_ERROR;
 }
diff --git a/libs/gui/TEST_MAPPING b/libs/gui/TEST_MAPPING
index a590c86..14d6df6 100644
--- a/libs/gui/TEST_MAPPING
+++ b/libs/gui/TEST_MAPPING
@@ -60,5 +60,34 @@
         }
       ]
     }
+  ],
+  "postsubmit": [
+    {
+      "name": "libgui_test",
+      "keywords": [ "primary-device" ],
+      "options": [
+        // TODO(b/397776630): Failing on real devices.
+        {
+          "exclude-filter": "InputSurfacesTest#input_respects_scaled_touchable_region_overflow"
+        },
+	// TODO(b/233363648): Failing on real devices.
+        {
+          "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferNpot"
+        },
+        {
+          "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferPow2"
+        },
+        {
+          "exclude-filter": "SurfaceTextureGLTest#TexturingFromCpuFilledYV12BufferWithCrop"
+        },
+	// TODO(b/233363648): Flaky on real devices.
+        {
+          "exclude-filter": "SurfaceTextureGLToGLTest#EglMakeCurrentBeforeConsumerDeathUnrefsBuffers"
+        },
+        {
+          "exclude-filter": "SurfaceTextureGLToGLTest#EglMakeCurrentAfterConsumerDeathUnrefsBuffers"
+        }
+      ]
+    }
   ]
 }
diff --git a/libs/gui/TransactionState.cpp b/libs/gui/TransactionState.cpp
new file mode 100644
index 0000000..9e09bc2
--- /dev/null
+++ b/libs/gui/TransactionState.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+#define LOG_TAG "TransactionState"
+#include <gui/LayerState.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/TransactionState.h>
+#include <private/gui/ParcelUtils.h>
+#include <algorithm>
+
+namespace android {
+
+status_t TransactionState::writeToParcel(Parcel* parcel) const {
+    SAFE_PARCEL(parcel->writeUint64, mId);
+    SAFE_PARCEL(parcel->writeUint32, mFlags);
+    SAFE_PARCEL(parcel->writeInt64, mDesiredPresentTime);
+    SAFE_PARCEL(parcel->writeBool, mIsAutoTimestamp);
+    SAFE_PARCEL(parcel->writeParcelable, mFrameTimelineInfo);
+    SAFE_PARCEL(parcel->writeStrongBinder, mApplyToken);
+    SAFE_PARCEL(parcel->writeBool, mMayContainBuffer);
+    SAFE_PARCEL(parcel->writeBool, mLogCallPoints);
+
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mDisplayStates.size()));
+    for (auto const& displayState : mDisplayStates) {
+        displayState.write(*parcel);
+    }
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mComposerStates.size()));
+    for (auto const& composerState : mComposerStates) {
+        composerState.write(*parcel);
+    }
+
+    mInputWindowCommands.write(*parcel);
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mUncacheBuffers.size()));
+    for (const client_cache_t& uncacheBuffer : mUncacheBuffers) {
+        SAFE_PARCEL(parcel->writeStrongBinder, uncacheBuffer.token.promote());
+        SAFE_PARCEL(parcel->writeUint64, uncacheBuffer.id);
+    }
+
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mMergedTransactionIds.size()));
+    for (auto mergedTransactionId : mMergedTransactionIds) {
+        SAFE_PARCEL(parcel->writeUint64, mergedTransactionId);
+    }
+
+    SAFE_PARCEL(parcel->writeBool, mHasListenerCallbacks);
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mListenerCallbacks.size()));
+    for (const auto& [listener, callbackIds] : mListenerCallbacks) {
+        SAFE_PARCEL(parcel->writeStrongBinder, listener);
+        SAFE_PARCEL(parcel->writeParcelableVector, callbackIds);
+    }
+
+    return NO_ERROR;
+}
+
+status_t TransactionState::readFromParcel(const Parcel* parcel) {
+    SAFE_PARCEL(parcel->readUint64, &mId);
+    SAFE_PARCEL(parcel->readUint32, &mFlags);
+    SAFE_PARCEL(parcel->readInt64, &mDesiredPresentTime);
+    SAFE_PARCEL(parcel->readBool, &mIsAutoTimestamp);
+    SAFE_PARCEL(parcel->readParcelable, &mFrameTimelineInfo);
+    SAFE_PARCEL(parcel->readNullableStrongBinder, &mApplyToken);
+    SAFE_PARCEL(parcel->readBool, &mMayContainBuffer);
+    SAFE_PARCEL(parcel->readBool, &mLogCallPoints);
+
+    uint32_t count;
+    SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+    mDisplayStates.clear();
+    mDisplayStates.reserve(count);
+    for (size_t i = 0; i < count; i++) {
+        DisplayState displayState;
+        if (displayState.read(*parcel) == BAD_VALUE) {
+            return BAD_VALUE;
+        }
+        mDisplayStates.emplace_back(std::move(displayState));
+    }
+
+    SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+    mComposerStates.clear();
+    mComposerStates.reserve(count);
+    for (size_t i = 0; i < count; i++) {
+        ComposerState composerState;
+        if (composerState.read(*parcel) == BAD_VALUE) {
+            return BAD_VALUE;
+        }
+        mComposerStates.emplace_back(std::move(composerState));
+    }
+
+    if (status_t status = mInputWindowCommands.read(*parcel) != NO_ERROR) {
+        return status;
+    }
+
+    SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+    mUncacheBuffers.clear();
+    mUncacheBuffers.reserve(count);
+    for (size_t i = 0; i < count; i++) {
+        client_cache_t client_cache;
+        sp<IBinder> tmpBinder;
+        SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
+        client_cache.token = tmpBinder;
+        SAFE_PARCEL(parcel->readUint64, &client_cache.id);
+        mUncacheBuffers.emplace_back(std::move(client_cache));
+    }
+
+    SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+    mMergedTransactionIds.clear();
+    mMergedTransactionIds.resize(count);
+    for (size_t i = 0; i < count; i++) {
+        SAFE_PARCEL(parcel->readUint64, &mMergedTransactionIds[i]);
+    }
+
+    SAFE_PARCEL(parcel->readBool, &mHasListenerCallbacks);
+    SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize());
+    mListenerCallbacks.clear();
+    mListenerCallbacks.reserve(count);
+    for (uint32_t i = 0; i < count; i++) {
+        sp<IBinder> tmpBinder;
+        SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
+        std::vector<CallbackId> callbackIds;
+        SAFE_PARCEL(parcel->readParcelableVector, &callbackIds);
+        mListenerCallbacks.emplace_back(tmpBinder, callbackIds);
+    }
+
+    return NO_ERROR;
+}
+
+void TransactionState::merge(TransactionState&& other,
+                             const std::function<void(layer_state_t&)>& onBufferOverwrite) {
+    while (mMergedTransactionIds.size() + other.mMergedTransactionIds.size() >
+                   MAX_MERGE_HISTORY_LENGTH - 1 &&
+           mMergedTransactionIds.size() > 0) {
+        mMergedTransactionIds.pop_back();
+    }
+    if (other.mMergedTransactionIds.size() == MAX_MERGE_HISTORY_LENGTH) {
+        mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
+                                     other.mMergedTransactionIds.begin(),
+                                     other.mMergedTransactionIds.end() - 1);
+    } else if (other.mMergedTransactionIds.size() > 0u) {
+        mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
+                                     other.mMergedTransactionIds.begin(),
+                                     other.mMergedTransactionIds.end());
+    }
+    mMergedTransactionIds.insert(mMergedTransactionIds.begin(), other.mId);
+
+    for (auto const& otherState : other.mComposerStates) {
+        if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(),
+                                   [&otherState](const auto& composerState) {
+                                       return composerState.state.surface ==
+                                               otherState.state.surface;
+                                   });
+            it != mComposerStates.end()) {
+            if (otherState.state.what & layer_state_t::eBufferChanged) {
+                onBufferOverwrite(it->state);
+            }
+            it->state.merge(otherState.state);
+        } else {
+            mComposerStates.push_back(otherState);
+        }
+    }
+
+    for (auto const& state : other.mDisplayStates) {
+        if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(),
+                                   [&state](const auto& displayState) {
+                                       return displayState.token == state.token;
+                                   });
+            it != mDisplayStates.end()) {
+            it->merge(state);
+        } else {
+            mDisplayStates.push_back(state);
+        }
+    }
+
+    for (const auto& cacheId : other.mUncacheBuffers) {
+        mUncacheBuffers.push_back(cacheId);
+    }
+
+    mInputWindowCommands.merge(other.mInputWindowCommands);
+    // TODO(b/385156191) Consider merging desired present time.
+    mFlags |= other.mFlags;
+    mMayContainBuffer |= other.mMayContainBuffer;
+    mLogCallPoints |= other.mLogCallPoints;
+
+    // mApplyToken is explicitly not merged. Token should be set before applying the transactions to
+    // make synchronization decisions a bit simpler.
+    mergeFrameTimelineInfo(other.mFrameTimelineInfo);
+    other.clear();
+}
+
+// copied from FrameTimelineInfo::merge()
+void TransactionState::mergeFrameTimelineInfo(const FrameTimelineInfo& other) {
+    // When merging vsync Ids we take the oldest valid one
+    if (mFrameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID &&
+        other.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
+        if (other.vsyncId > mFrameTimelineInfo.vsyncId) {
+            mFrameTimelineInfo = other;
+        }
+    } else if (mFrameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
+        mFrameTimelineInfo = other;
+    }
+}
+
+void TransactionState::clear() {
+    mComposerStates.clear();
+    mDisplayStates.clear();
+    mListenerCallbacks.clear();
+    mHasListenerCallbacks = false;
+    mInputWindowCommands.clear();
+    mUncacheBuffers.clear();
+    mDesiredPresentTime = 0;
+    mIsAutoTimestamp = true;
+    mApplyToken = nullptr;
+    mFrameTimelineInfo = {};
+    mMergedTransactionIds.clear();
+    mFlags = 0;
+    mMayContainBuffer = false;
+    mLogCallPoints = false;
+}
+
+layer_state_t* TransactionState::getLayerState(const sp<SurfaceControl>& sc) {
+    auto handle = sc->getLayerStateHandle();
+    if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(),
+                               [&handle](const auto& composerState) {
+                                   return composerState.state.surface == handle;
+                               });
+        it != mComposerStates.end()) {
+        return &it->state;
+    }
+
+    // we don't have it, add an initialized layer_state to our list
+    ComposerState s;
+    s.state.surface = handle;
+    s.state.layerId = sc->getLayerId();
+    mComposerStates.push_back(s);
+
+    return &mComposerStates.back().state;
+}
+
+DisplayState& TransactionState::getDisplayState(const sp<IBinder>& token) {
+    if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(),
+                               [token](const auto& display) { return display.token == token; });
+        it != mDisplayStates.end()) {
+        return *it;
+    }
+
+    // If display state doesn't exist, add a new one.
+    DisplayState s;
+    s.token = token;
+    mDisplayStates.push_back(s);
+    return mDisplayStates.back();
+}
+
+}; // namespace android
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 82d2554..3fb66d1 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -59,6 +59,32 @@
     return out;
 }
 
+status_t writeTransform(android::Parcel* parcel, const ui::Transform& transform) {
+    return parcel->writeFloat(transform.dsdx()) ?:
+            parcel->writeFloat(transform.dtdx()) ?:
+            parcel->writeFloat(transform.tx()) ?:
+            parcel->writeFloat(transform.dtdy()) ?:
+            parcel->writeFloat(transform.dsdy()) ?:
+            parcel->writeFloat(transform.ty());
+}
+
+status_t readTransform(const android::Parcel* parcel, ui::Transform& transform) {
+    float dsdx, dtdx, tx, dtdy, dsdy, ty;
+
+    const status_t status = parcel->readFloat(&dsdx) ?:
+            parcel->readFloat(&dtdx) ?:
+            parcel->readFloat(&tx) ?:
+            parcel->readFloat(&dtdy) ?:
+            parcel->readFloat(&dsdy) ?:
+            parcel->readFloat(&ty);
+    if (status != OK) {
+        return status;
+    }
+
+    transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+    return OK;
+}
+
 } // namespace
 
 void WindowInfo::setInputConfig(ftl::Flags<InputConfig> config, bool value) {
@@ -73,10 +99,6 @@
     touchableRegion.orSelf(region);
 }
 
-bool WindowInfo::supportsSplitTouch() const {
-    return !inputConfig.test(InputConfig::PREVENT_SPLITTING);
-}
-
 bool WindowInfo::isSpy() const {
     return inputConfig.test(InputConfig::SPY);
 }
@@ -135,12 +157,7 @@
         parcel->writeInt32(surfaceInset) ?:
         parcel->writeFloat(globalScaleFactor) ?:
         parcel->writeFloat(alpha) ?:
-        parcel->writeFloat(transform.dsdx()) ?:
-        parcel->writeFloat(transform.dtdx()) ?:
-        parcel->writeFloat(transform.tx()) ?:
-        parcel->writeFloat(transform.dtdy()) ?:
-        parcel->writeFloat(transform.dsdy()) ?:
-        parcel->writeFloat(transform.ty()) ?:
+        writeTransform(parcel, transform) ?:
         parcel->writeInt32(static_cast<int32_t>(touchOcclusionMode)) ?:
         parcel->writeInt32(ownerPid.val()) ?:
         parcel->writeInt32(ownerUid.val()) ?:
@@ -153,8 +170,12 @@
         parcel->writeStrongBinder(touchableRegionCropHandle.promote()) ?:
         parcel->writeStrongBinder(windowToken) ?:
         parcel->writeStrongBinder(focusTransferTarget) ?:
-        parcel->writeBool(canOccludePresentation);
+        parcel->writeBool(canOccludePresentation) ?:
+        parcel->writeBool(cloneLayerStackTransform.has_value());
     // clang-format on
+    if (cloneLayerStackTransform) {
+        status = status ?: writeTransform(parcel, *cloneLayerStackTransform);
+    }
     return status;
 }
 
@@ -174,10 +195,10 @@
         return status;
     }
 
-    float dsdx, dtdx, tx, dtdy, dsdy, ty;
     int32_t lpFlags, lpType, touchOcclusionModeInt, inputConfigInt, ownerPidInt, ownerUidInt,
             displayIdInt;
     sp<IBinder> touchableRegionCropHandleSp;
+    bool hasCloneLayerStackTransform = false;
 
     // clang-format off
     status = parcel->readInt32(&lpFlags) ?:
@@ -188,12 +209,7 @@
         parcel->readInt32(&surfaceInset) ?:
         parcel->readFloat(&globalScaleFactor) ?:
         parcel->readFloat(&alpha) ?:
-        parcel->readFloat(&dsdx) ?:
-        parcel->readFloat(&dtdx) ?:
-        parcel->readFloat(&tx) ?:
-        parcel->readFloat(&dtdy) ?:
-        parcel->readFloat(&dsdy) ?:
-        parcel->readFloat(&ty) ?:
+        readTransform(parcel, /*byRef*/ transform) ?:
         parcel->readInt32(&touchOcclusionModeInt) ?:
         parcel->readInt32(&ownerPidInt) ?:
         parcel->readInt32(&ownerUidInt) ?:
@@ -206,8 +222,8 @@
         parcel->readNullableStrongBinder(&touchableRegionCropHandleSp) ?:
         parcel->readNullableStrongBinder(&windowToken) ?:
         parcel->readNullableStrongBinder(&focusTransferTarget) ?:
-        parcel->readBool(&canOccludePresentation);
-
+        parcel->readBool(&canOccludePresentation)?:
+        parcel->readBool(&hasCloneLayerStackTransform);
     // clang-format on
 
     if (status != OK) {
@@ -216,7 +232,6 @@
 
     layoutParamsFlags = ftl::Flags<Flag>(lpFlags);
     layoutParamsType = static_cast<Type>(lpType);
-    transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
     touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
     inputConfig = ftl::Flags<InputConfig>(inputConfigInt);
     ownerPid = Pid{ownerPidInt};
@@ -224,6 +239,15 @@
     touchableRegionCropHandle = touchableRegionCropHandleSp;
     displayId = ui::LogicalDisplayId{displayIdInt};
 
+    cloneLayerStackTransform =
+            hasCloneLayerStackTransform ? std::make_optional<ui::Transform>() : std::nullopt;
+    if (cloneLayerStackTransform) {
+        status = readTransform(parcel, /*byRef*/ *cloneLayerStackTransform);
+        if (status != OK) {
+            return status;
+        }
+    }
+
     return OK;
 }
 
diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp
index 91c9a85..d633f9f 100644
--- a/libs/gui/WindowInfosListenerReporter.cpp
+++ b/libs/gui/WindowInfosListenerReporter.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <android/gui/ISurfaceComposer.h>
+#include <android/gui/IWindowInfosListener.h>
 #include <gui/AidlUtil.h>
 #include <gui/WindowInfosListenerReporter.h>
 #include "gui/WindowInfosUpdate.h"
@@ -27,7 +28,7 @@
 using gui::aidl_utils::statusTFromBinderStatus;
 
 sp<WindowInfosListenerReporter> WindowInfosListenerReporter::getInstance() {
-    static sp<WindowInfosListenerReporter> sInstance = new WindowInfosListenerReporter;
+    static sp<WindowInfosListenerReporter> sInstance = sp<WindowInfosListenerReporter>::make();
     return sInstance;
 }
 
@@ -116,7 +117,8 @@
     std::scoped_lock lock(mListenersMutex);
     if (!mWindowInfosListeners.empty()) {
         gui::WindowInfosListenerInfo listenerInfo;
-        composerService->addWindowInfosListener(this, &listenerInfo);
+        composerService->addWindowInfosListener(sp<gui::IWindowInfosListener>::fromExisting(this),
+                                                &listenerInfo);
         mWindowInfosPublisher = std::move(listenerInfo.windowInfosPublisher);
         mListenerId = listenerInfo.listenerId;
     }
diff --git a/libs/gui/aidl/android/gui/CaptureArgs.aidl b/libs/gui/aidl/android/gui/CaptureArgs.aidl
index 4920344..2bbed2b 100644
--- a/libs/gui/aidl/android/gui/CaptureArgs.aidl
+++ b/libs/gui/aidl/android/gui/CaptureArgs.aidl
@@ -69,10 +69,5 @@
     // exact colorspace is not an appropriate intermediate result.
     // Note that if the caller is requesting a specific dataspace, this hint does nothing.
     boolean hintForSeamlessTransition = false;
-
-    // Allows the screenshot to attach a gainmap, which allows for a per-pixel
-    // transformation of the screenshot to another luminance range, typically
-    // mapping an SDR base image into HDR.
-    boolean attachGainmap = false;
 }
 
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 8c19bbb..9b2f089 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -67,6 +67,11 @@
         frameRateOverride = 1 << 1,
     }
 
+    enum OptimizationPolicy {
+        optimizeForPower = 0,
+        optimizeForPerformance = 1,
+    }
+
     /**
      * Signal that we're done booting.
      * Requires ACCESS_SURFACE_FLINGER permission
@@ -97,6 +102,10 @@
      *     The name of the virtual display.
      * isSecure
      *     Whether this virtual display is secure.
+     * optimizationPolicy
+     *     Whether to optimize for power or performance. Displays that are optimizing for power may
+     *     be dependent on a different display that optimizes for performance when they are on,
+     *     which will guarantee performance for all of the other displays.
      * uniqueId
      *     The unique ID for the display.
      * requestedRefreshRate
@@ -108,7 +117,7 @@
      * requires ACCESS_SURFACE_FLINGER permission.
      */
     @nullable IBinder createVirtualDisplay(@utf8InCpp String displayName, boolean isSecure,
-            @utf8InCpp String uniqueId, float requestedRefreshRate);
+            OptimizationPolicy optimizationPolicy, @utf8InCpp String uniqueId, float requestedRefreshRate);
 
     /**
      * Destroy a virtual display.
@@ -607,8 +616,14 @@
     oneway void removeJankListener(int layerId, IJankListener listener, long afterVsync);
 
     /**
-     * Sets the listener used to monitor visible content that is being processed with picture
+     * Adds a listener used to monitor visible content that is being processed with picture
      * profiles.
      */
-    oneway void setActivePictureListener(IActivePictureListener listener);
+    oneway void addActivePictureListener(IActivePictureListener listener);
+
+    /**
+     * Removes a listener used to monitor visible content that is being processed with picture
+     * profiles.
+     */
+    oneway void removeActivePictureListener(IActivePictureListener listener);
 }
diff --git a/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl b/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl
index 0ccda56..7ff332c 100644
--- a/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl
+++ b/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl
@@ -23,6 +23,7 @@
 /** @hide */
 parcelable StaticDisplayInfo {
     DisplayConnectionType connectionType = DisplayConnectionType.Internal;
+    int port = -1;
     float density;
     boolean secure;
     @nullable DeviceProductInfo deviceProductInfo;
diff --git a/libs/gui/android/gui/BorderSettings.aidl b/libs/gui/android/gui/BorderSettings.aidl
new file mode 100644
index 0000000..547f57f
--- /dev/null
+++ b/libs/gui/android/gui/BorderSettings.aidl
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2025, 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 BorderSettings {
+    float strokeWidth;
+    // Space is sRGB, not premultiplied, bit pattern is 0xAARRGGBB.
+    int color;
+}
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
index fd8ffe1..b1a23b3 100644
--- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
@@ -971,7 +971,7 @@
 // H2BGraphicBufferProducer
 
 status_t H2BGraphicBufferProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
-    *buf = new GraphicBuffer();
+    *buf = sp<GraphicBuffer>::make();
     status_t fnStatus;
     status_t transStatus = toStatusT(mBase->requestBuffer(
             static_cast<int32_t>(slot),
@@ -999,7 +999,7 @@
                                                  uint32_t h, ::android::PixelFormat format,
                                                  uint64_t usage, uint64_t* outBufferAge,
                                                  FrameEventHistoryDelta* outTimestamps) {
-    *fence = new Fence();
+    *fence = sp<Fence>::make();
     status_t fnStatus;
     status_t transStatus = toStatusT(mBase->dequeueBuffer(
             w, h, static_cast<PixelFormat>(format), uint32_t(usage),
@@ -1035,8 +1035,8 @@
 
 status_t H2BGraphicBufferProducer::detachNextBuffer(
         sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
-    *outBuffer = new GraphicBuffer();
-    *outFence = new Fence();
+    *outBuffer = sp<GraphicBuffer>::make();
+    *outFence = sp<Fence>::make();
     status_t fnStatus;
     status_t transStatus = toStatusT(mBase->detachNextBuffer(
             [&fnStatus, outBuffer, outFence] (
@@ -1127,8 +1127,8 @@
 status_t H2BGraphicBufferProducer::connect(
         const sp<IProducerListener>& listener, int api,
         bool producerControlledByApp, QueueBufferOutput* output) {
-    sp<HProducerListener> tListener = listener == nullptr ?
-            nullptr : new B2HProducerListener(listener);
+    sp<HProducerListener> tListener =
+            listener == nullptr ? nullptr : sp<B2HProducerListener>::make(listener);
     status_t fnStatus;
     status_t transStatus = toStatusT(mBase->connect(
             tListener, static_cast<int32_t>(api), producerControlledByApp,
@@ -1205,13 +1205,13 @@
                     hidl_handle const& fence,
                     hidl_array<float, 16> const& transformMatrix) {
                 fnStatus = toStatusT(status);
-                *outBuffer = new GraphicBuffer();
+                *outBuffer = sp<GraphicBuffer>::make();
                 if (!convertTo(outBuffer->get(), buffer)) {
                     ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - "
                             "Invalid output buffer");
                     fnStatus = fnStatus == NO_ERROR ? BAD_VALUE : fnStatus;
                 }
-                *outFence = new Fence();
+                *outFence = sp<Fence>::make();
                 if (!convertTo(outFence->get(), fence)) {
                     ALOGE("H2BGraphicBufferProducer::getLastQueuedBuffer - "
                             "Invalid output fence");
diff --git a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
index c76d771..4384bd5 100644
--- a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
@@ -272,7 +272,7 @@
         HConnectionType hConnectionType,
         bool producerControlledByApp,
         connect_cb _hidl_cb) {
-    sp<BProducerListener> bListener = new H2BProducerListener(hListener);
+    sp<BProducerListener> bListener = sp<H2BProducerListener>::make(hListener);
     int bConnectionType{};
     if (!bListener || !h2b(hConnectionType, &bConnectionType)) {
         _hidl_cb(HStatus::UNKNOWN_ERROR, QueueBufferOutput{});
diff --git a/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
index ae00a26..7121bb7 100644
--- a/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
@@ -325,7 +325,7 @@
     }
     sp<HProducerListener> hListener = nullptr;
     if (listener && listener->needsReleaseNotify()) {
-        hListener = new B2HProducerListener(listener);
+        hListener = sp<B2HProducerListener>::make(listener);
         if (!hListener) {
             LOG(ERROR) << "connect: failed to wrap listener.";
             return UNKNOWN_ERROR;
diff --git a/libs/gui/bufferqueue/2.0/types.cpp b/libs/gui/bufferqueue/2.0/types.cpp
index cbd6cad..c245766 100644
--- a/libs/gui/bufferqueue/2.0/types.cpp
+++ b/libs/gui/bufferqueue/2.0/types.cpp
@@ -147,13 +147,13 @@
 
 bool h2b(native_handle_t const* from, sp<BFence>* to) {
     if (!from || from->numFds == 0) {
-        *to = new ::android::Fence();
+        *to = sp<::android::Fence>::make();
         return true;
     }
     if (from->numFds != 1 || from->numInts != 0) {
         return false;
     }
-    *to = new BFence(dup(from->data[0]));
+    *to = sp<BFence>::make(dup(from->data[0]));
     return true;
 }
 
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 07558aa..c69b0a7 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -20,6 +20,7 @@
 #include <optional>
 #include <queue>
 
+#include <ftl/small_map.h>
 #include <gui/BufferItem.h>
 #include <gui/BufferItemConsumer.h>
 #include <gui/IGraphicBufferConsumer.h>
@@ -36,26 +37,15 @@
 
 namespace android {
 
+// Sizes determined empirically to avoid allocations during common activity.
+constexpr size_t kSubmittedBuffersMapSizeHint = 8;
+constexpr size_t kDequeueTimestampsMapSizeHint = 32;
+
 class BLASTBufferQueue;
 class BufferItemConsumer;
 
 class BLASTBufferItemConsumer : public BufferItemConsumer {
 public:
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
-    BLASTBufferItemConsumer(const sp<IGraphicBufferProducer>& producer,
-                            const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
-                            int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
-          : BufferItemConsumer(producer, consumer, consumerUsage, bufferCount, controlledByApp),
-#else
-    BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
-                            int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
-          : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp),
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
-            mBLASTBufferQueue(std::move(bbq)),
-            mCurrentlyConnected(false),
-            mPreviouslyConnected(false) {
-    }
-
     void onDisconnect() override EXCLUDES(mMutex);
     void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
                                   FrameEventHistoryDelta* outDelta) override EXCLUDES(mMutex);
@@ -76,6 +66,23 @@
 #endif
 
 private:
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+    BLASTBufferItemConsumer(const sp<IGraphicBufferProducer>& producer,
+                            const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+                            int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
+          : BufferItemConsumer(producer, consumer, consumerUsage, bufferCount, controlledByApp),
+#else
+    BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+                            int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
+          : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp),
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+            mBLASTBufferQueue(std::move(bbq)),
+            mCurrentlyConnected(false),
+            mPreviouslyConnected(false) {
+    }
+
+    friend class sp<BLASTBufferItemConsumer>;
+
     const wp<BLASTBufferQueue> mBLASTBufferQueue;
 
     uint64_t mCurrentFrameNumber GUARDED_BY(mMutex) = 0;
@@ -89,10 +96,6 @@
 
 class BLASTBufferQueue : public ConsumerBase::FrameAvailableListener {
 public:
-    BLASTBufferQueue(const std::string& name, bool updateDestinationFrame = true);
-    BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width,
-                     int height, int32_t format);
-
     sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
         return mProducer;
     }
@@ -144,13 +147,24 @@
      */
     void setTransactionHangCallback(std::function<void(const std::string&)> callback);
     void setApplyToken(sp<IBinder>);
+
+    void setWaitForBufferReleaseCallback(std::function<void(const nsecs_t)> callback)
+            EXCLUDES(mWaitForBufferReleaseMutex);
+    std::function<void(const nsecs_t)> getWaitForBufferReleaseCallback() const
+            EXCLUDES(mWaitForBufferReleaseMutex);
+
     virtual ~BLASTBufferQueue();
 
     void onFirstRef() override;
 
 private:
+    // Not public to ensure construction via sp<>::make().
+    BLASTBufferQueue(const std::string& name, bool updateDestinationFrame = true);
+
+    friend class sp<BLASTBufferQueue>;
     friend class BLASTBufferQueueHelper;
     friend class BBQBufferQueueProducer;
+    friend class TestBLASTBufferQueue;
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
     friend class BBQBufferQueueCore;
 #endif
@@ -186,6 +200,7 @@
     sp<SurfaceControl> mSurfaceControl GUARDED_BY(mMutex);
 
     mutable std::mutex mMutex;
+    mutable std::mutex mWaitForBufferReleaseMutex;
     std::condition_variable mCallbackCV;
 
     // BufferQueue internally allows 1 more than
@@ -201,7 +216,7 @@
 
     // 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<ReleaseCallbackId, BufferItem, ReleaseBufferCallbackIdHash> mSubmitted
+    ftl::SmallMap<ReleaseCallbackId, BufferItem, kSubmittedBuffersMapSizeHint> mSubmitted
             GUARDED_BY(mMutex);
 
     // Keep a queue of the released buffers instead of immediately releasing
@@ -269,7 +284,7 @@
     std::function<void(SurfaceComposerClient::Transaction*)> mTransactionReadyCallback
             GUARDED_BY(mMutex);
     SurfaceComposerClient::Transaction* mSyncTransaction GUARDED_BY(mMutex);
-    std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
+    std::vector<std::pair<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
             mPendingTransactions GUARDED_BY(mMutex);
 
     std::queue<std::pair<uint64_t, FrameTimelineInfo>> mPendingFrameTimelines GUARDED_BY(mMutex);
@@ -286,8 +301,8 @@
     std::mutex mTimestampMutex;
     // Tracks buffer dequeue times by the client. This info is sent to SurfaceFlinger which uses
     // it for debugging purposes.
-    std::unordered_map<uint64_t /* bufferId */, nsecs_t> mDequeueTimestamps
-            GUARDED_BY(mTimestampMutex);
+    ftl::SmallMap<uint64_t /* bufferId */, nsecs_t, kDequeueTimestampsMapSizeHint>
+            mDequeueTimestamps GUARDED_BY(mTimestampMutex);
 
     // Keep track of SurfaceControls that have submitted a transaction and BBQ is waiting on a
     // callback for them.
@@ -324,6 +339,8 @@
 
     std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex);
 
+    std::function<void(const nsecs_t)> mWaitForBufferReleaseCallback
+            GUARDED_BY(mWaitForBufferReleaseMutex);
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
     // BufferReleaseChannel is used to communicate buffer releases from SurfaceFlinger to the
     // client.
diff --git a/libs/gui/include/gui/BufferItemConsumer.h b/libs/gui/include/gui/BufferItemConsumer.h
index 6810eda..fc31f46 100644
--- a/libs/gui/include/gui/BufferItemConsumer.h
+++ b/libs/gui/include/gui/BufferItemConsumer.h
@@ -47,6 +47,16 @@
     enum { INVALID_BUFFER_SLOT = BufferQueue::INVALID_BUFFER_SLOT };
     enum { NO_BUFFER_AVAILABLE = BufferQueue::NO_BUFFER_AVAILABLE };
 
+    static std::tuple<sp<BufferItemConsumer>, sp<Surface>> create(
+            uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS,
+            bool controlledByApp = false, bool isConsumerSurfaceFlinger = false);
+
+    static sp<BufferItemConsumer> create(const sp<IGraphicBufferConsumer>& consumer,
+                                         uint64_t consumerUsage,
+                                         int bufferCount = DEFAULT_MAX_BUFFERS,
+                                         bool controlledByApp = false)
+            __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+
     // Create a new buffer item consumer. The consumerUsage parameter determines
     // the consumer usage flags passed to the graphics allocator. The
     // bufferCount parameter specifies how many buffers can be locked for user
@@ -86,6 +96,14 @@
     status_t acquireBuffer(BufferItem* item, nsecs_t presentWhen,
             bool waitForFence = true);
 
+    // Transfer ownership of a buffer to the BufferQueue. On NO_ERROR, the buffer
+    // is considered as if it were acquired. Buffer must not be null.
+    //
+    // Returns
+    //  - BAD_VALUE if buffer is null
+    //  - INVALID_OPERATION if too many buffers have already been acquired
+    status_t attachBuffer(const sp<GraphicBuffer>& buffer);
+
     // Returns an acquired buffer to the queue, allowing it to be reused. Since
     // only a fixed number of buffers may be acquired at a time, old buffers
     // must be released by calling releaseBuffer to ensure new buffers can be
@@ -95,10 +113,8 @@
     status_t releaseBuffer(const BufferItem &item,
             const sp<Fence>& releaseFence = Fence::NO_FENCE);
 
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
     status_t releaseBuffer(const sp<GraphicBuffer>& buffer,
                            const sp<Fence>& releaseFence = Fence::NO_FENCE);
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
 
 protected:
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h
index 0948c4d0..7b97e13 100644
--- a/libs/gui/include/gui/BufferQueue.h
+++ b/libs/gui/include/gui/BufferQueue.h
@@ -57,7 +57,7 @@
     // reference in the BufferQueue class is because we're planning to expose the
     // consumer side of a BufferQueue as a binder interface, which doesn't support
     // weak references.
-    class ProxyConsumerListener : public BnConsumerListener {
+    class ProxyConsumerListener : public IConsumerListener {
     public:
         explicit ProxyConsumerListener(const wp<ConsumerListener>& consumerListener);
         ~ProxyConsumerListener() override;
@@ -76,6 +76,9 @@
         void onSetFrameRate(float frameRate, int8_t compatibility,
                             int8_t changeFrameRateStrategy) override;
 #endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+        void onSlotCountChanged(int slotCount) override;
+#endif
     private:
         // mConsumerListener is a weak reference to the IConsumerListener.  This is
         // the raison d'etre of ProxyConsumerListener.
diff --git a/libs/gui/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h
index 6aa801a..ba6a6a7 100644
--- a/libs/gui/include/gui/BufferQueueConsumer.h
+++ b/libs/gui/include/gui/BufferQueueConsumer.h
@@ -28,8 +28,7 @@
 
 class BufferQueueCore;
 
-class BufferQueueConsumer : public BnGraphicBufferConsumer {
-
+class BufferQueueConsumer : public IGraphicBufferConsumer {
 public:
     explicit BufferQueueConsumer(const sp<BufferQueueCore>& core);
     ~BufferQueueConsumer() override;
@@ -65,13 +64,14 @@
     // any references to the just-released buffer that it might have, as if it
     // had received a onBuffersReleased() call with a mask set for the released
     // buffer.
-    //
-    // Note that the dependencies on EGL will be removed once we switch to using
-    // the Android HW Sync HAL.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
+    virtual status_t releaseBuffer(int slot, uint64_t frameNumber,
+                                   const sp<Fence>& releaseFence) override;
+#else
     virtual status_t releaseBuffer(int slot, uint64_t frameNumber,
             const sp<Fence>& releaseFence, EGLDisplay display,
             EGLSyncKHR fence);
-
+#endif
     // connect connects a consumer to the BufferQueue.  Only one
     // consumer may be connected, and when that consumer disconnects the
     // BufferQueue is placed into the "abandoned" state, causing most
@@ -96,18 +96,36 @@
     // This should be called from the onBuffersReleased() callback.
     virtual status_t getReleasedBuffers(uint64_t* outSlotMask);
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    // getReleasedBuffers sets the values pointed to by outSlotMask to the bits
+    // indicating which buffer slots have been released by the BufferQueue
+    // but have not yet been released by the consumer.
+    //
+    // This should be called from the onBuffersReleased() callback when
+    // allowUnlimitedSlots has been called.
+    virtual status_t getReleasedBuffersExtended(std::vector<bool>* outSlotMask) override;
+#endif
+
     // setDefaultBufferSize is used to set the size of buffers returned by
     // dequeueBuffer when a width and height of zero is requested.  Default
     // is 1x1.
     virtual status_t setDefaultBufferSize(uint32_t width, uint32_t height);
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    // see IGraphicBufferConsumer::allowUnlimitedSlots
+    virtual status_t allowUnlimitedSlots(bool allowUnlimitedSlots) override;
+#endif
+
     // see IGraphicBufferConsumer::setMaxBufferCount
     virtual status_t setMaxBufferCount(int bufferCount);
 
     // setMaxAcquiredBufferCount sets the maximum number of buffers that can
     // be acquired by the consumer at one time (default 1).  This call will
     // fail if a producer is connected to the BufferQueue.
-    virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
+    virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) override;
+    virtual status_t setMaxAcquiredBufferCount(
+            int maxAcquiredBuffers,
+            std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) override;
 
     // setConsumerName sets the name used in logging
     status_t setConsumerName(const String8& name) override;
@@ -152,6 +170,7 @@
     // dump our state in a String
     status_t dumpState(const String8& prefix, String8* outResult) const override;
 
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
     // Functions required for backwards compatibility.
     // These will be modified/renamed in IGraphicBufferConsumer and will be
     // removed from this class at that time. See b/13306289.
@@ -161,6 +180,7 @@
             const sp<Fence>& releaseFence) {
         return releaseBuffer(buf, frameNumber, releaseFence, display, fence);
     }
+#endif
 
     virtual status_t consumerConnect(const sp<IConsumerListener>& consumer,
             bool controlledByApp) {
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 77cdf2c..7f92a46 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -32,10 +32,11 @@
 #include <utils/Trace.h>
 #include <utils/Vector.h>
 
-#include <list>
-#include <set>
-#include <mutex>
 #include <condition_variable>
+#include <list>
+#include <mutex>
+#include <set>
+#include <vector>
 
 #define ATRACE_BUFFER_INDEX(index)                                                        \
     do {                                                                                  \
@@ -91,6 +92,10 @@
     // Dump our state in a string
     void dumpState(const String8& prefix, String8* outResult) const;
 
+    // getTotalSlotCountLocked returns the total number of slots in use by the
+    // buffer queue at this time.
+    int getTotalSlotCountLocked() const;
+
     // getMinUndequeuedBufferCountLocked returns the minimum number of buffers
     // that must remain in a state other than DEQUEUED. The async parameter
     // tells whether we're in asynchronous mode.
@@ -120,6 +125,10 @@
     int getMaxBufferCountLocked(bool asyncMode,
             bool dequeueBufferCannotBlock, int maxBufferCount) const;
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    // This resizes mSlots to the given size, but only if it's increasing.
+    status_t extendSlotCountLocked(int size);
+#endif
     // clearBufferSlotLocked frees the GraphicBuffer and sync resources for the
     // given slot.
     void clearBufferSlotLocked(int slot);
@@ -204,7 +213,7 @@
     // mConnectedProducerListener will not trigger onBufferAttached() callback.
     bool mBufferAttachedCbEnabled;
 
-    // mSlots is an array of buffer slots that must be mirrored on the producer
+    // mSlots is a collection of buffer slots that must be mirrored on the producer
     // side. This allows buffer ownership to be transferred between the producer
     // and consumer without sending a GraphicBuffer over Binder. The entire
     // array is initialized to NULL at construction time, and buffers are
@@ -266,8 +275,14 @@
     // is specified.
     android_dataspace mDefaultBufferDataSpace;
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    // mAllowExtendedSlotCount is set by the consumer to permit the producer to
+    // request an unlimited number of slots.
+    bool mAllowExtendedSlotCount;
+#endif
+
     // mMaxBufferCount is the limit on the number of buffers that will be
-    // allocated at one time. This limit can be set by the consumer.
+    // allocated at one time.
     int mMaxBufferCount;
 
     // mMaxAcquiredBufferCount is the number of buffers that the consumer may
diff --git a/libs/gui/include/gui/BufferQueueDefs.h b/libs/gui/include/gui/BufferQueueDefs.h
index ffafb49..42cf439 100644
--- a/libs/gui/include/gui/BufferQueueDefs.h
+++ b/libs/gui/include/gui/BufferQueueDefs.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_GUI_BUFFERQUEUECOREDEFS_H
 #define ANDROID_GUI_BUFFERQUEUECOREDEFS_H
 
+#include <com_android_graphics_libgui_flags.h>
 #include <gui/BufferSlot.h>
 #include <ui/BufferQueueDefs.h>
 
@@ -24,7 +25,11 @@
     class BufferQueueCore;
 
     namespace BufferQueueDefs {
-        typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    typedef std::vector<BufferSlot> SlotsType;
+#else
+    typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
+#endif
     } // namespace BufferQueueDefs
 } // namespace android
 
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index 086ce7c..6a1e9f6 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -36,8 +36,6 @@
 public:
     friend class BufferQueue; // Needed to access binderDied
 
-    explicit BufferQueueProducer(const sp<BufferQueueCore>& core,
-                                 bool consumerIsSurfaceFlinger = false);
     ~BufferQueueProducer() override;
 
     // requestBuffer returns the GraphicBuffer for slot N.
@@ -47,6 +45,11 @@
     // flags indicating that previously-returned buffers are no longer valid.
     virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    // see IGraphicsBufferProducer::extendSlotCount
+    virtual status_t extendSlotCount(int size) override;
+#endif
+
     // see IGraphicsBufferProducer::setMaxDequeuedBufferCount
     virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
 
@@ -214,6 +217,9 @@
 #endif
 
 protected:
+    explicit BufferQueueProducer(const sp<BufferQueueCore>& core,
+                                 bool consumerIsSurfaceFlinger = false);
+    friend class sp<BufferQueueProducer>;
     // see IGraphicsBufferProducer::setMaxDequeuedBufferCount, but with the ability to retrieve the
     // total maximum buffer count for the buffer queue (dequeued AND acquired)
     status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers, int* maxBufferCount);
diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h
index a93ba14..c632098 100644
--- a/libs/gui/include/gui/Choreographer.h
+++ b/libs/gui/include/gui/Choreographer.h
@@ -81,6 +81,7 @@
 
     explicit Choreographer(const sp<Looper>& looper, const sp<IBinder>& layerHandle = nullptr)
             EXCLUDES(gChoreographers.lock);
+
     void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
                                   AChoreographer_frameCallback64 cb64,
                                   AChoreographer_vsyncCallback vsyncCallback, void* data,
@@ -103,7 +104,7 @@
     virtual void handleMessage(const Message& message) override;
 
     static void initJVM(JNIEnv* env);
-    static Choreographer* getForThread();
+    static sp<Choreographer> getForThread();
     static void signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock);
     static int64_t getStartTimeNanosForVsyncId(AVsyncId vsyncId) EXCLUDES(gChoreographers.lock);
     virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
index e976aa4..63c1ef3 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -98,6 +98,8 @@
     status_t detachBuffer(const sp<GraphicBuffer>& buffer);
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
 
+    status_t addReleaseFence(const sp<GraphicBuffer> buffer, const sp<Fence>& fence);
+
     // See IGraphicBufferConsumer::setDefaultBufferSize
     status_t setDefaultBufferSize(uint32_t width, uint32_t height);
 
@@ -121,9 +123,7 @@
     // See IGraphicBufferConsumer::setMaxAcquiredBufferCount
     status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
 
-#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
     status_t setConsumerIsProtected(bool isProtected);
-#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 
     // See IGraphicBufferConsumer::getSidebandStream
     sp<NativeHandle> getSidebandStream() const;
@@ -185,9 +185,13 @@
     virtual void onFrameDetached(const uint64_t bufferId) override;
     virtual void onBuffersReleased() override;
     virtual void onSidebandStreamChanged() override;
-
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    virtual void onSlotCountChanged(int slotCount) override;
+#endif
     virtual int getSlotForBufferLocked(const sp<GraphicBuffer>& buffer);
 
+    virtual void onBuffersReleasedLocked();
+
     virtual status_t detachBufferLocked(int slotIndex);
 
     // freeBufferLocked frees up the given buffer slot.  If the slot has been
@@ -243,10 +247,13 @@
     // must take place when a buffer is released back to the BufferQueue.  If
     // it is overridden the derived class's implementation must call
     // ConsumerBase::releaseBufferLocked.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
+    virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer);
+#else
     virtual status_t releaseBufferLocked(int slot,
             const sp<GraphicBuffer> graphicBuffer,
             EGLDisplay display = EGL_NO_DISPLAY, EGLSyncKHR eglFence = EGL_NO_SYNC_KHR);
-
+#endif
     // returns true iff the slot still has the graphicBuffer in it.
     bool stillTracking(int slot, const sp<GraphicBuffer> graphicBuffer);
 
@@ -284,7 +291,11 @@
     // slot that has not yet been used. The buffer allocated to a slot will also
     // be replaced if the requested buffer usage or geometry differs from that
     // of the buffer allocated to a slot.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    std::vector<Slot> mSlots;
+#else
     Slot mSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
+#endif
 
     // mAbandoned indicates that the BufferQueue will no longer be used to
     // consume images buffers pushed to it using the IGraphicBufferProducer
diff --git a/libs/gui/include/gui/CpuConsumer.h b/libs/gui/include/gui/CpuConsumer.h
index 2bba61b..995cdfb 100644
--- a/libs/gui/include/gui/CpuConsumer.h
+++ b/libs/gui/include/gui/CpuConsumer.h
@@ -31,6 +31,7 @@
 class BufferQueue;
 class GraphicBuffer;
 class String8;
+class Surface;
 
 /**
  * CpuConsumer is a BufferQueue consumer endpoint that allows direct CPU
@@ -92,6 +93,13 @@
 
     // Create a new CPU consumer. The maxLockedBuffers parameter specifies
     // how many buffers can be locked for user access at the same time.
+    static std::tuple<sp<CpuConsumer>, sp<Surface>> create(size_t maxLockedBuffers,
+                                                           bool controlledByApp = false,
+                                                           bool isConsumerSurfaceFlinger = false);
+    static sp<CpuConsumer> create(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers,
+                                  bool controlledByApp = false)
+            __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
     CpuConsumer(size_t maxLockedBuffers, bool controlledByApp = false,
                 bool isConsumerSurfaceFlinger = false);
@@ -100,8 +108,8 @@
                 bool controlledByApp = false)
             __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
 #else
-    CpuConsumer(const sp<IGraphicBufferConsumer>& bq,
-            size_t maxLockedBuffers, bool controlledByApp = false);
+    CpuConsumer(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers,
+                bool controlledByApp = false);
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 
     // Gets the next graphics buffer from the producer and locks it for CPU use,
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index b06ad07..cdf216c 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -23,12 +23,6 @@
 
 class DisplayEventDispatcher : public LooperCallback {
 public:
-    explicit DisplayEventDispatcher(const sp<Looper>& looper,
-                                    gui::ISurfaceComposer::VsyncSource vsyncSource =
-                                            gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp,
-                                    EventRegistrationFlags eventRegistration = {},
-                                    const sp<IBinder>& layerHandle = nullptr);
-
     status_t initialize();
     void dispose();
     status_t scheduleVsync();
@@ -38,6 +32,14 @@
     status_t getLatestVsyncEventData(ParcelableVsyncEventData* outVsyncEventData) const;
 
 protected:
+    explicit DisplayEventDispatcher(const sp<Looper>& looper,
+                                    gui::ISurfaceComposer::VsyncSource vsyncSource =
+                                            gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp,
+                                    EventRegistrationFlags eventRegistration = {},
+                                    const sp<IBinder>& layerHandle = nullptr);
+
+    friend class sp<DisplayEventDispatcher>;
+
     virtual ~DisplayEventDispatcher() = default;
 
 private:
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index ab6a6b7..f51390a 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -55,20 +55,20 @@
         static_cast<uint32_t>(c4);
 }
 
+enum class DisplayEventType : uint32_t {
+    DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
+    DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
+    DISPLAY_EVENT_MODE_CHANGE = fourcc('m', 'o', 'd', 'e'),
+    DISPLAY_EVENT_MODE_REJECTION = fourcc('r', 'e', 'j', 'e'),
+    DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'),
+    DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'),
+    DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'),
+    DISPLAY_EVENT_HDCP_LEVELS_CHANGE = fourcc('h', 'd', 'c', 'p'),
+};
+
 // ----------------------------------------------------------------------------
 class DisplayEventReceiver {
 public:
-    enum {
-        DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
-        DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
-        DISPLAY_EVENT_MODE_CHANGE = fourcc('m', 'o', 'd', 'e'),
-        DISPLAY_EVENT_MODE_REJECTION = fourcc('r', 'e', 'j', 'e'),
-        DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'),
-        DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'),
-        DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'),
-        DISPLAY_EVENT_HDCP_LEVELS_CHANGE = fourcc('h', 'd', 'c', 'p'),
-    };
-
     struct Event {
         // We add __attribute__((aligned(8))) for nsecs_t fields because
         // we need to make sure all fields are aligned the same with x86
@@ -77,7 +77,7 @@
         // https://en.wikipedia.org/wiki/Data_structure_alignment
 
         struct Header {
-            uint32_t type;
+            DisplayEventType type;
             PhysicalDisplayId displayId __attribute__((aligned(8)));
             nsecs_t timestamp __attribute__((aligned(8)));
         };
diff --git a/libs/gui/include/gui/DisplayLuts.h b/libs/gui/include/gui/DisplayLuts.h
index ab86ac4..187381c 100644
--- a/libs/gui/include/gui/DisplayLuts.h
+++ b/libs/gui/include/gui/DisplayLuts.h
@@ -18,6 +18,10 @@
 #include <android-base/unique_fd.h>
 #include <binder/Parcel.h>
 #include <binder/Parcelable.h>
+#include <cutils/ashmem.h>
+#include <sys/mman.h>
+#include <algorithm>
+#include <ostream>
 #include <vector>
 
 namespace android::gui {
@@ -62,4 +66,99 @@
     base::unique_fd fd;
 }; // struct DisplayLuts
 
+static inline void PrintTo(const std::vector<int32_t>& offsets, ::std::ostream* os) {
+    *os << "\n    .offsets = {";
+    for (size_t i = 0; i < offsets.size(); i++) {
+        *os << offsets[i];
+        if (i != offsets.size() - 1) {
+            *os << ", ";
+        }
+    }
+    *os << "}";
+}
+
+static inline void PrintTo(const std::vector<DisplayLuts::Entry>& entries, ::std::ostream* os) {
+    *os << "\n    .lutProperties = {\n";
+    for (auto& [dimension, size, samplingKey] : entries) {
+        *os << "        Entry{"
+            << "dimension: " << dimension << ", size: " << size << ", samplingKey: " << samplingKey
+            << "}\n";
+    }
+    *os << "    }";
+}
+
+static constexpr size_t kMaxPrintCount = 100;
+
+static inline void PrintTo(const std::vector<float>& buffer, size_t offset, int32_t dimension,
+                           size_t size, ::std::ostream* os) {
+    size_t range = std::min(size, kMaxPrintCount);
+    *os << "{";
+    if (dimension == 1) {
+        for (size_t i = 0; i < range; i++) {
+            *os << buffer[offset + i];
+            if (i != range - 1) {
+                *os << ", ";
+            }
+        }
+    } else {
+        *os << "\n        {R channel:";
+        for (size_t i = 0; i < range; i++) {
+            *os << buffer[offset + i];
+            if (i != range - 1) {
+                *os << ", ";
+            }
+        }
+        *os << "}\n        {G channel:";
+        for (size_t i = 0; i < range; i++) {
+            *os << buffer[offset + size + i];
+            if (i != range - 1) {
+                *os << ", ";
+            }
+        }
+        *os << "}\n        {B channel:";
+        for (size_t i = 0; i < range; i++) {
+            *os << buffer[offset + 2 * size + i];
+            if (i != range - 1) {
+                *os << ", ";
+            }
+        }
+    }
+    *os << "}";
+}
+
+static inline void PrintTo(const std::shared_ptr<DisplayLuts> luts, ::std::ostream* os) {
+    *os << "gui::DisplayLuts {";
+    auto& fd = luts->getLutFileDescriptor();
+    *os << "\n    .pfd = " << fd.get();
+    if (fd.ok()) {
+        PrintTo(luts->offsets, os);
+        PrintTo(luts->lutProperties, os);
+        // decode luts
+        int32_t fullLength = luts->offsets[luts->offsets.size() - 1];
+        if (luts->lutProperties[luts->offsets.size() - 1].dimension == 1) {
+            fullLength += luts->lutProperties[luts->offsets.size() - 1].size;
+        } else {
+            fullLength += (luts->lutProperties[luts->offsets.size() - 1].size *
+                           luts->lutProperties[luts->offsets.size() - 1].size *
+                           luts->lutProperties[luts->offsets.size() - 1].size * 3);
+        }
+        size_t bufferSize = static_cast<size_t>(fullLength) * sizeof(float);
+        float* ptr = (float*)mmap(NULL, bufferSize, PROT_READ, MAP_SHARED, fd.get(), 0);
+        if (ptr == MAP_FAILED) {
+            *os << "\n    .bufferdata cannot mmap!";
+            return;
+        }
+        std::vector<float> buffers(ptr, ptr + fullLength);
+        munmap(ptr, bufferSize);
+
+        *os << "\n    .bufferdata = ";
+        for (size_t i = 0; i < luts->offsets.size(); i++) {
+            PrintTo(buffers, static_cast<size_t>(luts->offsets[i]),
+                    luts->lutProperties[i].dimension,
+                    static_cast<size_t>(luts->lutProperties[i].size), os);
+        }
+    }
+    *os << "\n    }";
+}
+
 } // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/include/gui/Flags.h b/libs/gui/include/gui/Flags.h
index 845bc54..446841b 100644
--- a/libs/gui/include/gui/Flags.h
+++ b/libs/gui/include/gui/Flags.h
@@ -46,6 +46,7 @@
 
 namespace flagtools {
 sp<SurfaceType> surfaceToSurfaceType(const sp<Surface>& surface);
+ParcelableSurfaceType surfaceToParcelableSurfaceType(const sp<Surface>& surface);
 ParcelableSurfaceType toParcelableSurfaceType(const view::Surface& surface);
 sp<IGraphicBufferProducer> surfaceTypeToIGBP(const sp<SurfaceType>& surface);
 bool isSurfaceTypeValid(const sp<SurfaceType>& surface);
diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h
index 8a66dc0..254d8ac 100644
--- a/libs/gui/include/gui/GLConsumer.h
+++ b/libs/gui/include/gui/GLConsumer.h
@@ -83,6 +83,20 @@
     // If the constructor without the tex parameter is used, the GLConsumer is
     // created in a detached state, and attachToContext must be called before
     // calls to updateTexImage.
+    static std::tuple<sp<GLConsumer>, sp<Surface>> create(uint32_t tex, uint32_t textureTarget,
+                                                          bool useFenceSync,
+                                                          bool isControlledByApp);
+    static std::tuple<sp<GLConsumer>, sp<Surface>> create(uint32_t textureTarget, bool useFenceSync,
+                                                          bool isControlledByApp);
+    static sp<GLConsumer> create(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
+                                 uint32_t textureTarget, bool useFenceSync, bool isControlledByApp)
+            __attribute((deprecated(
+                    "Prefer create functions that create their own surface and consumer.")));
+    static sp<GLConsumer> create(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget,
+                                 bool useFenceSync, bool isControlledByApp)
+            __attribute((deprecated(
+                    "Prefer create functions that create their own surface and consumer.")));
+
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
     GLConsumer(uint32_t tex, uint32_t textureTarget, bool useFenceSync, bool isControlledByApp);
 
@@ -266,8 +280,12 @@
     virtual status_t acquireBufferLocked(BufferItem *item, nsecs_t presentWhen,
             uint64_t maxFrameNumber = 0) override;
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    virtual void onSlotCountChanged(int slotCount) override;
+#endif
     // releaseBufferLocked overrides the ConsumerBase method to update the
     // mEglSlots array in addition to the ConsumerBase.
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
     virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer,
                                          EGLDisplay display = EGL_NO_DISPLAY,
                                          EGLSyncKHR eglFence = EGL_NO_SYNC_KHR) override;
@@ -276,16 +294,14 @@
             const sp<GraphicBuffer> graphicBuffer, EGLSyncKHR eglFence) {
         return releaseBufferLocked(slot, graphicBuffer, mEglDisplay, eglFence);
     }
+#endif
 
     struct PendingRelease {
-        PendingRelease() : isPending(false), currentTexture(-1),
-                graphicBuffer(), display(nullptr), fence(nullptr) {}
+        PendingRelease() : isPending(false), currentTexture(-1), graphicBuffer() {}
 
         bool isPending;
         int currentTexture;
         sp<GraphicBuffer> graphicBuffer;
-        EGLDisplay display;
-        EGLSyncKHR fence;
     };
 
     // This releases the buffer in the slot referenced by mCurrentTexture,
@@ -465,16 +481,18 @@
     // EGLSlot contains the information and object references that
     // GLConsumer maintains about a BufferQueue buffer slot.
     struct EglSlot {
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
         EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
-
+#endif
         // mEglImage is the EGLImage created from mGraphicBuffer.
         sp<EglImage> mEglImage;
-
+#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
         // mFence is the EGL sync object that must signal before the buffer
         // associated with this buffer slot may be dequeued. It is initialized
         // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based
         // on a compile-time option) set to a new sync object in updateTexImage.
         EGLSyncKHR mEglFence;
+#endif
     };
 
     // mEglDisplay is the EGLDisplay with which this GLConsumer is currently
@@ -496,8 +514,11 @@
     // slot that has not yet been used. The buffer allocated to a slot will also
     // be replaced if the requested buffer usage or geometry differs from that
     // of the buffer allocated to a slot.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    std::vector<EglSlot> mEglSlots;
+#else
     EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
-
+#endif
     // mCurrentTexture is the buffer slot index of the buffer that is currently
     // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
     // indicating that no buffer slot is currently bound to the texture. Note,
diff --git a/libs/gui/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h
index 51d3959..95e66c7 100644
--- a/libs/gui/include/gui/IConsumerListener.h
+++ b/libs/gui/include/gui/IConsumerListener.h
@@ -16,9 +16,6 @@
 
 #pragma once
 
-#include <binder/IInterface.h>
-#include <binder/SafeInterface.h>
-
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 
@@ -98,27 +95,18 @@
     virtual void onSetFrameRate(float /*frameRate*/, int8_t /*compatibility*/,
                                 int8_t /*changeFrameRateStrategy*/) {}
 #endif
-};
 
-#ifndef NO_BINDER
-class IConsumerListener : public ConsumerListener, public IInterface {
-public:
-    DECLARE_META_INTERFACE(ConsumerListener)
-};
-
-class BnConsumerListener : public SafeBnInterface<IConsumerListener> {
-public:
-    BnConsumerListener() : SafeBnInterface<IConsumerListener>("BnConsumerListener") {}
-
-    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-                        uint32_t flags = 0) override;
-};
-
-#else
-class IConsumerListener : public ConsumerListener {
-};
-class BnConsumerListener : public IConsumerListener {
-};
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    // Notifies the consumer that IGraphicBufferProducer::extendSlotCount has
+    // been called and the total slot count has increased.
+    //
+    // This will only ever be called if
+    // IGraphicBufferConsumer::allowUnlimitedSlots has been called on the
+    // consumer.
+    virtual void onSlotCountChanged(int /* slotCount */) {}
 #endif
+};
+
+class IConsumerListener : public ConsumerListener {};
 
 } // namespace android
diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h
index 18f5488..8066b07 100644
--- a/libs/gui/include/gui/IGraphicBufferConsumer.h
+++ b/libs/gui/include/gui/IGraphicBufferConsumer.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <com_android_graphics_libgui_flags.h>
 #include <gui/OccupancyTracker.h>
 
 #include <binder/IInterface.h>
@@ -35,15 +36,12 @@
 class GraphicBuffer;
 class IConsumerListener;
 class NativeHandle;
-#ifndef NO_BINDER
-class IGraphicBufferConsumer : public IInterface {
-public:
-    DECLARE_META_INTERFACE(GraphicBufferConsumer)
-#else
+
+/*
+ * See IGraphicBufferProducer for details on SLOT_COUNT.
+ */
 class IGraphicBufferConsumer : public RefBase {
 public:
-#endif
-
     enum {
         // Returned by releaseBuffer, after which the consumer must free any references to the
         // just-released buffer that it might have.
@@ -92,7 +90,7 @@
     //
     // Return of a value other than NO_ERROR means an error has occurred:
     // * BAD_VALUE - the given slot number is invalid, either because it is out of the range
-    //               [0, NUM_BUFFER_SLOTS) or because the slot it refers to is not
+    //               [0, SLOT_COUNT) or because the slot it refers to is not
     //               currently acquired.
     virtual status_t detachBuffer(int slot) = 0;
 
@@ -134,12 +132,17 @@
     //               * the buffer slot was invalid
     //               * the fence was NULL
     //               * the buffer slot specified is not in the acquired state
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
+    virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
+                                   const sp<Fence>& releaseFence) = 0;
+#else
     virtual status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display,
                                    EGLSyncKHR fence, const sp<Fence>& releaseFence) = 0;
 
     status_t releaseBuffer(int buf, uint64_t frameNumber, const sp<Fence>& releaseFence) {
         return releaseBuffer(buf, frameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence);
     }
+#endif
 
     // consumerConnect connects a consumer to the BufferQueue. Only one consumer may be connected,
     // and when that consumer disconnects the BufferQueue is placed into the "abandoned" state,
@@ -173,6 +176,19 @@
     // * NO_INIT - the BufferQueue has been abandoned.
     virtual status_t getReleasedBuffers(uint64_t* slotMask) = 0;
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    // getReleasedBuffersExtended for each slot, sets slotMask[slot] to 1 if it
+    // corresponds to a released buffer slot. In particular, a released buffer
+    // is one that has been released by the BufferQueue but has not yet been
+    // released by the consumer.
+    //
+    // This should be called from the onBuffersReleased() callback.
+    //
+    // Return of a value other than NO_ERROR means an error has occurred:
+    // * NO_INIT - the BufferQueue has been abandoned.
+    virtual status_t getReleasedBuffersExtended(std::vector<bool>* slotMask) = 0;
+#endif
+
     // setDefaultBufferSize is used to set the size of buffers returned by dequeueBuffer when a
     // width and height of zero is requested. Default is 1x1.
     //
@@ -180,6 +196,26 @@
     // * BAD_VALUE - either w or h was zero
     virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0;
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    // allowUnlimitedSlots allows the producer to set the upper bound on slots.
+    //
+    // Must be called before the producer is connected. If the producer
+    // increases the slot count, an IConsumerListener::onSlotCountChanged
+    // update is sent.
+    //
+    // This can not be used with setMaxBufferCount. Calls after
+    // setMaxBufferCount will fail and calls to setMaxBufferCount after setting
+    // this to true will fail.
+    //
+    // Return of a value other than NO_ERROR means an error has occurred:
+    // * NO_INIT - the BufferQueue has been abandoned
+    // * INVALID_OPERATION - one of the following errors has occurred:
+    //                       * Producer has been connected
+    //                       * setMaxBufferCount has been called and shrunk the
+    //                         BufferQueue.
+    virtual status_t allowUnlimitedSlots(bool allowUnlimitedSlots) = 0;
+#endif
+
     // setMaxBufferCount sets the maximum value for the number of buffers used in the BufferQueue
     // (the initial default is NUM_BUFFER_SLOTS). If a call to setMaxAcquiredBufferCount (by the
     // consumer), or a call to setAsyncMode or setMaxDequeuedBufferCount (by the producer), would
@@ -207,6 +243,9 @@
     // maxAcquiredBuffers must be (inclusive) between 1 and MAX_MAX_ACQUIRED_BUFFERS. It also cannot
     // cause the maxBufferCount value to be exceeded.
     //
+    // If called with onBuffersReleasedCallback, that call back will be called in lieu of
+    // IConsumerListener::onBuffersReleased.
+    //
     // Return of a value other than NO_ERROR means an error has occurred:
     // * NO_INIT - the BufferQueue has been abandoned
     // * BAD_VALUE - one of the below conditions occurred:
@@ -217,6 +256,11 @@
     // * INVALID_OPERATION - attempting to call this after a producer connected.
     virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0;
 
+    using OnBufferReleasedCallback = std::function<void(void)>;
+    virtual status_t setMaxAcquiredBufferCount(
+            int maxAcquiredBuffers,
+            std::optional<OnBufferReleasedCallback> onBuffersReleasedCallback) = 0;
+
     // setConsumerName sets the name used in logging
     virtual status_t setConsumerName(const String8& name) = 0;
 
@@ -279,18 +323,4 @@
     }
 };
 
-#ifndef NO_BINDER
-class BnGraphicBufferConsumer : public SafeBnInterface<IGraphicBufferConsumer> {
-public:
-    BnGraphicBufferConsumer()
-          : SafeBnInterface<IGraphicBufferConsumer>("BnGraphicBufferConsumer") {}
-
-    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-                        uint32_t flags = 0) override;
-};
-#else
-class BnGraphicBufferConsumer : public IGraphicBufferConsumer {
-};
-#endif
-
 } // namespace android
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index a42ddc4..7accca6 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -72,6 +72,14 @@
  * dequeueBuffer() to get an empty buffer, fills it with data, then
  * calls queueBuffer() to make it available to the consumer.
  *
+ * BufferQueues have a size, which we'll refer to in other comments as
+ * SLOT_COUNT. Its default is 64 (NUM_BUFFER_SLOTS). It can be adjusted by
+ * the IGraphicBufferConsumer::setMaxBufferCount, or when
+ * IGraphicBufferConsumer::allowUnlimitedSlots is set to true, by
+ * IGraphicBufferProducer::extendSlotCount. The actual number of buffers in use
+ * is a function of various configurations, including whether we're in single
+ * buffer mode, the maximum dequeuable/aquirable buffers, and SLOT_COUNT.
+ *
  * This class was previously called ISurfaceTexture.
  */
 #ifndef NO_BINDER
@@ -106,7 +114,7 @@
     // slot->buffer mapping so that it's not necessary to transfer a
     // GraphicBuffer for every dequeue operation.
     //
-    // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
+    // The slot must be in the range of [0, SLOT_COUNT).
     //
     // Return of a value other than NO_ERROR means an error has occurred:
     // * NO_INIT - the buffer queue has been abandoned or the producer is not
@@ -116,6 +124,30 @@
     //              * buffer specified by the slot is not dequeued
     virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) = 0;
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    // extendSlotCount sets the maximum slot count (SLOT_COUNT) to the given
+    //  size. This feature must be enabled by the consumer to function via
+    // IGraphicBufferConsumer::allowUnlimitedSlots. This must be called before
+    // the producer connects.
+    //
+    // After calling this, any slot can be returned in the [0, size) range.
+    // Callers are responsible for the allocation of the appropriate slots
+    // array for their own buffer cache.
+    //
+    // On success, the consumer is notified (so that it can increase its own
+    // slot cache).
+    //
+    // Return of a value other than NO_ERROR means that an error has occurred:
+    // * NO_INIT - the buffer queue has been abandoned
+    // * INVALID_OPERATION - one of the following conditions has occurred:
+    //                     *  The producer is connected already
+    //                     *  The consumer didn't call allowUnlimitedSlots
+    // * BAD_VALUE - The value is smaller than the previous max size
+    //               (initialized to 64, then whatever the last call to this
+    //               was)
+    virtual status_t extendSlotCount(int size);
+#endif
+
     // setMaxDequeuedBufferCount sets the maximum number of buffers that can be
     // dequeued by the producer at one time. If this method succeeds, any new
     // buffer slots will be both unallocated and owned by the BufferQueue object
@@ -129,7 +161,7 @@
     // will result in a BAD_VALUE error.
     //
     // The buffer count should be at least 1 (inclusive), but at most
-    // (NUM_BUFFER_SLOTS - the minimum undequeued buffer count) (exclusive). The
+    // (SLOT_COUNT - the minimum undequeued buffer count) (exclusive). The
     // minimum undequeued buffer count can be obtained by calling
     // query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS).
     //
@@ -239,8 +271,8 @@
     // * NO_INIT - the buffer queue has been abandoned or the producer is not
     //             connected.
     // * BAD_VALUE - the given slot number is invalid, either because it is
-    //               out of the range [0, NUM_BUFFER_SLOTS), or because the slot
-    //               it refers to is not currently dequeued and requested.
+    //               out of the range [0, SLOT_COUNT), or because the slot it
+    //               refers to is not currently dequeued and requested.
     virtual status_t detachBuffer(int slot) = 0;
 
     // detachNextBuffer is equivalent to calling dequeueBuffer, requestBuffer,
@@ -415,6 +447,7 @@
         FrameEventHistoryDelta frameTimestamps;
         bool bufferReplaced{false};
         int maxBufferCount{BufferQueueDefs::NUM_BUFFER_SLOTS};
+        bool isSlotExpansionAllowed{false};
         status_t result{NO_ERROR};
     };
 
@@ -430,7 +463,7 @@
     // below). Any other properties (zero point, etc)
     // are client-dependent, and should be documented by the client.
     //
-    // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
+    // The slot must be in the range of [0, SLOT_COUNT).
     //
     // Upon success, the output will be filled with meaningful values
     // (refer to the documentation below).
@@ -460,7 +493,7 @@
     //
     // The buffer is not queued for use by the consumer.
     //
-    // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
+    // The slot must be in the range of [0, SLOT_COUNT).
     //
     // The buffer will not be overwritten until the fence signals.  The fence
     // will usually be the one obtained from dequeueBuffer.
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 9a422fd..de553ae 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -65,6 +65,7 @@
 struct InputWindowCommands;
 class HdrCapabilities;
 class Rect;
+class TransactionState;
 
 using gui::FrameTimelineInfo;
 using gui::IDisplayEventConnection;
@@ -105,13 +106,7 @@
     };
 
     /* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
-    virtual status_t setTransactionState(
-            const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
-            Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
-            InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
-            bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffer,
-            bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
-            uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) = 0;
+    virtual status_t setTransactionState(TransactionState&& state) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/libs/gui/include/gui/InputTransferToken.h b/libs/gui/include/gui/InputTransferToken.h
index 6530b50..b83f245 100644
--- a/libs/gui/include/gui/InputTransferToken.h
+++ b/libs/gui/include/gui/InputTransferToken.h
@@ -25,7 +25,7 @@
 namespace android {
 struct InputTransferToken : public RefBase, Parcelable {
 public:
-    InputTransferToken() { mToken = new BBinder(); }
+    InputTransferToken() { mToken = sp<BBinder>::make(); }
 
     InputTransferToken(const sp<IBinder>& token) { mToken = token; }
 
@@ -39,15 +39,9 @@
         return NO_ERROR;
     };
 
+    bool operator==(const InputTransferToken& other) const { return mToken == other.mToken; }
+
     sp<IBinder> mToken;
 };
 
-static inline bool operator==(const sp<InputTransferToken>& token1,
-                              const sp<InputTransferToken>& token2) {
-    if (token1.get() == token2.get()) {
-        return true;
-    }
-    return token1->mToken == token2->mToken;
-}
-
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index 7ee291d..6381db2 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -44,6 +44,10 @@
     LayerMetadata& operator=(const LayerMetadata& other);
     LayerMetadata& operator=(LayerMetadata&& other);
 
+    // Note: `default` is not feasible because Parcelable does not provide ==.
+    bool operator==(const LayerMetadata& rhs) const { return mMap == rhs.mMap; }
+    bool operator!=(const LayerMetadata&) const = default;
+
     // Merges other into this LayerMetadata. If eraseEmpty is true, any entries in
     // in this whose keys are paired with empty values in other will be erased.
     bool merge(const LayerMetadata& other, bool eraseEmpty = false);
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 1c31e46..e2d27ac 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -17,10 +17,11 @@
 #ifndef ANDROID_SF_LAYER_STATE_H
 #define ANDROID_SF_LAYER_STATE_H
 
-
 #include <stdint.h>
 #include <sys/types.h>
+#include <span>
 
+#include <android/gui/BorderSettings.h>
 #include <android/gui/DisplayCaptureArgs.h>
 #include <android/gui/IWindowInfosReportedListener.h>
 #include <android/gui/LayerCaptureArgs.h>
@@ -69,21 +70,39 @@
     uint64_t id;
 
     bool operator==(const client_cache_t& other) const { return id == other.id; }
+    bool operator!=(const client_cache_t&) const = default;
 
     bool isValid() const { return token != nullptr; }
 };
 
 class TrustedPresentationListener : public Parcelable {
 public:
-    sp<ITransactionCompletedListener> callbackInterface;
-    int callbackId = -1;
+    struct State {
+        sp<ITransactionCompletedListener> callbackInterface;
+        int callbackId = -1;
+        bool operator==(const State&) const = default;
+        bool operator!=(const State&) const = default;
+    };
 
     void invoke(bool presentedWithinThresholds) {
-        callbackInterface->onTrustedPresentationChanged(callbackId, presentedWithinThresholds);
+        mState.callbackInterface->onTrustedPresentationChanged(mState.callbackId,
+                                                               presentedWithinThresholds);
+    }
+    void configure(State&& state) { mState = std::move(state); }
+    const sp<ITransactionCompletedListener>& getCallback() { return mState.callbackInterface; }
+    void clear() {
+        mState.callbackInterface = nullptr;
+        mState.callbackId = -1;
     }
 
     status_t writeToParcel(Parcel* parcel) const;
     status_t readFromParcel(const Parcel* parcel);
+
+    bool operator==(const TrustedPresentationListener& rhs) const { return mState == rhs.mState; }
+    bool operator!=(const TrustedPresentationListener&) const = default;
+
+private:
+    State mState;
 };
 
 class BufferData : public Parcelable {
@@ -231,6 +250,8 @@
         eBufferReleaseChannelChanged = 0x40000'00000000,
         ePictureProfileHandleChanged = 0x80000'00000000,
         eAppContentPriorityChanged = 0x100000'00000000,
+        eClientDrawnCornerRadiusChanged = 0x200000'00000000,
+        eBorderSettingsChanged = 0x400000'00000000,
     };
 
     layer_state_t();
@@ -251,9 +272,9 @@
     // Geometry updates.
     static constexpr uint64_t GEOMETRY_CHANGES = layer_state_t::eBufferCropChanged |
             layer_state_t::eBufferTransformChanged | layer_state_t::eCornerRadiusChanged |
-            layer_state_t::eCropChanged | layer_state_t::eDestinationFrameChanged |
-            layer_state_t::eMatrixChanged | layer_state_t::ePositionChanged |
-            layer_state_t::eTransformToDisplayInverseChanged |
+            layer_state_t::eClientDrawnCornerRadiusChanged | layer_state_t::eCropChanged |
+            layer_state_t::eDestinationFrameChanged | layer_state_t::eMatrixChanged |
+            layer_state_t::ePositionChanged | layer_state_t::eTransformToDisplayInverseChanged |
             layer_state_t::eTransparentRegionChanged | layer_state_t::eEdgeExtensionChanged;
 
     // Buffer and related updates.
@@ -275,7 +296,7 @@
             layer_state_t::eCornerRadiusChanged | layer_state_t::eDimmingEnabledChanged |
             layer_state_t::eHdrMetadataChanged | layer_state_t::eShadowRadiusChanged |
             layer_state_t::eStretchChanged | layer_state_t::ePictureProfileHandleChanged |
-            layer_state_t::eAppContentPriorityChanged;
+            layer_state_t::eAppContentPriorityChanged | layer_state_t::eBorderSettingsChanged;
 
     // Changes which invalidates the layer's visible region in CE.
     static constexpr uint64_t CONTENT_DIRTY = layer_state_t::CONTENT_CHANGES |
@@ -300,9 +321,41 @@
     static constexpr uint64_t VISIBLE_REGION_CHANGES = layer_state_t::GEOMETRY_CHANGES |
             layer_state_t::HIERARCHY_CHANGES | layer_state_t::eAlphaChanged;
 
+    // Changes that force GPU composition.
+    static constexpr uint64_t COMPOSITION_EFFECTS = layer_state_t::eBackgroundBlurRadiusChanged |
+            layer_state_t::eBlurRegionsChanged | layer_state_t::eCornerRadiusChanged |
+            layer_state_t::eShadowRadiusChanged | layer_state_t::eStretchChanged |
+            layer_state_t::eBorderSettingsChanged;
+
     bool hasValidBuffer() const;
     void sanitize(int32_t permissions);
 
+    void updateTransparentRegion(const Region& transparentRegion);
+    const Region& getTransparentRegion() const { return mNotDefCmpState.transparentRegion; }
+    void updateSurfaceDamageRegion(const Region& surfaceDamageRegion);
+    const Region& getSurfaceDamageRegion() const { return mNotDefCmpState.surfaceDamageRegion; }
+    // Do not update state flags.  Used to set up test state.
+    void setSurfaceDamageRegion(Region&& surfaceDamageRegion) {
+        mNotDefCmpState.surfaceDamageRegion = std::move(surfaceDamageRegion);
+    }
+    void updateRelativeLayer(const sp<SurfaceControl>& relativeTo, int32_t z);
+    void updateParentLayer(const sp<SurfaceControl>& newParent);
+    void updateInputWindowInfo(sp<gui::WindowInfoHandle>&& info);
+    const gui::WindowInfo& getWindowInfo() const {
+        return *mNotDefCmpState.windowInfoHandle->getInfo();
+    }
+    gui::WindowInfo* editWindowInfo() { return mNotDefCmpState.windowInfoHandle->editInfo(); }
+
+    const sp<SurfaceControl>& getParentSurfaceControlForChild() const {
+        return mNotDefCmpState.parentSurfaceControlForChild;
+    }
+    const sp<SurfaceControl>& getRelativeLayerSurfaceControl() const {
+        return mNotDefCmpState.relativeLayerSurfaceControl;
+    }
+
+    bool operator==(const layer_state_t&) const = default;
+    bool operator!=(const layer_state_t&) const = default;
+
     struct matrix22_t {
         float dsdx{0};
         float dtdx{0};
@@ -328,30 +381,23 @@
     uint8_t reserved;
     matrix22_t matrix;
     float cornerRadius;
+    float clientDrawnCornerRadius;
     uint32_t backgroundBlurRadius;
 
-    sp<SurfaceControl> relativeLayerSurfaceControl;
-
-    sp<SurfaceControl> parentSurfaceControlForChild;
-
     half4 color;
 
     // non POD must be last. see write/read
-    Region transparentRegion;
     uint32_t bufferTransform;
     bool transformToDisplayInverse;
     FloatRect crop;
     std::shared_ptr<BufferData> bufferData = nullptr;
     ui::Dataspace dataspace;
     HdrMetadata hdrMetadata;
-    Region surfaceDamageRegion;
     int32_t api;
     sp<NativeHandle> sidebandStream;
     mat4 colorTransform;
     std::vector<BlurRegion> blurRegions;
 
-    sp<gui::WindowInfoHandle> windowInfoHandle = sp<gui::WindowInfoHandle>::make();
-
     LayerMetadata metadata;
 
     // The following refer to the alpha, and dataspace, respectively of
@@ -368,6 +414,9 @@
     // Draws a shadow around the surface.
     float shadowRadius;
 
+    // Draws an outline around the layer.
+    gui::BorderSettings borderSettings;
+
     // Priority of the layer assigned by Window Manager.
     int32_t frameRateSelectionPriority;
 
@@ -425,7 +474,7 @@
     PictureProfileHandle pictureProfileHandle{PictureProfileHandle::NONE};
 
     // A value indicating the significance of the layer's content to the app's desired user
-    // experience. A lower priority will result in more likelihood of getting access to limited
+    // experience. A higher value will result in more likelihood of getting access to limited
     // resources, such as picture processing hardware.
     int32_t appContentPriority = 0;
 
@@ -437,6 +486,18 @@
     std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel;
 
     std::shared_ptr<gui::DisplayLuts> luts;
+
+protected:
+    struct NotDefaultComparableState {
+        Region transparentRegion;
+        Region surfaceDamageRegion;
+        sp<gui::WindowInfoHandle> windowInfoHandle = sp<gui::WindowInfoHandle>::make();
+        sp<SurfaceControl> relativeLayerSurfaceControl;
+        sp<SurfaceControl> parentSurfaceControlForChild;
+
+        bool operator==(const NotDefaultComparableState& rhs) const;
+        bool operator!=(const NotDefaultComparableState& rhs) const = default;
+    } mNotDefCmpState;
 };
 
 class ComposerState {
@@ -444,6 +505,9 @@
     layer_state_t state;
     status_t write(Parcel& output) const;
     status_t read(const Parcel& input);
+
+    bool operator==(const ComposerState&) const = default;
+    bool operator!=(const ComposerState&) const = default;
 };
 
 struct DisplayState {
@@ -495,28 +559,49 @@
     Rect layerStackSpaceRect = Rect::EMPTY_RECT;
     Rect orientedDisplaySpaceRect = Rect::EMPTY_RECT;
 
-    // Exclusive to virtual displays: The sink surface into which the virtual display is rendered,
-    // and an optional resolution that overrides its default dimensions.
-    sp<IGraphicBufferProducer> surface;
+    // For physical displays, this is the resolution, which must match the active display mode. To
+    // change the resolution, the client must first call SurfaceControl.setDesiredDisplayModeSpecs
+    // with the new DesiredDisplayModeSpecs#defaultMode, then commit the matching width and height.
+    //
+    // For virtual displays, this is an optional resolution that overrides its default dimensions.
+    //
     uint32_t width = 0;
     uint32_t height = 0;
 
+    // For virtual displays, this is the sink surface into which the virtual display is rendered.
+    sp<IGraphicBufferProducer> surface;
+
     status_t write(Parcel& output) const;
     status_t read(const Parcel& input);
+
+    bool operator==(const DisplayState&) const = default;
+    bool operator!=(const DisplayState&) const = default;
 };
 
 struct InputWindowCommands {
-    std::vector<gui::FocusRequest> focusRequests;
-    std::unordered_set<sp<gui::IWindowInfosReportedListener>,
-                       SpHash<gui::IWindowInfosReportedListener>>
-            windowInfosReportedListeners;
-
+    using Listener = gui::IWindowInfosReportedListener;
+    using ListenerSet = std::unordered_set<sp<Listener>, SpHash<Listener>>;
     // Merges the passed in commands and returns true if there were any changes.
     bool merge(const InputWindowCommands& other);
     bool empty() const;
     void clear();
+    void addFocusRequest(const gui::FocusRequest& request) { focusRequests.push_back(request); }
+    void addWindowInfosReportedListener(const sp<Listener>& listener) {
+        windowInfosReportedListeners.insert(listener);
+    }
+    ListenerSet&& releaseListeners() { return std::move(windowInfosReportedListeners); }
+
     status_t write(Parcel& output) const;
     status_t read(const Parcel& input);
+
+    std::span<const gui::FocusRequest> getFocusRequests() const { return focusRequests; }
+    const ListenerSet& getListeners() const { return windowInfosReportedListeners; }
+    bool operator==(const InputWindowCommands&) const = default;
+    bool operator!=(const InputWindowCommands&) const = default;
+
+private:
+    std::vector<gui::FocusRequest> focusRequests;
+    ListenerSet windowInfosReportedListeners;
 };
 
 static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) {
diff --git a/libs/gui/include/gui/StreamSplitter.h b/libs/gui/include/gui/StreamSplitter.h
index b4eef29..8176f75 100644
--- a/libs/gui/include/gui/StreamSplitter.h
+++ b/libs/gui/include/gui/StreamSplitter.h
@@ -37,7 +37,7 @@
 // BufferQueue, where each buffer queued to the input is available to be
 // acquired by each of the outputs, and is able to be dequeued by the input
 // again only once all of the outputs have released it.
-class StreamSplitter : public BnConsumerListener {
+class StreamSplitter : public IConsumerListener {
 public:
     // createSplitter creates a new splitter, outSplitter, using inputQueue as
     // the input BufferQueue. Output BufferQueues must be added using addOutput
@@ -153,6 +153,8 @@
         size_t mReleaseCount;
     };
 
+    friend class sp<StreamSplitter>;
+
     // Only called from createSplitter
     explicit StreamSplitter(const sp<IGraphicBufferConsumer>& inputQueue);
 
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 14a3513..3cfbed1 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -442,6 +442,9 @@
     status_t detachBuffer(const sp<GraphicBuffer>& buffer);
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
 
+    // Sets outIsOwned to true if the given buffer is currently known to be owned by this Surface.
+    status_t isBufferOwned(const sp<GraphicBuffer>& buffer, bool* outIsOwned) const;
+
     // Batch version of dequeueBuffer, cancelBuffer and queueBuffer
     // Note that these batched operations are not supported when shared buffer mode is being used.
     struct BatchBuffer {
@@ -558,7 +561,11 @@
     // slot that has not yet been used. The buffer allocated to a slot will also
     // be replaced if the requested buffer usage or geometry differs from that
     // of the buffer allocated to a slot.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    std::vector<BufferSlot> mSlots;
+#else
     BufferSlot mSlots[NUM_BUFFER_SLOTS];
+#endif
 
     // mReqWidth is the buffer width that will be requested at the next dequeue
     // operation. It is initialized to 1.
@@ -732,6 +739,10 @@
     std::vector<sp<GraphicBuffer>> mRemovedBuffers;
     int mMaxBufferCount;
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+    bool mIsSlotExpansionAllowed;
+#endif
+
     sp<IProducerListener> mListenerProxy;
 
     // Get and flush the buffers of given slots, if the buffer in the slot
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 0ce0c0a..5c348cb 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -52,6 +52,7 @@
 #include <gui/ITransactionCompletedListener.h>
 #include <gui/LayerState.h>
 #include <gui/SurfaceControl.h>
+#include <gui/TransactionState.h>
 #include <gui/WindowInfosListenerReporter.h>
 #include <math/vec3.h>
 
@@ -298,7 +299,9 @@
     static status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken,
                                                const sp<gui::IHdrLayerInfoListener>& listener);
 
-    static status_t setActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+    static status_t addActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+
+    static status_t removeActivePictureListener(const sp<gui::IActivePictureListener>& listener);
 
     /*
      * Sends a power boost to the composer. This function is asynchronous.
@@ -343,8 +346,6 @@
     static std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>
     getDisplayDecorationSupport(const sp<IBinder>& displayToken);
 
-    static bool flagEdgeExtensionEffectUseShader();
-
     /**
      * Returns how many picture profiles are supported by the display.
      *
@@ -394,6 +395,7 @@
 
     static const std::string kEmpty;
     static sp<IBinder> createVirtualDisplay(const std::string& displayName, bool isSecure,
+                                            bool optimizeForPower = true,
                                             const std::string& uniqueId = kEmpty,
                                             float requestedRefreshRate = 0);
 
@@ -441,64 +443,16 @@
         virtual ~PresentationCallbackRAII();
     };
 
-    class Transaction : public Parcelable {
+    class Transaction {
     private:
         static sp<IBinder> sApplyToken;
         static std::mutex sApplyTokenMutex;
         void releaseBufferIfOverwriting(const layer_state_t& state);
-        static void mergeFrameTimelineInfo(FrameTimelineInfo& t, const FrameTimelineInfo& other);
         // Tracks registered callbacks
         sp<TransactionCompletedListener> mTransactionCompletedListener = nullptr;
-        // Prints debug logs when enabled.
-        bool mLogCallPoints = false;
 
-    protected:
-        std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
-        SortedVector<DisplayState> mDisplayStates;
-        std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
-                mListenerCallbacks;
-        std::vector<client_cache_t> mUncacheBuffers;
+        TransactionState mState;
 
-        // We keep track of the last MAX_MERGE_HISTORY_LENGTH merged transaction ids.
-        // Ordered most recently merged to least recently merged.
-        static const size_t MAX_MERGE_HISTORY_LENGTH = 10u;
-        std::vector<uint64_t> mMergedTransactionIds;
-
-        uint64_t mId;
-
-        bool mAnimation = false;
-        bool mEarlyWakeupStart = false;
-        bool mEarlyWakeupEnd = false;
-
-        // Indicates that the Transaction may contain buffers that should be cached. The reason this
-        // is only a guess is that buffers can be removed before cache is called. This is only a
-        // hint that at some point a buffer was added to this transaction before apply was called.
-        bool mMayContainBuffer = false;
-
-        // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction
-        // to be presented. When it is not possible to present at exactly that time, it will be
-        // presented after the time has passed.
-        //
-        // If the client didn't pass a desired presentation time, mDesiredPresentTime will be
-        // populated to the time setBuffer was called, and mIsAutoTimestamp will be set to true.
-        //
-        // Desired present times that are more than 1 second in the future may be ignored.
-        // When a desired present time has already passed, the transaction will be presented as soon
-        // as possible.
-        //
-        // Transactions from the same process are presented in the same order that they are applied.
-        // The desired present time does not affect this ordering.
-        int64_t mDesiredPresentTime = 0;
-        bool mIsAutoTimestamp = true;
-
-        // The vsync id provided by Choreographer.getVsyncId and the input event id
-        FrameTimelineInfo mFrameTimelineInfo;
-
-        // If not null, transactions will be queued up using this token otherwise a common token
-        // per process will be used.
-        sp<IBinder> mApplyToken = nullptr;
-
-        InputWindowCommands mInputWindowCommands;
         int mStatus = NO_ERROR;
 
         layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
@@ -508,23 +462,29 @@
         void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
         void setReleaseBufferCallback(BufferData*, ReleaseBufferCallback);
 
+    protected:
+        // Accessed in tests.
+        explicit Transaction(Transaction const& other) = default;
+        std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
+                mListenerCallbacks;
+
     public:
         Transaction();
-        virtual ~Transaction() = default;
-        Transaction(Transaction const& other);
+        Transaction(Transaction&& other);
+        Transaction& operator=(Transaction&& other) = default;
 
         // Factory method that creates a new Transaction instance from the parcel.
         static std::unique_ptr<Transaction> createFromParcel(const Parcel* parcel);
 
-        status_t writeToParcel(Parcel* parcel) const override;
-        status_t readFromParcel(const Parcel* parcel) override;
+        status_t writeToParcel(Parcel* parcel) const;
+        status_t readFromParcel(const Parcel* parcel);
 
         // Clears the contents of the transaction without applying it.
         void clear();
 
         // Returns the current id of the transaction.
         // The id is updated every time the transaction is applied.
-        uint64_t getId();
+        uint64_t getId() const;
 
         std::vector<uint64_t> getMergedTransactionIds();
 
@@ -565,6 +525,11 @@
         Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
         Transaction& setCrop(const sp<SurfaceControl>& sc, const FloatRect& crop);
         Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
+        // Sets the client drawn corner radius for the layer. If both a corner radius and a client
+        // radius are sent to SF, the client radius will be used. This indicates that the corner
+        // radius is drawn by the client and not SurfaceFlinger.
+        Transaction& setClientDrawnCornerRadius(const sp<SurfaceControl>& sc,
+                                                float clientDrawnCornerRadius);
         Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc,
                                              int backgroundBlurRadius);
         Transaction& setBlurRegions(const sp<SurfaceControl>& sc,
@@ -616,7 +581,7 @@
         Transaction& setExtendedRangeBrightness(const sp<SurfaceControl>& sc,
                                                 float currentBufferRatio, float desiredRatio);
         Transaction& setDesiredHdrHeadroom(const sp<SurfaceControl>& sc, float desiredRatio);
-        Transaction& setLuts(const sp<SurfaceControl>& sc, const base::unique_fd& lutFd,
+        Transaction& setLuts(const sp<SurfaceControl>& sc, base::unique_fd&& lutFd,
                              const std::vector<int32_t>& offsets,
                              const std::vector<int32_t>& dimensions,
                              const std::vector<int32_t>& sizes,
@@ -714,6 +679,8 @@
                 const Rect& source, const Rect& dst, int transform);
         Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius);
 
+        Transaction& setBorderSettings(const sp<SurfaceControl>& sc, gui::BorderSettings settings);
+
         Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate,
                                   int8_t compatibility, int8_t changeFrameRateStrategy);
 
diff --git a/libs/gui/include/gui/TransactionState.h b/libs/gui/include/gui/TransactionState.h
new file mode 100644
index 0000000..79124f3
--- /dev/null
+++ b/libs/gui/include/gui/TransactionState.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+#pragma once
+
+#include <android/gui/FrameTimelineInfo.h>
+#include <binder/Parcelable.h>
+#include <gui/LayerState.h>
+
+namespace android {
+
+// Class to store all the transaction data and the parcelling logic
+class TransactionState {
+public:
+    explicit TransactionState() = default;
+    TransactionState(TransactionState&& other) = default;
+    TransactionState& operator=(TransactionState&& other) = default;
+    status_t writeToParcel(Parcel* parcel) const;
+    status_t readFromParcel(const Parcel* parcel);
+    layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
+    DisplayState& getDisplayState(const sp<IBinder>& token);
+
+    // Returns the current id of the transaction.
+    // The id is updated every time the transaction is applied.
+    uint64_t getId() const { return mId; }
+    std::vector<uint64_t> getMergedTransactionIds() const { return mMergedTransactionIds; }
+    void enableDebugLogCallPoints() { mLogCallPoints = true; }
+    void merge(TransactionState&& other,
+               const std::function<void(layer_state_t&)>& onBufferOverwrite);
+
+    // copied from FrameTimelineInfo::merge()
+    void mergeFrameTimelineInfo(const FrameTimelineInfo& other);
+    void clear();
+    bool operator==(const TransactionState& rhs) const = default;
+    bool operator!=(const TransactionState& rhs) const = default;
+
+    uint64_t mId = 0;
+    std::vector<uint64_t> mMergedTransactionIds;
+    uint32_t mFlags = 0;
+    // The vsync id provided by Choreographer.getVsyncId and the input event id
+    gui::FrameTimelineInfo mFrameTimelineInfo;
+    // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction
+    // to be presented. When it is not possible to present at exactly that time, it will be
+    // presented after the time has passed.
+    //
+    // If the client didn't pass a desired presentation time, mDesiredPresentTime will be
+    // populated to the time setBuffer was called, and mIsAutoTimestamp will be set to true.
+    //
+    // Desired present times that are more than 1 second in the future may be ignored.
+    // When a desired present time has already passed, the transaction will be presented as soon
+    // as possible.
+    //
+    // Transactions from the same process are presented in the same order that they are applied.
+    // The desired present time does not affect this ordering.
+    int64_t mDesiredPresentTime = 0;
+    bool mIsAutoTimestamp = true;
+    // If not null, transactions will be queued up using this token otherwise a common token
+    // per process will be used.
+    sp<IBinder> mApplyToken;
+    // Indicates that the Transaction may contain buffers that should be cached. The reason this
+    // is only a guess is that buffers can be removed before cache is called. This is only a
+    // hint that at some point a buffer was added to this transaction before apply was called.
+    bool mMayContainBuffer = false;
+    // Prints debug logs when enabled.
+    bool mLogCallPoints = false;
+
+    std::vector<DisplayState> mDisplayStates;
+    std::vector<ComposerState> mComposerStates;
+    InputWindowCommands mInputWindowCommands;
+    std::vector<client_cache_t> mUncacheBuffers;
+    // Note: mHasListenerCallbacks can be true even if mListenerCallbacks is
+    // empty.
+    bool mHasListenerCallbacks = false;
+    std::vector<ListenerCallbacks> mListenerCallbacks;
+
+private:
+    explicit TransactionState(TransactionState const& other) = default;
+    friend class TransactionApplicationTest;
+    friend class SurfaceComposerClient;
+    // We keep track of the last MAX_MERGE_HISTORY_LENGTH merged transaction ids.
+    // Ordered most recently merged to least recently merged.
+    static constexpr size_t MAX_MERGE_HISTORY_LENGTH = 10u;
+};
+
+}; // namespace android
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index eb3be55..9ac49c0 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -150,8 +150,6 @@
                 static_cast<uint32_t>(os::InputConfig::NOT_FOCUSABLE),
         NOT_TOUCHABLE =
                 static_cast<uint32_t>(os::InputConfig::NOT_TOUCHABLE),
-        PREVENT_SPLITTING =
-                static_cast<uint32_t>(os::InputConfig::PREVENT_SPLITTING),
         DUPLICATE_TOUCH_TO_WALLPAPER =
                 static_cast<uint32_t>(os::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER),
         IS_WALLPAPER =
@@ -220,9 +218,14 @@
     // An alpha of 1.0 means fully opaque and 0.0 means fully transparent.
     float alpha;
 
-    // Transform applied to individual windows.
+    // Transform applied to individual windows for input.
+    // Maps display coordinates to the window's input coordinate space.
     ui::Transform transform;
 
+    // Transform applied to get to the layer stack space of the cloned window for input.
+    // Maps display coordinates of the clone window to the layer stack space of the cloned window.
+    std::optional<ui::Transform> cloneLayerStackTransform;
+
     /*
      * This is filled in by the WM relative to the frame and then translated
      * to absolute coordinates by SurfaceFlinger once the frame is computed.
@@ -265,6 +268,7 @@
     bool overlaps(const WindowInfo* other) const;
 
     bool operator==(const WindowInfo& inputChannel) const;
+    bool operator!=(const WindowInfo&) const = default;
 
     status_t writeToParcel(android::Parcel* parcel) const override;
 
@@ -316,6 +320,9 @@
     status_t readFromParcel(const android::Parcel* parcel);
     status_t writeToParcel(android::Parcel* parcel) const;
 
+    bool operator==(const WindowInfoHandle& rhs) const { return mInfo == rhs.mInfo; }
+    bool operator!=(const WindowInfoHandle&) const = default;
+
 protected:
     virtual ~WindowInfoHandle();
 
diff --git a/libs/gui/include/gui/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h
index 684e21a..f9a3ace 100644
--- a/libs/gui/include/gui/WindowInfosListenerReporter.h
+++ b/libs/gui/include/gui/WindowInfosListenerReporter.h
@@ -40,6 +40,9 @@
     void reconnect(const sp<gui::ISurfaceComposer>&);
 
 private:
+    WindowInfosListenerReporter() = default;
+    friend class sp<WindowInfosListenerReporter>;
+
     std::mutex mListenersMutex;
     std::unordered_set<sp<gui::WindowInfosListener>, gui::SpHash<gui::WindowInfosListener>>
             mWindowInfosListeners GUARDED_BY(mListenersMutex);
diff --git a/libs/gui/include/gui/mock/GraphicBufferConsumer.h b/libs/gui/include/gui/mock/GraphicBufferConsumer.h
index 98f24c2..18a7e12 100644
--- a/libs/gui/include/gui/mock/GraphicBufferConsumer.h
+++ b/libs/gui/include/gui/mock/GraphicBufferConsumer.h
@@ -18,6 +18,7 @@
 
 #include <gmock/gmock.h>
 
+#include <com_android_graphics_libgui_flags.h>
 #include <gui/IGraphicBufferConsumer.h>
 
 #include <utils/RefBase.h>
@@ -25,21 +26,28 @@
 namespace android {
 namespace mock {
 
-class GraphicBufferConsumer : public BnGraphicBufferConsumer, public virtual android::RefBase {
+class GraphicBufferConsumer : public IGraphicBufferConsumer {
 public:
     GraphicBufferConsumer();
-    ~GraphicBufferConsumer() override;
+    ~GraphicBufferConsumer();
 
     MOCK_METHOD3(acquireBuffer, status_t(BufferItem*, nsecs_t, uint64_t));
     MOCK_METHOD1(detachBuffer, status_t(int));
     MOCK_METHOD2(attachBuffer, status_t(int*, const sp<GraphicBuffer>&));
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_GL_FENCE_CLEANUP)
+    MOCK_METHOD3(releaseBuffer, status_t(int, uint64_t, const sp<Fence>&));
+#else
     MOCK_METHOD5(releaseBuffer, status_t(int, uint64_t, EGLDisplay, EGLSyncKHR, const sp<Fence>&));
+#endif
     MOCK_METHOD2(consumerConnect, status_t(const sp<IConsumerListener>&, bool));
     MOCK_METHOD0(consumerDisconnect, status_t());
+    MOCK_METHOD1(allowUnlimitedSlots, status_t(bool));
     MOCK_METHOD1(getReleasedBuffers, status_t(uint64_t*));
+    MOCK_METHOD1(getReleasedBuffersExtended, status_t(std::vector<bool>*));
     MOCK_METHOD2(setDefaultBufferSize, status_t(uint32_t, uint32_t));
     MOCK_METHOD1(setMaxBufferCount, status_t(int));
     MOCK_METHOD1(setMaxAcquiredBufferCount, status_t(int));
+    MOCK_METHOD2(setMaxAcquiredBufferCount, status_t(int, std::optional<OnBufferReleasedCallback>));
     MOCK_METHOD1(setConsumerName, status_t(const String8&));
     MOCK_METHOD1(setDefaultBufferFormat, status_t(PixelFormat));
     MOCK_METHOD1(setDefaultBufferDataSpace, status_t(android_dataspace));
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index 6bf38c0..ce1bc95 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -77,14 +77,6 @@
 } # wb_stream_splitter
 
 flag {
-  name: "edge_extension_shader"
-  namespace: "windowing_frontend"
-  description: "Enable edge extension via shader"
-  bug: "322036393"
-  is_fixed_read_only: true
-} # edge_extension_shader
-
-flag {
   name: "buffer_release_channel"
   namespace: "window_surfaces"
   description: "Enable BufferReleaseChannel to optimize buffer releases"
@@ -139,3 +131,33 @@
   bug: "339705065"
   is_fixed_read_only: true
 } # bq_gl_fence_cleanup
+
+flag {
+  name: "wb_media_migration"
+  namespace: "core_graphics"
+  description: "Main flag for the warren buffers media migration."
+  bug: "340934031"
+  is_fixed_read_only: true
+} # wb_media_migration
+
+flag {
+  name: "allocate_buffer_priority"
+  namespace: "wear_system_health"
+  description: "Boost priority for buffer allocation"
+  bug: "399701430"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+  is_fixed_read_only: true
+} # allocate_buffer_priority
+
+flag {
+  name: "bq_always_use_max_dequeued_buffer_count"
+  namespace: "core_graphics"
+  description: "BufferQueueProducer::dequeue's respects setMaxDequeuedBufferCount even before a buffer is dequeued."
+  bug: "399328309"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+  is_fixed_read_only: true
+} # bq_always_use_max_dequeued_buffer_count
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index f07747f..bd53031 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -51,10 +51,6 @@
         "-Werror",
         "-Wextra",
         "-Wthread-safety",
-        "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_SETFRAMERATE=true",
-        "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_EXTENDEDALLOCATE=true",
-        "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_CONSUMER_BASE_OWNS_BQ=true",
-        "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_PLATFORM_API_IMPROVEMENTS=true",
     ],
 
     srcs: [
@@ -89,6 +85,7 @@
         "testserver/TestServerClient.cpp",
         "testserver/TestServerHost.cpp",
         "TextureRenderer.cpp",
+        "TransactionState_test.cpp",
         "VsyncEventData_test.cpp",
         "WindowInfo_test.cpp",
     ],
@@ -98,6 +95,7 @@
         "android.hardware.configstore-utils",
         "libSurfaceFlingerProp",
         "libGLESv1_CM",
+        "libgui",
         "libgui_test_server_aidl-cpp",
         "libinput",
         "libnativedisplay",
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 53f4a36..4e4c8a2 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -81,7 +81,9 @@
 public:
     TestBLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width,
                          int height, int32_t format)
-          : BLASTBufferQueue(name, surface, width, height, format) {}
+          : BLASTBufferQueue(name) {
+        update(surface, width, height, format);
+    }
 
     void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
                              const std::vector<SurfaceControlStats>& stats) override {
@@ -112,8 +114,8 @@
 class BLASTBufferQueueHelper {
 public:
     BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) {
-        mBlastBufferQueueAdapter = new TestBLASTBufferQueue("TestBLASTBufferQueue", sc, width,
-                                                            height, PIXEL_FORMAT_RGBA_8888);
+        mBlastBufferQueueAdapter = sp<TestBLASTBufferQueue>::make("TestBLASTBufferQueue", sc, width,
+                                                                  height, PIXEL_FORMAT_RGBA_8888);
     }
 
     void update(const sp<SurfaceControl>& sc, int width, int height) {
@@ -199,7 +201,7 @@
 protected:
     void SetUp() {
         mComposer = ComposerService::getComposerService();
-        mClient = new SurfaceComposerClient();
+        mClient = sp<SurfaceComposerClient>::make();
         const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
         ASSERT_FALSE(ids.empty());
         // display 0 is picked as this test is not much display depedent
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
index 3b6a66e..80eea26 100644
--- a/libs/gui/tests/BufferItemConsumer_test.cpp
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -22,7 +22,11 @@
 #include <gui/BufferItemConsumer.h>
 #include <gui/IProducerListener.h>
 #include <gui/Surface.h>
+#include <ui/BufferQueueDefs.h>
 #include <ui/GraphicBuffer.h>
+#include <utils/Errors.h>
+
+#include <unordered_set>
 
 namespace android {
 
@@ -57,14 +61,17 @@
     };
 
     void SetUp() override {
-        mBIC = new BufferItemConsumer(kUsage, kMaxLockedBuffers, true);
+        mBuffers.resize(BufferQueueDefs::NUM_BUFFER_SLOTS);
+
+        sp<Surface> surface;
+        std::tie(mBIC, surface) = BufferItemConsumer::create(kUsage, kMaxLockedBuffers, true);
         String8 name("BufferItemConsumer_Under_Test");
         mBIC->setName(name);
         mBFL = new BufferFreedListener(this);
         mBIC->setBufferFreedListener(mBFL);
 
         sp<IProducerListener> producerListener = new TrackingProducerListener(this);
-        mProducer = mBIC->getSurface()->getIGraphicBufferProducer();
+        mProducer = surface->getIGraphicBufferProducer();
         IGraphicBufferProducer::QueueBufferOutput bufferOutput;
         ASSERT_EQ(NO_ERROR,
                   mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU,
@@ -137,6 +144,11 @@
         ASSERT_EQ(NO_ERROR, ret);
     }
 
+    void DetachBuffer(int slot) {
+        ALOGD("detachBuffer: slot=%d", slot);
+        status_t ret = mBIC->detachBuffer(mBuffers[slot]);
+        ASSERT_EQ(NO_ERROR, ret);
+    }
 
     std::mutex mMutex;
     int mFreedBufferCount{0};
@@ -146,7 +158,7 @@
     sp<BufferFreedListener> mBFL;
     sp<IGraphicBufferProducer> mProducer;
     sp<IGraphicBufferConsumer> mConsumer;
-    sp<GraphicBuffer> mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
+    std::vector<sp<GraphicBuffer>> mBuffers;
 };
 
 // Test that detaching buffer from consumer side triggers onBufferFreed.
@@ -224,6 +236,38 @@
     ASSERT_EQ(1, GetFreedBufferCount());
 }
 
+TEST_F(BufferItemConsumerTest, ResizeAcquireCount) {
+    EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 1));
+    EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 2));
+    EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 1));
+    EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 2));
+    EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers + 1));
+    EXPECT_EQ(OK, mBIC->setMaxAcquiredBufferCount(kMaxLockedBuffers - 1));
+}
+
+TEST_F(BufferItemConsumerTest, AttachBuffer) {
+    ASSERT_EQ(OK, mBIC->setMaxAcquiredBufferCount(1));
+
+    int slot;
+    DequeueBuffer(&slot);
+    QueueBuffer(slot);
+    AcquireBuffer(&slot);
+
+    sp<GraphicBuffer> newBuffer1 = sp<GraphicBuffer>::make(kWidth, kHeight, kFormat, kUsage);
+    sp<GraphicBuffer> newBuffer2 = sp<GraphicBuffer>::make(kWidth, kHeight, kFormat, kUsage);
+
+    // For some reason, you can attach an extra buffer?
+    // b/400973991 to investigate
+    EXPECT_EQ(OK, mBIC->attachBuffer(newBuffer1));
+    EXPECT_EQ(INVALID_OPERATION, mBIC->attachBuffer(newBuffer2));
+
+    ReleaseBuffer(slot);
+
+    EXPECT_EQ(OK, mBIC->attachBuffer(newBuffer2));
+    EXPECT_EQ(OK, mBIC->releaseBuffer(newBuffer1, Fence::NO_FENCE));
+    EXPECT_EQ(OK, mBIC->releaseBuffer(newBuffer2, Fence::NO_FENCE));
+}
+
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
 // Test that delete BufferItemConsumer triggers onBufferFreed.
 TEST_F(BufferItemConsumerTest, DetachBufferWithBuffer) {
@@ -239,4 +283,52 @@
 }
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+TEST_F(BufferItemConsumerTest, UnlimitedSlots_AcquireReleaseAll) {
+    ASSERT_EQ(OK, mProducer->extendSlotCount(256));
+    mBuffers.resize(256);
+
+    ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(100));
+
+    std::unordered_set<int> slots;
+    for (int i = 0; i < 100; i++) {
+        int slot;
+        DequeueBuffer(&slot);
+        slots.insert(slot);
+    }
+    EXPECT_EQ(100u, slots.size());
+
+    for (int dequeuedSlot : slots) {
+        QueueBuffer(dequeuedSlot);
+
+        int slot;
+        AcquireBuffer(&slot);
+        ReleaseBuffer(slot);
+    }
+}
+
+TEST_F(BufferItemConsumerTest, UnlimitedSlots_AcquireDetachAll) {
+    ASSERT_EQ(OK, mProducer->extendSlotCount(256));
+    mBuffers.resize(256);
+
+    ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(100));
+
+    std::unordered_set<int> slots;
+    for (int i = 0; i < 100; i++) {
+        int slot;
+        DequeueBuffer(&slot);
+        slots.insert(slot);
+    }
+    EXPECT_EQ(100u, slots.size());
+
+    for (int dequeuedSlot : slots) {
+        QueueBuffer(dequeuedSlot);
+
+        int slot;
+        AcquireBuffer(&slot);
+        DetachBuffer(slot);
+    }
+}
+#endif
+
 }  // namespace android
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 1606099..e22f57e 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -20,6 +20,8 @@
 #include "Constants.h"
 #include "MockConsumer.h"
 
+#include <EGL/egl.h>
+
 #include <gui/BufferItem.h>
 #include <gui/BufferItemConsumer.h>
 #include <gui/BufferQueue.h>
@@ -30,6 +32,7 @@
 #include <ui/PictureProfileHandle.h>
 
 #include <android-base/properties.h>
+#include <android-base/unique_fd.h>
 
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
@@ -43,8 +46,11 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <csignal>
 #include <future>
+#include <optional>
 #include <thread>
+#include <unordered_map>
 
 #include <com_android_graphics_libgui_flags.h>
 
@@ -61,6 +67,15 @@
 
 public:
 protected:
+    void TearDown() override {
+        std::vector<std::function<void()>> teardownFns;
+        teardownFns.swap(mTeardownFns);
+
+        for (auto& fn : teardownFns) {
+            fn();
+        }
+    }
+
     void GetMinUndequeuedBufferCount(int* bufferCount) {
         ASSERT_TRUE(bufferCount != nullptr);
         ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
@@ -95,6 +110,7 @@
 
     sp<IGraphicBufferProducer> mProducer;
     sp<IGraphicBufferConsumer> mConsumer;
+    std::vector<std::function<void()>> mTeardownFns;
 };
 
 static const uint32_t TEST_DATA = 0x12345678u;
@@ -102,12 +118,14 @@
 // XXX: Tests that fork a process to hold the BufferQueue must run before tests
 // that use a local BufferQueue, or else Binder will get unhappy
 //
-// In one instance this was a crash in the createBufferQueue where the
-// binder call to create a buffer allocator apparently got garbage back.
-// See b/36592665.
+// TODO(b/392945118): In one instance this was a crash in the createBufferQueue
+// where the binder call to create a buffer allocator apparently got garbage
+// back. See b/36592665.
 TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) {
     const String16 PRODUCER_NAME = String16("BQTestProducer");
-    const String16 CONSUMER_NAME = String16("BQTestConsumer");
+
+    base::unique_fd readfd, writefd;
+    ASSERT_TRUE(base::Pipe(&readfd, &writefd));
 
     pid_t forkPid = fork();
     ASSERT_NE(forkPid, -1);
@@ -119,23 +137,51 @@
         BufferQueue::createBufferQueue(&producer, &consumer);
         sp<IServiceManager> serviceManager = defaultServiceManager();
         serviceManager->addService(PRODUCER_NAME, IInterface::asBinder(producer));
-        serviceManager->addService(CONSUMER_NAME, IInterface::asBinder(consumer));
+
+        class ChildConsumerListener : public IConsumerListener {
+        public:
+            ChildConsumerListener(const sp<IGraphicBufferConsumer>& consumer,
+                                  base::unique_fd&& writeFd)
+                  : mConsumer(consumer), mWriteFd(std::move(writeFd)) {}
+
+            virtual void onFrameAvailable(const BufferItem&) override {
+                BufferItem item;
+                ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+
+                uint32_t* dataOut;
+                ASSERT_EQ(OK,
+                          item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
+                                                    reinterpret_cast<void**>(&dataOut)));
+                ASSERT_EQ(*dataOut, TEST_DATA);
+                ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
+
+                bool isOk = true;
+                write(mWriteFd, &isOk, sizeof(bool));
+            }
+            virtual void onBuffersReleased() override {}
+            virtual void onSidebandStreamChanged() override {}
+
+        private:
+            sp<IGraphicBufferConsumer> mConsumer;
+            base::unique_fd mWriteFd;
+        };
+
+        sp<ChildConsumerListener> mc =
+                sp<ChildConsumerListener>::make(consumer, std::move(writefd));
+        ASSERT_EQ(OK, consumer->consumerConnect(mc, false));
+
         ProcessState::self()->startThreadPool();
         IPCThreadState::self()->joinThreadPool();
         LOG_ALWAYS_FATAL("Shouldn't be here");
+    } else {
+        mTeardownFns.emplace_back([forkPid]() { kill(forkPid, SIGTERM); });
     }
 
     sp<IServiceManager> serviceManager = defaultServiceManager();
     sp<IBinder> binderProducer = serviceManager->waitForService(PRODUCER_NAME);
     mProducer = interface_cast<IGraphicBufferProducer>(binderProducer);
     EXPECT_TRUE(mProducer != nullptr);
-    sp<IBinder> binderConsumer =
-        serviceManager->getService(CONSUMER_NAME);
-    mConsumer = interface_cast<IGraphicBufferConsumer>(binderConsumer);
-    EXPECT_TRUE(mConsumer != nullptr);
 
-    sp<MockConsumer> mc(new MockConsumer);
-    ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false));
     IGraphicBufferProducer::QueueBufferOutput output;
     ASSERT_EQ(OK,
             mProducer->connect(nullptr, NATIVE_WINDOW_API_CPU, false, &output));
@@ -159,14 +205,9 @@
             NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
     ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
 
-    BufferItem item;
-    ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
-
-    uint32_t* dataOut;
-    ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
-            reinterpret_cast<void**>(&dataOut)));
-    ASSERT_EQ(*dataOut, TEST_DATA);
-    ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
+    bool isOk;
+    read(readfd, &isOk, sizeof(bool));
+    ASSERT_TRUE(isOk);
 }
 
 TEST_F(BufferQueueTest, GetMaxBufferCountInQueueBufferOutput_Succeeds) {
@@ -1411,10 +1452,6 @@
     ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
 }
 
-TEST_F(BufferQueueTest, TestBqSetFrameRateFlagBuildTimeIsSet) {
-    ASSERT_EQ(flags::bq_setframerate(), COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE));
-}
-
 struct BufferItemConsumerSetFrameRateListener : public BufferItemConsumer {
     BufferItemConsumerSetFrameRateListener() : BufferItemConsumer(GRALLOC_USAGE_SW_READ_OFTEN, 1) {}
 
@@ -1520,9 +1557,14 @@
             {.name = "android.hardware.graphics.common.Dataspace", ADATASPACE_DISPLAY_P3},
     }};
 
-    ASSERT_EQ(NO_INIT,
-              native_window_set_buffers_additional_options(surface.get(), extras.data(),
-                                                           extras.size()));
+    auto status = native_window_set_buffers_additional_options(surface.get(), extras.data(),
+                                                               extras.size());
+    if (flags::bq_extendedallocate()) {
+        ASSERT_EQ(NO_INIT, status);
+    } else {
+        ASSERT_EQ(INVALID_OPERATION, status);
+        GTEST_SKIP() << "Flag bq_extendedallocate not enabled";
+    }
 
     if (!IsCuttlefish()) {
         GTEST_SKIP() << "Not cuttlefish";
@@ -1612,4 +1654,221 @@
     }
 }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+struct MockUnlimitedSlotConsumer : public MockConsumer {
+    virtual void onSlotCountChanged(int size) override { mSize = size; }
+
+    std::optional<int> mSize;
+};
+
+TEST_F(BufferQueueTest, UnlimitedSlots_FailsWhenNotAllowed) {
+    createBufferQueue();
+
+    sp<MockUnlimitedSlotConsumer> mc = sp<MockUnlimitedSlotConsumer>::make();
+    EXPECT_EQ(OK, mConsumer->consumerConnect(mc, false));
+
+    EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(64));
+    EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(32));
+    EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(128));
+
+    EXPECT_EQ(std::nullopt, mc->mSize);
+}
+
+TEST_F(BufferQueueTest, UnlimitedSlots_OnlyAllowedForExtensions) {
+    createBufferQueue();
+
+    sp<MockUnlimitedSlotConsumer> consumerListener = sp<MockUnlimitedSlotConsumer>::make();
+    EXPECT_EQ(OK, mConsumer->consumerConnect(consumerListener, false));
+    EXPECT_EQ(OK, mConsumer->allowUnlimitedSlots(true));
+
+    EXPECT_EQ(BAD_VALUE, mProducer->extendSlotCount(32));
+    EXPECT_EQ(OK, mProducer->extendSlotCount(64));
+    EXPECT_EQ(OK, mProducer->extendSlotCount(128));
+    EXPECT_EQ(128, *consumerListener->mSize);
+
+    EXPECT_EQ(OK, mProducer->extendSlotCount(128));
+    EXPECT_EQ(BAD_VALUE, mProducer->extendSlotCount(127));
+}
+
+class BufferQueueUnlimitedTest : public BufferQueueTest {
+protected:
+    static constexpr auto kMaxBufferCount = 128;
+    static constexpr auto kAcquirableBufferCount = 2;
+    static constexpr auto kDequeableBufferCount = kMaxBufferCount - kAcquirableBufferCount;
+
+    virtual void SetUp() override {
+        BufferQueueTest::SetUp();
+
+        createBufferQueue();
+        setUpConsumer();
+        setUpProducer();
+    }
+
+    void setUpConsumer() {
+        EXPECT_EQ(OK, mConsumer->consumerConnect(mConsumerListener, false));
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+        EXPECT_EQ(OK, mConsumer->allowUnlimitedSlots(true));
+#endif
+        EXPECT_EQ(OK, mConsumer->setConsumerUsageBits(GraphicBuffer::USAGE_SW_READ_OFTEN));
+        EXPECT_EQ(OK, mConsumer->setDefaultBufferSize(10, 10));
+        EXPECT_EQ(OK, mConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888));
+        EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(kAcquirableBufferCount));
+    }
+
+    void setUpProducer() {
+        EXPECT_EQ(OK, mProducer->extendSlotCount(kMaxBufferCount));
+
+        IGraphicBufferProducer::QueueBufferOutput output;
+        EXPECT_EQ(OK,
+                  mProducer->connect(mProducerListener, NATIVE_WINDOW_API_CPU,
+                                     /*producerControlledByApp*/ true, &output));
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+        ASSERT_TRUE(output.isSlotExpansionAllowed);
+#endif
+        ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(kDequeableBufferCount));
+        ASSERT_EQ(OK, mProducer->allowAllocation(true));
+    }
+
+    std::unordered_map<int, sp<Fence>> dequeueAll() {
+        std::unordered_map<int, sp<Fence>> slotsToFences;
+
+        for (int i = 0; i < kDequeableBufferCount; ++i) {
+            int slot;
+            sp<Fence> fence;
+            sp<GraphicBuffer> buffer;
+
+            status_t ret =
+                    mProducer->dequeueBuffer(&slot, &fence, /*w*/ 0, /*h*/ 0, /*format*/ 0,
+                                             /*uint64_t*/ 0,
+                                             /*outBufferAge*/ nullptr, /*outTimestamps*/ nullptr);
+            if (ret & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+                EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer))
+                        << "Unable to request buffer for slot " << slot;
+            }
+            EXPECT_FALSE(slotsToFences.contains(slot));
+            slotsToFences.emplace(slot, fence);
+        }
+        EXPECT_EQ(kDequeableBufferCount, (int)slotsToFences.size());
+        return slotsToFences;
+    }
+
+    sp<MockUnlimitedSlotConsumer> mConsumerListener = sp<MockUnlimitedSlotConsumer>::make();
+    sp<StubProducerListener> mProducerListener = sp<StubProducerListener>::make();
+};
+
+TEST_F(BufferQueueUnlimitedTest, ExpandOverridesConsumerMaxBuffers) {
+    createBufferQueue();
+    setUpConsumer();
+    EXPECT_EQ(OK, mConsumer->setMaxBufferCount(10));
+
+    setUpProducer();
+
+    EXPECT_EQ(kDequeableBufferCount, (int)dequeueAll().size());
+}
+
+TEST_F(BufferQueueUnlimitedTest, CanDetachAll) {
+    auto slotsToFences = dequeueAll();
+    for (auto& [slot, fence] : slotsToFences) {
+        EXPECT_EQ(OK, mProducer->detachBuffer(slot));
+    }
+}
+
+TEST_F(BufferQueueUnlimitedTest, CanCancelAll) {
+    auto slotsToFences = dequeueAll();
+    for (auto& [slot, fence] : slotsToFences) {
+        EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence));
+    }
+}
+
+TEST_F(BufferQueueUnlimitedTest, CanAcquireAndReleaseAll) {
+    auto slotsToFences = dequeueAll();
+    for (auto& [slot, fence] : slotsToFences) {
+        IGraphicBufferProducer::QueueBufferInput input;
+        input.fence = fence;
+
+        IGraphicBufferProducer::QueueBufferOutput output;
+        EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+        BufferItem buffer;
+        EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0));
+        EXPECT_EQ(OK,
+                  mConsumer->releaseBuffer(buffer.mSlot, buffer.mFrameNumber, EGL_NO_DISPLAY,
+                                           EGL_NO_SYNC, buffer.mFence));
+    }
+}
+
+TEST_F(BufferQueueUnlimitedTest, CanAcquireAndDetachAll) {
+    auto slotsToFences = dequeueAll();
+    for (auto& [slot, fence] : slotsToFences) {
+        IGraphicBufferProducer::QueueBufferInput input;
+        input.fence = fence;
+
+        IGraphicBufferProducer::QueueBufferOutput output;
+        EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+        BufferItem buffer;
+        EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0));
+        EXPECT_EQ(OK, mConsumer->detachBuffer(buffer.mSlot));
+    }
+}
+
+TEST_F(BufferQueueUnlimitedTest, GetReleasedBuffersExtended) {
+    // First, acquire and release all the buffers so the consumer "knows" about
+    // them
+    auto slotsToFences = dequeueAll();
+
+    std::vector<bool> releasedSlots;
+    EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots));
+    for (auto& [slot, _] : slotsToFences) {
+        EXPECT_TRUE(releasedSlots[slot])
+                << "Slots that haven't been acquired will show up as released.";
+    }
+    for (auto& [slot, fence] : slotsToFences) {
+        IGraphicBufferProducer::QueueBufferInput input;
+        input.fence = fence;
+
+        IGraphicBufferProducer::QueueBufferOutput output;
+        EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+        BufferItem buffer;
+        EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0));
+        EXPECT_EQ(OK,
+                  mConsumer->releaseBuffer(buffer.mSlot, buffer.mFrameNumber, EGL_NO_DISPLAY,
+                                           EGL_NO_SYNC_KHR, buffer.mFence));
+    }
+
+    EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots));
+    for (auto& [slot, _] : slotsToFences) {
+        EXPECT_FALSE(releasedSlots[slot])
+                << "Slots that have been acquired will show up as not released.";
+    }
+
+    // Then, alternatively cancel and detach (release) buffers. Only detached
+    // buffers should be returned by getReleasedBuffersExtended
+    slotsToFences = dequeueAll();
+    std::set<int> cancelledSlots;
+    std::set<int> detachedSlots;
+    bool cancel;
+    for (auto& [slot, fence] : slotsToFences) {
+        if (cancel) {
+            EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence));
+            cancelledSlots.insert(slot);
+        } else {
+            EXPECT_EQ(OK, mProducer->detachBuffer(slot));
+            detachedSlots.insert(slot);
+        }
+        cancel = !cancel;
+    }
+
+    EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots));
+    for (int slot : detachedSlots) {
+        EXPECT_TRUE(releasedSlots[slot]) << "Slots that are detached are released.";
+    }
+    for (int slot : cancelledSlots) {
+        EXPECT_FALSE(releasedSlots[slot])
+                << "Slots that are still held in the queue are not released.";
+    }
+}
+#endif //  COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
 } // namespace android
diff --git a/libs/gui/tests/Choreographer_test.cpp b/libs/gui/tests/Choreographer_test.cpp
index 8db48d2..314dea6 100644
--- a/libs/gui/tests/Choreographer_test.cpp
+++ b/libs/gui/tests/Choreographer_test.cpp
@@ -50,7 +50,7 @@
 
 TEST_F(ChoreographerTest, InputCallbackBeforeAnimation) {
     sp<Looper> looper = Looper::prepare(0);
-    Choreographer* choreographer = Choreographer::getForThread();
+    sp<Choreographer> choreographer = Choreographer::getForThread();
     VsyncCallback animationCb;
     choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &animationCb, 0,
                                             CALLBACK_ANIMATION);
@@ -83,4 +83,4 @@
                                            animationCb.frameTime.count());
 }
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index f4239cb..482cfde 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -66,10 +66,9 @@
                 test_info->name(),
                 params.width, params.height,
                 params.maxLockedBuffers, params.format);
-        mCC = new CpuConsumer(params.maxLockedBuffers);
+        std::tie(mCC, mSTC) = CpuConsumer::create(params.maxLockedBuffers);
         String8 name("CpuConsumer_Under_Test");
         mCC->setName(name);
-        mSTC = mCC->getSurface();
         mANW = mSTC;
     }
 
@@ -803,6 +802,27 @@
         ::testing::ValuesIn(rgba8888TestSets));
 #endif
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+TEST(CpuConsumerSlotTest, UnlimitedSlots_AcquireReleaseAll) {
+    sp<CpuConsumer> cpuConsumer = sp<CpuConsumer>::make(3);
+    sp<Surface> surface = cpuConsumer->getSurface();
+    sp<SurfaceListener> listener = sp<StubSurfaceListener>::make();
 
+    ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, listener));
+    ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(256));
 
+    std::vector<Surface::BatchBuffer> buffers(256);
+    EXPECT_EQ(OK, surface->dequeueBuffers(&buffers));
+
+    for (auto& buffer : buffers) {
+        sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(buffer.buffer);
+        sp<Fence> fence = sp<Fence>::make(buffer.fenceFd);
+        EXPECT_EQ(OK, surface->queueBuffer(graphicBuffer, fence));
+
+        CpuConsumer::LockedBuffer nativeBuffer;
+        EXPECT_EQ(OK, cpuConsumer->lockNextBuffer(&nativeBuffer));
+        EXPECT_EQ(OK, cpuConsumer->unlockBuffer(nativeBuffer));
+    }
+}
+#endif
 } // namespace android
diff --git a/libs/gui/tests/DisconnectWaiter.h b/libs/gui/tests/DisconnectWaiter.h
index 6e6915b..3d5f633 100644
--- a/libs/gui/tests/DisconnectWaiter.h
+++ b/libs/gui/tests/DisconnectWaiter.h
@@ -29,7 +29,7 @@
 // no way to forward the events.  This DisconnectWaiter will not let the
 // disconnect finish until finishDisconnect() is called.  It will
 // also block until a disconnect is called
-class DisconnectWaiter : public BnConsumerListener {
+class DisconnectWaiter : public IConsumerListener {
 public:
     DisconnectWaiter () :
         mWaitForDisconnect(false),
diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp
index bffb3f0..62d73ca 100644
--- a/libs/gui/tests/DisplayedContentSampling_test.cpp
+++ b/libs/gui/tests/DisplayedContentSampling_test.cpp
@@ -30,7 +30,7 @@
 class DisplayedContentSamplingTest : public ::testing::Test {
 protected:
     void SetUp() {
-        mComposerClient = new SurfaceComposerClient;
+        mComposerClient = sp<SurfaceComposerClient>::make();
         ASSERT_EQ(OK, mComposerClient->initCheck());
         const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
         ASSERT_FALSE(ids.empty());
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 06f00a4..5a5067b 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -85,6 +85,7 @@
 // We use the top 10 layers as a way to haphazardly place ourselves above anything else.
 static const int LAYER_BASE = INT32_MAX - 10;
 static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s;
+static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
 
 class SynchronousWindowInfosReportedListener : public gui::BnWindowInfosReportedListener {
 public:
@@ -203,8 +204,8 @@
         ASSERT_EQ(InputEventType::MOTION, ev->getType());
         MotionEvent* mev = static_cast<MotionEvent*>(ev);
         EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
-        EXPECT_EQ(x, mev->getX(0));
-        EXPECT_EQ(y, mev->getY(0));
+        EXPECT_NEAR(x, mev->getX(0), EPSILON);
+        EXPECT_NEAR(y, mev->getY(0), EPSILON);
         EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
 
         ev = consumeEvent();
@@ -221,8 +222,8 @@
         ASSERT_EQ(InputEventType::MOTION, ev->getType());
         MotionEvent* mev = static_cast<MotionEvent*>(ev);
         EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
-        EXPECT_EQ(x, mev->getX(0));
-        EXPECT_EQ(y, mev->getY(0));
+        EXPECT_NEAR(x, mev->getX(0), EPSILON);
+        EXPECT_NEAR(y, mev->getY(0), EPSILON);
         EXPECT_EQ(flags, mev->getFlags() & flags);
 
         ev = consumeEvent();
@@ -240,8 +241,8 @@
         MotionEvent* mev = static_cast<MotionEvent*>(ev);
         EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
         const PointerCoords& coords = *mev->getRawPointerCoords(0 /*pointerIndex*/);
-        EXPECT_EQ(displayX, coords.getX());
-        EXPECT_EQ(displayY, coords.getY());
+        EXPECT_NEAR(displayX, coords.getX(), EPSILON);
+        EXPECT_NEAR(displayY, coords.getY(), EPSILON);
         EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
 
         ev = consumeEvent();
@@ -397,7 +398,7 @@
     InputSurfacesTest() { ProcessState::self()->startThreadPool(); }
 
     void SetUp() {
-        mComposerClient = new SurfaceComposerClient;
+        mComposerClient = sp<SurfaceComposerClient>::make();
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
         const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
         ASSERT_FALSE(ids.empty());
@@ -1230,7 +1231,7 @@
         consumer->setConsumerName(String8("Virtual disp consumer (MultiDisplayTests)"));
         consumer->setDefaultBufferSize(width, height);
 
-        class StubConsumerListener : public BnConsumerListener {
+        class StubConsumerListener : public IConsumerListener {
             virtual void onFrameAvailable(const BufferItem&) override {}
             virtual void onBuffersReleased() override {}
             virtual void onSidebandStreamChanged() override {}
diff --git a/libs/gui/tests/FillBuffer.cpp b/libs/gui/tests/FillBuffer.cpp
index b60995a..11383d9 100644
--- a/libs/gui/tests/FillBuffer.cpp
+++ b/libs/gui/tests/FillBuffer.cpp
@@ -76,7 +76,7 @@
 }
 
 void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) {
-    const size_t PIXEL_SIZE = 4;
+    constexpr size_t PIXEL_SIZE = 4;
     for (int x = 0; x < w; x++) {
         for (int y = 0; y < h; y++) {
             off_t offset = (y * stride + x) * PIXEL_SIZE;
@@ -89,6 +89,21 @@
     }
 }
 
+void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride, uint8_t r, uint8_t g, uint8_t b,
+                     uint8_t a) {
+    constexpr size_t PIXEL_SIZE = 4;
+
+    for (int x = 0; x < w; x++) {
+        for (int y = 0; y < h; y++) {
+            off_t offset = (y * stride + x) * PIXEL_SIZE;
+            buf[offset] = r;
+            buf[offset + 1] = g;
+            buf[offset + 2] = b;
+            buf[offset + 3] = a;
+        }
+    }
+}
+
 void produceOneRGBA8Frame(const sp<ANativeWindow>& anw) {
     android_native_buffer_t* anb;
     ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(),
diff --git a/libs/gui/tests/FillBuffer.h b/libs/gui/tests/FillBuffer.h
index b584179..f5d6b8b 100644
--- a/libs/gui/tests/FillBuffer.h
+++ b/libs/gui/tests/FillBuffer.h
@@ -30,6 +30,8 @@
         const android_native_rect_t& rect);
 
 void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride);
+void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride, uint8_t r, uint8_t g, uint8_t b,
+                     uint8_t a);
 
 // Produce a single RGBA8 frame by filling a buffer with a checkerboard pattern
 // using the CPU.  This assumes that the ANativeWindow is already configured to
diff --git a/libs/gui/tests/FrameRateUtilsTest.cpp b/libs/gui/tests/FrameRateUtilsTest.cpp
index 9ffe91f..c502533 100644
--- a/libs/gui/tests/FrameRateUtilsTest.cpp
+++ b/libs/gui/tests/FrameRateUtilsTest.cpp
@@ -34,7 +34,7 @@
                                   ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS, ""));
     EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
                                   ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
-    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+    EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST,
                                   ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
 
     // Privileged APIs.
diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp
index 40af8e8..407c18e 100644
--- a/libs/gui/tests/GLTest.cpp
+++ b/libs/gui/tests/GLTest.cpp
@@ -56,7 +56,7 @@
     }
 
     if (mDisplaySecs > 0) {
-        mComposerClient = new SurfaceComposerClient;
+        mComposerClient = sp<SurfaceComposerClient>::make();
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
 
         mSurfaceControl = mComposerClient->createSurface(
diff --git a/libs/gui/tests/Malicious.cpp b/libs/gui/tests/Malicious.cpp
index 376420c..cc16383 100644
--- a/libs/gui/tests/Malicious.cpp
+++ b/libs/gui/tests/Malicious.cpp
@@ -129,7 +129,7 @@
     int32_t mExpectedSlot = 0;
 };
 
-class FakeListener : public BnConsumerListener {
+class FakeListener : public IConsumerListener {
 public:
     void onFrameAvailable(const BufferItem&) override {}
     void onBuffersReleased() override {}
diff --git a/libs/gui/tests/MockConsumer.h b/libs/gui/tests/MockConsumer.h
index 4a6c51c..2e8bc63 100644
--- a/libs/gui/tests/MockConsumer.h
+++ b/libs/gui/tests/MockConsumer.h
@@ -18,7 +18,7 @@
 
 namespace android {
 
-struct MockConsumer : public BnConsumerListener {
+struct MockConsumer : public IConsumerListener {
     void onFrameAvailable(const BufferItem& /* item */) override {}
     void onBuffersReleased() override {}
     void onSidebandStreamChanged() override {}
diff --git a/libs/gui/tests/MultiTextureConsumer_test.cpp b/libs/gui/tests/MultiTextureConsumer_test.cpp
index 2428bb3..7ae6f40 100644
--- a/libs/gui/tests/MultiTextureConsumer_test.cpp
+++ b/libs/gui/tests/MultiTextureConsumer_test.cpp
@@ -34,8 +34,8 @@
 
     virtual void SetUp() {
         GLTest::SetUp();
-        mGlConsumer = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
-        mSurface = mGlConsumer->getSurface();
+        std::tie(mGlConsumer, mSurface) =
+                GLConsumer::create(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
         mANW = mSurface.get();
 
     }
diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp
index a0d8c53..d80d223 100644
--- a/libs/gui/tests/RegionSampling_test.cpp
+++ b/libs/gui/tests/RegionSampling_test.cpp
@@ -40,7 +40,7 @@
         std::unique_lock<decltype(mutex_)> lk(mutex_);
 
         auto check_event = [](auto const& ev) -> bool {
-            return ev.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
+            return ev.header.type == DisplayEventType::DISPLAY_EVENT_VSYNC;
         };
         DisplayEventReceiver::Event ev_;
         int evs = receiver_.getEvents(&ev_, 1);
@@ -180,7 +180,7 @@
     }
 
     void SetUp() override {
-        mSurfaceComposerClient = new SurfaceComposerClient;
+        mSurfaceComposerClient = sp<SurfaceComposerClient>::make();
         ASSERT_EQ(NO_ERROR, mSurfaceComposerClient->initCheck());
 
         mBackgroundLayer =
diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp
index 8fea689..a2fe8fd 100644
--- a/libs/gui/tests/SamplingDemo.cpp
+++ b/libs/gui/tests/SamplingDemo.cpp
@@ -36,7 +36,7 @@
 class Button : public gui::BnRegionSamplingListener {
 public:
     Button(const char* name, const Rect& samplingArea) {
-        sp<SurfaceComposerClient> client = new SurfaceComposerClient;
+        sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make();
 
         mButton = client->createSurface(String8(name), 0, 0, PIXEL_FORMAT_RGBA_8888,
                                         ISurfaceComposerClient::eFXSurfaceEffect);
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
index 1c439cd..9570a36 100644
--- a/libs/gui/tests/StreamSplitter_test.cpp
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -32,7 +32,7 @@
 
 class StreamSplitterTest : public ::testing::Test {};
 
-struct FakeListener : public BnConsumerListener {
+struct FakeListener : public IConsumerListener {
     virtual void onFrameAvailable(const BufferItem& /* item */) {}
     virtual void onBuffersReleased() {}
     virtual void onSidebandStreamChanged() {}
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 59d05b6..d3434ea 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -40,8 +40,7 @@
     }
 
     virtual void SetUp() {
-        mST = new GLConsumer(123, GLConsumer::TEXTURE_EXTERNAL, true, false);
-        mSTC = mST->getSurface();
+        std::tie(mST, mSTC) = GLConsumer::create(123, GLConsumer::TEXTURE_EXTERNAL, true, false);
         mANW = mSTC;
 
         // We need a valid GL context so we can test updateTexImage()
@@ -727,8 +726,7 @@
         ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
 
         for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) {
-            sp<GLConsumer> st(new GLConsumer(i, GLConsumer::TEXTURE_EXTERNAL, true, false));
-            sp<Surface> stc = st->getSurface();
+            auto [st, stc] = GLConsumer::create(i, GLConsumer::TEXTURE_EXTERNAL, true, false);
             mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig,
                     static_cast<ANativeWindow*>(stc.get()), nullptr);
             ASSERT_EQ(EGL_SUCCESS, eglGetError());
diff --git a/libs/gui/tests/SurfaceTextureGL.h b/libs/gui/tests/SurfaceTextureGL.h
index 1309635..eb31cd1 100644
--- a/libs/gui/tests/SurfaceTextureGL.h
+++ b/libs/gui/tests/SurfaceTextureGL.h
@@ -38,8 +38,7 @@
 
     void SetUp() {
         GLTest::SetUp();
-        mST = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
-        mSTC = mST->getSurface();
+        std::tie(mST, mSTC) = GLConsumer::create(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
         mANW = mSTC;
         ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), TEST_PRODUCER_USAGE_BITS));
         mTextureRenderer = new TextureRenderer(TEX_ID, mST);
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index 449533a..b22b853 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -17,6 +17,8 @@
 #define LOG_TAG "SurfaceTextureGL_test"
 //#define LOG_NDEBUG 0
 
+#include <gmock/gmock.h>
+
 #include "SurfaceTextureGL.h"
 
 #include "DisconnectWaiter.h"
@@ -735,4 +737,30 @@
     ASSERT_NE(NO_ERROR, mST->updateTexImage());
 }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+TEST_F(SurfaceTextureGLTest, TestUnlimitedSlots) {
+    ASSERT_EQ(OK, mSTC->connect(NATIVE_WINDOW_API_CPU, sp<StubSurfaceListener>::make()));
+    ASSERT_EQ(OK, mSTC->setMaxDequeuedBufferCount(256));
+
+    std::vector<Surface::BatchBuffer> buffers(256);
+    ASSERT_EQ(OK, mSTC->dequeueBuffers(&buffers));
+    ASSERT_EQ(256u, buffers.size());
+    ASSERT_THAT(buffers, Each(Field(&Surface::BatchBuffer::buffer, ::testing::NotNull())));
+
+    for (size_t i = 0; i < buffers.size(); ++i) {
+        sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(buffers[i].buffer);
+        sp<Fence> fence = sp<Fence>::make(buffers[i].fenceFd);
+
+        void* buf;
+        ASSERT_EQ(OK, graphicBuffer->lock(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, &buf));
+        fillRGBA8Buffer((uint8_t*)buf, graphicBuffer->getWidth(), graphicBuffer->getHeight(),
+                        graphicBuffer->getStride(), i, i, i, i);
+        graphicBuffer->unlock();
+
+        ASSERT_EQ(OK, mSTC->queueBuffer(graphicBuffer, fence));
+        ASSERT_EQ(OK, mST->updateTexImage());
+        checkPixel(0, 0, i, i, i, i);
+    }
+}
+#endif
 } // namespace android
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 76362ff..4fee11c 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-#include "gui/view/Surface.h"
-#include "Constants.h"
-#include "MockConsumer.h"
-
 #include <gtest/gtest.h>
 
 #include <SurfaceFlingerProperties.h>
@@ -36,10 +32,13 @@
 #include <gui/IConsumerListener.h>
 #include <gui/IGraphicBufferConsumer.h>
 #include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 #include <gui/SyncScreenCaptureListener.h>
+#include <gui/view/Surface.h>
+#include <nativebase/nativebase.h>
 #include <private/gui/ComposerService.h>
 #include <private/gui/ComposerServiceAIDL.h>
 #include <sys/types.h>
@@ -47,6 +46,7 @@
 #include <ui/BufferQueueDefs.h>
 #include <ui/DisplayMode.h>
 #include <ui/GraphicBuffer.h>
+#include <ui/PixelFormat.h>
 #include <ui/Rect.h>
 #include <utils/Errors.h>
 #include <utils/String8.h>
@@ -55,9 +55,12 @@
 #include <cstddef>
 #include <cstdint>
 #include <future>
+#include <iterator>
 #include <limits>
 #include <thread>
 
+#include "Constants.h"
+#include "MockConsumer.h"
 #include "testserver/TestServerClient.h"
 
 namespace android {
@@ -131,7 +134,7 @@
     }
 
     virtual void SetUp() {
-        mComposerClient = new SurfaceComposerClient;
+        mComposerClient = sp<SurfaceComposerClient>::make();
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
 
         // TODO(brianderson): The following sometimes fails and is a source of
@@ -291,9 +294,7 @@
 TEST_F(SurfaceTest, QueryConsumerUsage) {
     const int TEST_USAGE_FLAGS =
             GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
-    sp<BufferItemConsumer> c = new BufferItemConsumer(TEST_USAGE_FLAGS);
-
-    sp<Surface> s = c->getSurface();
+    auto [c, s] = BufferItemConsumer::create(TEST_USAGE_FLAGS);
     sp<ANativeWindow> anw(s);
 
     int flags = -1;
@@ -306,10 +307,8 @@
 TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) {
     const android_dataspace TEST_DATASPACE = HAL_DATASPACE_V0_SRGB;
 
-    sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
+    auto [cpuConsumer, s] = CpuConsumer::create(1);
     cpuConsumer->setDefaultBufferDataSpace(TEST_DATASPACE);
-
-    sp<Surface> s = cpuConsumer->getSurface();
     sp<ANativeWindow> anw(s);
 
     android_dataspace dataSpace;
@@ -322,8 +321,7 @@
 }
 
 TEST_F(SurfaceTest, SettingGenerationNumber) {
-    sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
-    sp<Surface> surface = cpuConsumer->getSurface();
+    auto [cpuConsumer, surface] = CpuConsumer::create(1);
     sp<ANativeWindow> window(surface);
 
     // Allocate a buffer with a generation number of 0
@@ -600,7 +598,7 @@
     ASSERT_GE(after, lastDequeueTime);
 }
 
-class FakeConsumer : public BnConsumerListener {
+class FakeConsumer : public IConsumerListener {
 public:
     void onFrameAvailable(const BufferItem& /*item*/) override {}
     void onBuffersReleased() override {}
@@ -650,16 +648,7 @@
         mSupportsPresent = supportsPresent;
     }
 
-    status_t setTransactionState(
-            const FrameTimelineInfo& /*frameTimelineInfo*/, Vector<ComposerState>& /*state*/,
-            Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
-            const sp<IBinder>& /*applyToken*/, InputWindowCommands /*inputWindowCommands*/,
-            int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
-            const std::vector<client_cache_t>& /*cachedBuffer*/, bool /*hasListenerCallbacks*/,
-            const std::vector<ListenerCallbacks>& /*listenerCallbacks*/, uint64_t /*transactionId*/,
-            const std::vector<uint64_t>& /*mergedTransactionIds*/) override {
-        return NO_ERROR;
-    }
+    status_t setTransactionState(TransactionState&&) override { return NO_ERROR; }
 
 protected:
     IBinder* onAsBinder() override { return nullptr; }
@@ -689,10 +678,11 @@
         return binder::Status::ok();
     }
 
-    binder::Status createVirtualDisplay(const std::string& /*displayName*/, bool /*isSecure*/,
-                                        const std::string& /*uniqueId*/,
-                                        float /*requestedRefreshRate*/,
-                                        sp<IBinder>* /*outDisplay*/) override {
+    binder::Status createVirtualDisplay(
+            const std::string& /*displayName*/, bool /*isSecure*/,
+            gui::ISurfaceComposer::OptimizationPolicy /*optimizationPolicy*/,
+            const std::string& /*uniqueId*/, float /*requestedRefreshRate*/,
+            sp<IBinder>* /*outDisplay*/) override {
         return binder::Status::ok();
     }
 
@@ -1016,7 +1006,11 @@
         return binder::Status::ok();
     }
 
-    binder::Status setActivePictureListener(const sp<gui::IActivePictureListener>&) {
+    binder::Status addActivePictureListener(const sp<gui::IActivePictureListener>&) {
+        return binder::Status::ok();
+    }
+
+    binder::Status removeActivePictureListener(const sp<gui::IActivePictureListener>&) {
         return binder::Status::ok();
     }
 
@@ -2173,8 +2167,7 @@
     const int BUFFER_COUNT = 16;
     const int BATCH_SIZE = 8;
 
-    sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
-    sp<Surface> surface = cpuConsumer->getSurface();
+    auto [cpuConsumer, surface] = CpuConsumer::create(1);
     sp<ANativeWindow> window(surface);
     sp<StubSurfaceListener> listener = new StubSurfaceListener();
 
@@ -2222,8 +2215,7 @@
     const int BUFFER_COUNT = 16;
     const int BATCH_SIZE = 8;
 
-    sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
-    sp<Surface> surface = cpuConsumer->getSurface();
+    auto [cpuConsumer, surface] = CpuConsumer::create(1);
     sp<ANativeWindow> window(surface);
     sp<StubSurfaceListener> listener = new StubSurfaceListener();
 
@@ -2245,6 +2237,52 @@
     ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
 }
 
+TEST_F(SurfaceTest, setMaxDequeuedBufferCount_setMaxAcquiredBufferCount_allocations) {
+    //
+    // Set up the consumer and producer--nothing fancy.
+    //
+    auto [consumer, surface] =
+            BufferItemConsumer::create(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER);
+    sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make();
+    surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener);
+    sp<GraphicBuffer> buffer;
+    sp<Fence> fence;
+
+    //
+    // These values are independent. The consumer can dequeue 3 and the consumer can acquire 3 at
+    // the same time.
+    //
+    ASSERT_EQ(OK, consumer->setMaxAcquiredBufferCount(3));
+    ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(3));
+
+    //
+    // Take all three buffers out of the queue--a fourth can't be retrieved. Then queue them.
+    //
+    std::vector<Surface::BatchBuffer> dequeuedBuffers(3);
+    EXPECT_EQ(OK, surface->dequeueBuffers(&dequeuedBuffers));
+    if (::com::android::graphics::libgui::flags::bq_always_use_max_dequeued_buffer_count()) {
+        EXPECT_EQ(INVALID_OPERATION, surface->dequeueBuffer(&buffer, &fence));
+    }
+
+    for (auto& batchBuffer : dequeuedBuffers) {
+        EXPECT_EQ(OK,
+                  surface->queueBuffer(GraphicBuffer::from(batchBuffer.buffer),
+                                       sp<Fence>::make(batchBuffer.fenceFd)));
+    }
+    dequeuedBuffers.assign(3, {});
+
+    //
+    // Acquire all three, then we should be able to dequeue 3 more.
+    //
+    std::vector<BufferItem> acquiredBuffers(3);
+    for (auto& bufferItem : acquiredBuffers) {
+        EXPECT_EQ(OK, consumer->acquireBuffer(&bufferItem, 0));
+    }
+
+    EXPECT_EQ(OK, surface->dequeueBuffers(&dequeuedBuffers));
+    EXPECT_EQ(INVALID_OPERATION, surface->dequeueBuffer(&buffer, &fence));
+}
+
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
 
 TEST_F(SurfaceTest, PlatformBufferMethods) {
@@ -2370,8 +2408,7 @@
     sp<IGraphicBufferConsumer> bqConsumer;
     BufferQueue::createBufferQueue(&bqProducer, &bqConsumer);
 
-    sp<BufferItemConsumer> consumer = sp<BufferItemConsumer>::make(bqConsumer, 3);
-    sp<Surface> surface = sp<Surface>::make(bqProducer);
+    auto [consumer, surface] = BufferItemConsumer::create(3);
     sp<ImmediateReleaseConsumerListener> consumerListener =
             sp<ImmediateReleaseConsumerListener>::make(consumer);
     consumer->setFrameAvailableListener(consumerListener);
@@ -2529,4 +2566,181 @@
 }
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+TEST_F(SurfaceTest, UnlimitedSlots_FailsOnIncompatibleConsumer) {
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+
+    sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make();
+
+    EXPECT_EQ(OK, consumer->allowUnlimitedSlots(false));
+    EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true));
+
+    sp<Surface> surface = sp<Surface>::make(producer);
+    sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make();
+    EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener));
+
+    EXPECT_NE(OK, surface->setMaxDequeuedBufferCount(128))
+            << "We shouldn't be able to set high max buffer counts if the consumer doesn't allow "
+               "it";
+}
+
+TEST_F(SurfaceTest, UnlimitedSlots_CanDequeueAndQueueMoreThanOldMaximum) {
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+
+    sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make();
+
+    EXPECT_EQ(OK, consumer->allowUnlimitedSlots(true));
+    EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true));
+    EXPECT_EQ(OK, consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888));
+    EXPECT_EQ(OK, consumer->setConsumerUsageBits(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN));
+
+    sp<Surface> surface = sp<Surface>::make(producer);
+    sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make();
+    EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener));
+
+    EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(128))
+            << "If unlimited slots are allowed, we should be able increase the max dequeued buffer "
+               "count arbitrarily";
+
+    std::vector<std::tuple<sp<GraphicBuffer>, sp<Fence>, int>> buffers;
+    for (int i = 0; i < 128; i++) {
+        sp<GraphicBuffer> buffer;
+        sp<Fence> fence;
+        ASSERT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)) << "Unable to dequeue buffer #" << i;
+        buffers.push_back({buffer, fence, i});
+    }
+
+    for (auto& [buffer, fence, idx] : buffers) {
+        ASSERT_EQ(OK, surface->queueBuffer(buffer, fence)) << "Unable to queue buffer #" << idx;
+    }
+}
+
+TEST_F(SurfaceTest, UnlimitedSlots_CanDequeueAndDetachMoreThanOldMaximum) {
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+
+    sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make();
+
+    EXPECT_EQ(OK, consumer->allowUnlimitedSlots(true));
+    EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true));
+    EXPECT_EQ(OK, consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888));
+    EXPECT_EQ(OK, consumer->setConsumerUsageBits(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN));
+
+    sp<Surface> surface = sp<Surface>::make(producer);
+    sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make();
+    EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener));
+
+    EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(128))
+            << "If unlimited slots are allowed, we should be able increase the max dequeued buffer "
+               "count arbitrarily";
+
+    std::vector<std::tuple<sp<GraphicBuffer>, sp<Fence>, int>> buffers;
+    for (int i = 0; i < 128; i++) {
+        sp<GraphicBuffer> buffer;
+        sp<Fence> fence;
+        ASSERT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)) << "Unable to dequeue buffer #" << i;
+        buffers.push_back({buffer, fence, i});
+    }
+
+    for (auto& [buffer, _, idx] : buffers) {
+        ASSERT_EQ(OK, surface->detachBuffer(buffer)) << "Unable to detach buffer #" << idx;
+    }
+}
+
+TEST_F(SurfaceTest, UnlimitedSlots_BatchOperations) {
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+
+    sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make();
+
+    EXPECT_EQ(OK, consumer->allowUnlimitedSlots(true));
+    EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true));
+    EXPECT_EQ(OK, consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888));
+    EXPECT_EQ(OK, consumer->setConsumerUsageBits(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN));
+
+    sp<Surface> surface = sp<Surface>::make(producer);
+    sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make();
+    EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener));
+
+    EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(128))
+            << "If unlimited slots are allowed, we should be able increase the max dequeued buffer "
+               "count arbitrarily";
+
+    std::vector<Surface::BatchBuffer> buffers(128);
+    EXPECT_EQ(OK, surface->dequeueBuffers(&buffers));
+    EXPECT_EQ(128u, buffers.size());
+
+    std::vector<Surface::BatchQueuedBuffer> queuedBuffers;
+    std::transform(buffers.begin(), buffers.end(), std::back_inserter(queuedBuffers),
+                   [](Surface::BatchBuffer& buffer) {
+                       Surface::BatchQueuedBuffer out;
+                       out.buffer = buffer.buffer;
+                       out.fenceFd = buffer.fenceFd;
+                       return out;
+                   });
+
+    std::vector<SurfaceQueueBufferOutput> outputs;
+    EXPECT_EQ(OK, surface->queueBuffers(queuedBuffers, &outputs));
+    EXPECT_EQ(128u, outputs.size());
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+
+TEST_F(SurfaceTest, isBufferOwned) {
+    const int TEST_USAGE_FLAGS = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
+    auto [bufferItemConsumer, surface] = BufferItemConsumer::create(TEST_USAGE_FLAGS);
+
+    sp<SurfaceListener> listener = sp<StubSurfaceListener>::make();
+    ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, listener));
+
+    sp<GraphicBuffer> surfaceAttachableBuffer =
+            sp<GraphicBuffer>::make(10, 10, PIXEL_FORMAT_RGBA_8888, 1, TEST_USAGE_FLAGS);
+
+    //
+    // Attaching a buffer makes it owned.
+    //
+
+    bool isOwned;
+    EXPECT_EQ(OK, surface->isBufferOwned(surfaceAttachableBuffer, &isOwned));
+    EXPECT_FALSE(isOwned);
+
+    EXPECT_EQ(OK, surface->attachBuffer(surfaceAttachableBuffer.get()));
+    EXPECT_EQ(OK, surface->isBufferOwned(surfaceAttachableBuffer, &isOwned));
+    EXPECT_TRUE(isOwned);
+
+    //
+    // A dequeued buffer is always owned.
+    //
+
+    sp<GraphicBuffer> buffer;
+    sp<Fence> fence;
+    EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence));
+    EXPECT_EQ(OK, surface->isBufferOwned(buffer, &isOwned));
+    EXPECT_TRUE(isOwned);
+
+    //
+    // A detached buffer is no longer owned.
+    //
+
+    EXPECT_EQ(OK, surface->detachBuffer(buffer));
+    EXPECT_EQ(OK, surface->isBufferOwned(buffer, &isOwned));
+    EXPECT_FALSE(isOwned);
+
+    //
+    // It's not currently possible to verify whether or not a consumer has attached a buffer until
+    // it shows up on the Surface.
+    //
+
+    sp<GraphicBuffer> consumerAttachableBuffer =
+            sp<GraphicBuffer>::make(10, 10, PIXEL_FORMAT_RGBA_8888, 1, TEST_USAGE_FLAGS);
+
+    ASSERT_EQ(OK, bufferItemConsumer->attachBuffer(consumerAttachableBuffer));
+    EXPECT_EQ(OK, surface->isBufferOwned(consumerAttachableBuffer, &isOwned));
+    EXPECT_FALSE(isOwned);
+}
 } // namespace android
diff --git a/libs/gui/tests/TransactionState_test.cpp b/libs/gui/tests/TransactionState_test.cpp
new file mode 100644
index 0000000..179b264
--- /dev/null
+++ b/libs/gui/tests/TransactionState_test.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2025 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 <gmock/gmock.h>
+
+#include <gtest/gtest.h>
+#include <unordered_map>
+#include "android/gui/FocusRequest.h"
+#include "binder/Binder.h"
+#include "binder/Parcel.h"
+#include "gtest/gtest.h"
+#include "gui/LayerState.h"
+#include "gui/WindowInfo.h"
+
+#include "gui/TransactionState.h"
+
+namespace android {
+
+void sprintf(std::string& out, const char* format, ...) {
+    va_list arg_list;
+    va_start(arg_list, format);
+
+    int len = vsnprintf(nullptr, 0, format, arg_list);
+    if (len < 0) {
+        va_end(arg_list);
+    }
+    std::string line(len, '\0');
+    int written = vsnprintf(line.data(), len + 1, format, arg_list);
+    if (written != len) {
+        va_end(arg_list);
+    }
+    line.pop_back();
+    out += line;
+    va_end(arg_list);
+}
+
+constexpr std::string dump_struct(auto& x) {
+    std::string s;
+#if __has_builtin(__builtin_dump_struct)
+    __builtin_dump_struct(&x, sprintf, s);
+#else
+    (void)x;
+#endif
+    return s;
+}
+
+void PrintTo(const TransactionState& state, ::std::ostream* os) {
+    *os << dump_struct(state);
+    *os << state.mFrameTimelineInfo.toString();
+    for (auto mergedId : state.mMergedTransactionIds) {
+        *os << mergedId << ",";
+    }
+}
+
+void PrintTo(const ComposerState& state, ::std::ostream* os) {
+    *os << dump_struct(state.state);
+    *os << state.state.getWindowInfo();
+}
+
+// In case EXPECT_EQ fails, this function is useful to pinpoint exactly which
+// field did not compare ==.
+void Compare(const TransactionState& s1, const TransactionState& s2) {
+    EXPECT_EQ(s1.mId, s2.mId);
+    EXPECT_EQ(s1.mMergedTransactionIds, s2.mMergedTransactionIds);
+    EXPECT_EQ(s1.mFlags, s2.mFlags);
+    EXPECT_EQ(s1.mFrameTimelineInfo, s2.mFrameTimelineInfo);
+    EXPECT_EQ(s1.mDesiredPresentTime, s2.mDesiredPresentTime);
+    EXPECT_EQ(s1.mIsAutoTimestamp, s2.mIsAutoTimestamp);
+    EXPECT_EQ(s1.mApplyToken, s2.mApplyToken);
+    EXPECT_EQ(s1.mMayContainBuffer, s2.mMayContainBuffer);
+    EXPECT_EQ(s1.mLogCallPoints, s2.mLogCallPoints);
+    EXPECT_EQ(s1.mDisplayStates.size(), s2.mDisplayStates.size());
+    EXPECT_THAT(s1.mDisplayStates, ::testing::ContainerEq(s2.mDisplayStates));
+    EXPECT_EQ(s1.mComposerStates.size(), s2.mComposerStates.size());
+    EXPECT_EQ(s1.mComposerStates, s2.mComposerStates);
+    EXPECT_EQ(s1.mInputWindowCommands, s2.mInputWindowCommands);
+    EXPECT_EQ(s1.mUncacheBuffers, s2.mUncacheBuffers);
+    EXPECT_EQ(s1.mHasListenerCallbacks, s2.mHasListenerCallbacks);
+    EXPECT_EQ(s1.mListenerCallbacks.size(), s2.mListenerCallbacks.size());
+    EXPECT_EQ(s1.mListenerCallbacks, s2.mListenerCallbacks);
+}
+
+std::unique_ptr<std::unordered_map<int, sp<BBinder>>> createTokenMap(size_t maxSize) {
+    auto result = std::make_unique<std::unordered_map<int, sp<BBinder>>>();
+    for (size_t i = 0; i < maxSize; ++i) {
+        result->emplace(i, sp<BBinder>::make());
+    }
+    return result;
+}
+
+constexpr size_t kMaxComposerStates = 2;
+ComposerState createComposerStateForTest(size_t i) {
+    static const auto* const sLayerHandle = createTokenMap(kMaxComposerStates).release();
+
+    ComposerState state;
+    state.state.what = layer_state_t::eFlagsChanged;
+    state.state.surface = sLayerHandle->at(i);
+    state.state.layerId = i;
+    state.state.flags = 20 * i;
+    return state;
+}
+
+constexpr size_t kMaxDisplayStates = 5;
+DisplayState createDisplayStateForTest(size_t i) {
+    static const auto* const sDisplayTokens = createTokenMap(kMaxDisplayStates).release();
+
+    DisplayState displayState;
+    displayState.what = DisplayState::eFlagsChanged;
+    displayState.token = sDisplayTokens->at(i);
+    displayState.flags = 20 * i;
+    return displayState;
+}
+
+TransactionState createTransactionStateForTest() {
+    static sp<BBinder> sApplyToken = sp<BBinder>::make();
+
+    TransactionState state;
+    state.mId = 123;
+    state.mMergedTransactionIds.push_back(15);
+    state.mMergedTransactionIds.push_back(0);
+    state.mFrameTimelineInfo.vsyncId = 14;
+    state.mDesiredPresentTime = 11;
+    state.mIsAutoTimestamp = true;
+    state.mApplyToken = sApplyToken;
+    for (size_t i = 0; i < kMaxDisplayStates; i++) {
+        state.mDisplayStates.push_back(createDisplayStateForTest(i));
+    }
+    for (size_t i = 0; i < kMaxComposerStates; i++) {
+        state.mComposerStates.push_back(createComposerStateForTest(i));
+    }
+    static const auto* const sFocusRequestTokens = createTokenMap(5).release();
+    for (int i = 0; i < 5; i++) {
+        gui::FocusRequest request;
+        request.token = sFocusRequestTokens->at(i);
+        request.timestamp = i;
+        state.mInputWindowCommands.addFocusRequest(request);
+    }
+    static const auto* const sCacheToken = createTokenMap(5).release();
+    for (int i = 0; i < 5; i++) {
+        client_cache_t cache;
+        cache.token = sCacheToken->at(i);
+        cache.id = i;
+        state.mUncacheBuffers.emplace_back(std::move(cache));
+    }
+    static const auto* const sListenerCallbacks = []() {
+        auto* callbacks = new std::vector<ListenerCallbacks>();
+        for (int i = 0; i < 5; i++) {
+            callbacks->emplace_back(sp<BBinder>::make(),
+                                    std::unordered_set<CallbackId, CallbackIdHash>{});
+        }
+        return callbacks;
+    }();
+    state.mHasListenerCallbacks = true;
+    state.mListenerCallbacks = *sListenerCallbacks;
+    return state;
+}
+
+TransactionState createEmptyTransaction(uint64_t id) {
+    TransactionState state;
+    state.mId = id;
+    return state;
+}
+
+TEST(TransactionStateTest, parcel) {
+    TransactionState state = createTransactionStateForTest();
+    Parcel p;
+    state.writeToParcel(&p);
+    p.setDataPosition(0);
+    TransactionState parcelledState;
+    parcelledState.readFromParcel(&p);
+    EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, parcelDisplayState) {
+    DisplayState state = createDisplayStateForTest(0);
+    Parcel p;
+    state.write(p);
+    p.setDataPosition(0);
+    DisplayState parcelledState;
+    parcelledState.read(p);
+    EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, parcelLayerState) {
+    ComposerState state = createComposerStateForTest(0);
+    Parcel p;
+    state.write(p);
+    p.setDataPosition(0);
+    ComposerState parcelledState;
+    parcelledState.read(p);
+    EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, parcelEmptyState) {
+    TransactionState state;
+    Parcel p;
+    state.writeToParcel(&p);
+    p.setDataPosition(0);
+    TransactionState parcelledState;
+    state.readFromParcel(&p);
+    EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, mergeLayerState) {
+    ComposerState composerState = createComposerStateForTest(0);
+    ComposerState update;
+    update.state.surface = composerState.state.surface;
+    update.state.layerId = 0;
+    update.state.what = layer_state_t::eAlphaChanged;
+    update.state.color.a = .42;
+    composerState.state.merge(update.state);
+
+    ComposerState expectedMergedState = createComposerStateForTest(0);
+    expectedMergedState.state.what |= layer_state_t::eAlphaChanged;
+    expectedMergedState.state.color.a = .42;
+    EXPECT_EQ(composerState, expectedMergedState);
+};
+
+TEST(TransactionStateTest, merge) {
+    // Setup.
+    static constexpr uint64_t kUpdateTransactionId = 200;
+
+    TransactionState state = createTransactionStateForTest();
+
+    TransactionState update;
+    update.mId = kUpdateTransactionId;
+    {
+        ComposerState composerState;
+        composerState.state.surface = state.mComposerStates[0].state.surface;
+        composerState.state.what = layer_state_t::eAlphaChanged;
+        composerState.state.color.a = .42;
+        update.mComposerStates.push_back(composerState);
+    }
+    {
+        ComposerState composerState;
+        composerState.state.surface = state.mComposerStates[1].state.surface;
+        composerState.state.what = layer_state_t::eBufferChanged;
+        update.mComposerStates.push_back(composerState);
+    }
+    int32_t overrwiteLayerId = -1;
+    // Mutation.
+    state.merge(std::move(update),
+                [&overrwiteLayerId](layer_state_t ls) { overrwiteLayerId = ls.layerId; });
+    // Assertions.
+    EXPECT_EQ(1, overrwiteLayerId);
+    EXPECT_EQ(update, createEmptyTransaction(update.getId()));
+
+    TransactionState expectedMergedState = createTransactionStateForTest();
+    expectedMergedState.mMergedTransactionIds
+            .insert(expectedMergedState.mMergedTransactionIds.begin(), kUpdateTransactionId);
+    expectedMergedState.mComposerStates.at(0).state.what |= layer_state_t::eAlphaChanged;
+    expectedMergedState.mComposerStates.at(0).state.color.a = .42;
+    expectedMergedState.mComposerStates.at(1).state.what |= layer_state_t::eBufferChanged;
+    auto inputCommands = expectedMergedState.mInputWindowCommands;
+
+    // desired present time is not merged.
+    expectedMergedState.mDesiredPresentTime = state.mDesiredPresentTime;
+
+    EXPECT_EQ(state.mComposerStates[0], expectedMergedState.mComposerStates[0]);
+    EXPECT_EQ(state.mInputWindowCommands, expectedMergedState.mInputWindowCommands);
+    EXPECT_EQ(state, expectedMergedState);
+};
+
+TEST(TransactionStateTest, clear) {
+    TransactionState state = createTransactionStateForTest();
+    state.clear();
+    TransactionState emptyState = createEmptyTransaction(state.getId());
+    EXPECT_EQ(state, emptyState);
+};
+
+} // namespace android
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index ce22082..e3f9a07 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -40,7 +40,18 @@
     ASSERT_EQ(OK, i.writeToParcel(&p));
     p.setDataPosition(0);
     i2.readFromParcel(&p);
-    ASSERT_TRUE(i2.token == nullptr);
+    ASSERT_EQ(i2.token, nullptr);
+}
+
+TEST(WindowInfo, ParcellingWithoutCloneTransform) {
+    WindowInfo i, i2;
+    i.cloneLayerStackTransform.reset();
+
+    Parcel p;
+    ASSERT_EQ(OK, i.writeToParcel(&p));
+    p.setDataPosition(0);
+    i2.readFromParcel(&p);
+    ASSERT_EQ(i2.cloneLayerStackTransform, std::nullopt);
 }
 
 TEST(WindowInfo, Parcelling) {
@@ -71,6 +82,8 @@
     i.applicationInfo.token = new BBinder();
     i.applicationInfo.dispatchingTimeoutMillis = 0x12345678ABCD;
     i.focusTransferTarget = new BBinder();
+    i.cloneLayerStackTransform = ui::Transform();
+    i.cloneLayerStackTransform->set({5, -1, 100, 4, 0, 40, 0, 0, 1});
 
     Parcel p;
     i.writeToParcel(&p);
@@ -100,6 +113,7 @@
     ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle);
     ASSERT_EQ(i.applicationInfo, i2.applicationInfo);
     ASSERT_EQ(i.focusTransferTarget, i2.focusTransferTarget);
+    ASSERT_EQ(i.cloneLayerStackTransform, i2.cloneLayerStackTransform);
 }
 
 TEST(InputApplicationInfo, Parcelling) {
diff --git a/libs/gui/tests/benchmarks/Android.bp b/libs/gui/tests/benchmarks/Android.bp
new file mode 100644
index 0000000..a728bef
--- /dev/null
+++ b/libs/gui/tests/benchmarks/Android.bp
@@ -0,0 +1,27 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_benchmark {
+    name: "libgui_benchmarks",
+    srcs: [
+        "*.cpp",
+    ],
+    defaults: ["libgui-defaults"],
+    static_libs: [
+        "libgmock",
+        "libgtest",
+    ],
+    shared_libs: [
+        "libgui",
+    ],
+    header_libs: [
+        "libsurfaceflinger_mocks_headers",
+        "surfaceflinger_tests_common_headers",
+    ],
+}
diff --git a/libs/gui/tests/benchmarks/Transaction_benchmarks.cpp b/libs/gui/tests/benchmarks/Transaction_benchmarks.cpp
new file mode 100644
index 0000000..0a51895
--- /dev/null
+++ b/libs/gui/tests/benchmarks/Transaction_benchmarks.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 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 <benchmark/benchmark.h>
+#include <cstddef>
+#include <optional>
+#include <vector>
+#include "binder/Parcel.h"
+#include "gui/SurfaceComposerClient.h"
+#include "gui/SurfaceControl.h"
+#include "log/log_main.h"
+
+namespace android {
+namespace {
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+std::vector<sp<SurfaceControl>> createSurfaceControl(const char* name, size_t num) {
+    sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make();
+    LOG_FATAL_IF(client->initCheck() != OK, "Could not init SurfaceComposerClient");
+    std::vector<sp<SurfaceControl>> surfaceControls;
+    for (size_t i = 0; i < num; i++) {
+        surfaceControls.push_back(
+                client->createSurface(String8(name), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                      ISurfaceComposerClient::eFXSurfaceBufferState));
+    }
+    return surfaceControls;
+}
+
+void applyTransaction(benchmark::State& state) {
+    std::vector<sp<SurfaceControl>> surfaceControls = createSurfaceControl(__func__, 5 /* num */);
+    for (auto _ : state) {
+        SurfaceComposerClient::Transaction t;
+        for (auto& sc : surfaceControls) {
+            t.setCrop(sc, FloatRect{1, 2, 3, 4});
+            t.setAutoRefresh(sc, true);
+            t.hide(sc);
+            t.setAlpha(sc, 0.5);
+            t.setCornerRadius(sc, 0.8);
+        }
+        Parcel p;
+        t.writeToParcel(&p);
+        t.clear();
+        benchmark::DoNotOptimize(t);
+    }
+}
+BENCHMARK(applyTransaction);
+
+// Mimic a buffer transaction with callbacks
+void applyBufferTransaction(benchmark::State& state) {
+    std::vector<sp<SurfaceControl>> surfaceControls = createSurfaceControl(__func__, 5 /* num */);
+    std::vector<sp<GraphicBuffer>> buffers;
+    for (size_t i = 0; i < surfaceControls.size(); i++) {
+        int64_t usageFlags = BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE;
+        buffers.emplace_back(
+                sp<GraphicBuffer>::make(5, 5, PIXEL_FORMAT_RGBA_8888, 1, usageFlags, "test"));
+    }
+
+    for (auto _ : state) {
+        SurfaceComposerClient::Transaction t;
+        int i = 0;
+        for (auto& sc : surfaceControls) {
+            std::function<void(const ReleaseCallbackId&, const sp<Fence>& /*releaseFence*/,
+                               std::optional<uint32_t> currentMaxAcquiredBufferCount)>
+                    releaseBufferCallback;
+            t.setBuffer(sc, buffers[i], std::nullopt, std::nullopt, 5, releaseBufferCallback);
+        }
+        Parcel p;
+        // proxy for applying the transaction
+        t.writeToParcel(&p);
+        t.clear();
+        benchmark::DoNotOptimize(t);
+    }
+}
+BENCHMARK(applyBufferTransaction);
+
+void mergeTransaction(benchmark::State& state) {
+    std::vector<sp<SurfaceControl>> surfaceControls = createSurfaceControl(__func__, 5 /* num */);
+    for (auto _ : state) {
+        SurfaceComposerClient::Transaction t1;
+        for (auto& sc : surfaceControls) {
+            t1.setCrop(sc, FloatRect{1, 2, 3, 4});
+            t1.setAutoRefresh(sc, true);
+            t1.hide(sc);
+            t1.setAlpha(sc, 0.5);
+            t1.setCornerRadius(sc, 0.8);
+        }
+
+        SurfaceComposerClient::Transaction t2;
+        for (auto& sc : surfaceControls) {
+            t2.hide(sc);
+            t2.setAlpha(sc, 0.5);
+            t2.setCornerRadius(sc, 0.8);
+            t2.setBackgroundBlurRadius(sc, 5);
+        }
+        t1.merge(std::move(t2));
+        benchmark::DoNotOptimize(t1);
+    }
+}
+BENCHMARK(mergeTransaction);
+
+void readTransactionFromParcel(benchmark::State& state) {
+    std::vector<sp<SurfaceControl>> surfaceControls = createSurfaceControl(__func__, 5 /* num */);
+    SurfaceComposerClient::Transaction t;
+    for (auto& sc : surfaceControls) {
+        t.setCrop(sc, FloatRect{1, 2, 3, 4});
+        t.setAutoRefresh(sc, true);
+        t.hide(sc);
+        t.setAlpha(sc, 0.5);
+        t.setCornerRadius(sc, 0.8);
+    }
+    Parcel p;
+    t.writeToParcel(&p);
+    t.clear();
+
+    for (auto _ : state) {
+        SurfaceComposerClient::Transaction t2;
+        t2.readFromParcel(&p);
+        p.setDataPosition(0);
+        benchmark::DoNotOptimize(t2);
+    }
+}
+BENCHMARK(readTransactionFromParcel);
+
+} // namespace
+} // namespace android
diff --git a/libs/gui/tests/benchmarks/main.cpp b/libs/gui/tests/benchmarks/main.cpp
new file mode 100644
index 0000000..685c7c6
--- /dev/null
+++ b/libs/gui/tests/benchmarks/main.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2024 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 <benchmark/benchmark.h>
+BENCHMARK_MAIN();
diff --git a/libs/gui/tests/testserver/TestServer.cpp b/libs/gui/tests/testserver/TestServer.cpp
index cd8824e..17d1b4a 100644
--- a/libs/gui/tests/testserver/TestServer.cpp
+++ b/libs/gui/tests/testserver/TestServer.cpp
@@ -45,7 +45,7 @@
 namespace android {
 
 namespace {
-class TestConsumerListener : public BnConsumerListener {
+class TestConsumerListener : public IConsumerListener {
     virtual void onFrameAvailable(const BufferItem&) override {}
     virtual void onBuffersReleased() override {}
     virtual void onSidebandStreamChanged() override {}