Change slot generation for BufferState

BufferState layers now do slot generation with buffer death considered
appropriately.  When a buffer dies, the slot will be pushed onto a stack
of available slots to be reused at the next opportunity.  This should
mimic BufferQueue slot behavior and prevent Composer Resources from
growing too large.

Test: build, boot, manual
Bug: 129351223

Change-Id: Icef9592593cacb0b5c6b12f6679fc2c4dabdcd19
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 9080d29..4cd0a13 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -116,7 +116,7 @@
         "BufferLayerConsumer.cpp",
         "BufferQueueLayer.cpp",
         "BufferStateLayer.cpp",
-        "BufferStateLayerCache.cpp",
+        "ClientCache.cpp",
         "Client.cpp",
         "ColorLayer.cpp",
         "ContainerLayer.cpp",
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 2cbb917..6ef4518 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -26,6 +26,7 @@
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <gui/BufferQueue.h>
 #include <private/gui/SyncFeatures.h>
 #include <renderengine/Image.h>
 
@@ -44,7 +45,8 @@
 };
 // clang-format on
 
-BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args) : BufferLayer(args) {
+BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args)
+      : BufferLayer(args), mHwcSlotGenerator(new HwcSlotGenerator()) {
     mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
     mCurrentState.dataspace = ui::Dataspace::V0_SRGB;
 }
@@ -210,12 +212,13 @@
 }
 
 bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, nsecs_t postTime,
-                                 nsecs_t desiredPresentTime) {
+                                 nsecs_t desiredPresentTime, const client_cache_t& clientCacheId) {
     if (mCurrentState.buffer) {
         mReleasePreviousBuffer = true;
     }
 
     mCurrentState.buffer = buffer;
+    mCurrentState.clientCacheId = clientCacheId;
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
 
@@ -589,8 +592,8 @@
 
     uint32_t hwcSlot;
     sp<GraphicBuffer> buffer;
-    hwcInfo.hwcBufferCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, s.buffer, &hwcSlot,
-                                        &buffer);
+    hwcInfo.hwcBufferCache.getHwcBuffer(mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId),
+                                        s.buffer, &hwcSlot, &buffer);
 
     auto error = hwcLayer->setBuffer(hwcSlot, buffer, s.acquireFence);
     if (error != HWC2::Error::None) {
@@ -609,4 +612,76 @@
     }
 }
 
+void BufferStateLayer::HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) {
+    std::lock_guard lock(mMutex);
+    if (!clientCacheId.isValid()) {
+        ALOGE("invalid process, failed to erase buffer");
+        return;
+    }
+    eraseBufferLocked(clientCacheId);
+}
+
+uint32_t BufferStateLayer::HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    auto itr = mCachedBuffers.find(clientCacheId);
+    if (itr == mCachedBuffers.end()) {
+        return addCachedBuffer(clientCacheId);
+    }
+    auto& [hwcCacheSlot, counter] = itr->second;
+    counter = mCounter++;
+    return hwcCacheSlot;
+}
+
+uint32_t BufferStateLayer::HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId)
+        REQUIRES(mMutex) {
+    if (!clientCacheId.isValid()) {
+        ALOGE("invalid process, returning invalid slot");
+        return BufferQueue::INVALID_BUFFER_SLOT;
+    }
+
+    ClientCache::getInstance().registerErasedRecipient(clientCacheId, wp<ErasedRecipient>(this));
+
+    uint32_t hwcCacheSlot = getFreeHwcCacheSlot();
+    mCachedBuffers[clientCacheId] = {hwcCacheSlot, mCounter++};
+    return hwcCacheSlot;
+}
+
+uint32_t BufferStateLayer::HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) {
+    if (mFreeHwcCacheSlots.empty()) {
+        evictLeastRecentlyUsed();
+    }
+
+    uint32_t hwcCacheSlot = mFreeHwcCacheSlots.top();
+    mFreeHwcCacheSlots.pop();
+    return hwcCacheSlot;
+}
+
+void BufferStateLayer::HwcSlotGenerator::evictLeastRecentlyUsed() REQUIRES(mMutex) {
+    uint64_t minCounter = UINT_MAX;
+    client_cache_t minClientCacheId = {};
+    for (const auto& [clientCacheId, slotCounter] : mCachedBuffers) {
+        const auto& [hwcCacheSlot, counter] = slotCounter;
+        if (counter < minCounter) {
+            minCounter = counter;
+            minClientCacheId = clientCacheId;
+        }
+    }
+    eraseBufferLocked(minClientCacheId);
+
+    ClientCache::getInstance().unregisterErasedRecipient(minClientCacheId, this);
+}
+
+void BufferStateLayer::HwcSlotGenerator::eraseBufferLocked(const client_cache_t& clientCacheId)
+        REQUIRES(mMutex) {
+    auto itr = mCachedBuffers.find(clientCacheId);
+    if (itr == mCachedBuffers.end()) {
+        return;
+    }
+    auto& [hwcCacheSlot, counter] = itr->second;
+
+    // TODO send to hwc cache and resources
+
+    mFreeHwcCacheSlots.push(hwcCacheSlot);
+    mCachedBuffers.erase(clientCacheId);
+}
 } // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 864a15d..12f0141 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -25,6 +25,8 @@
 #include <system/window.h>
 #include <utils/String8.h>
 
+#include <stack>
+
 namespace android {
 
 class BufferStateLayer : public BufferLayer {
@@ -63,8 +65,8 @@
     bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
     bool setCrop(const Rect& crop) override;
     bool setFrame(const Rect& frame) override;
-    bool setBuffer(const sp<GraphicBuffer>& buffer, nsecs_t postTime,
-                   nsecs_t desiredPresentTime) override;
+    bool setBuffer(const sp<GraphicBuffer>& buffer, nsecs_t postTime, nsecs_t desiredPresentTime,
+                   const client_cache_t& clientCacheId) override;
     bool setAcquireFence(const sp<Fence>& fence) override;
     bool setDataspace(ui::Dataspace dataspace) override;
     bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
@@ -154,6 +156,45 @@
     nsecs_t mDesiredPresentTime = -1;
 
     // TODO(marissaw): support sticky transform for LEGACY camera mode
+
+    class HwcSlotGenerator : public ClientCache::ErasedRecipient {
+    public:
+        HwcSlotGenerator() {
+            for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+                mFreeHwcCacheSlots.push(i);
+            }
+        }
+
+        void bufferErased(const client_cache_t& clientCacheId);
+
+        uint32_t getHwcCacheSlot(const client_cache_t& clientCacheId);
+
+    private:
+        uint32_t addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex);
+        uint32_t getFreeHwcCacheSlot() REQUIRES(mMutex);
+        void evictLeastRecentlyUsed() REQUIRES(mMutex);
+        void eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex);
+
+        struct CachedBufferHash {
+            std::size_t operator()(const client_cache_t& clientCacheId) const {
+                return std::hash<uint64_t>{}(clientCacheId.id);
+            }
+        };
+
+        std::mutex mMutex;
+
+        std::unordered_map<client_cache_t,
+                           std::pair<uint32_t /*HwcCacheSlot*/, uint32_t /*counter*/>,
+                           CachedBufferHash>
+                mCachedBuffers GUARDED_BY(mMutex);
+        std::stack<uint32_t /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex);
+
+        // The cache increments this counter value when a slot is updated or used.
+        // Used to track the least recently-used buffer
+        uint64_t mCounter = 0;
+    };
+
+    sp<HwcSlotGenerator> mHwcSlotGenerator;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferStateLayerCache.cpp b/services/surfaceflinger/BufferStateLayerCache.cpp
deleted file mode 100644
index 51ca45c..0000000
--- a/services/surfaceflinger/BufferStateLayerCache.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2019 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_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "BufferStateLayerCache"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <cinttypes>
-
-#include "BufferStateLayerCache.h"
-
-namespace android {
-
-ANDROID_SINGLETON_STATIC_INSTANCE(BufferStateLayerCache);
-
-BufferStateLayerCache::BufferStateLayerCache() : mDeathRecipient(new CacheDeathRecipient) {}
-
-void BufferStateLayerCache::add(const sp<IBinder>& processToken, uint64_t id,
-                                const sp<GraphicBuffer>& buffer) {
-    if (!processToken) {
-        ALOGE("failed to cache buffer: invalid process token");
-        return;
-    }
-
-    if (!buffer) {
-        ALOGE("failed to cache buffer: invalid buffer");
-        return;
-    }
-
-    std::lock_guard lock(mMutex);
-
-    // If this is a new process token, set a death recipient. If the client process dies, we will
-    // get a callback through binderDied.
-    if (mBuffers.find(processToken) == mBuffers.end()) {
-        status_t err = processToken->linkToDeath(mDeathRecipient);
-        if (err != NO_ERROR) {
-            ALOGE("failed to cache buffer: could not link to death");
-            return;
-        }
-    }
-
-    auto& processBuffers = mBuffers[processToken];
-
-    if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) {
-        ALOGE("failed to cache buffer: cache is full");
-        return;
-    }
-
-    processBuffers[id] = buffer;
-}
-
-void BufferStateLayerCache::erase(const sp<IBinder>& processToken, uint64_t id) {
-    if (!processToken) {
-        ALOGE("failed to uncache buffer: invalid process token");
-        return;
-    }
-
-    std::lock_guard lock(mMutex);
-
-    if (mBuffers.find(processToken) == mBuffers.end()) {
-        ALOGE("failed to uncache buffer: process token not found");
-        return;
-    }
-
-    auto& processBuffers = mBuffers[processToken];
-    processBuffers.erase(id);
-}
-
-sp<GraphicBuffer> BufferStateLayerCache::get(const sp<IBinder>& processToken, uint64_t id) {
-    if (!processToken) {
-        ALOGE("failed to cache buffer: invalid process token");
-        return nullptr;
-    }
-
-    std::lock_guard lock(mMutex);
-    auto itr = mBuffers.find(processToken);
-    if (itr == mBuffers.end()) {
-        ALOGE("failed to get buffer: process token not found");
-        return nullptr;
-    }
-
-    if (itr->second.find(id) == itr->second.end()) {
-        ALOGE("failed to get buffer: buffer not found");
-        return nullptr;
-    }
-
-    return itr->second[id];
-}
-
-void BufferStateLayerCache::removeProcess(const wp<IBinder>& processToken) {
-    std::lock_guard lock(mMutex);
-    mBuffers.erase(processToken);
-}
-
-void BufferStateLayerCache::CacheDeathRecipient::binderDied(const wp<IBinder>& who) {
-    BufferStateLayerCache::getInstance().removeProcess(who);
-}
-
-}; // namespace android
diff --git a/services/surfaceflinger/BufferStateLayerCache.h b/services/surfaceflinger/BufferStateLayerCache.h
deleted file mode 100644
index 415c09c..0000000
--- a/services/surfaceflinger/BufferStateLayerCache.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2019 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-base/thread_annotations.h>
-#include <binder/IBinder.h>
-#include <ui/GraphicBuffer.h>
-#include <utils/RefBase.h>
-#include <utils/Singleton.h>
-
-#include <array>
-#include <map>
-#include <mutex>
-
-#define BUFFER_CACHE_MAX_SIZE 64
-
-namespace android {
-
-class BufferStateLayerCache : public Singleton<BufferStateLayerCache> {
-public:
-    BufferStateLayerCache();
-
-    void add(const sp<IBinder>& processToken, uint64_t id, const sp<GraphicBuffer>& buffer);
-    void erase(const sp<IBinder>& processToken, uint64_t id);
-
-    sp<GraphicBuffer> get(const sp<IBinder>& processToken, uint64_t id);
-
-    void removeProcess(const wp<IBinder>& processToken);
-
-private:
-    std::mutex mMutex;
-    std::map<wp<IBinder> /*caching process*/, std::map<uint64_t /*Cache id*/, sp<GraphicBuffer>>>
-            mBuffers GUARDED_BY(mMutex);
-
-    class CacheDeathRecipient : public IBinder::DeathRecipient {
-    public:
-        void binderDied(const wp<IBinder>& who) override;
-    };
-
-    sp<CacheDeathRecipient> mDeathRecipient;
-};
-
-}; // namespace android
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
new file mode 100644
index 0000000..77f2f57
--- /dev/null
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2019 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_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "ClientCache"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <cinttypes>
+
+#include "ClientCache.h"
+
+namespace android {
+
+ANDROID_SINGLETON_STATIC_INSTANCE(ClientCache);
+
+ClientCache::ClientCache() : mDeathRecipient(new CacheDeathRecipient) {}
+
+bool ClientCache::getBuffer(const client_cache_t& cacheId,
+                            ClientCacheBuffer** outClientCacheBuffer) {
+    auto& [processToken, id] = cacheId;
+    if (processToken == nullptr) {
+        ALOGE("failed to get buffer, invalid (nullptr) process token");
+        return false;
+    }
+    auto it = mBuffers.find(processToken);
+    if (it == mBuffers.end()) {
+        ALOGE("failed to get buffer, invalid process token");
+        return false;
+    }
+
+    auto& processBuffers = it->second;
+
+    auto bufItr = processBuffers.find(id);
+    if (bufItr == processBuffers.end()) {
+        ALOGE("failed to get buffer, invalid buffer id");
+        return false;
+    }
+
+    ClientCacheBuffer& buf = bufItr->second;
+    *outClientCacheBuffer = &buf;
+    return true;
+}
+
+void ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) {
+    auto& [processToken, id] = cacheId;
+    if (processToken == nullptr) {
+        ALOGE("failed to cache buffer: invalid process token");
+        return;
+    }
+
+    if (!buffer) {
+        ALOGE("failed to cache buffer: invalid buffer");
+        return;
+    }
+
+    std::lock_guard lock(mMutex);
+    sp<IBinder> token;
+
+    // If this is a new process token, set a death recipient. If the client process dies, we will
+    // get a callback through binderDied.
+    auto it = mBuffers.find(processToken);
+    if (it == mBuffers.end()) {
+        token = processToken.promote();
+        if (!token) {
+            ALOGE("failed to cache buffer: invalid token");
+            return;
+        }
+
+        status_t err = token->linkToDeath(mDeathRecipient);
+        if (err != NO_ERROR) {
+            ALOGE("failed to cache buffer: could not link to death");
+            return;
+        }
+        auto [itr, success] =
+                mBuffers.emplace(processToken, std::unordered_map<uint64_t, ClientCacheBuffer>());
+        LOG_ALWAYS_FATAL_IF(!success, "failed to insert new process into client cache");
+        it = itr;
+    }
+
+    auto& processBuffers = it->second;
+
+    if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) {
+        ALOGE("failed to cache buffer: cache is full");
+        return;
+    }
+
+    processBuffers[id].buffer = buffer;
+}
+
+void ClientCache::erase(const client_cache_t& cacheId) {
+    auto& [processToken, id] = cacheId;
+    std::vector<sp<ErasedRecipient>> pendingErase;
+    {
+        std::lock_guard lock(mMutex);
+        ClientCacheBuffer* buf = nullptr;
+        if (!getBuffer(cacheId, &buf)) {
+            ALOGE("failed to erase buffer, could not retrieve buffer");
+            return;
+        }
+
+        for (auto& recipient : buf->recipients) {
+            sp<ErasedRecipient> erasedRecipient = recipient.promote();
+            if (erasedRecipient) {
+                pendingErase.push_back(erasedRecipient);
+            }
+        }
+
+        mBuffers[processToken].erase(id);
+    }
+
+    for (auto& recipient : pendingErase) {
+        recipient->bufferErased(cacheId);
+    }
+}
+
+sp<GraphicBuffer> ClientCache::get(const client_cache_t& cacheId) {
+    std::lock_guard lock(mMutex);
+
+    ClientCacheBuffer* buf = nullptr;
+    if (!getBuffer(cacheId, &buf)) {
+        ALOGE("failed to get buffer, could not retrieve buffer");
+        return nullptr;
+    }
+
+    return buf->buffer;
+}
+
+void ClientCache::registerErasedRecipient(const client_cache_t& cacheId,
+                                          const wp<ErasedRecipient>& recipient) {
+    std::lock_guard lock(mMutex);
+
+    ClientCacheBuffer* buf = nullptr;
+    if (!getBuffer(cacheId, &buf)) {
+        ALOGE("failed to register erased recipient, could not retrieve buffer");
+        return;
+    }
+    buf->recipients.insert(recipient);
+}
+
+void ClientCache::unregisterErasedRecipient(const client_cache_t& cacheId,
+                                            const wp<ErasedRecipient>& recipient) {
+    std::lock_guard lock(mMutex);
+
+    ClientCacheBuffer* buf = nullptr;
+    if (!getBuffer(cacheId, &buf)) {
+        ALOGE("failed to unregister erased recipient");
+        return;
+    }
+
+    buf->recipients.erase(recipient);
+}
+
+void ClientCache::removeProcess(const wp<IBinder>& processToken) {
+    std::vector<std::pair<sp<ErasedRecipient>, client_cache_t>> pendingErase;
+    {
+        if (processToken == nullptr) {
+            ALOGE("failed to remove process, invalid (nullptr) process token");
+            return;
+        }
+        std::lock_guard lock(mMutex);
+        auto itr = mBuffers.find(processToken);
+        if (itr == mBuffers.end()) {
+            ALOGE("failed to remove process, could not find process");
+            return;
+        }
+
+        for (auto& [id, clientCacheBuffer] : itr->second) {
+            client_cache_t cacheId = {processToken, id};
+            for (auto& recipient : clientCacheBuffer.recipients) {
+                sp<ErasedRecipient> erasedRecipient = recipient.promote();
+                if (erasedRecipient) {
+                    pendingErase.emplace_back(erasedRecipient, cacheId);
+                }
+            }
+        }
+        mBuffers.erase(itr);
+    }
+
+    for (auto& [recipient, cacheId] : pendingErase) {
+        recipient->bufferErased(cacheId);
+    }
+}
+
+void ClientCache::CacheDeathRecipient::binderDied(const wp<IBinder>& who) {
+    ClientCache::getInstance().removeProcess(who);
+}
+
+}; // namespace android
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
new file mode 100644
index 0000000..9f057c4
--- /dev/null
+++ b/services/surfaceflinger/ClientCache.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2019 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-base/thread_annotations.h>
+#include <binder/IBinder.h>
+#include <gui/LayerState.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
+#include <utils/Singleton.h>
+
+#include <map>
+#include <mutex>
+#include <set>
+#include <unordered_map>
+
+#define BUFFER_CACHE_MAX_SIZE 64
+
+namespace android {
+
+class ClientCache : public Singleton<ClientCache> {
+public:
+    ClientCache();
+
+    void add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer);
+    void erase(const client_cache_t& cacheId);
+
+    sp<GraphicBuffer> get(const client_cache_t& cacheId);
+
+    void removeProcess(const wp<IBinder>& processToken);
+
+    class ErasedRecipient : public virtual RefBase {
+    public:
+        virtual void bufferErased(const client_cache_t& clientCacheId) = 0;
+    };
+
+    void registerErasedRecipient(const client_cache_t& cacheId,
+                                 const wp<ErasedRecipient>& recipient);
+    void unregisterErasedRecipient(const client_cache_t& cacheId,
+                                   const wp<ErasedRecipient>& recipient);
+
+private:
+    std::mutex mMutex;
+
+    struct ClientCacheBuffer {
+        sp<GraphicBuffer> buffer;
+        std::set<wp<ErasedRecipient>> recipients;
+    };
+    std::map<wp<IBinder> /*caching process*/,
+             std::unordered_map<uint64_t /*cache id*/, ClientCacheBuffer>>
+            mBuffers GUARDED_BY(mMutex);
+
+    class CacheDeathRecipient : public IBinder::DeathRecipient {
+    public:
+        void binderDied(const wp<IBinder>& who) override;
+    };
+
+    sp<CacheDeathRecipient> mDeathRecipient;
+
+    bool getBuffer(const client_cache_t& cacheId, ClientCacheBuffer** outClientCacheBuffer)
+            REQUIRES(mMutex);
+};
+
+}; // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index 97bdc8f..8eec035 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -48,20 +48,11 @@
     void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
                       sp<GraphicBuffer>* outBuffer);
 
-protected:
-    bool getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot);
-    uint32_t getLeastRecentlyUsedSlot();
-    uint64_t getCounter();
-
 private:
     // an array where the index corresponds to a slot and the value corresponds to a (counter,
     // buffer) pair. "counter" is a unique value that indicates the last time this slot was updated
     // or used and allows us to keep track of the least-recently used buffer.
-    std::pair<uint64_t, wp<GraphicBuffer>> mBuffers[BufferQueue::NUM_BUFFER_SLOTS];
-
-    // The cache increments this counter value when a slot is updated or used.
-    // Used to track the least recently-used buffer
-    uint64_t mCounter = 1;
+    wp<GraphicBuffer> mBuffers[BufferQueue::NUM_BUFFER_SLOTS];
 };
 
 } // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index a941e09..f72862b 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -21,60 +21,30 @@
 namespace android::compositionengine::impl {
 
 HwcBufferCache::HwcBufferCache() {
-    std::fill(std::begin(mBuffers), std::end(mBuffers),
-              std::pair<uint64_t, wp<GraphicBuffer>>(0, nullptr));
-}
-bool HwcBufferCache::getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot) {
-    // search for cached buffer first
-    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
-        // Weak pointers in the cache may have had their object destroyed.
-        // Comparisons between weak pointers will accurately reflect this case,
-        // but comparisons between weak and strong may not.  Thus, we create a weak
-        // pointer from strong pointer buffer
-        wp<GraphicBuffer> weakCopy(buffer);
-        if (mBuffers[i].second == weakCopy) {
-            *outSlot = i;
-            return true;
-        }
-    }
-
-    // use the least-recently used slot
-    *outSlot = getLeastRecentlyUsedSlot();
-    return false;
-}
-
-uint32_t HwcBufferCache::getLeastRecentlyUsedSlot() {
-    auto iter = std::min_element(std::begin(mBuffers), std::end(mBuffers));
-    return std::distance(std::begin(mBuffers), iter);
+    std::fill(std::begin(mBuffers), std::end(mBuffers), wp<GraphicBuffer>(nullptr));
 }
 
 void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
                                   sp<GraphicBuffer>* outBuffer) {
-    // if this slot corresponds to a BufferStateLayer, generate the slot
-    if (slot == BufferQueue::INVALID_BUFFER_SLOT) {
-        getSlot(buffer, outSlot);
-    } else if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) {
+    // default is 0
+    if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0 ||
+        slot >= BufferQueue::NUM_BUFFER_SLOTS) {
         *outSlot = 0;
     } else {
         *outSlot = slot;
     }
 
-    auto& [currentCounter, currentBuffer] = mBuffers[*outSlot];
+    auto& currentBuffer = mBuffers[*outSlot];
     wp<GraphicBuffer> weakCopy(buffer);
     if (currentBuffer == weakCopy) {
         // already cached in HWC, skip sending the buffer
         *outBuffer = nullptr;
-        currentCounter = getCounter();
     } else {
         *outBuffer = buffer;
 
         // update cache
         currentBuffer = buffer;
-        currentCounter = getCounter();
     }
 }
 
-uint64_t HwcBufferCache::getCounter() {
-    return mCounter++;
-}
 } // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
index b261493..00eafb1 100644
--- a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
@@ -28,10 +28,6 @@
                       sp<GraphicBuffer>* outBuffer) {
         HwcBufferCache::getHwcBuffer(slot, buffer, outSlot, outBuffer);
     }
-    bool getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot) {
-        return HwcBufferCache::getSlot(buffer, outSlot);
-    }
-    uint32_t getLeastRecentlyUsedSlot() { return HwcBufferCache::getLeastRecentlyUsedSlot(); }
 };
 
 class HwcBufferCacheTest : public testing::Test {
@@ -86,41 +82,5 @@
     testSlot(-123, 0);
 }
 
-TEST_F(HwcBufferCacheTest, cacheGeneratesSlotForInvalidBufferSlot) {
-    uint32_t outSlot;
-    sp<GraphicBuffer> outBuffer;
-
-    mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer1, &outSlot, &outBuffer);
-    EXPECT_EQ(0, outSlot);
-    EXPECT_EQ(mBuffer1, outBuffer);
-
-    mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer1, &outSlot, &outBuffer);
-    EXPECT_EQ(0, outSlot);
-    EXPECT_EQ(nullptr, outBuffer.get());
-
-    mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer2, &outSlot, &outBuffer);
-    EXPECT_EQ(1, outSlot);
-    EXPECT_EQ(mBuffer2, outBuffer);
-
-    mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer2, &outSlot, &outBuffer);
-    EXPECT_EQ(1, outSlot);
-    EXPECT_EQ(nullptr, outBuffer.get());
-
-    mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, sp<GraphicBuffer>(), &outSlot,
-                        &outBuffer);
-    EXPECT_EQ(2, outSlot);
-    EXPECT_EQ(nullptr, outBuffer.get());
-
-    // note that sending mBuffer1 with explicit slot 1 will overwrite mBuffer2
-    // and also cause mBuffer1 to be stored in two places
-    mCache.getHwcBuffer(1, mBuffer1, &outSlot, &outBuffer);
-    EXPECT_EQ(1, outSlot);
-    EXPECT_EQ(mBuffer1, outBuffer);
-
-    mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer2, &outSlot, &outBuffer);
-    EXPECT_EQ(3, outSlot);
-    EXPECT_EQ(mBuffer2, outBuffer);
-}
-
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index f4545e0..c76394c 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -199,6 +199,7 @@
         Region transparentRegionHint;
 
         sp<GraphicBuffer> buffer;
+        client_cache_t clientCacheId;
         sp<Fence> acquireFence;
         HdrMetadata hdrMetadata;
         Region surfaceDamageRegion;
@@ -312,9 +313,10 @@
     virtual bool setCrop(const Rect& /*crop*/) { return false; };
     virtual bool setFrame(const Rect& /*frame*/) { return false; };
     virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, nsecs_t /*postTime*/,
-                           nsecs_t /*desiredPresentTime*/) {
+                           nsecs_t /*desiredPresentTime*/,
+                           const client_cache_t& /*clientCacheId*/) {
         return false;
-    }
+    };
     virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
     virtual bool setDataspace(ui::Dataspace /*dataspace*/) { return false; };
     virtual bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/) { return false; };
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 6063555..29441d9 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -75,7 +75,6 @@
 #include "BufferLayer.h"
 #include "BufferQueueLayer.h"
 #include "BufferStateLayer.h"
-#include "BufferStateLayerCache.h"
 #include "Client.h"
 #include "ColorLayer.h"
 #include "Colorizer.h"
@@ -3542,7 +3541,7 @@
                                          const sp<IBinder>& applyToken,
                                          const InputWindowCommands& inputWindowCommands,
                                          int64_t desiredPresentTime,
-                                         const cached_buffer_t& uncacheBuffer,
+                                         const client_cache_t& uncacheBuffer,
                                          const std::vector<ListenerCallbacks>& listenerCallbacks) {
     ATRACE_CALL();
 
@@ -3574,7 +3573,7 @@
                                            const Vector<DisplayState>& displays, uint32_t flags,
                                            const InputWindowCommands& inputWindowCommands,
                                            const int64_t desiredPresentTime,
-                                           const cached_buffer_t& uncacheBuffer,
+                                           const client_cache_t& uncacheBuffer,
                                            const std::vector<ListenerCallbacks>& listenerCallbacks,
                                            const int64_t postTime, bool privileged,
                                            bool isMainThread) {
@@ -3624,8 +3623,8 @@
 
     transactionFlags |= addInputWindowCommands(inputWindowCommands);
 
-    if (uncacheBuffer.token) {
-        BufferStateLayerCache::getInstance().erase(uncacheBuffer.token, uncacheBuffer.cacheId);
+    if (uncacheBuffer.isValid()) {
+        ClientCache::getInstance().erase(uncacheBuffer);
     }
 
     // If a synchronous transaction is explicitly requested without any changes, force a transaction
@@ -3980,17 +3979,15 @@
     bool cacheIdChanged = what & layer_state_t::eCachedBufferChanged;
     sp<GraphicBuffer> buffer;
     if (bufferChanged && cacheIdChanged) {
-        BufferStateLayerCache::getInstance().add(s.cachedBuffer.token, s.cachedBuffer.cacheId,
-                                                 s.buffer);
+        ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
         buffer = s.buffer;
     } else if (cacheIdChanged) {
-        buffer = BufferStateLayerCache::getInstance().get(s.cachedBuffer.token,
-                                                          s.cachedBuffer.cacheId);
+        buffer = ClientCache::getInstance().get(s.cachedBuffer);
     } else if (bufferChanged) {
         buffer = s.buffer;
     }
     if (buffer) {
-        if (layer->setBuffer(buffer, postTime, desiredPresentTime)) {
+        if (layer->setBuffer(buffer, postTime, desiredPresentTime, s.cachedBuffer)) {
             flags |= eTraversalNeeded;
         }
     }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 7a7ad33..72b4721 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -46,6 +46,7 @@
 #include <utils/Trace.h>
 #include <utils/threads.h>
 
+#include "ClientCache.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWC2.h"
 #include "DisplayHardware/PowerAdvisor.h"
@@ -386,7 +387,7 @@
                              const Vector<DisplayState>& displays, uint32_t flags,
                              const sp<IBinder>& applyToken,
                              const InputWindowCommands& inputWindowCommands,
-                             int64_t desiredPresentTime, const cached_buffer_t& uncacheBuffer,
+                             int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
                              const std::vector<ListenerCallbacks>& listenerCallbacks) override;
     void bootFinished() override;
     bool authenticateSurfaceTexture(
@@ -545,7 +546,7 @@
                                const Vector<DisplayState>& displays, uint32_t flags,
                                const InputWindowCommands& inputWindowCommands,
                                const int64_t desiredPresentTime,
-                               const cached_buffer_t& uncacheBuffer,
+                               const client_cache_t& uncacheBuffer,
                                const std::vector<ListenerCallbacks>& listenerCallbacks,
                                const int64_t postTime, bool privileged, bool isMainThread = false)
             REQUIRES(mStateLock);
@@ -1031,7 +1032,7 @@
     struct TransactionState {
         TransactionState(const Vector<ComposerState>& composerStates,
                          const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
-                         int64_t desiredPresentTime, const cached_buffer_t& uncacheBuffer,
+                         int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
                          const std::vector<ListenerCallbacks>& listenerCallbacks, int64_t postTime,
                          bool privileged)
               : states(composerStates),
@@ -1047,7 +1048,7 @@
         Vector<DisplayState> displays;
         uint32_t flags;
         const int64_t desiredPresentTime;
-        cached_buffer_t buffer;
+        client_cache_t buffer;
         std::vector<ListenerCallbacks> callback;
         const int64_t postTime;
         bool privileged;