SF: Do not toggle idle timer on display hotplug
As a short-term measure to avoid a deadlock during display hotplug, skip
stopping and starting the pacesetter's idle timer on demotion/promotion.
This assumes that hotplug results in demoting/promoting the same display
as pacesetter, currently always the active internal display.
Fixes: 329450361
Flag: com.android.graphics.surfaceflinger.flags.connected_display
Test: Manual (foldable, connected display)
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:b972419619256ea07ebdb35b737946faf5c21bdc)
Merged-In: Ieeb026666c8abdd14e0d4690a624fb60306b1bc1
Change-Id: Ieeb026666c8abdd14e0d4690a624fb60306b1bc1
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 9ea3f35..5ec7e48 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -82,7 +82,7 @@
mTouchTimer.reset();
// Stop idle timer and clear callbacks, as the RefreshRateSelector may outlive the Scheduler.
- demotePacesetterDisplay();
+ demotePacesetterDisplay({.toggleIdleTimer = true});
}
void Scheduler::initVsync(frametimeline::TokenManager& tokenManager,
@@ -118,9 +118,10 @@
}
void Scheduler::setPacesetterDisplay(PhysicalDisplayId pacesetterId) {
- demotePacesetterDisplay();
+ constexpr PromotionParams kPromotionParams = {.toggleIdleTimer = true};
- promotePacesetterDisplay(pacesetterId);
+ demotePacesetterDisplay(kPromotionParams);
+ promotePacesetterDisplay(pacesetterId, kPromotionParams);
}
void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
@@ -139,16 +140,22 @@
RefreshRateSelectorPtr selectorPtr,
VsyncSchedulePtr schedulePtr,
PhysicalDisplayId activeDisplayId) {
- demotePacesetterDisplay();
+ const bool isPrimary = (ftl::FakeGuard(mDisplayLock), !mPacesetterDisplayId);
- auto [pacesetterVsyncSchedule, isNew] = [&]() FTL_FAKE_GUARD(kMainThreadContext) {
+ // Start the idle timer for the first registered (i.e. primary) display.
+ const PromotionParams promotionParams = {.toggleIdleTimer = isPrimary};
+
+ demotePacesetterDisplay(promotionParams);
+
+ auto [pacesetterVsyncSchedule, isNew] = [&]() REQUIRES(kMainThreadContext) {
std::scoped_lock lock(mDisplayLock);
const bool isNew = mDisplays
.emplace_or_replace(displayId, displayId, std::move(selectorPtr),
std::move(schedulePtr), mFeatures)
.second;
- return std::make_pair(promotePacesetterDisplayLocked(activeDisplayId), isNew);
+ return std::make_pair(promotePacesetterDisplayLocked(activeDisplayId, promotionParams),
+ isNew);
}();
applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
@@ -166,7 +173,8 @@
dispatchHotplug(displayId, Hotplug::Disconnected);
- demotePacesetterDisplay();
+ constexpr PromotionParams kPromotionParams = {.toggleIdleTimer = false};
+ demotePacesetterDisplay(kPromotionParams);
std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
{
@@ -178,7 +186,7 @@
// headless virtual display.)
LOG_ALWAYS_FATAL_IF(mDisplays.empty(), "Cannot unregister all displays!");
- pacesetterVsyncSchedule = promotePacesetterDisplayLocked(activeDisplayId);
+ pacesetterVsyncSchedule = promotePacesetterDisplayLocked(activeDisplayId, kPromotionParams);
}
applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
}
@@ -917,19 +925,18 @@
return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
}
-void Scheduler::promotePacesetterDisplay(PhysicalDisplayId pacesetterId) {
+void Scheduler::promotePacesetterDisplay(PhysicalDisplayId pacesetterId, PromotionParams params) {
std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
-
{
std::scoped_lock lock(mDisplayLock);
- pacesetterVsyncSchedule = promotePacesetterDisplayLocked(pacesetterId);
+ pacesetterVsyncSchedule = promotePacesetterDisplayLocked(pacesetterId, params);
}
applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
}
std::shared_ptr<VsyncSchedule> Scheduler::promotePacesetterDisplayLocked(
- PhysicalDisplayId pacesetterId) {
+ PhysicalDisplayId pacesetterId, PromotionParams params) {
// TODO: b/241286431 - Choose the pacesetter among mDisplays.
mPacesetterDisplayId = pacesetterId;
ALOGI("Display %s is the pacesetter", to_string(pacesetterId).c_str());
@@ -938,15 +945,18 @@
if (const auto pacesetterOpt = pacesetterDisplayLocked()) {
const Display& pacesetter = *pacesetterOpt;
- pacesetter.selectorPtr->setIdleTimerCallbacks(
- {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
- .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
- .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
- .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }},
- .vrr = {.onReset = [this] { mSchedulerCallback.vrrDisplayIdle(false); },
- .onExpired = [this] { mSchedulerCallback.vrrDisplayIdle(true); }}});
+ if (!FlagManager::getInstance().connected_display() || params.toggleIdleTimer) {
+ pacesetter.selectorPtr->setIdleTimerCallbacks(
+ {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
+ .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
+ .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
+ .onExpired =
+ [this] { kernelIdleTimerCallback(TimerState::Expired); }},
+ .vrr = {.onReset = [this] { mSchedulerCallback.vrrDisplayIdle(false); },
+ .onExpired = [this] { mSchedulerCallback.vrrDisplayIdle(true); }}});
- pacesetter.selectorPtr->startIdleTimer();
+ pacesetter.selectorPtr->startIdleTimer();
+ }
newVsyncSchedulePtr = pacesetter.schedulePtr;
@@ -966,11 +976,14 @@
}
}
-void Scheduler::demotePacesetterDisplay() {
- // No need to lock for reads on kMainThreadContext.
- if (const auto pacesetterPtr = FTL_FAKE_GUARD(mDisplayLock, pacesetterSelectorPtrLocked())) {
- pacesetterPtr->stopIdleTimer();
- pacesetterPtr->clearIdleTimerCallbacks();
+void Scheduler::demotePacesetterDisplay(PromotionParams params) {
+ if (!FlagManager::getInstance().connected_display() || params.toggleIdleTimer) {
+ // No need to lock for reads on kMainThreadContext.
+ if (const auto pacesetterPtr =
+ FTL_FAKE_GUARD(mDisplayLock, pacesetterSelectorPtrLocked())) {
+ pacesetterPtr->stopIdleTimer();
+ pacesetterPtr->clearIdleTimerCallbacks();
+ }
}
// Clear state that depends on the pacesetter's RefreshRateSelector.