Merge "SF: Adding Idle Timer, to detect when devices are idle."
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 22e4d1e..0106b25 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -139,6 +139,7 @@
         "Scheduler/DispSyncSource.cpp",
         "Scheduler/EventControlThread.cpp",
         "Scheduler/EventThread.cpp",
+        "Scheduler/IdleTimer.cpp",
         "Scheduler/LayerHistory.cpp",
         "Scheduler/MessageQueue.cpp",
         "Scheduler/Scheduler.cpp",
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 49e7ef6..1f08f4e 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -48,10 +48,14 @@
 namespace impl {
 
 EventThread::EventThread(std::unique_ptr<VSyncSource> src,
-                         ResyncWithRateLimitCallback resyncWithRateLimitCallback,
-                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
+                         const ResyncWithRateLimitCallback& resyncWithRateLimitCallback,
+                         const InterceptVSyncsCallback& interceptVSyncsCallback,
+                         const ResetIdleTimerCallback& resetIdleTimerCallback,
+                         const char* threadName)
       : EventThread(nullptr, std::move(src), resyncWithRateLimitCallback, interceptVSyncsCallback,
-                    threadName) {}
+                    threadName) {
+    mResetIdleTimer = resetIdleTimerCallback;
+}
 
 EventThread::EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
                          InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
@@ -150,6 +154,9 @@
 
 void EventThread::requestNextVsync(const sp<EventThread::Connection>& connection) {
     std::lock_guard<std::mutex> lock(mMutex);
+    if (mResetIdleTimer) {
+        mResetIdleTimer();
+    }
 
     if (mResyncWithRateLimitCallback) {
         mResyncWithRateLimitCallback();
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 15b5bba..0773c05 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -107,13 +107,15 @@
 public:
     using ResyncWithRateLimitCallback = std::function<void()>;
     using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
+    using ResetIdleTimerCallback = std::function<void()>;
 
     // TODO(b/113612090): Once the Scheduler is complete this constructor will become obsolete.
     EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
                 InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
     EventThread(std::unique_ptr<VSyncSource> src,
-                ResyncWithRateLimitCallback resyncWithRateLimitCallback,
-                InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
+                const ResyncWithRateLimitCallback& resyncWithRateLimitCallback,
+                const InterceptVSyncsCallback& interceptVSyncsCallback,
+                const ResetIdleTimerCallback& resetIdleTimerCallback, const char* threadName);
     ~EventThread();
 
     sp<BnDisplayEventConnection> createEventConnection() const override;
@@ -177,6 +179,9 @@
 
     // for debugging
     bool mDebugVsyncEnabled GUARDED_BY(mMutex) = false;
+
+    // Callback that resets the idle timer when the next vsync is received.
+    ResetIdleTimerCallback mResetIdleTimer;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.cpp b/services/surfaceflinger/Scheduler/IdleTimer.cpp
new file mode 100644
index 0000000..5a76dbc
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/IdleTimer.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#include "IdleTimer.h"
+
+#include <chrono>
+#include <thread>
+
+namespace android {
+namespace scheduler {
+
+IdleTimer::IdleTimer(const Interval& interval, const TimeoutCallback& timeoutCallback)
+      : mInterval(interval), mTimeoutCallback(timeoutCallback) {}
+
+IdleTimer::~IdleTimer() {
+    stop();
+}
+
+void IdleTimer::start() {
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mState = TimerState::RESET;
+    }
+    mThread = std::thread(&IdleTimer::loop, this);
+}
+
+void IdleTimer::stop() {
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mState = TimerState::STOPPED;
+    }
+    mCondition.notify_all();
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+}
+
+void IdleTimer::loop() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    while (mState != TimerState::STOPPED) {
+        if (mState == TimerState::IDLE) {
+            mCondition.wait(mMutex);
+        } else if (mState == TimerState::RESET) {
+            mState = TimerState::WAITING;
+            if (mCondition.wait_for(mMutex, mInterval) == std::cv_status::timeout) {
+                if (mTimeoutCallback) {
+                    mTimeoutCallback();
+                }
+            }
+            if (mState == TimerState::WAITING) {
+                mState = TimerState::IDLE;
+            }
+        }
+    }
+}
+
+void IdleTimer::reset() {
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mState = TimerState::RESET;
+    }
+    mCondition.notify_all();
+}
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.h b/services/surfaceflinger/Scheduler/IdleTimer.h
new file mode 100644
index 0000000..aee3fa3
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/IdleTimer.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <condition_variable>
+#include <thread>
+
+#include <android-base/thread_annotations.h>
+
+namespace android {
+namespace scheduler {
+
+/*
+ * Class that sets off a timer for a given interval, and fires a callback when the
+ * interval expires.
+ */
+class IdleTimer {
+public:
+    using Interval = std::chrono::milliseconds;
+    using TimeoutCallback = std::function<void()>;
+
+    IdleTimer(const Interval& interval, const TimeoutCallback& timeoutCallback);
+    ~IdleTimer();
+
+    void start();
+    void stop();
+    void reset();
+
+private:
+    // Enum to track in what state is the timer.
+    enum class TimerState { STOPPED = 0, RESET = 1, WAITING = 2, IDLE = 3 };
+
+    // Function that loops until the condition for stopping is met.
+    void loop();
+
+    // Thread waiting for timer to expire.
+    std::thread mThread;
+
+    // Condition used to notify mThread.
+    std::condition_variable_any mCondition;
+
+    // Lock used for synchronizing the waiting thread with the application thread.
+    std::mutex mMutex;
+
+    TimerState mState GUARDED_BY(mMutex) = TimerState::RESET;
+
+    // Interval after which timer expires.
+    const Interval mInterval;
+
+    // Callback that happens when timer expires.
+    const TimeoutCallback mTimeoutCallback;
+};
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5b8cc10..fad56e6 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -29,6 +29,7 @@
 #include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
 
+#include <cutils/properties.h>
 #include <gui/ISurfaceComposer.h>
 #include <ui/DisplayStatInfo.h>
 #include <utils/Timers.h>
@@ -38,6 +39,7 @@
 #include "DispSyncSource.h"
 #include "EventControlThread.h"
 #include "EventThread.h"
+#include "IdleTimer.h"
 #include "InjectVSyncSource.h"
 #include "SchedulerUtils.h"
 
@@ -68,6 +70,17 @@
     primaryDispSync->init(mHasSyncFramework, mDispSyncPresentTimeOffset);
     mPrimaryDispSync = std::move(primaryDispSync);
     mEventControlThread = std::make_unique<impl::EventControlThread>(function);
+
+    char value[PROPERTY_VALUE_MAX];
+    property_get("debug.sf.set_idle_timer_ms", value, "0");
+    mSetIdleTimerMs = atoi(value);
+
+    if (mSetIdleTimerMs > 0) {
+        mIdleTimer =
+                std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(mSetIdleTimerMs),
+                                                       [this] { expiredTimerCallback(); });
+        mIdleTimer->start();
+    }
 }
 
 Scheduler::~Scheduler() = default;
@@ -98,7 +111,8 @@
             std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, true, sourceName.c_str());
     const std::string threadName = connectionName + "Thread";
     return std::make_unique<impl::EventThread>(std::move(eventThreadSource), resyncCallback,
-                                               interceptCallback, threadName.c_str());
+                                               interceptCallback, [this] { resetIdleTimer(); },
+                                               threadName.c_str());
 }
 
 sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
@@ -309,4 +323,18 @@
     }
 }
 
+void Scheduler::resetIdleTimer() {
+    if (mIdleTimer) {
+        mIdleTimer->reset();
+        ATRACE_INT("ExpiredIdleTimer", 0);
+    }
+}
+
+void Scheduler::expiredTimerCallback() {
+    // TODO(b/113612090): Each time a timer expired, we should record the information into
+    // a circular buffer. Once this has happened a given amount (TBD) of times, we can comfortably
+    // say that the device is sitting in idle.
+    ATRACE_INT("ExpiredIdleTimer", 1);
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index ea90824..8d4514b 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -25,6 +25,7 @@
 #include "DispSync.h"
 #include "EventControlThread.h"
 #include "EventThread.h"
+#include "IdleTimer.h"
 #include "InjectVSyncSource.h"
 #include "LayerHistory.h"
 #include "SchedulerUtils.h"
@@ -126,6 +127,10 @@
     // Collects the average difference between timestamps for each frame regardless
     // of which layer the timestamp came from.
     void determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime);
+    // Function that resets the idle timer.
+    void resetIdleTimer();
+    // Function that is called when the timer expires.
+    void expiredTimerCallback();
 
     // TODO(b/113612090): Instead of letting BufferQueueLayer to access mDispSync directly, it
     // should make request to Scheduler to compute next refresh.
@@ -163,6 +168,11 @@
     size_t mCounter = 0;
 
     LayerHistory mLayerHistory;
+
+    // Timer that records time between requests for next vsync. If the time is higher than a given
+    // interval, a callback is fired. Set this variable to >0 to use this feature.
+    int64_t mSetIdleTimerMs = 0;
+    std::unique_ptr<scheduler::IdleTimer> mIdleTimer;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 1a13f77..2f35ae5 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -30,6 +30,7 @@
         "DisplayTransactionTest.cpp",
         "EventControlThreadTest.cpp",
         "EventThreadTest.cpp",
+        "IdleTimerTest.cpp",
         "LayerHistoryTest.cpp",
         "SchedulerTest.cpp",
         "SchedulerUtilsTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
new file mode 100644
index 0000000..9fe9a18
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#include "AsyncCallRecorder.h"
+#include "Scheduler/IdleTimer.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace scheduler {
+
+class IdleTimerTest : public testing::Test {
+protected:
+    IdleTimerTest() = default;
+    ~IdleTimerTest() override = default;
+
+    AsyncCallRecorder<void (*)()> mExpiredTimerCallback;
+
+    std::unique_ptr<IdleTimer> mIdleTimer;
+};
+
+namespace {
+TEST_F(IdleTimerTest, createAndDestroyTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, [] {});
+}
+
+TEST_F(IdleTimerTest, startStopTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, mExpiredTimerCallback.getInvocable());
+    mIdleTimer->start();
+    std::this_thread::sleep_for(std::chrono::milliseconds(10));
+    // The timer expires after 30 ms, so the call to the callback should not happen.
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall().has_value());
+    mIdleTimer->stop();
+}
+
+TEST_F(IdleTimerTest, resetTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mExpiredTimerCallback.getInvocable());
+    mIdleTimer->start();
+    std::this_thread::sleep_for(std::chrono::milliseconds(10));
+    // The timer expires after 30 ms, so the call to the callback should not happen.
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value());
+    mIdleTimer->reset();
+    // The timer was reset, so the call to the callback should not happen.
+    std::this_thread::sleep_for(std::chrono::milliseconds(15));
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value());
+    mIdleTimer->stop();
+}
+
+TEST_F(IdleTimerTest, startNotCalledTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+    std::this_thread::sleep_for(6ms);
+    // The start hasn't happened, so the callback does not happen.
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value());
+    mIdleTimer->stop();
+}
+
+TEST_F(IdleTimerTest, idleTimerIdlesTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+    mIdleTimer->start();
+    std::this_thread::sleep_for(6ms);
+    // The timer expires after 3 ms, so the call to the callback happens.
+    EXPECT_TRUE(mExpiredTimerCallback.waitForCall(1us).has_value());
+    std::this_thread::sleep_for(6ms);
+    // Timer can be idle.
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value());
+    // Timer can be reset.
+    mIdleTimer->reset();
+    std::this_thread::sleep_for(6ms);
+    // Timer fires again.
+    EXPECT_TRUE(mExpiredTimerCallback.waitForCall(1us).has_value());
+    mIdleTimer->stop();
+}
+
+TEST_F(IdleTimerTest, timeoutCallbackExecutionTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+
+    mIdleTimer->start();
+    std::this_thread::sleep_for(6ms);
+    // The timer expires after 3 ms, so the call to the callback should happen.
+    EXPECT_TRUE(mExpiredTimerCallback.waitForCall(1us).has_value());
+    mIdleTimer->stop();
+}
+
+TEST_F(IdleTimerTest, noCallbacksAfterStopAndResetTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+    mIdleTimer->start();
+    std::this_thread::sleep_for(6ms);
+    EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value());
+    mIdleTimer->stop();
+    mIdleTimer->reset();
+    std::this_thread::sleep_for(6ms);
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall().has_value());
+}
+
+TEST_F(IdleTimerTest, noCallbacksAfterStopTest) {
+    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable());
+    mIdleTimer->start();
+    std::this_thread::sleep_for(1ms);
+    mIdleTimer->stop();
+    std::this_thread::sleep_for(3ms);
+    EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value());
+}
+
+} // namespace
+} // namespace scheduler
+} // namespace android
\ No newline at end of file