Support repurposing idle timer to work with kernel timer.

Idle timer would instead enable/disable hw vsyncs in accordance with
inactivity.

Bug: 130684082
Test: systrace
Change-Id: I289600fc41b7ded863c9486cf6c2ab6155b06550
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.cpp b/services/surfaceflinger/Scheduler/IdleTimer.cpp
index b28b1aa..37fdfc7 100644
--- a/services/surfaceflinger/Scheduler/IdleTimer.cpp
+++ b/services/surfaceflinger/Scheduler/IdleTimer.cpp
@@ -84,8 +84,11 @@
                 constexpr auto zero = std::chrono::steady_clock::duration::zero();
                 auto waitTime = triggerTime - std::chrono::steady_clock::now();
                 if (waitTime > zero) mCondition.wait_for(mMutex, waitTime);
-                if (mState == TimerState::WAITING &&
-                    (triggerTime - std::chrono::steady_clock::now()) <= zero) {
+                if (mState == TimerState::RESET) {
+                    triggerTime = std::chrono::steady_clock::now() + mInterval;
+                    mState = TimerState::WAITING;
+                } else if (mState == TimerState::WAITING &&
+                           (triggerTime - std::chrono::steady_clock::now()) <= zero) {
                     triggerTimeout = true;
                     mState = TimerState::IDLE;
                 }
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.h b/services/surfaceflinger/Scheduler/IdleTimer.h
index 19f1267..2646688 100644
--- a/services/surfaceflinger/Scheduler/IdleTimer.h
+++ b/services/surfaceflinger/Scheduler/IdleTimer.h
@@ -39,13 +39,31 @@
               const TimeoutCallback& timeoutCallback);
     ~IdleTimer();
 
+    // Initializes and turns on the idle timer.
     void start();
+    // Stops the idle timer and any held resources.
     void stop();
+    // Resets the wakeup time and fires the reset callback.
     void reset();
 
 private:
     // Enum to track in what state is the timer.
-    enum class TimerState { STOPPED = 0, RESET = 1, WAITING = 2, IDLE = 3 };
+    enum class TimerState {
+        // The internal timer thread has been destroyed, and no state is
+        // tracked.
+        // Possible state transitions: RESET
+        STOPPED = 0,
+        // An external thread has just reset this timer.
+        // If there is a reset callback, then that callback is fired.
+        // Possible state transitions: STOPPED, WAITING
+        RESET = 1,
+        // This timer is waiting for the timeout interval to expire.
+        // Possible state transaitions: STOPPED, RESET, IDLE
+        WAITING = 2,
+        // The timeout interval has expired, so we are sleeping now.
+        // Possible state transaitions: STOPPED, RESET
+        IDLE = 3
+    };
 
     // Function that loops until the condition for stopping is met.
     void loop();
@@ -59,6 +77,7 @@
     // Lock used for synchronizing the waiting thread with the application thread.
     std::mutex mMutex;
 
+    // Current timer state
     TimerState mState GUARDED_BY(mMutex) = TimerState::RESET;
 
     // Interval after which timer expires.
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 88d2638..b91c66e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -73,6 +73,7 @@
     mEventControlThread = std::make_unique<impl::EventControlThread>(function);
 
     mSetIdleTimerMs = set_idle_timer_ms(0);
+    mSupportKernelTimer = support_kernel_idle_timer(false);
 
     char value[PROPERTY_VALUE_MAX];
     property_get("debug.sf.set_idle_timer_ms", value, "0");
@@ -82,10 +83,20 @@
     }
 
     if (mSetIdleTimerMs > 0) {
-        mIdleTimer =
-                std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(mSetIdleTimerMs),
-                                                       [this] { resetTimerCallback(); },
-                                                       [this] { expiredTimerCallback(); });
+        if (mSupportKernelTimer) {
+            mIdleTimer =
+                    std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
+                                                                   mSetIdleTimerMs),
+                                                           [this] { resetKernelTimerCallback(); },
+                                                           [this] {
+                                                               expiredKernelTimerCallback();
+                                                           });
+        } else {
+            mIdleTimer = std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
+                                                                        mSetIdleTimerMs),
+                                                                [this] { resetTimerCallback(); },
+                                                                [this] { expiredTimerCallback(); });
+        }
         mIdleTimer->start();
     }
 }
@@ -344,6 +355,11 @@
     mChangeRefreshRateCallback = changeRefreshRateCallback;
 }
 
+void Scheduler::setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod) {
+    std::lock_guard<std::mutex> lock(mCallbackLock);
+    mGetVsyncPeriod = getVsyncPeriod;
+}
+
 void Scheduler::updateFrameSkipping(const int64_t skipCount) {
     ATRACE_INT("FrameSkipCount", skipCount);
     if (mSkipCount != skipCount) {
@@ -360,17 +376,30 @@
 }
 
 void Scheduler::resetTimerCallback() {
-    // We do not notify the applications about config changes when idle timer is reset.
     timerChangeRefreshRate(IdleTimerState::RESET);
     ATRACE_INT("ExpiredIdleTimer", 0);
 }
 
+void Scheduler::resetKernelTimerCallback() {
+    ATRACE_INT("ExpiredKernelIdleTimer", 0);
+    std::lock_guard<std::mutex> lock(mCallbackLock);
+    if (mGetVsyncPeriod) {
+        resyncToHardwareVsync(false, mGetVsyncPeriod());
+    }
+}
+
 void Scheduler::expiredTimerCallback() {
-    // We do not notify the applications about config changes when idle timer expires.
     timerChangeRefreshRate(IdleTimerState::EXPIRED);
     ATRACE_INT("ExpiredIdleTimer", 1);
 }
 
+void Scheduler::expiredKernelTimerCallback() {
+    ATRACE_INT("ExpiredKernelIdleTimer", 1);
+    // Disable HW Vsync if the timer expired, as we don't need it
+    // enabled if we're not pushing frames.
+    disableHardwareVsync(false);
+}
+
 std::string Scheduler::doDump() {
     std::ostringstream stream;
     stream << "+  Idle timer interval: " << mSetIdleTimerMs << " ms" << std::endl;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 34327b5..c8ff377 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -157,6 +157,7 @@
     void updateFpsBasedOnContent();
     // Callback that gets invoked when Scheduler wants to change the refresh rate.
     void setChangeRefreshRateCallback(const ChangeRefreshRateCallback& changeRefreshRateCallback);
+    void setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod);
 
     // Returns whether idle timer is enabled or not
     bool isIdleTimerEnabled() { return mSetIdleTimerMs > 0; }
@@ -191,6 +192,14 @@
     void resetTimerCallback();
     // Function that is called when the timer expires.
     void expiredTimerCallback();
+    // Function that is called when the timer resets when paired with a display
+    // driver timeout in the kernel. This enables hardware vsync when we move
+    // out from idle.
+    void resetKernelTimerCallback();
+    // Function that is called when the timer expires when paired with a display
+    // driver timeout in the kernel. This disables hardware vsync when we move
+    // into idle.
+    void expiredKernelTimerCallback();
     // Sets vsync period.
     void setVsyncPeriod(const nsecs_t period);
     // Idle timer feature's function to change the refresh rate.
@@ -242,9 +251,13 @@
     // interval, a callback is fired. Set this variable to >0 to use this feature.
     int64_t mSetIdleTimerMs = 0;
     std::unique_ptr<scheduler::IdleTimer> mIdleTimer;
+    // Enables whether to use idle timer callbacks that support the kernel
+    // timer.
+    bool mSupportKernelTimer;
 
     std::mutex mCallbackLock;
     ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
+    GetVsyncPeriod mGetVsyncPeriod GUARDED_BY(mCallbackLock);
 
     // In order to make sure that the features don't override themselves, we need a state machine
     // to keep track which feature requested the config change.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 661c889..897f7b4 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -704,6 +704,10 @@
                 Mutex::Autolock lock(mStateLock);
                 setRefreshRateTo(type, event);
             });
+    mScheduler->setGetVsyncPeriodCallback([this] {
+        Mutex::Autolock lock(mStateLock);
+        return getVsyncPeriod();
+    });
 
     mRefreshRateConfigs.populate(getHwComposer().getConfigs(*display->getId()));
     mRefreshRateStats.setConfigMode(getHwComposer().getActiveConfigIndex(*display->getId()));
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 3522429..c43d095 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -246,6 +246,14 @@
     return defaultValue;
 }
 
+bool support_kernel_idle_timer(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::support_kernel_idle_timer();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return defaultValue;
+}
+
 #define DISPLAY_PRIMARY_SIZE 3
 
 constexpr float kSrgbRedX = 0.4123f;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 1864290..d772fa8 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -76,6 +76,8 @@
 
 bool enable_protected_contents(bool defaultValue);
 
+bool support_kernel_idle_timer(bool defaultValue);
+
 android::ui::DisplayPrimaries getDisplayNativePrimaries();
 } // namespace sysprop
 } // namespace android
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index d369096..bd8aacd 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -315,3 +315,13 @@
     access: Readonly
     prop_name: "ro.surface_flinger.protected_contents"
 }
+
+# Indicates whether Scheduler's idle timer should support a display driver timeout in the kernel.
+# The value of set_idle_timer_ms should be shorter in time than the timeout duration in the kernel.
+prop {
+    api_name: "support_kernel_idle_timer"
+    type: Boolean
+    scope: System
+    access: Readonly
+    prop_name: "ro.surface_flinger.support_kernel_idle_timer"
+}
diff --git a/services/surfaceflinger/sysprop/api/system-current.txt b/services/surfaceflinger/sysprop/api/system-current.txt
index 3c39b51..dd5dbe0 100644
--- a/services/surfaceflinger/sysprop/api/system-current.txt
+++ b/services/surfaceflinger/sysprop/api/system-current.txt
@@ -19,6 +19,7 @@
     method public static java.util.Optional<java.lang.Boolean> running_without_sync_framework();
     method public static java.util.Optional<java.lang.Integer> set_idle_timer_ms();
     method public static java.util.Optional<java.lang.Boolean> start_graphics_allocator_service();
+    method public static java.util.Optional<java.lang.Boolean> support_kernel_idle_timer();
     method public static java.util.Optional<java.lang.Boolean> use_color_management();
     method public static java.util.Optional<java.lang.Boolean> use_context_priority();
     method public static java.util.Optional<java.lang.Boolean> use_smart_90_for_video();
diff --git a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
index ea39bf5..eff22b6 100644
--- a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
@@ -111,19 +111,19 @@
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
 
     mIdleTimer->reset();
-    EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
     EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
 
     mIdleTimer->reset();
-    EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
     EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
 
     mIdleTimer->reset();
-    EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
     EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
 
     mIdleTimer->reset();
-    EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+    EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
     EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
 
     // A single callback should be generated after 30ms