Notify listeners about active picture profiles
Bug: 337330263
Test: atest ActivePictureUpdaterTest
Test: atest SurfaceControlPictureProfileTest
Flag: com.android.graphics.libgui.flags.apply_picture_profiles
Change-Id: If08b79faf3d3c4c07248ecd7385a75cfe5357726
diff --git a/services/surfaceflinger/ActivePictureUpdater.cpp b/services/surfaceflinger/ActivePictureUpdater.cpp
new file mode 100644
index 0000000..210e948
--- /dev/null
+++ b/services/surfaceflinger/ActivePictureUpdater.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright 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 "ActivePictureUpdater.h"
+
+#include <algorithm>
+
+#include "Layer.h"
+#include "LayerFE.h"
+
+namespace android {
+
+void ActivePictureUpdater::onLayerComposed(const Layer& layer, const LayerFE& layerFE,
+ const CompositionResult& result) {
+ if (result.wasPictureProfileCommitted) {
+ gui::ActivePicture picture;
+ picture.layerId = int32_t(layer.sequence);
+ picture.ownerUid = int32_t(layer.getOwnerUid());
+ // TODO(b/337330263): Why does LayerFE coming from SF have a null composition state?
+ if (layerFE.getCompositionState()) {
+ picture.pictureProfileId = layerFE.getCompositionState()->pictureProfileHandle.getId();
+ } else {
+ picture.pictureProfileId = result.pictureProfileHandle.getId();
+ }
+ mNewActivePictures.push_back(picture);
+ }
+}
+
+bool ActivePictureUpdater::updateAndHasChanged() {
+ bool hasChanged = true;
+ if (mNewActivePictures.size() == mOldActivePictures.size()) {
+ auto compare = [](const gui::ActivePicture& lhs, const gui::ActivePicture& rhs) -> int {
+ if (lhs.layerId == rhs.layerId) {
+ return lhs.pictureProfileId < rhs.pictureProfileId;
+ }
+ return lhs.layerId < rhs.layerId;
+ };
+ std::sort(mNewActivePictures.begin(), mNewActivePictures.end(), compare);
+ if (std::equal(mNewActivePictures.begin(), mNewActivePictures.end(),
+ mOldActivePictures.begin())) {
+ hasChanged = false;
+ }
+ }
+ std::swap(mOldActivePictures, mNewActivePictures);
+ mNewActivePictures.resize(0);
+ return hasChanged;
+}
+
+const std::vector<gui::ActivePicture>& ActivePictureUpdater::getActivePictures() const {
+ return mOldActivePictures;
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/ActivePictureUpdater.h b/services/surfaceflinger/ActivePictureUpdater.h
new file mode 100644
index 0000000..20779bb
--- /dev/null
+++ b/services/surfaceflinger/ActivePictureUpdater.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <android/gui/ActivePicture.h>
+
+namespace android {
+
+class Layer;
+class LayerFE;
+struct CompositionResult;
+
+// Keeps track of active pictures - layers that are undergoing picture processing.
+class ActivePictureUpdater {
+public:
+ // 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();
+
+ // The current set of active pictures.
+ const std::vector<gui::ActivePicture>& getActivePictures() const;
+
+private:
+ std::vector<gui::ActivePicture> mOldActivePictures;
+ std::vector<gui::ActivePicture> mNewActivePictures;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 8a667ae..3f3d2c6 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -199,6 +199,7 @@
name: "libsurfaceflinger_sources",
srcs: [
":libsurfaceflinger_backend_sources",
+ "ActivePictureUpdater.cpp",
"BackgroundExecutor.cpp",
"Client.cpp",
"ClientCache.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index a5e9dde..cda4edc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -161,6 +161,9 @@
// Checks if the buffer's release fence has been set
virtual LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() = 0;
+ // Indicates that the picture profile request was applied to this layer.
+ virtual void onPictureProfileCommitted() = 0;
+
// Gets some kind of identifier for the layer for debug purposes.
virtual const char* getDebugName() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index c7ff704..272fa3e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -58,6 +58,7 @@
MOCK_CONST_METHOD0(hasRoundedCorners, bool());
MOCK_CONST_METHOD0(getMetadata, gui::LayerMetadata*());
MOCK_CONST_METHOD0(getRelativeMetadata, gui::LayerMetadata*());
+ MOCK_METHOD0(onPictureProfileCommitted, void());
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index ee813bf..f9ed92d 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -830,11 +830,13 @@
for (int i = 0; i < getMaxLayerPictureProfiles() && !layersWithProfiles.empty();
layersWithProfiles.pop(), ++i) {
layersWithProfiles.top()->commitPictureProfileToCompositionState();
+ layersWithProfiles.top()->getLayerFE().onPictureProfileCommitted();
}
// No layer-specific picture processing, so apply the highest priority picture profile to
// the entire display.
} else if (!layersWithProfiles.empty()) {
editState().pictureProfileHandle = layersWithProfiles.top()->getPictureProfileHandle();
+ layersWithProfiles.top()->getLayerFE().onPictureProfileCommitted();
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index f6d9a1a..65ded8b 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -432,7 +432,7 @@
}
const auto* layerState = getLayerFE().getCompositionState();
if (layerState) {
- editState().pictureProfileHandle = getLayerFE().getCompositionState()->pictureProfileHandle;
+ editState().pictureProfileHandle = layerState->pictureProfileHandle;
}
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 99e68eb..442b603 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -5117,6 +5117,11 @@
// Sets display picture profile to the highest priority layer's profile
EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle(_, Eq(profileForLayer2)));
+ // Marks only the highest priority layer as committed
+ EXPECT_CALL(*layer1.layerFE, onPictureProfileCommitted).Times(0);
+ EXPECT_CALL(*layer2.layerFE, onPictureProfileCommitted);
+ EXPECT_CALL(*layer3.layerFE, onPictureProfileCommitted).Times(0);
+
mOutput->editState().isEnabled = true;
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = false;
@@ -5172,6 +5177,11 @@
EXPECT_CALL(*layer2.outputLayer, commitPictureProfileToCompositionState);
EXPECT_CALL(*layer3.outputLayer, commitPictureProfileToCompositionState);
+ // Marks only the highest priority layers as committed
+ EXPECT_CALL(*layer1.layerFE, onPictureProfileCommitted).Times(0);
+ EXPECT_CALL(*layer2.layerFE, onPictureProfileCommitted);
+ EXPECT_CALL(*layer3.layerFE, onPictureProfileCommitted);
+
// No display picture profile is sent
EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle).Times(0);
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index 231b40b..fea7671 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -342,8 +342,15 @@
caster.shadow = state;
}
-CompositionResult&& LayerFE::stealCompositionResult() {
- return std::move(mCompositionResult);
+void LayerFE::onPictureProfileCommitted() {
+ mCompositionResult.wasPictureProfileCommitted = true;
+ mCompositionResult.pictureProfileHandle = mSnapshot->pictureProfileHandle;
+}
+
+CompositionResult LayerFE::stealCompositionResult() {
+ CompositionResult result;
+ std::swap(mCompositionResult, result);
+ return result;
}
const char* LayerFE::getDebugName() const {
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index 5081e10..9483aeb 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -18,6 +18,9 @@
#include <android/gui/CachingHint.h>
#include <gui/LayerMetadata.h>
+#include <ui/LayerStack.h>
+#include <ui/PictureProfileHandle.h>
+
#include "FrontEnd/LayerSnapshot.h"
#include "compositionengine/LayerFE.h"
#include "compositionengine/LayerFECompositionState.h"
@@ -29,6 +32,10 @@
struct CompositionResult {
sp<Fence> lastClientCompositionFence = nullptr;
+ bool wasPictureProfileCommitted = false;
+ // TODO(b/337330263): Why does LayerFE coming from SF have a null composition state?
+ // It would be better not to duplicate this information
+ PictureProfileHandle pictureProfileHandle = PictureProfileHandle::NONE;
};
class LayerFE : public virtual RefBase, public virtual compositionengine::LayerFE {
@@ -47,10 +54,11 @@
const gui::LayerMetadata* getRelativeMetadata() const override;
std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
- CompositionResult&& stealCompositionResult();
+ CompositionResult stealCompositionResult();
ftl::Future<FenceResult> createReleaseFenceFuture() override;
void setReleaseFence(const FenceResult& releaseFence) override;
LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() override;
+ void onPictureProfileCommitted() override;
std::unique_ptr<surfaceflinger::frontend::LayerSnapshot> mSnapshot;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1f35173..dfaf8b6 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -126,6 +126,7 @@
#include <gui/SchedulingPolicy.h>
#include <gui/SyncScreenCaptureListener.h>
#include <ui/DisplayIdentification.h>
+#include "ActivePictureUpdater.h"
#include "BackgroundExecutor.h"
#include "Client.h"
#include "ClientCache.h"
@@ -372,6 +373,7 @@
const String16 sRotateSurfaceFlinger("android.permission.ROTATE_SURFACE_FLINGER");
const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS");
+const String16 sObservePictureProfiles("android.permission.OBSERVE_PICTURE_PROFILES");
const String16 sDump("android.permission.DUMP");
const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT");
const String16 sInternalSystemWindow("android.permission.INTERNAL_SYSTEM_WINDOW");
@@ -2851,6 +2853,9 @@
if (compositionResult.lastClientCompositionFence) {
layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
}
+ if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+ mActivePictureUpdater.onLayerComposed(*layer, *layerFE, compositionResult);
+ }
}
SFTRACE_NAME("postComposition");
@@ -3146,30 +3151,6 @@
layer->releasePendingBuffer(presentTime.ns());
}
- std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
- hdrInfoListeners;
- bool haveNewListeners = false;
- {
- Mutex::Autolock lock(mStateLock);
- if (mFpsReporter) {
- mFpsReporter->dispatchLayerFps(mLayerHierarchyBuilder.getHierarchy());
- }
-
- if (mTunnelModeEnabledReporter) {
- mTunnelModeEnabledReporter->updateTunnelModeStatus();
- }
- hdrInfoListeners.reserve(mHdrLayerInfoListeners.size());
- for (const auto& [displayId, reporter] : mHdrLayerInfoListeners) {
- if (reporter && reporter->hasListeners()) {
- if (const auto display = getDisplayDeviceLocked(displayId)) {
- hdrInfoListeners.emplace_back(display->getCompositionDisplay(), reporter);
- }
- }
- }
- haveNewListeners = mAddingHDRLayerInfoListener; // grab this with state lock
- mAddingHDRLayerInfoListener = false;
- }
-
for (const auto& layerEvent : mLayerEvents) {
auto result =
stats::stats_write(stats::SURFACE_CONTROL_EVENT,
@@ -3180,10 +3161,40 @@
ALOGW("Failed to report layer event with error: %d", result);
}
}
-
mLayerEvents.clear();
- if (haveNewListeners || mHdrLayerInfoChanged) {
+ std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
+ hdrInfoListeners;
+ bool haveNewHdrInfoListeners = false;
+ sp<gui::IActivePictureListener> activePictureListener;
+ bool haveNewActivePictureListener = false;
+ {
+ Mutex::Autolock lock(mStateLock);
+ if (mFpsReporter) {
+ mFpsReporter->dispatchLayerFps(mLayerHierarchyBuilder.getHierarchy());
+ }
+
+ if (mTunnelModeEnabledReporter) {
+ mTunnelModeEnabledReporter->updateTunnelModeStatus();
+ }
+
+ hdrInfoListeners.reserve(mHdrLayerInfoListeners.size());
+ for (const auto& [displayId, reporter] : mHdrLayerInfoListeners) {
+ if (reporter && reporter->hasListeners()) {
+ if (const auto display = getDisplayDeviceLocked(displayId)) {
+ hdrInfoListeners.emplace_back(display->getCompositionDisplay(), reporter);
+ }
+ }
+ }
+ haveNewHdrInfoListeners = mAddingHDRLayerInfoListener; // grab this with state lock
+ mAddingHDRLayerInfoListener = false;
+
+ activePictureListener = mActivePictureListener;
+ haveNewActivePictureListener = mHaveNewActivePictureListener;
+ mHaveNewActivePictureListener = false;
+ }
+
+ if (haveNewHdrInfoListeners || mHdrLayerInfoChanged) {
for (auto& [compositionDisplay, listener] : hdrInfoListeners) {
HdrLayerInfoReporter::HdrLayerInfo info;
int32_t maxArea = 0;
@@ -3233,9 +3244,19 @@
listener->dispatchHdrLayerInfo(info);
}
}
-
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 (mActivePictureUpdater.updateAndHasChanged() || haveNewActivePictureListener) {
+ if (activePictureListener) {
+ activePictureListener->onActivePicturesChanged(
+ mActivePictureUpdater.getActivePictures());
+ }
+ }
+ }
+
mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
mTransactionCallbackInvoker.clearCompletedTransactions();
@@ -8073,6 +8094,14 @@
}));
}
+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;
+ }
+}
+
std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData(
BufferData& bufferData, const char* layerName, uint64_t transactionId) {
if (bufferData.buffer &&
@@ -9008,6 +9037,15 @@
return binderStatusFromStatusT(status);
}
+binder::Status SurfaceComposerAIDL::setActivePictureListener(
+ const sp<gui::IActivePictureListener>& listener) {
+ status_t status = checkObservePictureProfilesPermission();
+ if (status == OK) {
+ mFlinger->setActivePictureListener(listener);
+ }
+ return binderStatusFromStatusT(status);
+}
+
binder::Status SurfaceComposerAIDL::notifyPowerBoost(int boostId) {
status_t status = checkAccessPermission();
if (status == OK) {
@@ -9263,6 +9301,17 @@
return OK;
}
+status_t SurfaceComposerAIDL::checkObservePictureProfilesPermission() {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if (!PermissionCache::checkPermission(sObservePictureProfiles, pid, uid)) {
+ ALOGE("Permission Denial: can't manage picture profiles pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+ return OK;
+}
+
void SurfaceFlinger::forceFutureUpdate(int delayInMs) {
static_cast<void>(mScheduler->scheduleDelayed([&]() { scheduleRepaint(); }, ms2ns(delayInMs)));
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 7e9d5b8..cbe2739 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -24,9 +24,11 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/thread_annotations.h>
+#include <android/gui/ActivePicture.h>
#include <android/gui/BnSurfaceComposer.h>
#include <android/gui/DisplayStatInfo.h>
#include <android/gui/DisplayState.h>
+#include <android/gui/IActivePictureListener.h>
#include <android/gui/IJankListener.h>
#include <android/gui/ISurfaceComposerClient.h>
#include <common/trace.h>
@@ -67,6 +69,8 @@
#include <ui/FenceResult.h>
#include <common/FlagManager.h>
+#include "ActivePictureUpdater.h"
+#include "BackgroundExecutor.h"
#include "Display/DisplayModeController.h"
#include "Display/PhysicalDisplay.h"
#include "DisplayDevice.h"
@@ -93,6 +97,7 @@
#include "TransactionState.h"
#include "Utils/OnceFuture.h"
+#include <algorithm>
#include <atomic>
#include <cstdint>
#include <functional>
@@ -660,6 +665,8 @@
void updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel, int32_t maxLevel);
+ void setActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+
// IBinder::DeathRecipient overrides:
void binderDied(const wp<IBinder>& who) override;
@@ -1377,6 +1384,10 @@
std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
GUARDED_BY(mStateLock);
+ sp<gui::IActivePictureListener> mActivePictureListener GUARDED_BY(mStateLock);
+ bool mHaveNewActivePictureListener GUARDED_BY(mStateLock);
+ ActivePictureUpdater mActivePictureUpdater GUARDED_BY(kMainThreadContext);
+
std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint;
// Must only be accessed on the main thread.
@@ -1610,12 +1621,15 @@
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();
private:
static const constexpr bool kUsePermissionCache = true;
status_t checkAccessPermission(bool usePermissionCache = kUsePermissionCache);
status_t checkControlDisplayBrightnessPermission();
status_t checkReadFrameBufferPermission();
+ status_t checkObservePictureProfilesPermission();
static void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info,
gui::DynamicDisplayInfo*& outInfo);
diff --git a/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp b/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp
new file mode 100644
index 0000000..b926d2f
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp
@@ -0,0 +1,336 @@
+/*
+ * Copyright 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android/gui/ActivePicture.h>
+#include <android/gui/IActivePictureListener.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <mock/DisplayHardware/MockComposer.h>
+#include <mock/MockLayer.h>
+#include <renderengine/mock/RenderEngine.h>
+
+#include "ActivePictureUpdater.h"
+#include "LayerFE.h"
+#include "TestableSurfaceFlinger.h"
+
+namespace android {
+
+using android::compositionengine::LayerFECompositionState;
+using android::gui::ActivePicture;
+using android::gui::IActivePictureListener;
+using android::mock::MockLayer;
+using surfaceflinger::frontend::LayerSnapshot;
+using testing::_;
+using testing::NiceMock;
+using testing::Return;
+
+class TestableLayerFE : public LayerFE {
+public:
+ TestableLayerFE() : LayerFE("TestableLayerFE"), snapshot(*(new LayerSnapshot)) {
+ mSnapshot = std::unique_ptr<LayerSnapshot>(&snapshot);
+ }
+
+ LayerSnapshot& snapshot;
+};
+
+class ActivePictureUpdaterTest : public testing::Test {
+protected:
+ SurfaceFlinger* flinger() {
+ if (!mFlingerSetup) {
+ mFlinger.setupMockScheduler();
+ mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+ mFlinger.setupRenderEngine(std::make_unique<renderengine::mock::RenderEngine>());
+ mFlingerSetup = true;
+ }
+ return mFlinger.flinger();
+ }
+
+private:
+ TestableSurfaceFlinger mFlinger;
+ bool mFlingerSetup = false;
+};
+
+// 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) {
+ std::vector<ActivePicture> activePictures;
+ for (auto tuple : tuples) {
+ ActivePicture ap;
+ ap.layerId = std::get<0>(tuple);
+ ap.ownerUid = std::get<1>(tuple);
+ ap.pictureProfileId = std::get<2>(tuple);
+ activePictures.push_back(ap);
+ }
+ return testing::UnorderedElementsAreArray(activePictures);
+}
+
+// Parcelables don't define this for matchers, which is unfortunate
+void PrintTo(const ActivePicture& activePicture, std::ostream* os) {
+ *os << activePicture.toString();
+}
+
+TEST_F(ActivePictureUpdaterTest, notCalledWithNoProfile) {
+ sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE;
+ EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_FALSE(updater.updateAndHasChanged());
+ }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenLayerStartsUsingProfile) {
+ sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE;
+ EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_FALSE(updater.updateAndHasChanged());
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+ }
+}
+
+TEST_F(ActivePictureUpdaterTest, notCalledWhenLayerContinuesUsingProfile) {
+ sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE;
+ EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_FALSE(updater.updateAndHasChanged());
+ }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenLayerStopsUsingProfile) {
+ sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE;
+ EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({}));
+ }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenLayerChangesProfile) {
+ sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE;
+ EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 2}}));
+ }
+}
+
+TEST_F(ActivePictureUpdaterTest, notCalledWhenUncommittedLayerChangesProfile) {
+ sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE1;
+ EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+ TestableLayerFE layerFE2;
+ EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+ }
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ ASSERT_FALSE(updater.updateAndHasChanged());
+ }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenDifferentLayerUsesSameProfile) {
+ sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE1;
+ EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+ TestableLayerFE layerFE2;
+ EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE2.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(),
+ UnorderedElementsAre({{100, 10, 1}, {200, 20, 2}}));
+ }
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE1.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE2.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(),
+ UnorderedElementsAre({{100, 10, 2}, {200, 20, 1}}));
+ }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenSameUidUsesSameProfile) {
+ sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE1;
+ EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+ TestableLayerFE layerFE2;
+ EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE2.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(),
+ UnorderedElementsAre({{100, 10, 1}, {200, 10, 2}}));
+ }
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE1.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE2.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(),
+ UnorderedElementsAre({{100, 10, 2}, {200, 10, 1}}));
+ }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenNewLayerUsesSameProfile) {
+ sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+ TestableLayerFE layerFE1;
+ EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ ActivePictureUpdater updater;
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+ }
+
+ sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+ TestableLayerFE layerFE2;
+ EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE2.onPictureProfileCommitted();
+ updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ ASSERT_TRUE(updater.updateAndHasChanged());
+ EXPECT_THAT(updater.getActivePictures(),
+ UnorderedElementsAre({{100, 10, 1}, {200, 10, 1}}));
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp b/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp
index 5413bae..72d1351 100644
--- a/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp
@@ -1,3 +1,19 @@
+/*
+ * Copyright 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 <gtest/gtest.h>
#include <condition_variable>
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 45f86fa..59f0966 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -19,6 +19,8 @@
#include <gmock/gmock.h>
#include <optional>
+#include "Layer.h"
+
namespace android::mock {
class MockLayer : public Layer {
@@ -26,8 +28,11 @@
MockLayer(SurfaceFlinger* flinger, std::string name)
: Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {}
- MockLayer(SurfaceFlinger* flinger, std::string name, std::optional<uint32_t> uid)
- : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, uid)) {}
+ MockLayer(SurfaceFlinger* flinger, std::string name, std::optional<uint32_t> id)
+ : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, id)) {}
+
+ MockLayer(SurfaceFlinger* flinger, std::optional<uint32_t> id)
+ : Layer(LayerCreationArgs(flinger, nullptr, "TestLayer", 0, {}, id)) {}
explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}