diff options
21 files changed, 670 insertions, 22 deletions
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 61aabaa7f6..1f7d16fccb 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -3280,6 +3280,13 @@ status_t SurfaceComposerClient::removeHdrLayerInfoListener( return statusTFromBinderStatus(status); } +status_t SurfaceComposerClient::setActivePictureListener( + const sp<gui::IActivePictureListener>& listener) { + binder::Status status = + ComposerServiceAIDL::getComposerService()->setActivePictureListener(listener); + return statusTFromBinderStatus(status); +} + status_t SurfaceComposerClient::notifyPowerBoost(int32_t boostId) { binder::Status status = ComposerServiceAIDL::getComposerService()->notifyPowerBoost(boostId); return statusTFromBinderStatus(status); diff --git a/libs/gui/aidl/android/gui/ActivePicture.aidl b/libs/gui/aidl/android/gui/ActivePicture.aidl new file mode 100644 index 0000000000..9b83be16ca --- /dev/null +++ b/libs/gui/aidl/android/gui/ActivePicture.aidl @@ -0,0 +1,32 @@ +/* + * 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. + */ + +package android.gui; + +/** + * Visible content that is using picture processing. + * @hide + */ +parcelable ActivePicture { + /** The layer ID that is using picture processing. */ + int layerId; + + /** UID that owns layer using picture processing. */ + int ownerUid; + + /** ID of the picture profile that was used to configure the picture processing. */ + long pictureProfileId; +} diff --git a/libs/gui/aidl/android/gui/IActivePictureListener.aidl b/libs/gui/aidl/android/gui/IActivePictureListener.aidl new file mode 100644 index 0000000000..ad310bbd66 --- /dev/null +++ b/libs/gui/aidl/android/gui/IActivePictureListener.aidl @@ -0,0 +1,30 @@ +/* + * 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. + */ + +package android.gui; + +import android.gui.ActivePicture; + +/** + * Receive callbacks whenever the visible content using picture profiles changes. + * @hide + */ +interface IActivePictureListener { + /** + * Callback reporting the visible content on the screen using picture profiles. + */ + oneway void onActivePicturesChanged(in ActivePicture[] activePictures); +} diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index ac14138e8c..c8bf0ef444 100644 --- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl +++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl @@ -33,6 +33,7 @@ import android.gui.FrameEvent; import android.gui.FrameStats; import android.gui.HdrConversionCapability; import android.gui.HdrConversionStrategy; +import android.gui.IActivePictureListener; import android.gui.IDisplayEventConnection; import android.gui.IFpsListener; import android.gui.IHdrLayerInfoListener; @@ -599,4 +600,10 @@ interface ISurfaceComposer { * past the provided VSync. */ oneway void removeJankListener(int layerId, IJankListener listener, long afterVsync); + + /** + * Sets the listener used to monitor visible content that is being processed with picture + * profiles. + */ + oneway void setActivePictureListener(IActivePictureListener listener); } diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 0d7f8c2824..ec62ec3dfb 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -298,6 +298,8 @@ public: static status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener); + static status_t setActivePictureListener(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 c74186682e..38c7f2b70a 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -21,6 +21,7 @@ #include <gtest/gtest.h> #include <SurfaceFlingerProperties.h> +#include <android/gui/IActivePictureListener.h> #include <android/gui/IDisplayEventConnection.h> #include <android/gui/ISurfaceComposer.h> #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> @@ -1015,6 +1016,10 @@ public: return binder::Status::ok(); } + binder::Status setActivePictureListener(const sp<gui::IActivePictureListener>&) { + return binder::Status::ok(); + } + protected: IBinder* onAsBinder() override { return nullptr; } diff --git a/services/surfaceflinger/ActivePictureUpdater.cpp b/services/surfaceflinger/ActivePictureUpdater.cpp new file mode 100644 index 0000000000..210e948a49 --- /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 0000000000..20779bb0ff --- /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 8a667aef72..3f3d2c6cc6 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -199,6 +199,7 @@ filegroup { 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 a5e9dded8b..cda4edc216 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h @@ -161,6 +161,9 @@ public: // 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 c7ff704491..272fa3eef7 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h @@ -58,6 +58,7 @@ public: 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 ee813bf340..f9ed92d1ee 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -830,11 +830,13 @@ void Output::commitPictureProfilesToCompositionState() { 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 f6d9a1ae6c..65ded8b4c1 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -432,7 +432,7 @@ void OutputLayer::commitPictureProfileToCompositionState() { } 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 99e68eb71a..442b603ca0 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -5117,6 +5117,11 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsDisplayProfileBasedOnLay // 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 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsLayerProfileBasedOnLayer 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 231b40b0ac..fea7671af2 100644 --- a/services/surfaceflinger/LayerFE.cpp +++ b/services/surfaceflinger/LayerFE.cpp @@ -342,8 +342,15 @@ void LayerFE::prepareShadowClientComposition(LayerFE::LayerSettings& caster, 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 5081e102b8..9483aebafa 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 @@ namespace android { 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 @@ public: 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 1f35173f65..dfaf8b6c04 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 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER" 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 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( 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,9 +3151,23 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, layer->releasePendingBuffer(presentTime.ns()); } + for (const auto& layerEvent : mLayerEvents) { + auto result = + stats::stats_write(stats::SURFACE_CONTROL_EVENT, + static_cast<int32_t>(layerEvent.uid), + static_cast<int64_t>(layerEvent.timeSinceLastEvent.count()), + static_cast<int32_t>(layerEvent.dataspace)); + if (result < 0) { + ALOGW("Failed to report layer event with error: %d", result); + } + } + mLayerEvents.clear(); + std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>> hdrInfoListeners; - bool haveNewListeners = false; + bool haveNewHdrInfoListeners = false; + sp<gui::IActivePictureListener> activePictureListener; + bool haveNewActivePictureListener = false; { Mutex::Autolock lock(mStateLock); if (mFpsReporter) { @@ -3158,6 +3177,7 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, if (mTunnelModeEnabledReporter) { mTunnelModeEnabledReporter->updateTunnelModeStatus(); } + hdrInfoListeners.reserve(mHdrLayerInfoListeners.size()); for (const auto& [displayId, reporter] : mHdrLayerInfoListeners) { if (reporter && reporter->hasListeners()) { @@ -3166,24 +3186,15 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, } } } - haveNewListeners = mAddingHDRLayerInfoListener; // grab this with state lock + haveNewHdrInfoListeners = mAddingHDRLayerInfoListener; // grab this with state lock mAddingHDRLayerInfoListener = false; - } - for (const auto& layerEvent : mLayerEvents) { - auto result = - stats::stats_write(stats::SURFACE_CONTROL_EVENT, - static_cast<int32_t>(layerEvent.uid), - static_cast<int64_t>(layerEvent.timeSinceLastEvent.count()), - static_cast<int32_t>(layerEvent.dataspace)); - if (result < 0) { - ALOGW("Failed to report layer event with error: %d", result); - } + activePictureListener = mActivePictureListener; + haveNewActivePictureListener = mHaveNewActivePictureListener; + mHaveNewActivePictureListener = false; } - mLayerEvents.clear(); - - if (haveNewListeners || mHdrLayerInfoChanged) { + if (haveNewHdrInfoListeners || mHdrLayerInfoChanged) { for (auto& [compositionDisplay, listener] : hdrInfoListeners) { HdrLayerInfoReporter::HdrLayerInfo info; int32_t maxArea = 0; @@ -3233,9 +3244,19 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, 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::updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t con })); } +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 @@ binder::Status SurfaceComposerAIDL::removeHdrLayerInfoListener( 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 @@ status_t SurfaceComposerAIDL::checkReadFrameBufferPermission() { 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 7e9d5b881f..cbe2739664 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 @@ private: 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 @@ private: 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 @@ public: 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 0000000000..b926d2f4da --- /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 5413bae55c..72d1351eb4 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 45f86fa1a8..59f0966047 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 @@ public: 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") {} |