ComposerClient 2.4: Clean cache on hotplug

On subsequent hotplug connected event for a display
SurfaceFlinger destroys the previous framebuffers and
recreates them. When the new buffers are created
ComposerClient still holds a handle to the old buffers and
they are not destroyed. This way the new framebuffers
may get allocated on non continuous memory causing garbled
screens for the user.

This is already implemented for ComposerClient 2.1-2.3. In
this CL only the behavior for ComposerClient 2.4 is changed.

Test: manually flash a device
Bug: 178785393
Change-Id: I6aa1243d61676c0a3d42cb7aecf19e6f8802cb1a
diff --git a/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h b/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h
index 3d74af4..46f5d6e 100644
--- a/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h
+++ b/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h
@@ -98,7 +98,7 @@
                      // display change and thus the framework may want to reallocate buffers. We
                      // need to free all cached handles, since they are holding a strong reference
                      // to the underlying buffers.
-                     cleanDisplayResources(display);
+                     cleanDisplayResources(display, mResources, mHal);
                      mResources->removeDisplay(display);
                  }
                  mResources->addPhysicalDisplay(display);
@@ -125,56 +125,6 @@
          Hal* const mHal;
          const sp<IComposerCallback> mCallback;
          ComposerResources* const mResources;
-
-         void cleanDisplayResources(Display display) {
-             size_t cacheSize;
-             Error err = mResources->getDisplayClientTargetCacheSize(display, &cacheSize);
-             if (err == Error::NONE) {
-                 for (int slot = 0; slot < cacheSize; slot++) {
-                     ComposerResources::ReplacedHandle replacedBuffer(/*isBuffer*/ true);
-                     // Replace the buffer slots with NULLs. Keep the old handle until it is
-                     // replaced in ComposerHal, otherwise we risk leaving a dangling pointer.
-                     const native_handle_t* clientTarget = nullptr;
-                     err = mResources->getDisplayClientTarget(display, slot, /*useCache*/ true,
-                                                              /*rawHandle*/ nullptr, &clientTarget,
-                                                              &replacedBuffer);
-                     if (err != Error::NONE) {
-                         continue;
-                     }
-                     const std::vector<hwc_rect_t> damage;
-                     err = mHal->setClientTarget(display, clientTarget, /*fence*/ -1, 0, damage);
-                     ALOGE_IF(err != Error::NONE,
-                              "Can't clean slot %d of the client target buffer"
-                              "cache for display %" PRIu64,
-                              slot, display);
-                 }
-             } else {
-                 ALOGE("Can't clean client target cache for display %" PRIu64, display);
-             }
-
-             err = mResources->getDisplayOutputBufferCacheSize(display, &cacheSize);
-             if (err == Error::NONE) {
-                 for (int slot = 0; slot < cacheSize; slot++) {
-                     // Replace the buffer slots with NULLs. Keep the old handle until it is
-                     // replaced in ComposerHal, otherwise we risk leaving a dangling pointer.
-                     ComposerResources::ReplacedHandle replacedBuffer(/*isBuffer*/ true);
-                     const native_handle_t* outputBuffer = nullptr;
-                     err = mResources->getDisplayOutputBuffer(display, slot, /*useCache*/ true,
-                                                              /*rawHandle*/ nullptr, &outputBuffer,
-                                                              &replacedBuffer);
-                     if (err != Error::NONE) {
-                         continue;
-                     }
-                     err = mHal->setOutputBuffer(display, outputBuffer, /*fence*/ -1);
-                     ALOGE_IF(err != Error::NONE,
-                              "Can't clean slot %d of the output buffer cache"
-                              "for display %" PRIu64,
-                              slot, display);
-                 }
-             } else {
-                 ALOGE("Can't clean output buffer cache for display %" PRIu64, display);
-             }
-         }
     };
 
     Return<void> registerCallback(const sp<IComposerCallback>& callback) override {
@@ -380,6 +330,57 @@
         return std::make_unique<ComposerCommandEngine>(mHal, mResources.get());
     }
 
+    static void cleanDisplayResources(Display display, ComposerResources* const resources,
+                                      Hal* const hal) {
+        size_t cacheSize;
+        Error err = resources->getDisplayClientTargetCacheSize(display, &cacheSize);
+        if (err == Error::NONE) {
+            for (int slot = 0; slot < cacheSize; slot++) {
+                ComposerResources::ReplacedHandle replacedBuffer(/*isBuffer*/ true);
+                // Replace the buffer slots with NULLs. Keep the old handle until it is
+                // replaced in ComposerHal, otherwise we risk leaving a dangling pointer.
+                const native_handle_t* clientTarget = nullptr;
+                err = resources->getDisplayClientTarget(display, slot, /*useCache*/ true,
+                                                        /*rawHandle*/ nullptr, &clientTarget,
+                                                        &replacedBuffer);
+                if (err != Error::NONE) {
+                    continue;
+                }
+                const std::vector<hwc_rect_t> damage;
+                err = hal->setClientTarget(display, clientTarget, /*fence*/ -1, 0, damage);
+                ALOGE_IF(err != Error::NONE,
+                         "Can't clean slot %d of the client target buffer"
+                         "cache for display %" PRIu64,
+                         slot, display);
+            }
+        } else {
+            ALOGE("Can't clean client target cache for display %" PRIu64, display);
+        }
+
+        err = resources->getDisplayOutputBufferCacheSize(display, &cacheSize);
+        if (err == Error::NONE) {
+            for (int slot = 0; slot < cacheSize; slot++) {
+                // Replace the buffer slots with NULLs. Keep the old handle until it is
+                // replaced in ComposerHal, otherwise we risk leaving a dangling pointer.
+                ComposerResources::ReplacedHandle replacedBuffer(/*isBuffer*/ true);
+                const native_handle_t* outputBuffer = nullptr;
+                err = resources->getDisplayOutputBuffer(display, slot, /*useCache*/ true,
+                                                        /*rawHandle*/ nullptr, &outputBuffer,
+                                                        &replacedBuffer);
+                if (err != Error::NONE) {
+                    continue;
+                }
+                err = hal->setOutputBuffer(display, outputBuffer, /*fence*/ -1);
+                ALOGE_IF(err != Error::NONE,
+                         "Can't clean slot %d of the output buffer cache"
+                         "for display %" PRIu64,
+                         slot, display);
+            }
+        } else {
+            ALOGE("Can't clean output buffer cache for display %" PRIu64, display);
+        }
+    }
+
     void destroyResources() {
         // We want to call hwc2_close here (and move hwc2_open to the
         // constructor), with the assumption that hwc2_close would
diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
index c889069..3464342 100644
--- a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
+++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
@@ -45,12 +45,21 @@
 
     class HalEventCallback : public Hal::EventCallback_2_4 {
       public:
-        HalEventCallback(const sp<IComposerCallback> callback,
+        HalEventCallback(Hal* hal, const sp<IComposerCallback> callback,
                          V2_1::hal::ComposerResources* resources)
-            : mCallback(callback), mResources(resources) {}
+            : mHal(hal), mCallback(callback), mResources(resources) {}
 
         void onHotplug(Display display, IComposerCallback::Connection connected) override {
             if (connected == IComposerCallback::Connection::CONNECTED) {
+                if (mResources->hasDisplay(display)) {
+                    // This is a subsequent hotplug "connected" for a display. This signals a
+                    // display change and thus the framework may want to reallocate buffers. We
+                    // need to free all cached handles, since they are holding a strong reference
+                    // to the underlying buffers.
+                    V2_1::hal::detail::ComposerClientImpl<Interface, Hal>::cleanDisplayResources(
+                            display, mResources, mHal);
+                    mResources->removeDisplay(display);
+                }
                 mResources->addPhysicalDisplay(display);
             } else if (connected == IComposerCallback::Connection::DISCONNECTED) {
                 mResources->removeDisplay(display);
@@ -91,13 +100,15 @@
         }
 
       protected:
+        Hal* const mHal;
         const sp<IComposerCallback> mCallback;
         V2_1::hal::ComposerResources* const mResources;
     };
 
     Return<void> registerCallback_2_4(const sp<IComposerCallback>& callback) override {
         // no locking as we require this function to be called only once
-        mHalEventCallback_2_4 = std::make_unique<HalEventCallback>(callback, mResources.get());
+        mHalEventCallback_2_4 =
+                std::make_unique<HalEventCallback>(mHal, callback, mResources.get());
         mHal->registerEventCallback_2_4(mHalEventCallback_2_4.get());
         return Void();
     }