Merge changes from topic "sf-kernel-idle-timeout" into qt-dev

* changes:
  When building dispsync model, only callback on observed vsync + offset.
  Support repurposing idle timer to work with kernel timer.
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index abf7b71..cd6fa41 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -64,6 +64,7 @@
     DispSyncThread(const char* name, bool showTraceDetailedInfo)
           : mName(name),
             mStop(false),
+            mModelLocked(false),
             mPeriod(0),
             mPhase(0),
             mReferenceTime(0),
@@ -78,6 +79,11 @@
         Mutex::Autolock lock(mMutex);
 
         mPhase = phase;
+        if (mReferenceTime != referenceTime) {
+            for (auto& eventListener : mEventListeners) {
+                eventListener.mHasFired = false;
+            }
+        }
         mReferenceTime = referenceTime;
         if (mPeriod != 0 && mPeriod != period && mReferenceTime != 0) {
             // Inflate the reference time to be the most recent predicted
@@ -106,6 +112,16 @@
         mCond.signal();
     }
 
+    void lockModel() {
+        Mutex::Autolock lock(mMutex);
+        mModelLocked = true;
+    }
+
+    void unlockModel() {
+        Mutex::Autolock lock(mMutex);
+        mModelLocked = false;
+    }
+
     virtual bool threadLoop() {
         status_t err;
         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -288,6 +304,7 @@
         nsecs_t mLastEventTime;
         nsecs_t mLastCallbackTime;
         DispSync::Callback* mCallback;
+        bool mHasFired = false;
     };
 
     struct CallbackInvocation {
@@ -335,6 +352,12 @@
                           eventListener.mName);
                     continue;
                 }
+                if (eventListener.mHasFired && !mModelLocked) {
+                    eventListener.mLastEventTime = t;
+                    ALOGV("[%s] [%s] Skipping event due to already firing", mName,
+                          eventListener.mName);
+                    continue;
+                }
                 CallbackInvocation ci;
                 ci.mCallback = eventListener.mCallback;
                 ci.mEventTime = t;
@@ -343,6 +366,7 @@
                 callbackInvocations.push_back(ci);
                 eventListener.mLastEventTime = t;
                 eventListener.mLastCallbackTime = now;
+                eventListener.mHasFired = true;
             }
         }
 
@@ -410,6 +434,7 @@
     const char* const mName;
 
     bool mStop;
+    bool mModelLocked;
 
     nsecs_t mPeriod;
     nsecs_t mPhase;
@@ -497,6 +522,7 @@
     mNumResyncSamples = 0;
     mFirstResyncSample = 0;
     mNumResyncSamplesSincePresent = 0;
+    mThread->unlockModel();
     resetErrorLocked();
 }
 
@@ -519,6 +545,7 @@
 void DispSync::beginResync() {
     Mutex::Autolock lock(mMutex);
     ALOGV("[%s] beginResync", mName);
+    mThread->unlockModel();
     mModelUpdated = false;
     mNumResyncSamples = 0;
 }
@@ -581,10 +608,15 @@
     // resync again
     bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2) && mPendingPeriod == 0;
     ALOGV("[%s] addResyncSample returning %s", mName, modelLocked ? "locked" : "unlocked");
+    if (modelLocked) {
+        mThread->lockModel();
+    }
     return !modelLocked;
 }
 
-void DispSync::endResync() {}
+void DispSync::endResync() {
+    mThread->lockModel();
+}
 
 status_t DispSync::addEventListener(const char* name, nsecs_t phase, Callback* callback,
                                     nsecs_t lastCallbackTime) {
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 5b87a3d..3f9a88d 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();
     }
 }
@@ -354,6 +365,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) {
@@ -370,17 +386,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 76b1ee3..7c8adf0 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -160,6 +160,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; }
@@ -194,6 +195,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.
@@ -245,9 +254,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 3d71267..3f2240e 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