SurfaceFlinger: override frame rate for applications that use setFrameRate

(Uploaded again with a fix for b/176299745)

When possible, override the frame rate of an app if it explicitly
set a preferred frame rate.

Test: atest FrameRateOverrideHostTest
Test: SF unit tests
Bug: 170502573
Change-Id: I84a5a99f439ec7a8a808a0b26c630d4344acca4f
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index effdbcb..1f0fc98 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -206,8 +206,32 @@
                                                        readyDuration, traceVsync, name);
 }
 
+std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
+    std::lock_guard lock(mFrameRateOverridesMutex);
+    {
+        const auto iter = mFrameRateOverridesFromBackdoor.find(uid);
+        if (iter != mFrameRateOverridesFromBackdoor.end()) {
+            return std::make_optional<Fps>(iter->second);
+        }
+    }
+
+    {
+        const auto iter = mFrameRateOverridesByContent.find(uid);
+        if (iter != mFrameRateOverridesByContent.end()) {
+            return std::make_optional<Fps>(iter->second);
+        }
+    }
+
+    return std::nullopt;
+}
+
 bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const {
-    const auto divider = mRefreshRateConfigs.getRefreshRateDividerForUid(uid);
+    const auto frameRate = getFrameRateOverride(uid);
+    if (!frameRate.has_value()) {
+        return true;
+    }
+
+    const auto divider = mRefreshRateConfigs.getRefreshRateDivider(*frameRate);
     if (divider <= 1) {
         return true;
     }
@@ -290,11 +314,22 @@
     thread->onScreenReleased();
 }
 
-void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
-                                            std::vector<FrameRateOverride> overrides) {
+void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
+    std::vector<FrameRateOverride> overrides;
+    {
+        std::lock_guard lock(mFrameRateOverridesMutex);
+        for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
+            overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+        }
+        for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
+            if (mFrameRateOverridesFromBackdoor.count(uid) == 0) {
+                overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+            }
+        }
+    }
     android::EventThread* thread;
     {
-        std::lock_guard<std::mutex> lock(mConnectionsLock);
+        std::lock_guard lock(mConnectionsLock);
         RETURN_IF_INVALID_HANDLE(handle);
         thread = mConnections[handle].thread.get();
     }
@@ -303,9 +338,11 @@
 
 void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
                                               HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
-    std::lock_guard<std::mutex> lock(mFeatureStateLock);
-    // Cache the last reported config for primary display.
-    mFeatures.cachedConfigChangedParams = {handle, displayId, configId, vsyncPeriod};
+    {
+        std::lock_guard<std::mutex> lock(mFeatureStateLock);
+        // Cache the last reported config for primary display.
+        mFeatures.cachedConfigChangedParams = {handle, displayId, configId, vsyncPeriod};
+    }
     onNonPrimaryDisplayConfigChanged(handle, displayId, configId, vsyncPeriod);
 }
 
@@ -550,7 +587,10 @@
     ATRACE_CALL();
 
     scheduler::LayerHistory::Summary summary = mLayerHistory->summarize(systemTime());
+    scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
     HwcConfigIndexType newConfigId;
+    bool frameRateChanged;
+    bool frameRateOverridesChanged;
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
         if (mFeatures.contentRequirements == summary) {
@@ -558,22 +598,32 @@
         }
         mFeatures.contentRequirements = summary;
 
-        scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
         newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
+        auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+        frameRateOverridesChanged =
+                updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
+
         if (mFeatures.configId == newConfigId) {
             // We don't need to change the config, but we might need to send an event
             // about a config change, since it was suppressed due to a previous idleConsidered
             if (!consideredSignals.idle) {
                 dispatchCachedReportedConfig();
             }
-            return;
+            frameRateChanged = false;
+        } else {
+            mFeatures.configId = newConfigId;
+            frameRateChanged = true;
         }
-        mFeatures.configId = newConfigId;
+    }
+    if (frameRateChanged) {
         auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
         mSchedulerCallback.changeRefreshRate(newRefreshRate,
                                              consideredSignals.idle ? ConfigEvent::None
                                                                     : ConfigEvent::Changed);
     }
+    if (frameRateOverridesChanged) {
+        mSchedulerCallback.triggerOnFrameRateOverridesChanged();
+    }
 }
 
 void Scheduler::resetIdleTimer() {
@@ -666,6 +716,21 @@
     StringAppendF(&result, "+  Content detection: %s %s\n\n",
                   toContentDetectionString(mOptions.useContentDetection),
                   mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
+
+    {
+        std::lock_guard lock(mFrameRateOverridesMutex);
+        StringAppendF(&result, "Frame Rate Overrides (backdoor): {");
+        for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
+            StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+        }
+        StringAppendF(&result, "}\n");
+
+        StringAppendF(&result, "Frame Rate Overrides (setFrameRate): {");
+        for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
+            StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+        }
+        StringAppendF(&result, "}\n");
+    }
 }
 
 void Scheduler::dumpVsync(std::string& s) const {
@@ -677,9 +742,37 @@
     mVsyncSchedule.dispatch->dump(s);
 }
 
+bool Scheduler::updateFrameRateOverrides(
+        scheduler::RefreshRateConfigs::GlobalSignals consideredSignals, Fps displayRefreshRate) {
+    if (consideredSignals.touch) {
+        std::lock_guard lock(mFrameRateOverridesMutex);
+        const bool changed = !mFrameRateOverridesByContent.empty();
+        mFrameRateOverridesByContent.clear();
+        return changed;
+    }
+
+    if (!consideredSignals.idle) {
+        const auto frameRateOverrides =
+                mRefreshRateConfigs.getFrameRateOverrides(mFeatures.contentRequirements,
+                                                          displayRefreshRate);
+        std::lock_guard lock(mFrameRateOverridesMutex);
+        if (!std::equal(mFrameRateOverridesByContent.begin(), mFrameRateOverridesByContent.end(),
+                        frameRateOverrides.begin(), frameRateOverrides.end(),
+                        [](const std::pair<uid_t, Fps>& a, const std::pair<uid_t, Fps>& b) {
+                            return a.first == b.first && a.second.equalsWithMargin(b.second);
+                        })) {
+            mFrameRateOverridesByContent = frameRateOverrides;
+            return true;
+        }
+    }
+    return false;
+}
+
 template <class T>
 bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
     HwcConfigIndexType newConfigId;
+    bool refreshRateChanged = false;
+    bool frameRateOverridesChanged;
     scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
@@ -688,20 +781,32 @@
         }
         *currentState = newState;
         newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
+        const RefreshRate& newRefreshRate =
+                mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+        frameRateOverridesChanged =
+                updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
         if (mFeatures.configId == newConfigId) {
             // We don't need to change the config, but we might need to send an event
             // about a config change, since it was suppressed due to a previous idleConsidered
             if (!consideredSignals.idle) {
                 dispatchCachedReportedConfig();
             }
-            return consideredSignals.touch;
+        } else {
+            mFeatures.configId = newConfigId;
+            refreshRateChanged = true;
         }
-        mFeatures.configId = newConfigId;
     }
-    const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
-    mSchedulerCallback.changeRefreshRate(newRefreshRate,
-                                         consideredSignals.idle ? ConfigEvent::None
-                                                                : ConfigEvent::Changed);
+    if (refreshRateChanged) {
+        const RefreshRate& newRefreshRate =
+                mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+
+        mSchedulerCallback.changeRefreshRate(newRefreshRate,
+                                             consideredSignals.idle ? ConfigEvent::None
+                                                                    : ConfigEvent::Changed);
+    }
+    if (frameRateOverridesChanged) {
+        mSchedulerCallback.triggerOnFrameRateOverridesChanged();
+    }
     return consideredSignals.touch;
 }
 
@@ -775,4 +880,17 @@
     }
 }
 
+void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
+    if (frameRateOverride.frameRateHz > 0.f && frameRateOverride.frameRateHz < 1.f) {
+        return;
+    }
+
+    std::lock_guard lock(mFrameRateOverridesMutex);
+    if (frameRateOverride.frameRateHz != 0.f) {
+        mFrameRateOverridesFromBackdoor[frameRateOverride.uid] = Fps(frameRateOverride.frameRateHz);
+    } else {
+        mFrameRateOverridesFromBackdoor.erase(frameRateOverride.uid);
+    }
+}
+
 } // namespace android