Support multiple active picture listeners

Bug: 337330263
Test: atest SurfaceControlPictureProfileTest
Test: atest ActivePictureTrackerTest
Flag: com.android.graphics.libgui.flags.apply_picture_profiles
Change-Id: If030b3f6177b6bd641ed7953b10a37319c9e537a
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index be88b11..cabde22 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -3293,10 +3293,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/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 8c19bbb..da47ee2 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -607,8 +607,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/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 0ce0c0a..0f66c8b 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -298,7 +298,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.
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 76362ff..3185778 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -1016,7 +1016,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();
     }
 
diff --git a/services/surfaceflinger/ActivePictureTracker.cpp b/services/surfaceflinger/ActivePictureTracker.cpp
index 0319408..4e6fa66 100644
--- a/services/surfaceflinger/ActivePictureTracker.cpp
+++ b/services/surfaceflinger/ActivePictureTracker.cpp
@@ -23,6 +23,9 @@
 
 namespace android {
 
+using gui::ActivePicture;
+using gui::IActivePictureListener;
+
 void ActivePictureTracker::onLayerComposed(const Layer& layer, const LayerFE& layerFE,
                                            const CompositionResult& result) {
     if (result.wasPictureProfileCommitted) {
@@ -39,10 +42,47 @@
     }
 }
 
+void ActivePictureTracker::updateAndNotifyListeners(const Listeners& listenersToAdd,
+                                                    const Listeners& listenersToRemove) {
+    Listeners newListeners = updateListeners(listenersToAdd, listenersToRemove);
+    if (updateAndHasChanged()) {
+        for (auto listener : mListeners) {
+            listener->onActivePicturesChanged(getActivePictures());
+        }
+    } else {
+        for (auto listener : newListeners) {
+            listener->onActivePicturesChanged(getActivePictures());
+        }
+    }
+}
+
+ActivePictureTracker::Listeners ActivePictureTracker::updateListeners(
+        const Listeners& listenersToAdd, const Listeners& listenersToRemove) {
+    Listeners newListeners;
+    for (auto listener : listenersToRemove) {
+        std::erase_if(mListeners, [listener](const sp<IActivePictureListener>& otherListener) {
+            return IInterface::asBinder(listener) == IInterface::asBinder(otherListener);
+        });
+    }
+    for (auto listener : listenersToAdd) {
+        if (std::find_if(mListeners.begin(), mListeners.end(),
+                         [listener](const sp<IActivePictureListener>& otherListener) {
+                             return IInterface::asBinder(listener) ==
+                                     IInterface::asBinder(otherListener);
+                         }) == mListeners.end()) {
+            newListeners.push_back(listener);
+        }
+    }
+    for (auto listener : newListeners) {
+        mListeners.push_back(listener);
+    }
+    return newListeners;
+}
+
 bool ActivePictureTracker::updateAndHasChanged() {
     bool hasChanged = true;
     if (mNewActivePictures.size() == mOldActivePictures.size()) {
-        auto compare = [](const gui::ActivePicture& lhs, const gui::ActivePicture& rhs) -> int {
+        auto compare = [](const ActivePicture& lhs, const ActivePicture& rhs) -> int {
             if (lhs.layerId == rhs.layerId) {
                 return lhs.pictureProfileId < rhs.pictureProfileId;
             }
@@ -59,7 +99,7 @@
     return hasChanged;
 }
 
-const std::vector<gui::ActivePicture>& ActivePictureTracker::getActivePictures() const {
+const std::vector<ActivePicture>& ActivePictureTracker::getActivePictures() const {
     return mOldActivePictures;
 }
 
diff --git a/services/surfaceflinger/ActivePictureTracker.h b/services/surfaceflinger/ActivePictureTracker.h
index 95a698b..cb319a5 100644
--- a/services/surfaceflinger/ActivePictureTracker.h
+++ b/services/surfaceflinger/ActivePictureTracker.h
@@ -19,6 +19,7 @@
 #include <vector>
 
 #include <android/gui/ActivePicture.h>
+#include <android/gui/IActivePictureListener.h>
 
 namespace android {
 
@@ -29,19 +30,26 @@
 // Keeps track of active pictures - layers that are undergoing picture processing.
 class ActivePictureTracker {
 public:
+    typedef std::vector<sp<gui::IActivePictureListener>> Listeners;
+
     // Called for each visible layer when SurfaceFlinger finishes composing.
     void onLayerComposed(const Layer& layer, const LayerFE& layerFE,
                          const CompositionResult& result);
 
     // Update internals and return whether the set of active pictures have changed.
-    bool updateAndHasChanged();
+    void updateAndNotifyListeners(const Listeners& activePictureListenersToAdd,
+                                  const Listeners& activePictureListenersToRemove);
 
     // The current set of active pictures.
     const std::vector<gui::ActivePicture>& getActivePictures() const;
 
 private:
+    Listeners updateListeners(const Listeners& listenersToAdd, const Listeners& listenersToRemove);
+    bool updateAndHasChanged();
+
     std::vector<gui::ActivePicture> mOldActivePictures;
     std::vector<gui::ActivePicture> mNewActivePictures;
+    Listeners mListeners;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 3f3d2c6..0f8d38f 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -199,7 +199,7 @@
     name: "libsurfaceflinger_sources",
     srcs: [
         ":libsurfaceflinger_backend_sources",
-        "ActivePictureUpdater.cpp",
+        "ActivePictureTracker.cpp",
         "BackgroundExecutor.cpp",
         "Client.cpp",
         "ClientCache.cpp",
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1498fff..2147a85 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -126,7 +126,6 @@
 #include <gui/SchedulingPolicy.h>
 #include <gui/SyncScreenCaptureListener.h>
 #include <ui/DisplayIdentification.h>
-#include "ActivePictureTracker.h"
 #include "BackgroundExecutor.h"
 #include "Client.h"
 #include "ClientCache.h"
@@ -3223,8 +3222,8 @@
     std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
             hdrInfoListeners;
     bool haveNewHdrInfoListeners = false;
-    sp<gui::IActivePictureListener> activePictureListener;
-    bool haveNewActivePictureListener = false;
+    ActivePictureTracker::Listeners activePictureListenersToAdd;
+    ActivePictureTracker::Listeners activePictureListenersToRemove;
     {
         Mutex::Autolock lock(mStateLock);
         if (mFpsReporter) {
@@ -3246,9 +3245,8 @@
         haveNewHdrInfoListeners = mAddingHDRLayerInfoListener; // grab this with state lock
         mAddingHDRLayerInfoListener = false;
 
-        activePictureListener = mActivePictureListener;
-        haveNewActivePictureListener = mHaveNewActivePictureListener;
-        mHaveNewActivePictureListener = false;
+        std::swap(activePictureListenersToAdd, mActivePictureListenersToAdd);
+        std::swap(activePictureListenersToRemove, mActivePictureListenersToRemove);
     }
 
     if (haveNewHdrInfoListeners || mHdrLayerInfoChanged) {
@@ -3312,14 +3310,10 @@
     mHdrLayerInfoChanged = false;
 
     if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
-        // Track, update and notify changes to active pictures - layers that are undergoing picture
-        // processing
-        if (mActivePictureTracker.updateAndHasChanged() || haveNewActivePictureListener) {
-            if (activePictureListener) {
-                activePictureListener->onActivePicturesChanged(
-                        mActivePictureTracker.getActivePictures());
-            }
-        }
+        // Track, update and notify changes to active pictures - layers that are undergoing
+        // picture processing
+        mActivePictureTracker.updateAndNotifyListeners(activePictureListenersToAdd,
+                                                       activePictureListenersToRemove);
     }
 
     mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
@@ -8171,12 +8165,20 @@
     }));
 }
 
-void SurfaceFlinger::setActivePictureListener(const sp<gui::IActivePictureListener>& listener) {
-    if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
-        Mutex::Autolock lock(mStateLock);
-        mActivePictureListener = listener;
-        mHaveNewActivePictureListener = listener != nullptr;
-    }
+void SurfaceFlinger::addActivePictureListener(const sp<gui::IActivePictureListener>& listener) {
+    Mutex::Autolock lock(mStateLock);
+    std::erase_if(mActivePictureListenersToRemove, [listener](const auto& otherListener) {
+        return IInterface::asBinder(listener) == IInterface::asBinder(otherListener);
+    });
+    mActivePictureListenersToAdd.push_back(listener);
+}
+
+void SurfaceFlinger::removeActivePictureListener(const sp<gui::IActivePictureListener>& listener) {
+    Mutex::Autolock lock(mStateLock);
+    std::erase_if(mActivePictureListenersToAdd, [listener](const auto& otherListener) {
+        return IInterface::asBinder(listener) == IInterface::asBinder(otherListener);
+    });
+    mActivePictureListenersToRemove.push_back(listener);
 }
 
 std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData(
@@ -9129,11 +9131,20 @@
     return binderStatusFromStatusT(status);
 }
 
-binder::Status SurfaceComposerAIDL::setActivePictureListener(
+binder::Status SurfaceComposerAIDL::addActivePictureListener(
         const sp<gui::IActivePictureListener>& listener) {
     status_t status = checkObservePictureProfilesPermission();
     if (status == OK) {
-        mFlinger->setActivePictureListener(listener);
+        mFlinger->addActivePictureListener(listener);
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::removeActivePictureListener(
+        const sp<gui::IActivePictureListener>& listener) {
+    status_t status = checkObservePictureProfilesPermission();
+    if (status == OK) {
+        mFlinger->removeActivePictureListener(listener);
     }
     return binderStatusFromStatusT(status);
 }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b16ea08..0ae236f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -667,7 +667,9 @@
 
     void updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel, int32_t maxLevel);
 
-    void setActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+    void addActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+
+    void removeActivePictureListener(const sp<gui::IActivePictureListener>& listener);
 
     // IBinder::DeathRecipient overrides:
     void binderDied(const wp<IBinder>& who) override;
@@ -1402,9 +1404,9 @@
     std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
             GUARDED_BY(mStateLock);
 
-    sp<gui::IActivePictureListener> mActivePictureListener GUARDED_BY(mStateLock);
-    bool mHaveNewActivePictureListener GUARDED_BY(mStateLock);
     ActivePictureTracker mActivePictureTracker GUARDED_BY(kMainThreadContext);
+    ActivePictureTracker::Listeners mActivePictureListenersToAdd GUARDED_BY(mStateLock);
+    ActivePictureTracker::Listeners mActivePictureListenersToRemove GUARDED_BY(mStateLock);
 
     std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint;
 
@@ -1641,8 +1643,8 @@
     binder::Status flushJankData(int32_t layerId) override;
     binder::Status removeJankListener(int32_t layerId, const sp<gui::IJankListener>& listener,
                                       int64_t afterVsync) override;
-    binder::Status setActivePictureListener(const sp<gui::IActivePictureListener>& listener);
-    binder::Status clearActivePictureListener();
+    binder::Status addActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+    binder::Status removeActivePictureListener(const sp<gui::IActivePictureListener>& listener);
 
 private:
     static const constexpr bool kUsePermissionCache = true;
diff --git a/services/surfaceflinger/tests/unittests/ActivePictureTrackerTest.cpp b/services/surfaceflinger/tests/unittests/ActivePictureTrackerTest.cpp
index ff9a456..7d9004a 100644
--- a/services/surfaceflinger/tests/unittests/ActivePictureTrackerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/ActivePictureTrackerTest.cpp
@@ -38,6 +38,8 @@
 using testing::_;
 using testing::NiceMock;
 using testing::Return;
+using testing::SizeIs;
+using testing::StrictMock;
 
 class TestableLayerFE : public LayerFE {
 public:
@@ -48,8 +50,20 @@
     LayerSnapshot& snapshot;
 };
 
+class MockActivePictureListener : public gui::BnActivePictureListener {
+public:
+    operator ActivePictureTracker::Listeners const() {
+        return {sp<IActivePictureListener>::fromExisting(this)};
+    }
+
+    MOCK_METHOD(binder::Status, onActivePicturesChanged, (const std::vector<ActivePicture>&),
+                (override));
+};
+
 class ActivePictureTrackerTest : public testing::Test {
 protected:
+    const static ActivePictureTracker::Listeners NO_LISTENERS;
+
     SurfaceFlinger* flinger() {
         if (!mFlingerSetup) {
             mFlinger.setupMockScheduler();
@@ -60,11 +74,26 @@
         return mFlinger.flinger();
     }
 
+    sp<NiceMock<MockLayer>> createMockLayer(int layerId, int ownerUid) {
+        auto layer = sp<NiceMock<MockLayer>>::make(flinger(), layerId);
+        EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(ownerUid)));
+        return layer;
+    }
+
+    sp<StrictMock<MockActivePictureListener>> createMockListener() {
+        return sp<StrictMock<MockActivePictureListener>>::make();
+    }
+
+    ActivePictureTracker::Listeners mListenersToAdd;
+    ActivePictureTracker::Listeners mListenersToRemove;
+
 private:
     TestableSurfaceFlinger mFlinger;
     bool mFlingerSetup = false;
 };
 
+const ActivePictureTracker::Listeners ActivePictureTrackerTest::NO_LISTENERS;
+
 // Hack to workaround initializer lists not working for parcelables because parcelables inherit from
 // Parcelable, which has a virtual destructor.
 auto UnorderedElementsAre(std::initializer_list<std::tuple<int32_t, int32_t, int64_t>> tuples) {
@@ -85,121 +114,137 @@
 }
 
 TEST_F(ActivePictureTrackerTest, notCalledWithNoProfile) {
-    sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    auto layer = createMockLayer(100, 10);
     TestableLayerFE layerFE;
-    EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
 
     ActivePictureTracker tracker;
     {
-        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
         tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
 
-        ASSERT_FALSE(tracker.updateAndHasChanged());
+        tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+    }
+    {
+        auto listener = createMockListener();
+        EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1);
+        tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
     }
 }
 
 TEST_F(ActivePictureTrackerTest, calledWhenLayerStartsUsingProfile) {
-    sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    auto layer = createMockLayer(100, 10);
     TestableLayerFE layerFE;
-    EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
 
     ActivePictureTracker tracker;
+    auto listener = createMockListener();
     {
-        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
         tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
 
-        ASSERT_FALSE(tracker.updateAndHasChanged());
+        EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1);
+        tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
     }
     {
         layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
         layerFE.onPictureProfileCommitted();
         tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
 
-        ASSERT_TRUE(tracker.updateAndHasChanged());
-        EXPECT_THAT(tracker.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+        EXPECT_CALL(*listener, onActivePicturesChanged(_))
+                .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+                    EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}}));
+                    return binder::Status::ok();
+                });
+        tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
     }
 }
 
 TEST_F(ActivePictureTrackerTest, notCalledWhenLayerContinuesUsingProfile) {
-    sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    auto layer = createMockLayer(100, 10);
     TestableLayerFE layerFE;
-    EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
 
     ActivePictureTracker tracker;
+    auto listener = createMockListener();
     {
         layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
         layerFE.onPictureProfileCommitted();
         tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
 
-        ASSERT_TRUE(tracker.updateAndHasChanged());
-        EXPECT_THAT(tracker.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+        EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1);
+        tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
     }
     {
         layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
         layerFE.onPictureProfileCommitted();
         tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
 
-        ASSERT_FALSE(tracker.updateAndHasChanged());
+        EXPECT_CALL(*listener, onActivePicturesChanged(_)).Times(0);
+        tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
     }
 }
 
 TEST_F(ActivePictureTrackerTest, calledWhenLayerStopsUsingProfile) {
-    sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    auto layer = createMockLayer(100, 10);
     TestableLayerFE layerFE;
-    EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
 
     ActivePictureTracker tracker;
+    auto listener = createMockListener();
     {
         layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
         layerFE.onPictureProfileCommitted();
         tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
 
-        ASSERT_TRUE(tracker.updateAndHasChanged());
-        EXPECT_THAT(tracker.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+        EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1);
+        tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
     }
     {
         layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
         tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
 
-        ASSERT_TRUE(tracker.updateAndHasChanged());
-        EXPECT_THAT(tracker.getActivePictures(), UnorderedElementsAre({}));
+        EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1);
+        tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
     }
 }
 
 TEST_F(ActivePictureTrackerTest, calledWhenLayerChangesProfile) {
-    sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    auto layer = createMockLayer(100, 10);
     TestableLayerFE layerFE;
-    EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
 
     ActivePictureTracker tracker;
+    auto listener = createMockListener();
     {
         layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
         layerFE.onPictureProfileCommitted();
         tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
 
-        ASSERT_TRUE(tracker.updateAndHasChanged());
-        EXPECT_THAT(tracker.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+        EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1)))
+                .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+                    EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}}));
+                    return binder::Status::ok();
+                });
+        tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
     }
     {
         layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(2);
         layerFE.onPictureProfileCommitted();
         tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
 
-        ASSERT_TRUE(tracker.updateAndHasChanged());
-        EXPECT_THAT(tracker.getActivePictures(), UnorderedElementsAre({{100, 10, 2}}));
+        EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1)))
+                .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+                    EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 2}}));
+                    return binder::Status::ok();
+                });
+        tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
     }
 }
 
 TEST_F(ActivePictureTrackerTest, notCalledWhenUncommittedLayerChangesProfile) {
-    sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    auto layer1 = createMockLayer(100, 10);
     TestableLayerFE layerFE1;
-    EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
 
-    sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+    auto layer2 = createMockLayer(200, 20);
     TestableLayerFE layerFE2;
-    EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20)));
 
     ActivePictureTracker tracker;
+    auto listener = createMockListener();
     {
         layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
         layerFE1.onPictureProfileCommitted();
@@ -208,8 +253,8 @@
         layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
         tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
 
-        ASSERT_TRUE(tracker.updateAndHasChanged());
-        EXPECT_THAT(tracker.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+        EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1);
+        tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
     }
     {
         layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
@@ -219,20 +264,20 @@
         layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
         tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
 
-        ASSERT_FALSE(tracker.updateAndHasChanged());
+        EXPECT_CALL(*listener, onActivePicturesChanged(_)).Times(0);
+        tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
     }
 }
 
 TEST_F(ActivePictureTrackerTest, calledWhenDifferentLayerUsesSameProfile) {
-    sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    auto layer1 = createMockLayer(100, 10);
     TestableLayerFE layerFE1;
-    EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
 
-    sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+    auto layer2 = createMockLayer(200, 20);
     TestableLayerFE layerFE2;
-    EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20)));
 
     ActivePictureTracker tracker;
+    auto listener = createMockListener();
     {
         layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
         layerFE1.onPictureProfileCommitted();
@@ -242,9 +287,12 @@
         layerFE2.onPictureProfileCommitted();
         tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
 
-        ASSERT_TRUE(tracker.updateAndHasChanged());
-        EXPECT_THAT(tracker.getActivePictures(),
-                    UnorderedElementsAre({{100, 10, 1}, {200, 20, 2}}));
+        EXPECT_CALL(*listener, onActivePicturesChanged(_))
+                .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+                    EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}, {200, 20, 2}}));
+                    return binder::Status::ok();
+                });
+        tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
     }
     {
         layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
@@ -255,22 +303,24 @@
         layerFE2.onPictureProfileCommitted();
         tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
 
-        ASSERT_TRUE(tracker.updateAndHasChanged());
-        EXPECT_THAT(tracker.getActivePictures(),
-                    UnorderedElementsAre({{100, 10, 2}, {200, 20, 1}}));
+        EXPECT_CALL(*listener, onActivePicturesChanged(_))
+                .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+                    EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 2}, {200, 20, 1}}));
+                    return binder::Status::ok();
+                });
+        tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
     }
 }
 
 TEST_F(ActivePictureTrackerTest, calledWhenSameUidUsesSameProfile) {
-    sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    auto layer1 = createMockLayer(100, 10);
     TestableLayerFE layerFE1;
-    EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
 
-    sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+    auto layer2 = createMockLayer(200, 10);
     TestableLayerFE layerFE2;
-    EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
 
     ActivePictureTracker tracker;
+    auto listener = createMockListener();
     {
         layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
         layerFE1.onPictureProfileCommitted();
@@ -280,9 +330,12 @@
         layerFE2.onPictureProfileCommitted();
         tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
 
-        ASSERT_TRUE(tracker.updateAndHasChanged());
-        EXPECT_THAT(tracker.getActivePictures(),
-                    UnorderedElementsAre({{100, 10, 1}, {200, 10, 2}}));
+        EXPECT_CALL(*listener, onActivePicturesChanged(_))
+                .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+                    EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}, {200, 10, 2}}));
+                    return binder::Status::ok();
+                });
+        tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
     }
     {
         layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
@@ -293,31 +346,32 @@
         layerFE2.onPictureProfileCommitted();
         tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
 
-        ASSERT_TRUE(tracker.updateAndHasChanged());
-        EXPECT_THAT(tracker.getActivePictures(),
-                    UnorderedElementsAre({{100, 10, 2}, {200, 10, 1}}));
+        EXPECT_CALL(*listener, onActivePicturesChanged(_))
+                .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+                    EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 2}, {200, 10, 1}}));
+                    return binder::Status::ok();
+                });
+        tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
     }
 }
 
 TEST_F(ActivePictureTrackerTest, calledWhenNewLayerUsesSameProfile) {
-    sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    auto layer1 = createMockLayer(100, 10);
     TestableLayerFE layerFE1;
-    EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
 
     ActivePictureTracker tracker;
+    auto listener = createMockListener();
     {
         layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
         layerFE1.onPictureProfileCommitted();
         tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
 
-        ASSERT_TRUE(tracker.updateAndHasChanged());
-        EXPECT_THAT(tracker.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+        EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1);
+        tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
     }
 
-    sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+    auto layer2 = createMockLayer(200, 10);
     TestableLayerFE layerFE2;
-    EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
     {
         layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
         layerFE1.onPictureProfileCommitted();
@@ -327,9 +381,12 @@
         layerFE2.onPictureProfileCommitted();
         tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
 
-        ASSERT_TRUE(tracker.updateAndHasChanged());
-        EXPECT_THAT(tracker.getActivePictures(),
-                    UnorderedElementsAre({{100, 10, 1}, {200, 10, 1}}));
+        EXPECT_CALL(*listener, onActivePicturesChanged(_))
+                .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+                    EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}, {200, 10, 1}}));
+                    return binder::Status::ok();
+                });
+        tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
     }
 }