SF: add partial implementation of VSyncReactor

Adds an adaptor for the VSyncDispatch/VSyncTracker system
that will implemente the DispSync interface. Implementation
is only partial, will build out rest in subsequent MPs.

Bug: 140303479
Test: 6 new unit tests (VSyncReactor)

Change-Id: I11441f0f5bac9095d14122f487ab99bbdf44a7e3
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 963d755..7c60e0f 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -173,6 +173,7 @@
         "Scheduler/VSyncDispatchTimerQueue.cpp",
         "Scheduler/VSyncPredictor.cpp",
         "Scheduler/VSyncModulator.cpp",
+        "Scheduler/VSyncReactor.cpp",
         "StartPropertySetThread.cpp",
         "SurfaceFlinger.cpp",
         "SurfaceFlingerDefaultFactory.cpp",
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
new file mode 100644
index 0000000..1398362
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2019 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 "VSyncReactor.h"
+#include "VSyncDispatch.h"
+#include "VSyncTracker.h"
+
+namespace android::scheduler {
+
+VSyncReactor::VSyncReactor(std::unique_ptr<VSyncDispatch> dispatch,
+                           std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit)
+      : mDispatch(std::move(dispatch)),
+        mTracker(std::move(tracker)),
+        mPendingLimit(pendingFenceLimit) {}
+
+bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) {
+    if (!fence) {
+        return false;
+    }
+
+    nsecs_t const signalTime = fence->getCachedSignalTime();
+    if (signalTime == Fence::SIGNAL_TIME_INVALID) {
+        return true;
+    }
+
+    std::lock_guard<std::mutex> lk(mMutex);
+    if (mIgnorePresentFences) {
+        return true;
+    }
+
+    for (auto it = mUnfiredFences.begin(); it != mUnfiredFences.end();) {
+        auto const time = (*it)->getCachedSignalTime();
+        if (time == Fence::SIGNAL_TIME_PENDING) {
+            it++;
+        } else if (time == Fence::SIGNAL_TIME_INVALID) {
+            it = mUnfiredFences.erase(it);
+        } else {
+            mTracker->addVsyncTimestamp(time);
+            it = mUnfiredFences.erase(it);
+        }
+    }
+
+    if (signalTime == Fence::SIGNAL_TIME_PENDING) {
+        if (mPendingLimit == mUnfiredFences.size()) {
+            mUnfiredFences.erase(mUnfiredFences.begin());
+        }
+        mUnfiredFences.push_back(fence);
+    } else {
+        mTracker->addVsyncTimestamp(signalTime);
+    }
+
+    return false; // TODO(b/144707443): add policy for turning on HWVsync.
+}
+
+void VSyncReactor::setIgnorePresentFences(bool ignoration) {
+    std::lock_guard<std::mutex> lk(mMutex);
+    mIgnorePresentFences = ignoration;
+    if (mIgnorePresentFences == true) {
+        mUnfiredFences.clear();
+    }
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
new file mode 100644
index 0000000..05fd0fd
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 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 <android-base/thread_annotations.h>
+#include <ui/FenceTime.h>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+namespace android::scheduler {
+
+class VSyncDispatch;
+class VSyncTracker;
+
+// TODO (b/145217110): consider renaming.
+class VSyncReactor /* TODO (b/140201379): : public android::DispSync */ {
+public:
+    VSyncReactor(std::unique_ptr<VSyncDispatch> dispatch, std::unique_ptr<VSyncTracker> tracker,
+                 size_t pendingFenceLimit);
+
+    bool addPresentFence(const std::shared_ptr<FenceTime>& fence);
+    void setIgnorePresentFences(bool ignoration);
+
+private:
+    std::unique_ptr<VSyncDispatch> const mDispatch;
+    std::unique_ptr<VSyncTracker> const mTracker;
+    size_t const mPendingLimit;
+
+    std::mutex mMutex;
+    bool mIgnorePresentFences GUARDED_BY(mMutex) = false;
+    std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 0c4a752..ccfa6c5 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -57,6 +57,7 @@
         "VSyncDispatchTimerQueueTest.cpp",
         "VSyncDispatchRealtimeTest.cpp",
         "VSyncPredictorTest.cpp",
+        "VSyncReactorTest.cpp",
         "mock/DisplayHardware/MockComposer.cpp",
         "mock/DisplayHardware/MockDisplay.cpp",
         "mock/DisplayHardware/MockPowerAdvisor.cpp",
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
new file mode 100644
index 0000000..b4aebf0
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2019 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 "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include "Scheduler/VSyncDispatch.h"
+#include "Scheduler/VSyncReactor.h"
+#include "Scheduler/VSyncTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <ui/Fence.h>
+#include <ui/FenceTime.h>
+#include <array>
+
+using namespace testing;
+using namespace std::literals;
+namespace android::scheduler {
+
+class MockVSyncTracker : public VSyncTracker {
+public:
+    MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
+    MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
+};
+
+class VSyncTrackerWrapper : public VSyncTracker {
+public:
+    VSyncTrackerWrapper(std::shared_ptr<VSyncTracker> const& tracker) : mTracker(tracker) {}
+
+    void addVsyncTimestamp(nsecs_t timestamp) final { mTracker->addVsyncTimestamp(timestamp); }
+    nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
+        return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
+    }
+
+private:
+    std::shared_ptr<VSyncTracker> const mTracker;
+};
+
+class MockVSyncDispatch : public VSyncDispatch {
+public:
+    MOCK_METHOD2(registerCallback, CallbackToken(std::function<void(nsecs_t)> const&, std::string));
+    MOCK_METHOD1(unregisterCallback, void(CallbackToken));
+    MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t));
+    MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
+};
+
+class VSyncDispatchWrapper : public VSyncDispatch {
+public:
+    VSyncDispatchWrapper(std::shared_ptr<VSyncDispatch> const& dispatch) : mDispatch(dispatch) {}
+    CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
+                                   std::string callbackName) final {
+        return mDispatch->registerCallback(callbackFn, callbackName);
+    }
+
+    void unregisterCallback(CallbackToken token) final { mDispatch->unregisterCallback(token); }
+
+    ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
+                            nsecs_t earliestVsync) final {
+        return mDispatch->schedule(token, workDuration, earliestVsync);
+    }
+
+    CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); }
+
+private:
+    std::shared_ptr<VSyncDispatch> const mDispatch;
+};
+
+std::shared_ptr<FenceTime> generateInvalidFence() {
+    sp<Fence> fence = new Fence();
+    return std::make_shared<FenceTime>(fence);
+}
+
+std::shared_ptr<FenceTime> generatePendingFence() {
+    sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
+    return std::make_shared<FenceTime>(fence);
+}
+
+void signalFenceWithTime(std::shared_ptr<FenceTime> const& fence, nsecs_t time) {
+    FenceTime::Snapshot snap(time);
+    fence->applyTrustedSnapshot(snap);
+}
+
+std::shared_ptr<FenceTime> generateSignalledFenceWithTime(nsecs_t time) {
+    sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
+    std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence);
+    signalFenceWithTime(ft, time);
+    return ft;
+}
+
+class VSyncReactorTest : public testing::Test {
+protected:
+    VSyncReactorTest()
+          : mMockDispatch(std::make_shared<MockVSyncDispatch>()),
+            mMockTracker(std::make_shared<MockVSyncTracker>()),
+            mReactor(std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
+                     std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit) {}
+
+    std::shared_ptr<MockVSyncDispatch> mMockDispatch;
+    std::shared_ptr<MockVSyncTracker> mMockTracker;
+    static constexpr size_t kPendingLimit = 3;
+    static constexpr nsecs_t dummyTime = 47;
+    VSyncReactor mReactor;
+};
+
+TEST_F(VSyncReactorTest, addingNullFenceCheck) {
+    EXPECT_FALSE(mReactor.addPresentFence(nullptr));
+}
+
+TEST_F(VSyncReactorTest, addingInvalidFenceSignalsNeedsMoreInfo) {
+    EXPECT_TRUE(mReactor.addPresentFence(generateInvalidFence()));
+}
+
+TEST_F(VSyncReactorTest, addingSignalledFenceAddsToTracker) {
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
+}
+
+TEST_F(VSyncReactorTest, addingPendingFenceAddsSignalled) {
+    nsecs_t anotherDummyTime = 2919019201;
+
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(0);
+    auto pendingFence = generatePendingFence();
+    EXPECT_FALSE(mReactor.addPresentFence(pendingFence));
+    Mock::VerifyAndClearExpectations(mMockTracker.get());
+
+    signalFenceWithTime(pendingFence, dummyTime);
+
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(anotherDummyTime));
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(anotherDummyTime)));
+}
+
+TEST_F(VSyncReactorTest, limitsPendingFences) {
+    std::array<std::shared_ptr<FenceTime>, kPendingLimit * 2> fences;
+    std::array<nsecs_t, fences.size()> fakeTimes;
+    std::generate(fences.begin(), fences.end(), [] { return generatePendingFence(); });
+    std::generate(fakeTimes.begin(), fakeTimes.end(), [i = 10]() mutable {
+        i++;
+        return i * i;
+    });
+
+    for (auto const& fence : fences) {
+        mReactor.addPresentFence(fence);
+    }
+
+    for (auto i = fences.size() - kPendingLimit; i < fences.size(); i++) {
+        EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimes[i]));
+    }
+
+    for (auto i = 0u; i < fences.size(); i++) {
+        signalFenceWithTime(fences[i], fakeTimes[i]);
+    }
+    mReactor.addPresentFence(generatePendingFence());
+}
+
+TEST_F(VSyncReactorTest, ignoresPresentFencesWhenToldTo) {
+    static constexpr size_t aFewTimes = 8;
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime)).Times(1);
+
+    mReactor.setIgnorePresentFences(true);
+    for (auto i = 0; i < aFewTimes; i++) {
+        mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime));
+    }
+
+    mReactor.setIgnorePresentFences(false);
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
+}
+
+} // namespace android::scheduler