blob: 8fa7e3a80f8bcd4edc81906acfed829432aab0b5 [file] [log] [blame] [edit]
/*
* Copyright (C) 2007 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.
*/
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wextra"
//#define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "SurfaceFlinger.h"
#include <aidl/android/hardware/power/Boost.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android/configuration.h>
#include <android/gui/IDisplayEventConnection.h>
#include <android/gui/StaticDisplayInfo.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/types.h>
#include <android/native_window.h>
#include <android/os/IInputFlinger.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
#include <com_android_graphics_surfaceflinger_flags.h>
#include <common/FlagManager.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/Display.h>
#include <compositionengine/DisplayColorProfile.h>
#include <compositionengine/DisplayColorProfileCreationArgs.h>
#include <compositionengine/DisplayCreationArgs.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/OutputLayer.h>
#include <compositionengine/RenderSurface.h>
#include <compositionengine/impl/DisplayColorProfile.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <configstore/Utils.h>
#include <cutils/compiler.h>
#include <cutils/properties.h>
#include <fmt/format.h>
#include <ftl/algorithm.h>
#include <ftl/concat.h>
#include <ftl/fake_guard.h>
#include <ftl/future.h>
#include <ftl/unit.h>
#include <gui/AidlStatusUtil.h>
#include <gui/BufferQueue.h>
#include <gui/DebugEGLImageTracker.h>
#include <gui/IProducerListener.h>
#include <gui/LayerMetadata.h>
#include <gui/LayerState.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/TraceUtils.h>
#include <hidl/ServiceManagement.h>
#include <layerproto/LayerProtoParser.h>
#include <linux/sched/types.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
#include <private/gui/SyncFeatures.h>
#include <processgroup/processgroup.h>
#include <renderengine/RenderEngine.h>
#include <renderengine/impl/ExternalTexture.h>
#include <scheduler/FrameTargeter.h>
#include <sys/types.h>
#include <ui/ColorSpace.h>
#include <ui/DebugUtils.h>
#include <ui/DisplayId.h>
#include <ui/DisplayMode.h>
#include <ui/DisplayStatInfo.h>
#include <ui/DisplayState.h>
#include <ui/DynamicDisplayInfo.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/HdrRenderTypeUtils.h>
#include <ui/LayerStack.h>
#include <ui/PixelFormat.h>
#include <ui/StaticDisplayInfo.h>
#include <unistd.h>
#include <utils/StopWatch.h>
#include <utils/String16.h>
#include <utils/String8.h>
#include <utils/Timers.h>
#include <utils/misc.h>
#include <algorithm>
#include <cerrno>
#include <cinttypes>
#include <cmath>
#include <cstdint>
#include <filesystem>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>
#include <common/FlagManager.h>
#include <gui/LayerStatePermissions.h>
#include <gui/SchedulingPolicy.h>
#include <gui/SyncScreenCaptureListener.h>
#include <ui/DisplayIdentification.h>
#include "BackgroundExecutor.h"
#include "Client.h"
#include "ClientCache.h"
#include "Colorizer.h"
#include "DisplayDevice.h"
#include "DisplayHardware/ComposerHal.h"
#include "DisplayHardware/FramebufferSurface.h"
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/Hal.h"
#include "DisplayHardware/PowerAdvisor.h"
#include "DisplayHardware/VirtualDisplaySurface.h"
#include "DisplayRenderArea.h"
#include "Effects/Daltonizer.h"
#include "FpsReporter.h"
#include "FrameTimeline/FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/LayerHandle.h"
#include "FrontEnd/LayerLifecycleManager.h"
#include "FrontEnd/LayerLog.h"
#include "FrontEnd/LayerSnapshot.h"
#include "HdrLayerInfoReporter.h"
#include "Layer.h"
#include "LayerProtoHelper.h"
#include "LayerRenderArea.h"
#include "LayerVector.h"
#include "MutexUtils.h"
#include "NativeWindowSurface.h"
#include "RegionSamplingThread.h"
#include "RenderAreaBuilder.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VsyncConfiguration.h"
#include "Scheduler/VsyncModulator.h"
#include "ScreenCaptureOutput.h"
#include "SurfaceFlingerProperties.h"
#include "TimeStats/TimeStats.h"
#include "TunnelModeEnabledReporter.h"
#include "Utils/Dumper.h"
#include "WindowInfosListenerInvoker.h"
#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
#include <aidl/android/hardware/graphics/composer3/RenderIntent.h>
#undef NO_THREAD_SAFETY_ANALYSIS
#define NO_THREAD_SAFETY_ANALYSIS \
_Pragma("GCC error \"Prefer <ftl/fake_guard.h> or MutexUtils.h helpers.\"")
namespace android {
using namespace std::chrono_literals;
using namespace std::string_literals;
using namespace std::string_view_literals;
using namespace hardware::configstore;
using namespace hardware::configstore::V1_0;
using namespace sysprop;
using ftl::Flags;
using namespace ftl::flag_operators;
using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
using aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::DisplayCapability;
using CompositionStrategyPredictionState = android::compositionengine::impl::
OutputCompositionState::CompositionStrategyPredictionState;
using base::StringAppendF;
using display::PhysicalDisplay;
using display::PhysicalDisplays;
using frontend::TransactionHandler;
using gui::DisplayInfo;
using gui::GameMode;
using gui::IDisplayEventConnection;
using gui::IWindowInfosListener;
using gui::LayerMetadata;
using gui::WindowInfo;
using gui::aidl_utils::binderStatusFromStatusT;
using scheduler::VsyncModulator;
using ui::Dataspace;
using ui::DisplayPrimaries;
using ui::RenderIntent;
using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController;
namespace hal = android::hardware::graphics::composer::hal;
namespace {
static constexpr int FOUR_K_WIDTH = 3840;
static constexpr int FOUR_K_HEIGHT = 2160;
// TODO(b/141333600): Consolidate with DisplayMode::Builder::getDefaultDensity.
constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV;
float getDensityFromProperty(const char* property, bool required) {
char value[PROPERTY_VALUE_MAX];
const float density = property_get(property, value, nullptr) > 0 ? std::atof(value) : 0.f;
if (!density && required) {
ALOGE("%s must be defined as a build property", property);
return FALLBACK_DENSITY;
}
return density;
}
// Currently we only support V0_SRGB and DISPLAY_P3 as composition preference.
bool validateCompositionDataspace(Dataspace dataspace) {
return dataspace == Dataspace::V0_SRGB || dataspace == Dataspace::DISPLAY_P3;
}
std::chrono::milliseconds getIdleTimerTimeout(PhysicalDisplayId displayId) {
if (const int32_t displayIdleTimerMs =
base::GetIntProperty("debug.sf.set_idle_timer_ms_"s +
std::to_string(displayId.value),
0);
displayIdleTimerMs > 0) {
return std::chrono::milliseconds(displayIdleTimerMs);
}
const int32_t setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms"s, 0);
const int32_t millis = setIdleTimerMs ? setIdleTimerMs : sysprop::set_idle_timer_ms(0);
return std::chrono::milliseconds(millis);
}
bool getKernelIdleTimerSyspropConfig(PhysicalDisplayId displayId) {
const bool displaySupportKernelIdleTimer =
base::GetBoolProperty("debug.sf.support_kernel_idle_timer_"s +
std::to_string(displayId.value),
false);
return displaySupportKernelIdleTimer || sysprop::support_kernel_idle_timer(false);
}
bool isAbove4k30(const ui::DisplayMode& outMode) {
using fps_approx_ops::operator>;
Fps refreshRate = Fps::fromValue(outMode.peakRefreshRate);
return outMode.resolution.getWidth() >= FOUR_K_WIDTH &&
outMode.resolution.getHeight() >= FOUR_K_HEIGHT && refreshRate > 30_Hz;
}
void excludeDolbyVisionIf4k30Present(const std::vector<ui::Hdr>& displayHdrTypes,
ui::DisplayMode& outMode) {
if (isAbove4k30(outMode) &&
std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
[](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION_4K30; })) {
for (ui::Hdr type : displayHdrTypes) {
if (type != ui::Hdr::DOLBY_VISION_4K30 && type != ui::Hdr::DOLBY_VISION) {
outMode.supportedHdrTypes.push_back(type);
}
}
} else {
for (ui::Hdr type : displayHdrTypes) {
if (type != ui::Hdr::DOLBY_VISION_4K30) {
outMode.supportedHdrTypes.push_back(type);
}
}
}
}
HdrCapabilities filterOut4k30(const HdrCapabilities& displayHdrCapabilities) {
std::vector<ui::Hdr> hdrTypes;
for (ui::Hdr type : displayHdrCapabilities.getSupportedHdrTypes()) {
if (type != ui::Hdr::DOLBY_VISION_4K30) {
hdrTypes.push_back(type);
}
}
return {hdrTypes, displayHdrCapabilities.getDesiredMaxLuminance(),
displayHdrCapabilities.getDesiredMaxAverageLuminance(),
displayHdrCapabilities.getDesiredMinLuminance()};
}
uint32_t getLayerIdFromSurfaceControl(sp<SurfaceControl> surfaceControl) {
if (!surfaceControl) {
return UNASSIGNED_LAYER_ID;
}
return LayerHandle::getLayerId(surfaceControl->getHandle());
}
/**
* Returns true if the file at path exists and is newer than duration.
*/
bool fileNewerThan(const std::string& path, std::chrono::minutes duration) {
using Clock = std::filesystem::file_time_type::clock;
std::error_code error;
std::filesystem::file_time_type updateTime = std::filesystem::last_write_time(path, error);
if (error) {
return false;
}
return duration > (Clock::now() - updateTime);
}
bool isFrameIntervalOnCadence(TimePoint expectedPresentTime, TimePoint lastExpectedPresentTimestamp,
Fps lastFrameInterval, Period timeout, Duration threshold) {
if (lastFrameInterval.getPeriodNsecs() == 0) {
return false;
}
const auto expectedPresentTimeDeltaNs =
expectedPresentTime.ns() - lastExpectedPresentTimestamp.ns();
if (expectedPresentTimeDeltaNs > timeout.ns()) {
return false;
}
const auto expectedPresentPeriods = static_cast<nsecs_t>(
std::round(static_cast<float>(expectedPresentTimeDeltaNs) /
static_cast<float>(lastFrameInterval.getPeriodNsecs())));
const auto calculatedPeriodsOutNs = lastFrameInterval.getPeriodNsecs() * expectedPresentPeriods;
const auto calculatedExpectedPresentTimeNs =
lastExpectedPresentTimestamp.ns() + calculatedPeriodsOutNs;
const auto presentTimeDelta =
std::abs(expectedPresentTime.ns() - calculatedExpectedPresentTimeNs);
return presentTimeDelta < threshold.ns();
}
bool isExpectedPresentWithinTimeout(TimePoint expectedPresentTime,
TimePoint lastExpectedPresentTimestamp,
std::optional<Period> timeoutOpt, Duration threshold) {
if (!timeoutOpt) {
// Always within timeout if timeoutOpt is absent and don't send hint
// for the timeout
return true;
}
if (timeoutOpt->ns() == 0) {
// Always outside timeout if timeoutOpt is 0 and always send
// the hint for the timeout.
return false;
}
if (expectedPresentTime.ns() < lastExpectedPresentTimestamp.ns() + timeoutOpt->ns()) {
return true;
}
// Check if within the threshold as it can be just outside the timeout
return std::abs(expectedPresentTime.ns() -
(lastExpectedPresentTimestamp.ns() + timeoutOpt->ns())) < threshold.ns();
}
} // namespace anonymous
// ---------------------------------------------------------------------------
const String16 sHardwareTest("android.permission.HARDWARE_TEST");
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 sDump("android.permission.DUMP");
const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT");
const String16 sInternalSystemWindow("android.permission.INTERNAL_SYSTEM_WINDOW");
const String16 sWakeupSurfaceFlinger("android.permission.WAKEUP_SURFACE_FLINGER");
const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
// ---------------------------------------------------------------------------
int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
bool SurfaceFlinger::useHwcForRgbToYuv;
bool SurfaceFlinger::hasSyncFramework;
int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
int64_t SurfaceFlinger::minAcquiredBuffers = 1;
uint32_t SurfaceFlinger::maxGraphicsWidth;
uint32_t SurfaceFlinger::maxGraphicsHeight;
bool SurfaceFlinger::useContextPriority;
Dataspace SurfaceFlinger::defaultCompositionDataspace = Dataspace::V0_SRGB;
ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
LatchUnsignaledConfig SurfaceFlinger::enableLatchUnsignaledConfig;
std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
switch(displayColorSetting) {
case DisplayColorSetting::kManaged:
return std::string("Managed");
case DisplayColorSetting::kUnmanaged:
return std::string("Unmanaged");
case DisplayColorSetting::kEnhanced:
return std::string("Enhanced");
default:
return std::string("Unknown ") +
std::to_string(static_cast<int>(displayColorSetting));
}
}
bool callingThreadHasPermission(const String16& permission) {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
PermissionCache::checkPermission(permission, pid, uid);
}
ui::Transform::RotationFlags SurfaceFlinger::sActiveDisplayRotationFlags = ui::Transform::ROT_0;
SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
: mFactory(factory),
mPid(getpid()),
mTimeStats(std::make_shared<impl::TimeStats>()),
mFrameTracer(mFactory.createFrameTracer()),
mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, mPid)),
mCompositionEngine(mFactory.createCompositionEngine()),
mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)),
mTunnelModeEnabledReporter(sp<TunnelModeEnabledReporter>::make()),
mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)),
mInternalDisplayDensity(
getDensityFromProperty("ro.sf.lcd_density", !mEmulatedDisplayDensity)),
mPowerAdvisor(std::make_unique<Hwc2::impl::PowerAdvisor>(*this)),
mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make()),
mSkipPowerOnForQuiescent(base::GetBoolProperty("ro.boot.quiescent"s, false)) {
ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str());
}
SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
ATRACE_CALL();
ALOGI("SurfaceFlinger is starting");
hasSyncFramework = running_without_sync_framework(true);
dispSyncPresentTimeOffset = present_time_offset_from_vsync_ns(0);
useHwcForRgbToYuv = force_hwc_copy_for_virtual_displays(false);
maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
minAcquiredBuffers =
SurfaceFlingerProperties::min_acquired_buffers().value_or(minAcquiredBuffers);
maxGraphicsWidth = std::max(max_graphics_width(0), 0);
maxGraphicsHeight = std::max(max_graphics_height(0), 0);
mSupportsWideColor = has_wide_color_display(false);
mDefaultCompositionDataspace =
static_cast<ui::Dataspace>(default_composition_dataspace(Dataspace::V0_SRGB));
mWideColorGamutCompositionDataspace = static_cast<ui::Dataspace>(wcg_composition_dataspace(
mSupportsWideColor ? Dataspace::DISPLAY_P3 : Dataspace::V0_SRGB));
defaultCompositionDataspace = mDefaultCompositionDataspace;
wideColorGamutCompositionDataspace = mWideColorGamutCompositionDataspace;
defaultCompositionPixelFormat = static_cast<ui::PixelFormat>(
default_composition_pixel_format(ui::PixelFormat::RGBA_8888));
wideColorGamutCompositionPixelFormat =
static_cast<ui::PixelFormat>(wcg_composition_pixel_format(ui::PixelFormat::RGBA_8888));
mLayerCachingEnabled =
base::GetBoolProperty("debug.sf.enable_layer_caching"s,
sysprop::SurfaceFlingerProperties::enable_layer_caching()
.value_or(false));
useContextPriority = use_context_priority(true);
mInternalDisplayPrimaries = sysprop::getDisplayNativePrimaries();
// debugging stuff...
char value[PROPERTY_VALUE_MAX];
property_get("ro.build.type", value, "user");
mIsUserBuild = strcmp(value, "user") == 0;
mDebugFlashDelay = base::GetUintProperty("debug.sf.showupdates"s, 0u);
mBackpressureGpuComposition = base::GetBoolProperty("debug.sf.enable_gl_backpressure"s, true);
ALOGI_IF(mBackpressureGpuComposition, "Enabling backpressure for GPU composition");
property_get("ro.surface_flinger.supports_background_blur", value, "0");
bool supportsBlurs = atoi(value);
mSupportsBlur = supportsBlurs;
ALOGI_IF(!mSupportsBlur, "Disabling blur effects, they are not supported.");
property_get("debug.sf.luma_sampling", value, "1");
mLumaSampling = atoi(value);
property_get("debug.sf.disable_client_composition_cache", value, "0");
mDisableClientCompositionCache = atoi(value);
property_get("debug.sf.predict_hwc_composition_strategy", value, "1");
mPredictCompositionStrategy = atoi(value);
property_get("debug.sf.treat_170m_as_sRGB", value, "0");
mTreat170mAsSrgb = atoi(value);
property_get("debug.sf.dim_in_gamma_in_enhanced_screenshots", value, 0);
mDimInGammaSpaceForEnhancedScreenshots = atoi(value);
mIgnoreHwcPhysicalDisplayOrientation =
base::GetBoolProperty("debug.sf.ignore_hwc_physical_display_orientation"s, false);
// We should be reading 'persist.sys.sf.color_saturation' here
// but since /data may be encrypted, we need to wait until after vold
// comes online to attempt to read the property. The property is
// instead read after the boot animation
if (base::GetBoolProperty("debug.sf.treble_testing_override"s, false)) {
// Without the override SurfaceFlinger cannot connect to HIDL
// services that are not listed in the manifests. Considered
// deriving the setting from the set service name, but it
// would be brittle if the name that's not 'default' is used
// for production purposes later on.
ALOGI("Enabling Treble testing override");
android::hardware::details::setTrebleTestingOverride(true);
}
// TODO (b/270966065) Update the HWC based refresh rate overlay to support spinner
mRefreshRateOverlaySpinner = property_get_bool("debug.sf.show_refresh_rate_overlay_spinner", 0);
mRefreshRateOverlayRenderRate =
property_get_bool("debug.sf.show_refresh_rate_overlay_render_rate", 0);
mRefreshRateOverlayShowInMiddle =
property_get_bool("debug.sf.show_refresh_rate_overlay_in_middle", 0);
if (!mIsUserBuild && base::GetBoolProperty("debug.sf.enable_transaction_tracing"s, true)) {
mTransactionTracing.emplace();
mLayerTracing.setTransactionTracing(*mTransactionTracing);
}
mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false);
mLayerLifecycleManagerEnabled =
base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, true);
// These are set by the HWC implementation to indicate that they will use the workarounds.
mIsHotplugErrViaNegVsync =
base::GetBoolProperty("debug.sf.hwc_hotplug_error_via_neg_vsync"s, false);
mIsHdcpViaNegVsync = base::GetBoolProperty("debug.sf.hwc_hdcp_via_neg_vsync"s, false);
}
LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
if (base::GetBoolProperty("debug.sf.auto_latch_unsignaled"s, true)) {
return LatchUnsignaledConfig::AutoSingleLayer;
}
return LatchUnsignaledConfig::Disabled;
}
SurfaceFlinger::~SurfaceFlinger() = default;
void SurfaceFlinger::binderDied(const wp<IBinder>&) {
// the window manager died on us. prepare its eulogy.
mBootFinished = false;
static_cast<void>(mScheduler->schedule([this]() FTL_FAKE_GUARD(kMainThreadContext) {
// Sever the link to inputflinger since it's gone as well.
mInputFlinger.clear();
initializeDisplays();
}));
mInitBootPropsFuture.callOnce([this] {
return std::async(std::launch::async, &SurfaceFlinger::initBootProperties, this);
});
mInitBootPropsFuture.wait();
}
void SurfaceFlinger::run() {
mScheduler->run();
}
sp<IBinder> SurfaceFlinger::createVirtualDisplay(const std::string& displayName, bool isSecure,
const std::string& uniqueId,
float requestedRefreshRate) {
// SurfaceComposerAIDL checks for some permissions, but adding an additional check here.
// This is to ensure that only root, system, and graphics can request to create a secure
// display. Secure displays can show secure content so we add an additional restriction on it.
const uid_t uid = IPCThreadState::self()->getCallingUid();
if (isSecure && uid != AID_ROOT && uid != AID_GRAPHICS && uid != AID_SYSTEM) {
ALOGE("Only privileged processes can create a secure display");
return nullptr;
}
class DisplayToken : public BBinder {
sp<SurfaceFlinger> flinger;
virtual ~DisplayToken() {
// no more references, this display must be terminated
Mutex::Autolock _l(flinger->mStateLock);
flinger->mCurrentState.displays.removeItem(wp<IBinder>::fromExisting(this));
flinger->setTransactionFlags(eDisplayTransactionNeeded);
}
public:
explicit DisplayToken(const sp<SurfaceFlinger>& flinger)
: flinger(flinger) {
}
};
sp<BBinder> token = sp<DisplayToken>::make(sp<SurfaceFlinger>::fromExisting(this));
Mutex::Autolock _l(mStateLock);
// Display ID is assigned when virtual display is allocated by HWC.
DisplayDeviceState state;
state.isSecure = isSecure;
// Set display as protected when marked as secure to ensure no behavior change
// TODO (b/314820005): separate as a different arg when creating the display.
state.isProtected = isSecure;
state.displayName = displayName;
state.uniqueId = uniqueId;
state.requestedRefreshRate = Fps::fromValue(requestedRefreshRate);
mCurrentState.displays.add(token, state);
return token;
}
status_t SurfaceFlinger::destroyVirtualDisplay(const sp<IBinder>& displayToken) {
Mutex::Autolock lock(mStateLock);
const ssize_t index = mCurrentState.displays.indexOfKey(displayToken);
if (index < 0) {
ALOGE("%s: Invalid display token %p", __func__, displayToken.get());
return NAME_NOT_FOUND;
}
const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
if (state.physical) {
ALOGE("%s: Invalid operation on physical display", __func__);
return INVALID_OPERATION;
}
mCurrentState.displays.removeItemsAt(index);
setTransactionFlags(eDisplayTransactionNeeded);
return NO_ERROR;
}
void SurfaceFlinger::enableHalVirtualDisplays(bool enable) {
auto& generator = mVirtualDisplayIdGenerators.hal;
if (!generator && enable) {
ALOGI("Enabling HAL virtual displays");
generator.emplace(getHwComposer().getMaxVirtualDisplayCount());
} else if (generator && !enable) {
ALOGW_IF(generator->inUse(), "Disabling HAL virtual displays while in use");
generator.reset();
}
}
VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution,
ui::PixelFormat format) {
if (auto& generator = mVirtualDisplayIdGenerators.hal) {
if (const auto id = generator->generateId()) {
if (getHwComposer().allocateVirtualDisplay(*id, resolution, &format)) {
return *id;
}
generator->releaseId(*id);
} else {
ALOGW("%s: Exhausted HAL virtual displays", __func__);
}
ALOGW("%s: Falling back to GPU virtual display", __func__);
}
const auto id = mVirtualDisplayIdGenerators.gpu.generateId();
LOG_ALWAYS_FATAL_IF(!id, "Failed to generate ID for GPU virtual display");
return *id;
}
void SurfaceFlinger::releaseVirtualDisplay(VirtualDisplayId displayId) {
if (const auto id = HalVirtualDisplayId::tryCast(displayId)) {
if (auto& generator = mVirtualDisplayIdGenerators.hal) {
generator->releaseId(*id);
}
return;
}
const auto id = GpuVirtualDisplayId::tryCast(displayId);
LOG_ALWAYS_FATAL_IF(!id);
mVirtualDisplayIdGenerators.gpu.releaseId(*id);
}
std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const {
std::vector<PhysicalDisplayId> displayIds;
displayIds.reserve(mPhysicalDisplays.size());
const auto defaultDisplayId = getDefaultDisplayDeviceLocked()->getPhysicalId();
displayIds.push_back(defaultDisplayId);
for (const auto& [id, display] : mPhysicalDisplays) {
if (id != defaultDisplayId) {
displayIds.push_back(id);
}
}
return displayIds;
}
std::optional<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdLocked(
const sp<display::DisplayToken>& displayToken) const {
return ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
.transform(&ftl::to_key<PhysicalDisplays>);
}
sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId) const {
Mutex::Autolock lock(mStateLock);
return getPhysicalDisplayTokenLocked(displayId);
}
HWComposer& SurfaceFlinger::getHwComposer() const {
return mCompositionEngine->getHwComposer();
}
renderengine::RenderEngine& SurfaceFlinger::getRenderEngine() const {
return *mRenderEngine;
}
compositionengine::CompositionEngine& SurfaceFlinger::getCompositionEngine() const {
return *mCompositionEngine.get();
}
void SurfaceFlinger::bootFinished() {
if (mBootFinished == true) {
ALOGE("Extra call to bootFinished");
return;
}
mBootFinished = true;
FlagManager::getMutableInstance().markBootCompleted();
mInitBootPropsFuture.wait();
mRenderEnginePrimeCacheFuture.wait();
const nsecs_t now = systemTime();
const nsecs_t duration = now - mBootTime;
ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
mFrameTracer->initialize();
mFrameTimeline->onBootFinished();
getRenderEngine().setEnableTracing(FlagManager::getInstance().use_skia_tracing());
// wait patiently for the window manager death
const String16 name("window");
mWindowManager = defaultServiceManager()->waitForService(name);
if (mWindowManager != 0) {
mWindowManager->linkToDeath(sp<IBinder::DeathRecipient>::fromExisting(this));
}
// stop boot animation
// formerly we would just kill the process, but we now ask it to exit so it
// can choose where to stop the animation.
property_set("service.bootanim.exit", "1");
const int LOGTAG_SF_STOP_BOOTANIM = 60110;
LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger")));
static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) {
if (input == nullptr) {
ALOGE("Failed to link to input service");
} else {
mInputFlinger = interface_cast<os::IInputFlinger>(input);
}
readPersistentProperties();
const bool hintSessionEnabled = FlagManager::getInstance().use_adpf_cpu_hint();
mPowerAdvisor->enablePowerHintSession(hintSessionEnabled);
const bool hintSessionUsed = mPowerAdvisor->usePowerHintSession();
// Ordering is important here, as onBootFinished signals to PowerAdvisor that concurrency
// is safe because its variables are initialized.
mPowerAdvisor->onBootFinished();
ALOGD("Power hint is %s",
hintSessionUsed ? "supported" : (hintSessionEnabled ? "unsupported" : "disabled"));
if (hintSessionUsed) {
std::optional<pid_t> renderEngineTid = getRenderEngine().getRenderEngineTid();
std::vector<int32_t> tidList;
tidList.emplace_back(gettid());
if (renderEngineTid.has_value()) {
tidList.emplace_back(*renderEngineTid);
}
if (!mPowerAdvisor->startPowerHintSession(std::move(tidList))) {
ALOGW("Cannot start power hint session");
}
}
mBootStage = BootStage::FINISHED;
if (base::GetBoolProperty("sf.debug.show_refresh_rate_overlay"s, false)) {
ftl::FakeGuard guard(mStateLock);
enableRefreshRateOverlay(true);
}
}));
}
void chooseRenderEngineType(renderengine::RenderEngineCreationArgs::Builder& builder) {
char prop[PROPERTY_VALUE_MAX];
property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
// TODO: b/293371537 - Once GraphiteVk is deemed relatively stable, log a warning that
// PROPERTY_DEBUG_RENDERENGINE_BACKEND is deprecated
if (strcmp(prop, "skiagl") == 0) {
builder.setThreaded(renderengine::RenderEngine::Threaded::NO)
.setGraphicsApi(renderengine::RenderEngine::GraphicsApi::GL);
} else if (strcmp(prop, "skiaglthreaded") == 0) {
builder.setThreaded(renderengine::RenderEngine::Threaded::YES)
.setGraphicsApi(renderengine::RenderEngine::GraphicsApi::GL);
} else if (strcmp(prop, "skiavk") == 0) {
builder.setThreaded(renderengine::RenderEngine::Threaded::NO)
.setGraphicsApi(renderengine::RenderEngine::GraphicsApi::VK);
} else if (strcmp(prop, "skiavkthreaded") == 0) {
builder.setThreaded(renderengine::RenderEngine::Threaded::YES)
.setGraphicsApi(renderengine::RenderEngine::GraphicsApi::VK);
} else {
const auto kVulkan = renderengine::RenderEngine::GraphicsApi::VK;
// TODO: b/341728634 - Clean up conditional compilation.
// Note: this guard in particular must check e.g.
// COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_GRAPHITE_RENDERENGINE directly (instead of calling e.g.
// COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS(GRAPHITE_RENDERENGINE)) because that macro is undefined
// in the libsurfaceflingerflags_test variant of com_android_graphics_surfaceflinger_flags.h, which
// is used by layertracegenerator (which also needs SurfaceFlinger.cpp). :)
#if COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_GRAPHITE_RENDERENGINE || \
COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_FORCE_COMPILE_GRAPHITE_RENDERENGINE
const bool useGraphite = FlagManager::getInstance().graphite_renderengine() &&
renderengine::RenderEngine::canSupport(kVulkan);
#else
const bool useGraphite = false;
if (FlagManager::getInstance().graphite_renderengine()) {
ALOGE("RenderEngine's Graphite Skia backend was requested with the "
"debug.renderengine.graphite system property, but it is not compiled in this "
"build! Falling back to Ganesh backend selection logic.");
}
#endif
const bool useVulkan = useGraphite ||
(FlagManager::getInstance().vulkan_renderengine() &&
renderengine::RenderEngine::canSupport(kVulkan));
builder.setSkiaBackend(useGraphite ? renderengine::RenderEngine::SkiaBackend::GRAPHITE
: renderengine::RenderEngine::SkiaBackend::GANESH);
builder.setGraphicsApi(useVulkan ? kVulkan : renderengine::RenderEngine::GraphicsApi::GL);
}
}
/**
* Choose a suggested blurring algorithm if supportsBlur is true. By default Kawase will be
* suggested as it's faster than a full Gaussian blur and looks close enough.
*/
renderengine::RenderEngine::BlurAlgorithm chooseBlurAlgorithm(bool supportsBlur) {
if (!supportsBlur) {
return renderengine::RenderEngine::BlurAlgorithm::NONE;
}
auto const algorithm = base::GetProperty(PROPERTY_DEBUG_RENDERENGINE_BLUR_ALGORITHM, "");
if (algorithm == "gaussian") {
return renderengine::RenderEngine::BlurAlgorithm::GAUSSIAN;
} else {
return renderengine::RenderEngine::BlurAlgorithm::KAWASE;
}
}
void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
ATRACE_CALL();
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
addTransactionReadyFilters();
Mutex::Autolock lock(mStateLock);
// Get a RenderEngine for the given display / config (can't fail)
// TODO(b/77156734): We need to stop casting and use HAL types when possible.
// Sending maxFrameBufferAcquiredBuffers as the cache size is tightly tuned to single-display.
auto builder = renderengine::RenderEngineCreationArgs::Builder()
.setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
.setImageCacheSize(maxFrameBufferAcquiredBuffers)
.setEnableProtectedContext(enable_protected_contents(false))
.setPrecacheToneMapperShaderOnly(false)
.setBlurAlgorithm(chooseBlurAlgorithm(mSupportsBlur))
.setContextPriority(
useContextPriority
? renderengine::RenderEngine::ContextPriority::REALTIME
: renderengine::RenderEngine::ContextPriority::MEDIUM);
chooseRenderEngineType(builder);
mRenderEngine = renderengine::RenderEngine::create(builder.build());
mCompositionEngine->setRenderEngine(mRenderEngine.get());
mMaxRenderTargetSize =
std::min(getRenderEngine().getMaxTextureSize(), getRenderEngine().getMaxViewportDims());
// Set SF main policy after initializing RenderEngine which has its own policy.
if (!SetTaskProfiles(0, {"SFMainPolicy"})) {
ALOGW("Failed to set main task profile");
}
mCompositionEngine->setTimeStats(mTimeStats);
mCompositionEngine->setHwComposer(getFactory().createHWComposer(mHwcServiceName));
auto& composer = mCompositionEngine->getHwComposer();
composer.setCallback(*this);
mDisplayModeController.setHwComposer(&composer);
ClientCache::getInstance().setRenderEngine(&getRenderEngine());
mHasReliablePresentFences =
!getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE);
enableLatchUnsignaledConfig = getLatchUnsignaledConfig();
if (base::GetBoolProperty("debug.sf.enable_hwc_vds"s, false)) {
enableHalVirtualDisplays(true);
}
// Process hotplug for displays connected at boot.
LOG_ALWAYS_FATAL_IF(!configureLocked(),
"Initial display configuration failed: HWC did not hotplug");
// Commit primary display.
sp<const DisplayDevice> display;
if (const auto indexOpt = mCurrentState.getDisplayIndex(getPrimaryDisplayIdLocked())) {
const auto& displays = mCurrentState.displays;
const auto& token = displays.keyAt(*indexOpt);
const auto& state = displays.valueAt(*indexOpt);
processDisplayAdded(token, state);
mDrawingState.displays.add(token, state);
display = getDefaultDisplayDeviceLocked();
}
LOG_ALWAYS_FATAL_IF(!display, "Failed to configure the primary display");
LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(display->getPhysicalId()),
"Primary display is disconnected");
// TODO(b/241285876): The Scheduler needlessly depends on creating the CompositionEngine part of
// the DisplayDevice, hence the above commit of the primary display. Remove that special case by
// initializing the Scheduler after configureLocked, once decoupled from DisplayDevice.
initScheduler(display);
// Start listening after creating the Scheduler, since the listener calls into it.
mDisplayModeController.setActiveModeListener(
display::DisplayModeController::ActiveModeListener::make(
[this](PhysicalDisplayId displayId, Fps vsyncRate, Fps renderRate) {
// This callback cannot lock mStateLock, as some callers already lock it.
// Instead, switch context to the main thread.
static_cast<void>(mScheduler->schedule([=,
this]() FTL_FAKE_GUARD(mStateLock) {
if (const auto display = getDisplayDeviceLocked(displayId)) {
display->updateRefreshRateOverlayRate(vsyncRate, renderRate);
}
}));
}));
mLayerTracing.setTakeLayersSnapshotProtoFunction([&](uint32_t traceFlags) {
auto snapshot = perfetto::protos::LayersSnapshotProto{};
mScheduler
->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
snapshot = takeLayersSnapshotProto(traceFlags, TimePoint::now(),
mLastCommittedVsyncId, true);
})
.wait();
return snapshot;
});
// Commit secondary display(s).
processDisplayChangesLocked();
// initialize our drawing state
mDrawingState = mCurrentState;
onActiveDisplayChangedLocked(nullptr, *display);
static_cast<void>(mScheduler->schedule(
[this]() FTL_FAKE_GUARD(kMainThreadContext) { initializeDisplays(); }));
mPowerAdvisor->init();
if (base::GetBoolProperty("service.sf.prime_shader_cache"s, true)) {
if (setSchedFifo(false) != NO_ERROR) {
ALOGW("Can't set SCHED_OTHER for primeCache");
}
mRenderEnginePrimeCacheFuture.callOnce([this] {
renderengine::PrimeCacheConfig config;
config.cacheHolePunchLayer =
base::GetBoolProperty("debug.sf.prime_shader_cache.hole_punch"s, true);
config.cacheSolidLayers =
base::GetBoolProperty("debug.sf.prime_shader_cache.solid_layers"s, true);
config.cacheSolidDimmedLayers =
base::GetBoolProperty("debug.sf.prime_shader_cache.solid_dimmed_layers"s, true);
config.cacheImageLayers =
base::GetBoolProperty("debug.sf.prime_shader_cache.image_layers"s, true);
config.cacheImageDimmedLayers =
base::GetBoolProperty("debug.sf.prime_shader_cache.image_dimmed_layers"s, true);
config.cacheClippedLayers =
base::GetBoolProperty("debug.sf.prime_shader_cache.clipped_layers"s, true);
config.cacheShadowLayers =
base::GetBoolProperty("debug.sf.prime_shader_cache.shadow_layers"s, true);
config.cachePIPImageLayers =
base::GetBoolProperty("debug.sf.prime_shader_cache.pip_image_layers"s, true);
config.cacheTransparentImageDimmedLayers = base::
GetBoolProperty("debug.sf.prime_shader_cache.transparent_image_dimmed_layers"s,
true);
config.cacheClippedDimmedImageLayers = base::
GetBoolProperty("debug.sf.prime_shader_cache.clipped_dimmed_image_layers"s,
true);
// ro.surface_flinger.prime_chader_cache.ultrahdr exists as a previous ro property
// which we maintain for backwards compatibility.
config.cacheUltraHDR =
base::GetBoolProperty("ro.surface_flinger.prime_shader_cache.ultrahdr"s, false);
return getRenderEngine().primeCache(config);
});
if (setSchedFifo(true) != NO_ERROR) {
ALOGW("Can't set SCHED_FIFO after primeCache");
}
}
// Avoid blocking the main thread on `init` to set properties.
mInitBootPropsFuture.callOnce([this] {
return std::async(std::launch::async, &SurfaceFlinger::initBootProperties, this);
});
initTransactionTraceWriter();
ALOGV("Done initializing");
}
// During boot, offload `initBootProperties` to another thread. `property_set` depends on
// `property_service`, which may be delayed by slow operations like `mount_all --late` in
// the `init` process. See b/34499826 and b/63844978.
void SurfaceFlinger::initBootProperties() {
property_set("service.sf.present_timestamp", mHasReliablePresentFences ? "1" : "0");
if (base::GetBoolProperty("debug.sf.boot_animation"s, true)) {
// Reset and (if needed) start BootAnimation.
property_set("service.bootanim.exit", "0");
property_set("service.bootanim.progress", "0");
property_set("ctl.start", "bootanim");
}
}
void SurfaceFlinger::initTransactionTraceWriter() {
if (!mTransactionTracing) {
return;
}
TransactionTraceWriter::getInstance().setWriterFunction(
[&](const std::string& filename, bool overwrite) {
auto writeFn = [&]() {
if (!overwrite && fileNewerThan(filename, std::chrono::minutes{10})) {
ALOGD("TransactionTraceWriter: file=%s already exists", filename.c_str());
return;
}
ALOGD("TransactionTraceWriter: writing file=%s", filename.c_str());
mTransactionTracing->writeToFile(filename);
mTransactionTracing->flush();
};
if (std::this_thread::get_id() == mMainThreadId) {
writeFn();
} else {
mScheduler->schedule(writeFn).get();
}
});
}
void SurfaceFlinger::readPersistentProperties() {
Mutex::Autolock _l(mStateLock);
char value[PROPERTY_VALUE_MAX];
property_get("persist.sys.sf.color_saturation", value, "1.0");
mGlobalSaturationFactor = atof(value);
updateColorMatrixLocked();
ALOGV("Saturation is set to %.2f", mGlobalSaturationFactor);
property_get("persist.sys.sf.native_mode", value, "0");
mDisplayColorSetting = static_cast<DisplayColorSetting>(atoi(value));
mForceColorMode =
static_cast<ui::ColorMode>(base::GetIntProperty("persist.sys.sf.color_mode"s, 0));
}
status_t SurfaceFlinger::getSupportedFrameTimestamps(
std::vector<FrameEvent>* outSupported) const {
*outSupported = {
FrameEvent::REQUESTED_PRESENT,
FrameEvent::ACQUIRE,
FrameEvent::LATCH,
FrameEvent::FIRST_REFRESH_START,
FrameEvent::LAST_REFRESH_START,
FrameEvent::GPU_COMPOSITION_DONE,
FrameEvent::DEQUEUE_READY,
FrameEvent::RELEASE,
};
if (mHasReliablePresentFences) {
outSupported->push_back(FrameEvent::DISPLAY_PRESENT);
}
return NO_ERROR;
}
status_t SurfaceFlinger::getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState* state) {
if (!displayToken || !state) {
return BAD_VALUE;
}
Mutex::Autolock lock(mStateLock);
const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
return NAME_NOT_FOUND;
}
state->layerStack = display->getLayerStack();
state->orientation = display->getOrientation();
const Rect layerStackRect = display->getLayerStackSpaceRect();
state->layerStackSpaceRect =
layerStackRect.isValid() ? layerStackRect.getSize() : display->getSize();
return NO_ERROR;
}
status_t SurfaceFlinger::getStaticDisplayInfo(int64_t displayId, ui::StaticDisplayInfo* info) {
if (!info) {
return BAD_VALUE;
}
Mutex::Autolock lock(mStateLock);
const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId));
const auto displayOpt = mPhysicalDisplays.get(*id).and_then(getDisplayDeviceAndSnapshot());
if (!displayOpt) {
return NAME_NOT_FOUND;
}
const auto& [display, snapshotRef] = *displayOpt;
const auto& snapshot = snapshotRef.get();
info->connectionType = snapshot.connectionType();
info->deviceProductInfo = snapshot.deviceProductInfo();
if (mEmulatedDisplayDensity) {
info->density = mEmulatedDisplayDensity;
} else {
info->density = info->connectionType == ui::DisplayConnectionType::Internal
? mInternalDisplayDensity
: FALLBACK_DENSITY;
}
info->density /= ACONFIGURATION_DENSITY_MEDIUM;
info->secure = display->isSecure();
info->installOrientation = display->getPhysicalOrientation();
return NO_ERROR;
}
void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info,
const sp<DisplayDevice>& display,
const display::DisplaySnapshot& snapshot) {
const auto& displayModes = snapshot.displayModes();
info->supportedDisplayModes.clear();
info->supportedDisplayModes.reserve(displayModes.size());
for (const auto& [id, mode] : displayModes) {
ui::DisplayMode outMode;
outMode.id = ftl::to_underlying(id);
auto [width, height] = mode->getResolution();
auto [xDpi, yDpi] = mode->getDpi();
if (const auto physicalOrientation = display->getPhysicalOrientation();
physicalOrientation == ui::ROTATION_90 || physicalOrientation == ui::ROTATION_270) {
std::swap(width, height);
std::swap(xDpi, yDpi);
}
outMode.resolution = ui::Size(width, height);
outMode.xDpi = xDpi;
outMode.yDpi = yDpi;
const auto peakFps = mode->getPeakFps();
outMode.peakRefreshRate = peakFps.getValue();
outMode.vsyncRate = mode->getVsyncRate().getValue();
const auto vsyncConfigSet = mScheduler->getVsyncConfiguration().getConfigsForRefreshRate(
Fps::fromValue(outMode.peakRefreshRate));
outMode.appVsyncOffset = vsyncConfigSet.late.appOffset;
outMode.sfVsyncOffset = vsyncConfigSet.late.sfOffset;
outMode.group = mode->getGroup();
// This is how far in advance a buffer must be queued for
// presentation at a given time. If you want a buffer to appear
// on the screen at time N, you must submit the buffer before
// (N - presentationDeadline).
//
// Normally it's one full refresh period (to give SF a chance to
// latch the buffer), but this can be reduced by configuring a
// VsyncController offset. Any additional delays introduced by the hardware
// composer or panel must be accounted for here.
//
// We add an additional 1ms to allow for processing time and
// differences between the ideal and actual refresh rate.
outMode.presentationDeadline = peakFps.getPeriodNsecs() - outMode.sfVsyncOffset + 1000000;
excludeDolbyVisionIf4k30Present(display->getHdrCapabilities().getSupportedHdrTypes(),
outMode);
info->supportedDisplayModes.push_back(outMode);
}
info->supportedColorModes = snapshot.filterColorModes(mSupportsWideColor);
const PhysicalDisplayId displayId = snapshot.displayId();
const auto mode = display->refreshRateSelector().getActiveMode();
info->activeDisplayModeId = ftl::to_underlying(mode.modePtr->getId());
info->renderFrameRate = mode.fps.getValue();
info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities());
info->autoLowLatencyModeSupported =
getHwComposer().hasDisplayCapability(displayId,
DisplayCapability::AUTO_LOW_LATENCY_MODE);
info->gameContentTypeSupported =
getHwComposer().supportsContentType(displayId, hal::ContentType::GAME);
info->preferredBootDisplayMode = static_cast<ui::DisplayModeId>(-1);
if (getHwComposer().hasCapability(Capability::BOOT_DISPLAY_CONFIG)) {
if (const auto hwcId = getHwComposer().getPreferredBootDisplayMode(displayId)) {
if (const auto modeId = snapshot.translateModeId(*hwcId)) {
info->preferredBootDisplayMode = ftl::to_underlying(*modeId);
}
}
}
}
status_t SurfaceFlinger::getDynamicDisplayInfoFromId(int64_t physicalDisplayId,
ui::DynamicDisplayInfo* info) {
if (!info) {
return BAD_VALUE;
}
Mutex::Autolock lock(mStateLock);
const auto id_ =
DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(physicalDisplayId));
const auto displayOpt = mPhysicalDisplays.get(*id_).and_then(getDisplayDeviceAndSnapshot());
if (!displayOpt) {
return NAME_NOT_FOUND;
}
const auto& [display, snapshotRef] = *displayOpt;
getDynamicDisplayInfoInternal(info, display, snapshotRef.get());
return NO_ERROR;
}
status_t SurfaceFlinger::getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
ui::DynamicDisplayInfo* info) {
if (!displayToken || !info) {
return BAD_VALUE;
}
Mutex::Autolock lock(mStateLock);
const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
.transform(&ftl::to_mapped_ref<PhysicalDisplays>)
.and_then(getDisplayDeviceAndSnapshot());
if (!displayOpt) {
return NAME_NOT_FOUND;
}
const auto& [display, snapshotRef] = *displayOpt;
getDynamicDisplayInfoInternal(info, display, snapshotRef.get());
return NO_ERROR;
}
status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& displayToken,
DisplayStatInfo* outStats) {
if (!outStats) {
return BAD_VALUE;
}
std::optional<PhysicalDisplayId> displayIdOpt;
{
Mutex::Autolock lock(mStateLock);
if (displayToken) {
displayIdOpt = getPhysicalDisplayIdLocked(displayToken);
if (!displayIdOpt) {
ALOGW("%s: Invalid physical display token %p", __func__, displayToken.get());
return NAME_NOT_FOUND;
}
} else {
// TODO (b/277364366): Clients should be updated to pass in the display they
// want, rather than us picking an arbitrary one (the active display, in this
// case).
displayIdOpt = mActiveDisplayId;
}
}
const auto schedule = mScheduler->getVsyncSchedule(displayIdOpt);
if (!schedule) {
ALOGE("%s: Missing VSYNC schedule for display %s!", __func__,
to_string(*displayIdOpt).c_str());
return NAME_NOT_FOUND;
}
outStats->vsyncTime = schedule->vsyncDeadlineAfter(TimePoint::now()).ns();
outStats->vsyncPeriod = schedule->period().ns();
return NO_ERROR;
}
void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode) {
const auto mode = desiredMode.mode;
const auto displayId = mode.modePtr->getPhysicalDisplayId();
ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
const bool emitEvent = desiredMode.emitEvent;
using DesiredModeAction = display::DisplayModeController::DesiredModeAction;
switch (mDisplayModeController.setDesiredMode(displayId, std::move(desiredMode))) {
case DesiredModeAction::InitiateDisplayModeSwitch: {
const auto selectorPtr = mDisplayModeController.selectorPtrFor(displayId);
if (!selectorPtr) break;
const Fps renderRate = selectorPtr->getActiveMode().fps;
// DisplayModeController::setDesiredMode updated the render rate, so inform Scheduler.
mScheduler->setRenderRate(displayId, renderRate, true /* applyImmediately */);
// Schedule a new frame to initiate the display mode switch.
scheduleComposite(FrameHint::kNone);
// Start receiving vsync samples now, so that we can detect a period
// switch.
mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */,
mode.modePtr.get());
// As we called to set period, we will call to onRefreshRateChangeCompleted once
// VsyncController model is locked.
mScheduler->modulateVsync(displayId, &VsyncModulator::onRefreshRateChangeInitiated);
if (displayId == mActiveDisplayId) {
mScheduler->updatePhaseConfiguration(mode.fps);
}
mScheduler->setModeChangePending(true);
break;
}
case DesiredModeAction::InitiateRenderRateSwitch:
mScheduler->setRenderRate(displayId, mode.fps, /*applyImmediately*/ false);
if (displayId == mActiveDisplayId) {
mScheduler->updatePhaseConfiguration(mode.fps);
}
if (emitEvent) {
dispatchDisplayModeChangeEvent(displayId, mode);
}
break;
case DesiredModeAction::None:
break;
}
}
status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToken>& displayToken,
DisplayModeId modeId, Fps minFps, Fps maxFps) {
ATRACE_CALL();
if (!displayToken) {
return BAD_VALUE;
}
const char* const whence = __func__;
auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t {
const auto displayOpt =
FTL_FAKE_GUARD(mStateLock,
ftl::find_if(mPhysicalDisplays,
PhysicalDisplay::hasToken(displayToken))
.transform(&ftl::to_mapped_ref<PhysicalDisplays>)
.and_then(getDisplayDeviceAndSnapshot()));
if (!displayOpt) {
ALOGE("%s: Invalid physical display token %p", whence, displayToken.get());
return NAME_NOT_FOUND;
}
const auto& [display, snapshotRef] = *displayOpt;
const auto& snapshot = snapshotRef.get();
const auto fpsOpt = snapshot.displayModes().get(modeId).transform(
[](const DisplayModePtr& mode) { return mode->getPeakFps(); });
if (!fpsOpt) {
ALOGE("%s: Invalid mode %d for display %s", whence, ftl::to_underlying(modeId),
to_string(snapshot.displayId()).c_str());
return BAD_VALUE;
}
const Fps fps = *fpsOpt;
const FpsRange physical = {fps, fps};
const FpsRange render = {minFps.isValid() ? minFps : fps, maxFps.isValid() ? maxFps : fps};
const FpsRanges ranges = {physical, render};
// Keep the old switching type.
const bool allowGroupSwitching =
display->refreshRateSelector().getCurrentPolicy().allowGroupSwitching;
const scheduler::RefreshRateSelector::DisplayManagerPolicy policy{modeId, ranges, ranges,
allowGroupSwitching};
return setDesiredDisplayModeSpecsInternal(display, policy);
});
return future.get();
}
// TODO: b/241285876 - Restore thread safety analysis once mStateLock below is unconditional.
[[clang::no_thread_safety_analysis]]
void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) {
ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
const auto pendingModeOpt = mDisplayModeController.getPendingMode(displayId);
if (!pendingModeOpt) {
// There is no pending mode change. This can happen if the active
// display changed and the mode change happened on a different display.
return;
}
const auto& activeMode = pendingModeOpt->mode;
if (const auto oldResolution =
mDisplayModeController.getActiveMode(displayId).modePtr->getResolution();
oldResolution != activeMode.modePtr->getResolution()) {
ConditionalLock lock(mStateLock, !FlagManager::getInstance().connected_display());
auto& state = mCurrentState.displays.editValueFor(getPhysicalDisplayTokenLocked(displayId));
// We need to generate new sequenceId in order to recreate the display (and this
// way the framebuffer).
state.sequenceId = DisplayDeviceState{}.sequenceId;
state.physical->activeMode = activeMode.modePtr.get();
processDisplayChangesLocked();
// processDisplayChangesLocked will update all necessary components so we're done here.
return;
}
mDisplayModeController.finalizeModeChange(displayId, activeMode.modePtr->getId(),
activeMode.modePtr->getVsyncRate(), activeMode.fps);
if (displayId == mActiveDisplayId) {
mScheduler->updatePhaseConfiguration(activeMode.fps);
}
if (pendingModeOpt->emitEvent) {
dispatchDisplayModeChangeEvent(displayId, activeMode);
}
}
void SurfaceFlinger::dropModeRequest(PhysicalDisplayId displayId) {
mDisplayModeController.clearDesiredMode(displayId);
if (displayId == mActiveDisplayId) {
// TODO(b/255635711): Check for pending mode changes on other displays.
mScheduler->setModeChangePending(false);
}
}
void SurfaceFlinger::applyActiveMode(PhysicalDisplayId displayId) {
const auto activeModeOpt = mDisplayModeController.getDesiredMode(displayId);
auto activeModePtr = activeModeOpt->mode.modePtr;
const auto renderFps = activeModeOpt->mode.fps;
dropModeRequest(displayId);
constexpr bool kAllowToEnable = true;
mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, std::move(activeModePtr).take());
mScheduler->setRenderRate(displayId, renderFps, /*applyImmediately*/ true);
if (displayId == mActiveDisplayId) {
mScheduler->updatePhaseConfiguration(renderFps);
}
}
void SurfaceFlinger::initiateDisplayModeChanges() {
ATRACE_CALL();
std::optional<PhysicalDisplayId> displayToUpdateImmediately;
for (const auto& [displayId, physical] : mPhysicalDisplays) {
auto desiredModeOpt = mDisplayModeController.getDesiredMode(displayId);
if (!desiredModeOpt) {
continue;
}
const auto desiredModeId = desiredModeOpt->mode.modePtr->getId();
const auto displayModePtrOpt = physical.snapshot().displayModes().get(desiredModeId);
if (!displayModePtrOpt) {
ALOGW("Desired display mode is no longer supported. Mode ID = %d",
ftl::to_underlying(desiredModeId));
continue;
}
ALOGV("%s changing active mode to %d(%s) for display %s", __func__,
ftl::to_underlying(desiredModeId),
to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(),
to_string(displayId).c_str());
if ((!FlagManager::getInstance().connected_display() || !desiredModeOpt->force) &&
mDisplayModeController.getActiveMode(displayId) == desiredModeOpt->mode) {
applyActiveMode(displayId);
continue;
}
const auto selectorPtr = mDisplayModeController.selectorPtrFor(displayId);
// Desired active mode was set, it is different than the mode currently in use, however
// allowed modes might have changed by the time we process the refresh.
// Make sure the desired mode is still allowed
if (!selectorPtr->isModeAllowed(desiredModeOpt->mode)) {
dropModeRequest(displayId);
continue;
}
// TODO(b/142753666) use constrains
hal::VsyncPeriodChangeConstraints constraints;
constraints.desiredTimeNanos = systemTime();
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline outTimeline;
if (!mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
constraints, outTimeline)) {
continue;
}
selectorPtr->onModeChangeInitiated();
mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
if (outTimeline.refreshRequired) {
scheduleComposite(FrameHint::kNone);
} else {
// TODO(b/255635711): Remove `displayToUpdateImmediately` to `finalizeDisplayModeChange`
// for all displays. This was only needed when the loop iterated over `mDisplays` rather
// than `mPhysicalDisplays`.
displayToUpdateImmediately = displayId;
}
}
if (displayToUpdateImmediately) {
const auto displayId = *displayToUpdateImmediately;
finalizeDisplayModeChange(displayId);
const auto desiredModeOpt = mDisplayModeController.getDesiredMode(displayId);
if (desiredModeOpt &&
mDisplayModeController.getActiveMode(displayId) == desiredModeOpt->mode) {
applyActiveMode(displayId);
}
}
}
void SurfaceFlinger::disableExpensiveRendering() {
const char* const whence = __func__;
auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
ATRACE_NAME(whence);
if (mPowerAdvisor->isUsingExpensiveRendering()) {
for (const auto& [_, display] : mDisplays) {
constexpr bool kDisable = false;
mPowerAdvisor->setExpensiveRenderingExpected(display->getId(), kDisable);
}
}
});
future.wait();
}
status_t SurfaceFlinger::getDisplayNativePrimaries(const sp<IBinder>& displayToken,
ui::DisplayPrimaries& primaries) {
if (!displayToken) {
return BAD_VALUE;
}
Mutex::Autolock lock(mStateLock);
const auto display = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
.transform(&ftl::to_mapped_ref<PhysicalDisplays>);
if (!display) {
return NAME_NOT_FOUND;
}
if (!display.transform(&PhysicalDisplay::isInternal).value()) {
return INVALID_OPERATION;
}
// TODO(b/229846990): For now, assume that all internal displays have the same primaries.
primaries = mInternalDisplayPrimaries;
return NO_ERROR;
}
status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode mode) {
if (!displayToken) {
return BAD_VALUE;
}
const char* const whence = __func__;
auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t {
const auto displayOpt =
ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
.transform(&ftl::to_mapped_ref<PhysicalDisplays>)
.and_then(getDisplayDeviceAndSnapshot());
if (!displayOpt) {
ALOGE("%s: Invalid physical display token %p", whence, displayToken.get());
return NAME_NOT_FOUND;
}
const auto& [display, snapshotRef] = *displayOpt;
const auto& snapshot = snapshotRef.get();
const auto modes = snapshot.filterColorModes(mSupportsWideColor);
const bool exists = std::find(modes.begin(), modes.end(), mode) != modes.end();
if (mode < ui::ColorMode::NATIVE || !exists) {
ALOGE("%s: Invalid color mode %s (%d) for display %s", whence,
decodeColorMode(mode).c_str(), mode, to_string(snapshot.displayId()).c_str());
return BAD_VALUE;
}
display->getCompositionDisplay()->setColorProfile(
{mode, Dataspace::UNKNOWN, RenderIntent::COLORIMETRIC});
return NO_ERROR;
});
// TODO(b/195698395): Propagate error.
future.wait();
return NO_ERROR;
}
status_t SurfaceFlinger::getBootDisplayModeSupport(bool* outSupport) const {
auto future = mScheduler->schedule(
[this] { return getHwComposer().hasCapability(Capability::BOOT_DISPLAY_CONFIG); });
*outSupport = future.get();
return NO_ERROR;
}
status_t SurfaceFlinger::getOverlaySupport(gui::OverlayProperties* outProperties) const {
const auto& aidlProperties = getHwComposer().getOverlaySupport();
// convert aidl OverlayProperties to gui::OverlayProperties
outProperties->combinations.reserve(aidlProperties.combinations.size());
for (const auto& combination : aidlProperties.combinations) {
std::vector<int32_t> pixelFormats;
pixelFormats.reserve(combination.pixelFormats.size());
std::transform(combination.pixelFormats.cbegin(), combination.pixelFormats.cend(),
std::back_inserter(pixelFormats),
[](const auto& val) { return static_cast<int32_t>(val); });
std::vector<int32_t> standards;
standards.reserve(combination.standards.size());
std::transform(combination.standards.cbegin(), combination.standards.cend(),
std::back_inserter(standards),
[](const auto& val) { return static_cast<int32_t>(val); });
std::vector<int32_t> transfers;
transfers.reserve(combination.transfers.size());
std::transform(combination.transfers.cbegin(), combination.transfers.cend(),
std::back_inserter(transfers),
[](const auto& val) { return static_cast<int32_t>(val); });
std::vector<int32_t> ranges;
ranges.reserve(combination.ranges.size());
std::transform(combination.ranges.cbegin(), combination.ranges.cend(),
std::back_inserter(ranges),
[](const auto& val) { return static_cast<int32_t>(val); });
gui::OverlayProperties::SupportedBufferCombinations outCombination;
outCombination.pixelFormats = std::move(pixelFormats);
outCombination.standards = std::move(standards);
outCombination.transfers = std::move(transfers);
outCombination.ranges = std::move(ranges);
outProperties->combinations.emplace_back(outCombination);
}
outProperties->supportMixedColorSpaces = aidlProperties.supportMixedColorSpaces;
return NO_ERROR;
}
status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& displayToken,
DisplayModeId modeId) {
const char* const whence = __func__;
auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t {
const auto snapshotOpt =
ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
.transform(&ftl::to_mapped_ref<PhysicalDisplays>)
.transform(&PhysicalDisplay::snapshotRef);
if (!snapshotOpt) {
ALOGE("%s: Invalid physical display token %p", whence, displayToken.get());
return NAME_NOT_FOUND;
}
const auto& snapshot = snapshotOpt->get();
const auto hwcIdOpt = snapshot.displayModes().get(modeId).transform(
[](const DisplayModePtr& mode) { return mode->getHwcId(); });
if (!hwcIdOpt) {
ALOGE("%s: Invalid mode %d for display %s", whence, ftl::to_underlying(modeId),
to_string(snapshot.displayId()).c_str());
return BAD_VALUE;
}
return getHwComposer().setBootDisplayMode(snapshot.displayId(), *hwcIdOpt);
});
return future.get();
}
status_t SurfaceFlinger::clearBootDisplayMode(const sp<IBinder>& displayToken) {
const char* const whence = __func__;
auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
return getHwComposer().clearBootDisplayMode(*displayId);
} else {
ALOGE("%s: Invalid display token %p", whence, displayToken.get());
return BAD_VALUE;
}
});
return future.get();
}
status_t SurfaceFlinger::getHdrConversionCapabilities(
std::vector<gui::HdrConversionCapability>* hdrConversionCapabilities) const {
bool hdrOutputConversionSupport;
getHdrOutputConversionSupport(&hdrOutputConversionSupport);
if (hdrOutputConversionSupport == false) {
ALOGE("hdrOutputConversion is not supported by this device.");
return INVALID_OPERATION;
}
const auto aidlConversionCapability = getHwComposer().getHdrConversionCapabilities();
for (auto capability : aidlConversionCapability) {
gui::HdrConversionCapability tempCapability;
tempCapability.sourceType = static_cast<int>(capability.sourceType);
tempCapability.outputType = static_cast<int>(capability.outputType);
tempCapability.addsLatency = capability.addsLatency;
hdrConversionCapabilities->push_back(tempCapability);
}
return NO_ERROR;
}
status_t SurfaceFlinger::setHdrConversionStrategy(
const gui::HdrConversionStrategy& hdrConversionStrategy,
int32_t* outPreferredHdrOutputType) {
bool hdrOutputConversionSupport;
getHdrOutputConversionSupport(&hdrOutputConversionSupport);
if (hdrOutputConversionSupport == false) {
ALOGE("hdrOutputConversion is not supported by this device.");
return INVALID_OPERATION;
}
auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) mutable -> status_t {
using AidlHdrConversionStrategy =
aidl::android::hardware::graphics::common::HdrConversionStrategy;
using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag;
AidlHdrConversionStrategy aidlConversionStrategy;
status_t status;
aidl::android::hardware::graphics::common::Hdr aidlPreferredHdrOutputType;
switch (hdrConversionStrategy.getTag()) {
case GuiHdrConversionStrategyTag::passthrough: {
aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::passthrough>(
hdrConversionStrategy.get<GuiHdrConversionStrategyTag::passthrough>());
status = getHwComposer().setHdrConversionStrategy(aidlConversionStrategy,
&aidlPreferredHdrOutputType);
*outPreferredHdrOutputType = static_cast<int32_t>(aidlPreferredHdrOutputType);
return status;
}
case GuiHdrConversionStrategyTag::autoAllowedHdrTypes: {
auto autoHdrTypes =
hdrConversionStrategy
.get<GuiHdrConversionStrategyTag::autoAllowedHdrTypes>();
std::vector<aidl::android::hardware::graphics::common::Hdr> aidlAutoHdrTypes;
for (auto type : autoHdrTypes) {
aidlAutoHdrTypes.push_back(
static_cast<aidl::android::hardware::graphics::common::Hdr>(type));
}
aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::autoAllowedHdrTypes>(
aidlAutoHdrTypes);
status = getHwComposer().setHdrConversionStrategy(aidlConversionStrategy,
&aidlPreferredHdrOutputType);
*outPreferredHdrOutputType = static_cast<int32_t>(aidlPreferredHdrOutputType);
return status;
}
case GuiHdrConversionStrategyTag::forceHdrConversion: {
auto forceHdrConversion =
hdrConversionStrategy
.get<GuiHdrConversionStrategyTag::forceHdrConversion>();
aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::forceHdrConversion>(
static_cast<aidl::android::hardware::graphics::common::Hdr>(
forceHdrConversion));
status = getHwComposer().setHdrConversionStrategy(aidlConversionStrategy,
&aidlPreferredHdrOutputType);
*outPreferredHdrOutputType = static_cast<int32_t>(aidlPreferredHdrOutputType);
return status;
}
}
});
return future.get();
}
status_t SurfaceFlinger::getHdrOutputConversionSupport(bool* outSupport) const {
auto future = mScheduler->schedule([this] {
return getHwComposer().hasCapability(Capability::HDR_OUTPUT_CONVERSION_CONFIG);
});
*outSupport = future.get();
return NO_ERROR;
}
void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
const char* const whence = __func__;
static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
getHwComposer().setAutoLowLatencyMode(*displayId, on);
} else {
ALOGE("%s: Invalid display token %p", whence, displayToken.get());
}
}));
}
void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
const char* const whence = __func__;
static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
const auto type = on ? hal::ContentType::GAME : hal::ContentType::NONE;
getHwComposer().setContentType(*displayId, type);
} else {
ALOGE("%s: Invalid display token %p", whence, displayToken.get());
}
}));
}
status_t SurfaceFlinger::overrideHdrTypes(const sp<IBinder>& displayToken,
const std::vector<ui::Hdr>& hdrTypes) {
Mutex::Autolock lock(mStateLock);
auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
ALOGE("%s: Invalid display token %p", __func__, displayToken.get());
return NAME_NOT_FOUND;
}
display->overrideHdrTypes(hdrTypes);
mScheduler->dispatchHotplug(display->getPhysicalId(), scheduler::Scheduler::Hotplug::Connected);
return NO_ERROR;
}
status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData,
bool* success) {
*success = mTimeStats->onPullAtom(atomId, pulledData);
return NO_ERROR;
}
status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
ui::PixelFormat* outFormat,
ui::Dataspace* outDataspace,
uint8_t* outComponentMask) const {
if (!outFormat || !outDataspace || !outComponentMask) {
return BAD_VALUE;
}
Mutex::Autolock lock(mStateLock);
const auto displayId = getPhysicalDisplayIdLocked(displayToken);
if (!displayId) {
return NAME_NOT_FOUND;
}
return getHwComposer().getDisplayedContentSamplingAttributes(*displayId, outFormat,
outDataspace, outComponentMask);
}
status_t SurfaceFlinger::setDisplayContentSamplingEnabled(const sp<IBinder>& displayToken,
bool enable, uint8_t componentMask,
uint64_t maxFrames) {
const char* const whence = __func__;
auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable,
componentMask, maxFrames);
} else {
ALOGE("%s: Invalid display token %p", whence, displayToken.get());
return NAME_NOT_FOUND;
}
});
return future.get();
}
status_t SurfaceFlinger::getDisplayedContentSample(const sp<IBinder>& displayToken,
uint64_t maxFrames, uint64_t timestamp,
DisplayedFrameStats* outStats) const {
Mutex::Autolock lock(mStateLock);
const auto displayId = getPhysicalDisplayIdLocked(displayToken);
if (!displayId) {
return NAME_NOT_FOUND;
}
return getHwComposer().getDisplayedContentSample(*displayId, maxFrames, timestamp, outStats);
}
status_t SurfaceFlinger::getProtectedContentSupport(bool* outSupported) const {
if (!outSupported) {
return BAD_VALUE;
}
*outSupported = getRenderEngine().supportsProtectedContent();
return NO_ERROR;
}
status_t SurfaceFlinger::isWideColorDisplay(const sp<IBinder>& displayToken,
bool* outIsWideColorDisplay) const {
if (!displayToken || !outIsWideColorDisplay) {
return BAD_VALUE;
}
Mutex::Autolock lock(mStateLock);
const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
return NAME_NOT_FOUND;
}
*outIsWideColorDisplay =
display->isPrimary() ? mSupportsWideColor : display->hasWideColorGamut();
return NO_ERROR;
}
status_t SurfaceFlinger::getCompositionPreference(
Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
Dataspace* outWideColorGamutDataspace,
ui::PixelFormat* outWideColorGamutPixelFormat) const {
*outDataspace = mDefaultCompositionDataspace;
*outPixelFormat = defaultCompositionPixelFormat;
*outWideColorGamutDataspace = mWideColorGamutCompositionDataspace;
*outWideColorGamutPixelFormat = wideColorGamutCompositionPixelFormat;
return NO_ERROR;
}
status_t SurfaceFlinger::addRegionSamplingListener(const Rect& samplingArea,
const sp<IBinder>& stopLayerHandle,
const sp<IRegionSamplingListener>& listener) {
if (!listener || samplingArea == Rect::INVALID_RECT || samplingArea.isEmpty()) {
return BAD_VALUE;
}
// LayerHandle::getLayer promotes the layer object in a binder thread but we will not destroy
// the layer here since the caller has a strong ref to the layer's handle.
const sp<Layer> stopLayer = LayerHandle::getLayer(stopLayerHandle);
mRegionSamplingThread->addListener(samplingArea,
stopLayer ? stopLayer->getSequence() : UNASSIGNED_LAYER_ID,
listener);
return NO_ERROR;
}
status_t SurfaceFlinger::removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) {
if (!listener) {
return BAD_VALUE;
}
mRegionSamplingThread->removeListener(listener);
return NO_ERROR;
}
status_t SurfaceFlinger::addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) {
if (!listener) {
return BAD_VALUE;
}
mFpsReporter->addListener(listener, taskId);
return NO_ERROR;
}
status_t SurfaceFlinger::removeFpsListener(const sp<gui::IFpsListener>& listener) {
if (!listener) {
return BAD_VALUE;
}
mFpsReporter->removeListener(listener);
return NO_ERROR;
}
status_t SurfaceFlinger::addTunnelModeEnabledListener(
const sp<gui::ITunnelModeEnabledListener>& listener) {
if (!listener) {
return BAD_VALUE;
}
mTunnelModeEnabledReporter->addListener(listener);
return NO_ERROR;
}
status_t SurfaceFlinger::removeTunnelModeEnabledListener(
const sp<gui::ITunnelModeEnabledListener>& listener) {
if (!listener) {
return BAD_VALUE;
}
mTunnelModeEnabledReporter->removeListener(listener);
return NO_ERROR;
}
status_t SurfaceFlinger::getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
bool* outSupport) const {
if (!displayToken || !outSupport) {
return BAD_VALUE;
}
Mutex::Autolock lock(mStateLock);
const auto displayId = getPhysicalDisplayIdLocked(displayToken);
if (!displayId) {
return NAME_NOT_FOUND;
}
*outSupport = getHwComposer().hasDisplayCapability(*displayId, DisplayCapability::BRIGHTNESS);
return NO_ERROR;
}
status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken,
const gui::DisplayBrightness& brightness) {
if (!displayToken) {
return BAD_VALUE;
}
const char* const whence = __func__;
return ftl::Future(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
// TODO(b/241285876): Validate that the display is physical instead of failing later.
if (const auto display = getDisplayDeviceLocked(displayToken)) {
const bool supportsDisplayBrightnessCommand =
getHwComposer().getComposer()->isSupported(
Hwc2::Composer::OptionalFeature::DisplayBrightnessCommand);
// If we support applying display brightness as a command, then we also support
// dimming SDR layers.
if (supportsDisplayBrightnessCommand) {
auto compositionDisplay = display->getCompositionDisplay();
float currentDimmingRatio =
compositionDisplay->editState().sdrWhitePointNits /
compositionDisplay->editState().displayBrightnessNits;
static constexpr float kDimmingThreshold = 0.02f;
if (brightness.sdrWhitePointNits == 0.f ||
abs(brightness.sdrWhitePointNits - brightness.displayBrightnessNits) /
brightness.sdrWhitePointNits >=
kDimmingThreshold) {
// to optimize, skip brightness setter if the brightness difference ratio
// is lower than threshold
compositionDisplay
->setDisplayBrightness(brightness.sdrWhitePointNits,
brightness.displayBrightnessNits);
} else {
compositionDisplay->setDisplayBrightness(brightness.sdrWhitePointNits,
brightness.sdrWhitePointNits);
}
FTL_FAKE_GUARD(kMainThreadContext,
display->stageBrightness(brightness.displayBrightness));
float currentHdrSdrRatio =
compositionDisplay->editState().displayBrightnessNits /
compositionDisplay->editState().sdrWhitePointNits;
FTL_FAKE_GUARD(kMainThreadContext,
display->updateHdrSdrRatioOverlayRatio(currentHdrSdrRatio));
if (brightness.sdrWhitePointNits / brightness.displayBrightnessNits !=
currentDimmingRatio) {
scheduleComposite(FrameHint::kNone);
} else {
scheduleCommit(FrameHint::kNone);
}
return ftl::yield<status_t>(OK);
} else {
return getHwComposer()
.setDisplayBrightness(display->getPhysicalId(),
brightness.displayBrightness,
brightness.displayBrightnessNits,
Hwc2::Composer::DisplayBrightnessOptions{
.applyImmediately = true});
}
} else {
ALOGE("%s: Invalid display token %p", whence, displayToken.get());
return ftl::yield<status_t>(NAME_NOT_FOUND);
}
}))
.then([](ftl::Future<status_t> task) { return task; })
.get();
}
status_t SurfaceFlinger::addHdrLayerInfoListener(const sp<IBinder>& displayToken,
const sp<gui::IHdrLayerInfoListener>& listener) {
if (!displayToken) {
return BAD_VALUE;
}
Mutex::Autolock lock(mStateLock);
const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
return NAME_NOT_FOUND;
}
const auto displayId = display->getId();
sp<HdrLayerInfoReporter>& hdrInfoReporter = mHdrLayerInfoListeners[displayId];
if (!hdrInfoReporter) {
hdrInfoReporter = sp<HdrLayerInfoReporter>::make();
}
hdrInfoReporter->addListener(listener);
mAddingHDRLayerInfoListener = true;
return OK;
}
status_t SurfaceFlinger::removeHdrLayerInfoListener(
const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener) {
if (!displayToken) {
return BAD_VALUE;
}
Mutex::Autolock lock(mStateLock);
const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
return NAME_NOT_FOUND;
}
const auto displayId = display->getId();
sp<HdrLayerInfoReporter>& hdrInfoReporter = mHdrLayerInfoListeners[displayId];
if (hdrInfoReporter) {
hdrInfoReporter->removeListener(listener);
}
return OK;
}
status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) {
using aidl::android::hardware::power::Boost;
Boost powerBoost = static_cast<Boost>(boostId);
if (powerBoost == Boost::INTERACTION) {
mScheduler->onTouchHint();
}
return NO_ERROR;
}
status_t SurfaceFlinger::getDisplayDecorationSupport(
const sp<IBinder>& displayToken,
std::optional<DisplayDecorationSupport>* outSupport) const {
if (!displayToken || !outSupport) {
return BAD_VALUE;
}
Mutex::Autolock lock(mStateLock);
const auto displayId = getPhysicalDisplayIdLocked(displayToken);
if (!displayId) {
return NAME_NOT_FOUND;
}
getHwComposer().getDisplayDecorationSupport(*displayId, outSupport);
return NO_ERROR;
}
// ----------------------------------------------------------------------------
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
gui::ISurfaceComposer::VsyncSource vsyncSource, EventRegistrationFlags eventRegistration,
const sp<IBinder>& layerHandle) {
const auto cycle = [&] {
if (FlagManager::getInstance().deprecate_vsync_sf()) {
ALOGW_IF(vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger,
"requested unsupported config eVsyncSourceSurfaceFlinger");
return scheduler::Cycle::Render;
}
return vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger
? scheduler::Cycle::LastComposite
: scheduler::Cycle::Render;
}();
return mScheduler->createDisplayEventConnection(cycle, eventRegistration, layerHandle);
}
void SurfaceFlinger::scheduleCommit(FrameHint hint) {
if (hint == FrameHint::kActive) {
mScheduler->resetIdleTimer();
}
mPowerAdvisor->notifyDisplayUpdateImminentAndCpuReset();
mScheduler->scheduleFrame();
}
void SurfaceFlinger::scheduleComposite(FrameHint hint) {
mMustComposite = true;
scheduleCommit(hint);
}
void SurfaceFlinger::scheduleRepaint() {
mGeometryDirty = true;
scheduleComposite(FrameHint::kActive);
}
void SurfaceFlinger::scheduleSample() {
static_cast<void>(mScheduler->schedule([this] { sample(); }));
}
nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const {
if (const auto display = getDefaultDisplayDeviceLocked()) {
return display->getVsyncPeriodFromHWC();
}
return 0;
}
void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
if (FlagManager::getInstance().connected_display() && timestamp < 0 &&
vsyncPeriod.has_value()) {
// use ~0 instead of -1 as AidlComposerHal.cpp passes the param as unsigned int32
if (mIsHotplugErrViaNegVsync && vsyncPeriod.value() == ~0) {
const auto errorCode = static_cast<int32_t>(-timestamp);
ALOGD("%s: Hotplug error %d for display %" PRIu64, __func__, errorCode, hwcDisplayId);
mScheduler->dispatchHotplugError(errorCode);
return;
}
if (mIsHdcpViaNegVsync && vsyncPeriod.value() == ~1) {
const int32_t value = static_cast<int32_t>(-timestamp);
// one byte is good enough to encode android.hardware.drm.HdcpLevel
const int32_t maxLevel = (value >> 8) & 0xFF;
const int32_t connectedLevel = value & 0xFF;
ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for display %" PRIu64, __func__,
connectedLevel, maxLevel, hwcDisplayId);
updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel);
return;
}
}
ATRACE_NAME(vsyncPeriod
? ftl::Concat(__func__, ' ', hwcDisplayId, ' ', *vsyncPeriod, "ns").c_str()
: ftl::Concat(__func__, ' ', hwcDisplayId).c_str());
Mutex::Autolock lock(mStateLock);
if (const auto displayIdOpt = getHwComposer().onVsync(hwcDisplayId, timestamp)) {
if (mScheduler->addResyncSample(*displayIdOpt, timestamp, vsyncPeriod)) {
// period flushed
mScheduler->modulateVsync(displayIdOpt, &VsyncModulator::onRefreshRateChangeCompleted);
}
}
}
void SurfaceFlinger::onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId,
DisplayHotplugEvent event) {
if (event == DisplayHotplugEvent::CONNECTED || event == DisplayHotplugEvent::DISCONNECTED) {
hal::Connection connection = (event == DisplayHotplugEvent::CONNECTED)
? hal::Connection::CONNECTED
: hal::Connection::DISCONNECTED;
{
std::lock_guard<std::mutex> lock(mHotplugMutex);
mPendingHotplugEvents.push_back(HotplugEvent{hwcDisplayId, connection});
}
if (mScheduler) {
mScheduler->scheduleConfigure();
}
return;
}
if (FlagManager::getInstance().hotplug2()) {
// TODO(b/311403559): use enum type instead of int
const auto errorCode = static_cast<int32_t>(event);
ALOGD("%s: Hotplug error %d for display %" PRIu64, __func__, errorCode, hwcDisplayId);
mScheduler->dispatchHotplugError(errorCode);
}
}
void SurfaceFlinger::onComposerHalVsyncPeriodTimingChanged(
hal::HWDisplayId, const hal::VsyncPeriodChangeTimeline& timeline) {
Mutex::Autolock lock(mStateLock);
mScheduler->onNewVsyncPeriodChangeTimeline(timeline);
if (timeline.refreshRequired) {
scheduleComposite(FrameHint::kNone);
}
}
void SurfaceFlinger::onComposerHalSeamlessPossible(hal::HWDisplayId) {
// TODO(b/142753666): use constraints when calling to setActiveModeWithConstraints and
// use this callback to know when to retry in case of SEAMLESS_NOT_POSSIBLE.
}
void SurfaceFlinger::onComposerHalRefresh(hal::HWDisplayId) {
Mutex::Autolock lock(mStateLock);
scheduleComposite(FrameHint::kNone);
}
void SurfaceFlinger::onComposerHalVsyncIdle(hal::HWDisplayId) {
ATRACE_CALL();
mScheduler->forceNextResync();
}
void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData& data) {
ATRACE_CALL();
const char* const whence = __func__;
static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
kMainThreadContext) {
if (const auto displayIdOpt = getHwComposer().toPhysicalDisplayId(data.display)) {
if (const auto display = getDisplayDeviceLocked(*displayIdOpt)) {
const Fps refreshRate = Fps::fromPeriodNsecs(
getHwComposer().getComposer()->isVrrSupported() ? data.refreshPeriodNanos
: data.vsyncPeriodNanos);
ATRACE_FORMAT("%s refresh rate = %d", whence, refreshRate.getIntValue());
const auto renderRate = mDisplayModeController.getActiveMode(*displayIdOpt).fps;
constexpr bool kSetByHwc = true;
display->updateRefreshRateOverlayRate(refreshRate, renderRate, kSetByHwc);
}
}
}));
}
void SurfaceFlinger::configure() {
Mutex::Autolock lock(mStateLock);
if (configureLocked()) {
setTransactionFlags(eDisplayTransactionNeeded);
}
}
bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs,
bool flushTransactions,
bool& outTransactionsAreEmpty) {
ATRACE_CALL();
frontend::Update update;
if (flushTransactions) {
update = flushLifecycleUpdates();
if (mTransactionTracing) {
mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), frameTimeNs,
update, mFrontEndDisplayInfos,
mFrontEndDisplayInfosChanged);
}
}
bool needsTraversal = false;
if (flushTransactions) {
needsTraversal |= commitMirrorDisplays(vsyncId);
needsTraversal |= commitCreatedLayers(vsyncId, update.layerCreatedStates);
needsTraversal |= applyTransactions(update.transactions);
}
outTransactionsAreEmpty = !needsTraversal;
const bool shouldCommit = (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal;
if (shouldCommit) {
commitTransactionsLegacy();
}
bool mustComposite = latchBuffers() || shouldCommit;
updateLayerGeometry();
return mustComposite;
}
void SurfaceFlinger::updateLayerHistory(nsecs_t now) {
for (const auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
using Changes = frontend::RequestedLayerState::Changes;
if (snapshot->path.isClone()) {
continue;
}
const bool updateSmallDirty = FlagManager::getInstance().enable_small_area_detection() &&
((snapshot->clientChanges & layer_state_t::eSurfaceDamageRegionChanged) ||
snapshot->changes.any(Changes::Geometry));
const bool hasChanges =
snapshot->changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation |
Changes::Geometry | Changes::Visibility) ||
(snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) !=
0;
if (!updateSmallDirty && !hasChanges) {
continue;
}
auto it = mLegacyLayers.find(snapshot->sequence);
LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
"Couldn't find layer object for %s",
snapshot->getDebugString().c_str());
if (updateSmallDirty) {
// Update small dirty flag while surface damage region or geometry changed
it->second->setIsSmallDirty(snapshot.get());
}
if (!hasChanges) {
continue;
}
const auto layerProps = scheduler::LayerProps{
.visible = snapshot->isVisible,
.bounds = snapshot->geomLayerBounds,
.transform = snapshot->geomLayerTransform,
.setFrameRateVote = snapshot->frameRate,
.frameRateSelectionPriority = snapshot->frameRateSelectionPriority,
.isSmallDirty = snapshot->isSmallDirty,
.isFrontBuffered = snapshot->isFrontBuffered(),
};
if (snapshot->changes.any(Changes::Geometry | Changes::Visibility)) {
mScheduler->setLayerProperties(snapshot->sequence, layerProps);
}
if (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
mScheduler->setDefaultFrameRateCompatibility(snapshot->sequence,
snapshot->defaultFrameRateCompatibility);
}
if (snapshot->changes.test(Changes::Animation)) {
it->second->recordLayerHistoryAnimationTx(layerProps, now);
}
if (snapshot->changes.test(Changes::FrameRate)) {
it->second->setFrameRateForLayerTree(snapshot->frameRate, layerProps, now);
}
if (snapshot->changes.test(Changes::Buffer)) {
it->second->recordLayerHistoryBufferUpdate(layerProps, now);
}
}
}
bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs,
bool flushTransactions, bool& outTransactionsAreEmpty) {
using Changes = frontend::RequestedLayerState::Changes;
ATRACE_CALL();
frontend::Update update;
if (flushTransactions) {
ATRACE_NAME("TransactionHandler:flushTransactions");
// Locking:
// 1. to prevent onHandleDestroyed from being called while the state lock is held,
// we must keep a copy of the transactions (specifically the composer
// states) around outside the scope of the lock.
// 2. Transactions and created layers do not share a lock. To prevent applying
// transactions with layers still in the createdLayer queue, collect the transactions
// before committing the created layers.
// 3. Transactions can only be flushed after adding layers, since the layer can be a newly
// created one
mTransactionHandler.collectTransactions();
{
// TODO(b/238781169) lockless queue this and keep order.
std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
update.layerCreatedStates = std::move(mCreatedLayers);
mCreatedLayers.clear();
update.newLayers = std::move(mNewLayers);
mNewLayers.clear();
update.layerCreationArgs = std::move(mNewLayerArgs);
mNewLayerArgs.clear();
update.destroyedHandles = std::move(mDestroyedHandles);
mDestroyedHandles.clear();
}
mLayerLifecycleManager.addLayers(std::move(update.newLayers));
update.transactions = mTransactionHandler.flushTransactions();
if (mTransactionTracing) {
mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), frameTimeNs,
update, mFrontEndDisplayInfos,
mFrontEndDisplayInfosChanged);
}
mLayerLifecycleManager.applyTransactions(update.transactions);
mLayerLifecycleManager.onHandlesDestroyed(update.destroyedHandles);
for (auto& legacyLayer : update.layerCreatedStates) {
sp<Layer> layer = legacyLayer.layer.promote();
if (layer) {
mLegacyLayers[layer->sequence] = layer;
}
}
mLayerHierarchyBuilder.update(mLayerLifecycleManager);
}
// Keep a copy of the drawing state (that is going to be overwritten
// by commitTransactionsLocked) outside of mStateLock so that the side
// effects of the State assignment don't happen with mStateLock held,
// which can cause deadlocks.
State drawingState(mDrawingState);
Mutex::Autolock lock(mStateLock);
bool mustComposite = false;
mustComposite |= applyAndCommitDisplayTransactionStatesLocked(update.transactions);
{
ATRACE_NAME("LayerSnapshotBuilder:update");
frontend::LayerSnapshotBuilder::Args
args{.root = mLayerHierarchyBuilder.getHierarchy(),
.layerLifecycleManager = mLayerLifecycleManager,
.includeMetadata = mCompositionEngine->getFeatureFlags().test(
compositionengine::Feature::kSnapshotLayerMetadata),
.displays = mFrontEndDisplayInfos,
.displayChanges = mFrontEndDisplayInfosChanged,
.globalShadowSettings = mDrawingState.globalShadowSettings,
.supportsBlur = mSupportsBlur,
.forceFullDamage = mForceFullDamage,
.supportedLayerGenericMetadata =
getHwComposer().getSupportedLayerGenericMetadata(),
.genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap(),
.skipRoundCornersWhenProtected =
!getRenderEngine().supportsProtectedContent()};
mLayerSnapshotBuilder.update(args);
}
if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Geometry | Changes::Input |
Changes::Hierarchy | Changes::Visibility)) {
mUpdateInputInfo = true;
}
if (mLayerLifecycleManager.getGlobalChanges().any(Changes::VisibleRegion | Changes::Hierarchy |
Changes::Visibility | Changes::Geometry)) {
mVisibleRegionsDirty = true;
}
if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Hierarchy | Changes::FrameRate)) {
// The frame rate of attached choreographers can only change as a result of a
// FrameRate change (including when Hierarchy changes).
mUpdateAttachedChoreographer = true;
}
outTransactionsAreEmpty = mLayerLifecycleManager.getGlobalChanges().get() == 0;
if (FlagManager::getInstance().vrr_bugfix_24q4()) {
mustComposite |= mLayerLifecycleManager.getGlobalChanges().any(
frontend::RequestedLayerState::kMustComposite);
} else {
mustComposite |= mLayerLifecycleManager.getGlobalChanges().get() != 0;
}
bool newDataLatched = false;
ATRACE_NAME("DisplayCallbackAndStatsUpdates");
mustComposite |= applyTransactionsLocked(update.transactions);
traverseLegacyLayers([&](Layer* layer) { layer->commitTransaction(); });
const nsecs_t latchTime = systemTime();
bool unused = false;
for (auto& layer : mLayerLifecycleManager.getLayers()) {
if (layer->changes.test(frontend::RequestedLayerState::Changes::Created) &&
layer->bgColorLayer) {
sp<Layer> bgColorLayer = getFactory().createEffectLayer(
LayerCreationArgs(this, nullptr, layer->name,
ISurfaceComposerClient::eFXSurfaceEffect, LayerMetadata(),
std::make_optional(layer->id), true));
mLegacyLayers[bgColorLayer->sequence] = bgColorLayer;
}
const bool willReleaseBufferOnLatch = layer->willReleaseBufferOnLatch();
auto it = mLegacyLayers.find(layer->id);
if (it == mLegacyLayers.end() &&
layer->changes.test(frontend::RequestedLayerState::Changes::Destroyed)) {
// Layer handle was created and immediately destroyed. It was destroyed before it
// was added to the map.
continue;
}
LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
"Couldnt find layer object for %s",
layer->getDebugString().c_str());
if (!layer->hasReadyFrame() && !willReleaseBufferOnLatch) {
if (!it->second->hasBuffer()) {
// The last latch time is used to classify a missed frame as buffer stuffing
// instead of a missed frame. This is used to identify scenarios where we
// could not latch a buffer or apply a transaction due to backpressure.
// We only update the latch time for buffer less layers here, the latch time
// is updated for buffer layers when the buffer is latched.
it->second->updateLastLatchTime(latchTime);
}
continue;
}
const bool bgColorOnly =
!layer->externalTexture && (layer->bgColorLayerId != UNASSIGNED_LAYER_ID);
if (willReleaseBufferOnLatch) {
mLayersWithBuffersRemoved.emplace(it->second);
}
it->second->latchBufferImpl(unused, latchTime, bgColorOnly);
newDataLatched = true;
mLayersWithQueuedFrames.emplace(it->second);
mLayersIdsWithQueuedFrames.emplace(it->second->sequence);
}
updateLayerHistory(latchTime);
mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) {
if (mLayersIdsWithQueuedFrames.find(snapshot.path.id) == mLayersIdsWithQueuedFrames.end())
return;
Region visibleReg;
visibleReg.set(snapshot.transformedBoundsWithoutTransparentRegion);
invalidateLayerStack(snapshot.outputFilter, visibleReg);
});
for (auto& destroyedLayer : mLayerLifecycleManager.getDestroyedLayers()) {
mLegacyLayers.erase(destroyedLayer->id);
}
{
ATRACE_NAME("LayerLifecycleManager:commitChanges");
mLayerLifecycleManager.commitChanges();
}
// enter boot animation on first buffer latch
if (CC_UNLIKELY(mBootStage == BootStage::BOOTLOADER && newDataLatched)) {
ALOGI("Enter boot animation");
mBootStage = BootStage::BOOTANIMATION;
}
mustComposite |= (getTransactionFlags() & ~eTransactionFlushNeeded) || newDataLatched;
if (mustComposite) {
commitTransactions();
}
return mustComposite;
}
bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId,
const scheduler::FrameTargets& frameTargets) {
const scheduler::FrameTarget& pacesetterFrameTarget = *frameTargets.get(pacesetterId)->get();
const VsyncId vsyncId = pacesetterFrameTarget.vsyncId();
ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
if (pacesetterFrameTarget.didMissFrame()) {
mTimeStats->incrementMissedFrames();
}
// If a mode set is pending and the fence hasn't fired yet, wait for the next commit.
if (std::any_of(frameTargets.begin(), frameTargets.end(),
[this](const auto& pair) FTL_FAKE_GUARD(kMainThreadContext) {
const auto [displayId, target] = pair;
return target->isFramePending() &&
mDisplayModeController.isModeSetPending(displayId);
})) {
mScheduler->scheduleFrame();
return false;
}
{
ConditionalLock lock(mStateLock, FlagManager::getInstance().connected_display());
for (const auto [displayId, _] : frameTargets) {
if (mDisplayModeController.isModeSetPending(displayId)) {
finalizeDisplayModeChange(displayId);
}
}
}
if (pacesetterFrameTarget.isFramePending()) {
if (mBackpressureGpuComposition || pacesetterFrameTarget.didMissHwcFrame()) {
if (FlagManager::getInstance().vrr_config()) {
mScheduler->getVsyncSchedule()->getTracker().onFrameMissed(
pacesetterFrameTarget.expectedPresentTime());
}
scheduleCommit(FrameHint::kNone);
return false;
}
}
const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period();
// Save this once per commit + composite to ensure consistency
// TODO (b/240619471): consider removing active display check once AOD is fixed
const auto activeDisplay = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayId));
mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession() && activeDisplay &&
activeDisplay->getPowerMode() == hal::PowerMode::ON;
if (mPowerHintSessionEnabled) {
mPowerAdvisor->setCommitStart(pacesetterFrameTarget.frameBeginTime());
mPowerAdvisor->setExpectedPresentTime(pacesetterFrameTarget.expectedPresentTime());
// Frame delay is how long we should have minus how long we actually have.
const Duration idealSfWorkDuration =
mScheduler->vsyncModulator().getVsyncConfig().sfWorkDuration;
const Duration frameDelay =
idealSfWorkDuration - pacesetterFrameTarget.expectedFrameDuration();
mPowerAdvisor->setFrameDelay(frameDelay);
mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration);
const Period idealVsyncPeriod =
mDisplayModeController.getActiveMode(pacesetterId).fps.getPeriod();
mPowerAdvisor->updateTargetWorkDuration(idealVsyncPeriod);
}
if (mRefreshRateOverlaySpinner || mHdrSdrRatioOverlay) {
Mutex::Autolock lock(mStateLock);
if (const auto display = getDefaultDisplayDeviceLocked()) {
display->animateOverlay();
}
}
// Composite if transactions were committed, or if requested by HWC.
bool mustComposite = mMustComposite.exchange(false);
{
mFrameTimeline->setSfWakeUp(ftl::to_underlying(vsyncId),
pacesetterFrameTarget.frameBeginTime().ns(),
Fps::fromPeriodNsecs(vsyncPeriod.ns()),
mScheduler->getPacesetterRefreshRate());
const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded);
bool transactionsAreEmpty = false;
if (mLayerLifecycleManagerEnabled) {
mustComposite |=
updateLayerSnapshots(vsyncId, pacesetterFrameTarget.frameBeginTime().ns(),
flushTransactions, transactionsAreEmpty);
}
// Tell VsyncTracker that we are going to present this frame before scheduling
// setTransactionFlags which will schedule another SF frame. This was if the tracker
// needs to adjust the vsync timeline, it will be done before the next frame.
if (FlagManager::getInstance().vrr_config() && mustComposite) {
mScheduler->getVsyncSchedule()->getTracker().onFrameBegin(
pacesetterFrameTarget.expectedPresentTime(),
pacesetterFrameTarget.lastSignaledFrameTime());
}
if (transactionFlushNeeded()) {
setTransactionFlags(eTransactionFlushNeeded);
}
// This has to be called after latchBuffers because we want to include the layers that have
// been latched in the commit callback
if (transactionsAreEmpty) {
// Invoke empty transaction callbacks early.
mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
} else {
// Invoke OnCommit callbacks.
mTransactionCallbackInvoker.sendCallbacks(true /* onCommitOnly */);
}
}
// Layers need to get updated (in the previous line) before we can use them for
// choosing the refresh rate.
// Hold mStateLock as chooseRefreshRateForContent promotes wp<Layer> to sp<Layer>
// and may eventually call to ~Layer() if it holds the last reference
{
bool updateAttachedChoreographer = mUpdateAttachedChoreographer;
mUpdateAttachedChoreographer = false;
Mutex::Autolock lock(mStateLock);
mScheduler->chooseRefreshRateForContent(mLayerLifecycleManagerEnabled
? &mLayerHierarchyBuilder.getHierarchy()
: nullptr,
updateAttachedChoreographer);
if (FlagManager::getInstance().connected_display()) {
initiateDisplayModeChanges();
}
}
if (!FlagManager::getInstance().connected_display()) {
ftl::FakeGuard guard(mStateLock);
initiateDisplayModeChanges();
}
updateCursorAsync();
if (!mustComposite) {
updateInputFlinger(vsyncId, pacesetterFrameTarget.frameBeginTime());
}
doActiveLayersTracingIfNeeded(false, mVisibleRegionsDirty,
pacesetterFrameTarget.frameBeginTime(), vsyncId);
mLastCommittedVsyncId = vsyncId;
persistDisplayBrightness(mustComposite);
return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER);
}
CompositeResultsPerDisplay SurfaceFlinger::composite(
PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters& frameTargeters) {
const scheduler::FrameTarget& pacesetterTarget =
frameTargeters.get(pacesetterId)->get()->target();
const VsyncId vsyncId = pacesetterTarget.vsyncId();
ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
compositionengine::CompositionRefreshArgs refreshArgs;
refreshArgs.powerCallback = this;
const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays);
refreshArgs.outputs.reserve(displays.size());
// Add outputs for physical displays.
for (const auto& [id, targeter] : frameTargeters) {
ftl::FakeGuard guard(mStateLock);
if (const auto display = getCompositionDisplayLocked(id)) {
refreshArgs.outputs.push_back(display);
}
refreshArgs.frameTargets.try_emplace(id, &targeter->target());
}
std::vector<DisplayId> displayIds;
for (const auto& [_, display] : displays) {
displayIds.push_back(display->getId());
display->tracePowerMode();
// Add outputs for virtual displays.
if (display->isVirtual()) {
const Fps re