TouchpadInputMapper: add timer provider

Adding a timer provider allows the Gestures library to perform some
asynchronous tasks, including ones which improve tap-to-click detection.

Bug: 297192727
Test: set the
      persist.device_config.aconfig_flags.input.com.android.input.flags.enable_gestures_library_timer_provider
      sysprop to true, restart, then make fast tap-to-click gestures
      (where the finger is contacting for fewer than 3 frames) on the
      touchpad, and check they're reported immediately
Change-Id: Ib9b8dacc71c88b6fd47bdd747f90ef6a44b37cc4
diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h
index 0e3fbb1..63c0e40 100644
--- a/include/input/PrintTools.h
+++ b/include/input/PrintTools.h
@@ -75,11 +75,12 @@
 }
 
 /**
- * Convert a map to string. Both keys and values of the map should be integral type.
+ * Convert a map or multimap to string. Both keys and values of the map should be integral type.
  */
-template <typename K, typename V>
-std::string dumpMap(const std::map<K, V>& map, std::string (*keyToString)(const K&) = constToString,
-                    std::string (*valueToString)(const V&) = constToString) {
+template <typename T>
+std::string dumpMap(const T& map,
+                    std::string (*keyToString)(const typename T::key_type&) = constToString,
+                    std::string (*valueToString)(const typename T::mapped_type&) = constToString) {
     std::string out;
     for (const auto& [k, v] : map) {
         if (!out.empty()) {
@@ -104,15 +105,13 @@
     return out.empty() ? "{}" : (out + "}");
 }
 
-/**
- * Convert a vector to a string. The values of the vector should be of a type supported by
- * constToString.
- */
+/** Convert a vector to a string. */
 template <typename T>
-std::string dumpVector(std::vector<T> values) {
-    std::string dump = constToString(values[0]);
+std::string dumpVector(const std::vector<T>& values,
+                       std::string (*valueToString)(const T&) = constToString) {
+    std::string dump = valueToString(values[0]);
     for (size_t i = 1; i < values.size(); i++) {
-        dump += ", " + constToString(values[i]);
+        dump += ", " + valueToString(values[i]);
     }
     return dump;
 }
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 2af5a4a..a0563f9 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -20,3 +20,10 @@
   description: "Set to true to enable PointerChoreographer: the new pipeline for showing pointer icons"
   bug: "293587049"
 }
+
+flag {
+  name: "enable_gestures_library_timer_provider"
+  namespace: "input"
+  description: "Set to true to enable timer support for the touchpad Gestures library"
+  bug: "297192727"
+}
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 8fe6411..e1806a0 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -69,6 +69,7 @@
         "mapper/gestures/HardwareProperties.cpp",
         "mapper/gestures/HardwareStateConverter.cpp",
         "mapper/gestures/PropertyProvider.cpp",
+        "mapper/gestures/TimerProvider.cpp",
     ],
 }
 
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 0aea0b3..5766b14 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -946,6 +946,7 @@
         device->dump(dump, eventHubDevStr);
     }
 
+    dump += StringPrintf(INDENT "NextTimeout: %" PRId64 "\n", mNextTimeout);
     dump += INDENT "Configuration:\n";
     dump += INDENT2 "ExcludedDeviceNames: [";
     for (size_t i = 0; i < mConfig.excludedDeviceNames.size(); i++) {
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 6ea004d..3e5e01e 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -25,6 +25,7 @@
 
 #include <android-base/stringprintf.h>
 #include <android/input.h>
+#include <com_android_input_flags.h>
 #include <ftl/enum.h>
 #include <input/PrintTools.h>
 #include <linux/input-event-codes.h>
@@ -34,8 +35,11 @@
 #include "TouchCursorInputMapperCommon.h"
 #include "TouchpadInputMapper.h"
 #include "gestures/HardwareProperties.h"
+#include "gestures/TimerProvider.h"
 #include "ui/Rotation.h"
 
+namespace input_flags = com::android::input::flags;
+
 namespace android {
 
 namespace {
@@ -232,6 +236,7 @@
       : InputMapper(deviceContext, readerConfig),
         mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter),
         mPointerController(getContext()->getPointerController(getDeviceId())),
+        mTimerProvider(*getContext()),
         mStateConverter(deviceContext, mMotionAccumulator),
         mGestureConverter(*getContext(), deviceContext, getDeviceId()),
         mCapturedEventConverter(*getContext(), deviceContext, mMotionAccumulator, getDeviceId()),
@@ -253,8 +258,12 @@
     // 2) TouchpadInputMapper is stored as a unique_ptr and not moved.
     mGestureInterpreter->SetPropProvider(const_cast<GesturesPropProvider*>(&gesturePropProvider),
                                          &mPropertyProvider);
+    if (input_flags::enable_gestures_library_timer_provider()) {
+        mGestureInterpreter->SetTimerProvider(const_cast<GesturesTimerProvider*>(
+                                                      &kGestureTimerProvider),
+                                              &mTimerProvider);
+    }
     mGestureInterpreter->SetCallback(gestureInterpreterCallback, this);
-    // TODO(b/251196347): set a timer provider, so the library can use timers.
 }
 
 TouchpadInputMapper::~TouchpadInputMapper() {
@@ -262,14 +271,14 @@
         mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
     }
 
-    // The gesture interpreter's destructor will call its property provider's free function for all
-    // gesture properties, in this case calling PropertyProvider::freeProperty using a raw pointer
-    // to mPropertyProvider. Depending on the declaration order in TouchpadInputMapper.h, this may
-    // happen after mPropertyProvider has been destructed, causing allocation errors. Depending on
-    // declaration order to avoid crashes seems rather fragile, so explicitly clear the property
-    // provider here to ensure all the freeProperty calls happen before mPropertyProvider is
-    // destructed.
+    // The gesture interpreter's destructor will try to free its property and timer providers,
+    // calling PropertyProvider::freeProperty and TimerProvider::freeTimer using a raw pointers.
+    // Depending on the declaration order in TouchpadInputMapper.h, those providers may have already
+    // been freed, causing allocation errors or use-after-free bugs. Depending on declaration order
+    // to avoid this seems rather fragile, so explicitly clear the providers here to ensure all the
+    // freeProperty and freeTimer calls happen before the providers are destructed.
     mGestureInterpreter->SetPropProvider(nullptr, nullptr);
+    mGestureInterpreter->SetTimerProvider(nullptr, nullptr);
 }
 
 uint32_t TouchpadInputMapper::getSources() const {
@@ -287,9 +296,6 @@
 
 void TouchpadInputMapper::dump(std::string& dump) {
     dump += INDENT2 "Touchpad Input Mapper:\n";
-    if (mProcessing) {
-        dump += INDENT3 "Currently processing a hardware state\n";
-    }
     if (mResettingInterpreter) {
         dump += INDENT3 "Currently resetting gesture interpreter\n";
     }
@@ -298,6 +304,12 @@
     dump += addLinePrefix(mGestureConverter.dump(), INDENT4);
     dump += INDENT3 "Gesture properties:\n";
     dump += addLinePrefix(mPropertyProvider.dump(), INDENT4);
+    if (input_flags::enable_gestures_library_timer_provider()) {
+        dump += INDENT3 "Timer provider:\n";
+        dump += addLinePrefix(mTimerProvider.dump(), INDENT4);
+    } else {
+        dump += INDENT3 "Timer provider: disabled by flag\n";
+    }
     dump += INDENT3 "Captured event converter:\n";
     dump += addLinePrefix(mCapturedEventConverter.dump(), INDENT4);
     dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str());
@@ -443,13 +455,18 @@
 std::list<NotifyArgs> TouchpadInputMapper::sendHardwareState(nsecs_t when, nsecs_t readTime,
                                                              SelfContainedHardwareState schs) {
     ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "New hardware state: %s", schs.state.String().c_str());
-    mProcessing = true;
     mGestureInterpreter->PushHardwareState(&schs.state);
-    mProcessing = false;
-
     return processGestures(when, readTime);
 }
 
+std::list<NotifyArgs> TouchpadInputMapper::timeoutExpired(nsecs_t when) {
+    if (!input_flags::enable_gestures_library_timer_provider()) {
+        return {};
+    }
+    mTimerProvider.triggerCallbacks(when);
+    return processGestures(when, when);
+}
+
 void TouchpadInputMapper::consumeGesture(const Gesture* gesture) {
     ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "Gesture ready: %s", gesture->String().c_str());
     if (mResettingInterpreter) {
@@ -457,10 +474,6 @@
         // ignore any gestures produced from the interpreter while we're resetting it.
         return;
     }
-    if (!mProcessing) {
-        ALOGE("Received gesture outside of the normal processing flow; ignoring it.");
-        return;
-    }
     mGesturesToProcess.push_back(*gesture);
 }
 
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index 47d712e..a68ae43 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -34,6 +34,7 @@
 #include "gestures/GestureConverter.h"
 #include "gestures/HardwareStateConverter.h"
 #include "gestures/PropertyProvider.h"
+#include "gestures/TimerProvider.h"
 
 #include "include/gestures.h"
 
@@ -56,6 +57,7 @@
                                                     ConfigurationChanges changes) override;
     [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
     [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when) override;
 
     void consumeGesture(const Gesture* gesture);
 
@@ -80,6 +82,7 @@
     std::shared_ptr<PointerControllerInterface> mPointerController;
 
     PropertyProvider mPropertyProvider;
+    TimerProvider mTimerProvider;
 
     // The MultiTouchMotionAccumulator is shared between the HardwareStateConverter and
     // CapturedTouchpadEventConverter, so that if the touchpad is captured or released while touches
@@ -92,7 +95,6 @@
     CapturedTouchpadEventConverter mCapturedEventConverter;
 
     bool mPointerCaptured = false;
-    bool mProcessing = false;
     bool mResettingInterpreter = false;
     std::vector<Gesture> mGesturesToProcess;
 
diff --git a/services/inputflinger/reader/mapper/gestures/TimerProvider.cpp b/services/inputflinger/reader/mapper/gestures/TimerProvider.cpp
new file mode 100644
index 0000000..df2f260
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/TimerProvider.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2023 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 "TimerProvider.h"
+
+#include <chrono>
+#include <string>
+
+#include <android-base/logging.h>
+#include <input/PrintTools.h>
+
+namespace android {
+
+namespace {
+
+nsecs_t stimeToNsecs(stime_t time) {
+    return std::chrono::duration_cast<std::chrono::nanoseconds>(
+                   std::chrono::duration<stime_t>(time))
+            .count();
+}
+
+stime_t nsecsToStime(nsecs_t time) {
+    return std::chrono::duration_cast<std::chrono::duration<stime_t>>(
+                   std::chrono::nanoseconds(time))
+            .count();
+}
+
+GesturesTimer* createTimer(void* data) {
+    return static_cast<TimerProvider*>(data)->createTimer();
+}
+
+void setDeadline(void* data, GesturesTimer* timer, stime_t delay, GesturesTimerCallback callback,
+                 void* callbackData) {
+    static_cast<TimerProvider*>(data)->setDeadline(timer, stimeToNsecs(delay), callback,
+                                                   callbackData);
+};
+
+void cancelTimer(void* data, GesturesTimer* timer) {
+    static_cast<TimerProvider*>(data)->cancelTimer(timer);
+}
+
+void freeTimer(void* data, GesturesTimer* timer) {
+    static_cast<TimerProvider*>(data)->freeTimer(timer);
+}
+
+} // namespace
+
+const GesturesTimerProvider kGestureTimerProvider = {
+        .create_fn = createTimer,
+        .set_fn = setDeadline,
+        .cancel_fn = cancelTimer,
+        .free_fn = freeTimer,
+};
+
+TimerProvider::TimerProvider(InputReaderContext& context) : mReaderContext(context) {}
+
+std::string TimerProvider::dump() {
+    std::string dump;
+    auto timerPtrToString = [](const std::unique_ptr<GesturesTimer>& timer) {
+        return std::to_string(timer->id);
+    };
+    dump += "Timer IDs: " + dumpVector<std::unique_ptr<GesturesTimer>>(mTimers, timerPtrToString) +
+            "\n";
+    dump += "Deadlines and corresponding timer IDs:\n";
+    dump += addLinePrefix(dumpMap(mDeadlines, constToString,
+                                  [](const Deadline& deadline) {
+                                      return std::to_string(deadline.timerId);
+                                  }),
+                          "  ") +
+            "\n";
+    return dump;
+}
+
+void TimerProvider::triggerCallbacks(nsecs_t when) {
+    while (!mDeadlines.empty() && when >= mDeadlines.begin()->first) {
+        const auto& deadlinePair = mDeadlines.begin();
+        deadlinePair->second.callback(when);
+        mDeadlines.erase(deadlinePair);
+    }
+    requestTimeout();
+}
+
+GesturesTimer* TimerProvider::createTimer() {
+    mTimers.push_back(std::make_unique<GesturesTimer>());
+    mTimers.back()->id = mNextTimerId;
+    mNextTimerId++;
+    return mTimers.back().get();
+}
+
+void TimerProvider::setDeadline(GesturesTimer* timer, nsecs_t delay, GesturesTimerCallback callback,
+                                void* callbackData) {
+    setDeadlineWithoutRequestingTimeout(timer, delay, callback, callbackData);
+    requestTimeout();
+}
+
+void TimerProvider::setDeadlineWithoutRequestingTimeout(GesturesTimer* timer, nsecs_t delay,
+                                                        GesturesTimerCallback callback,
+                                                        void* callbackData) {
+    const nsecs_t now = getCurrentTime();
+    const nsecs_t time = now + delay;
+    std::function<void(nsecs_t)> wrappedCallback = [=, this](nsecs_t triggerTime) {
+        stime_t nextDelay = callback(nsecsToStime(triggerTime), callbackData);
+        if (nextDelay >= 0.0) {
+            // When rescheduling a deadline, we know that we're running inside a call to
+            // triggerCallbacks, at the end of which requestTimeout will be called. This means that
+            // we don't want to call the public setDeadline, as that will request a timeout before
+            // triggerCallbacks has removed this current deadline, resulting in a request for a
+            // timeout that has already passed.
+            setDeadlineWithoutRequestingTimeout(timer, stimeToNsecs(nextDelay), callback,
+                                                callbackData);
+        }
+    };
+    mDeadlines.insert({time, Deadline(wrappedCallback, timer->id)});
+}
+
+void TimerProvider::cancelTimer(GesturesTimer* timer) {
+    int id = timer->id;
+    std::erase_if(mDeadlines, [id](const auto& item) { return item.second.timerId == id; });
+    requestTimeout();
+}
+
+void TimerProvider::freeTimer(GesturesTimer* timer) {
+    cancelTimer(timer);
+    std::erase_if(mTimers, [timer](std::unique_ptr<GesturesTimer>& t) { return t.get() == timer; });
+}
+
+void TimerProvider::requestTimeout() {
+    if (!mDeadlines.empty()) {
+        // Because a std::multimap is sorted by key, we simply use the time for the first entry.
+        mReaderContext.requestTimeoutAtTime(mDeadlines.begin()->first);
+    }
+}
+
+nsecs_t TimerProvider::getCurrentTime() {
+    return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/gestures/TimerProvider.h b/services/inputflinger/reader/mapper/gestures/TimerProvider.h
new file mode 100644
index 0000000..7c870e0
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/TimerProvider.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2023 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 <functional>
+#include <list>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <utils/Timers.h>
+
+#include "InputReaderContext.h"
+#include "NotifyArgs.h"
+#include "include/gestures.h"
+
+namespace android {
+
+extern const GesturesTimerProvider kGestureTimerProvider;
+
+// Implementation of a gestures library timer provider, which allows the library to set and cancel
+// callbacks.
+class TimerProvider {
+public:
+    TimerProvider(InputReaderContext& context);
+    virtual ~TimerProvider() = default;
+
+    // Disable copy and move, since pointers to TimerProvider objects are used in callbacks.
+    TimerProvider(const TimerProvider&) = delete;
+    TimerProvider& operator=(const TimerProvider&) = delete;
+
+    std::string dump();
+    void triggerCallbacks(nsecs_t when);
+
+    // Methods to be called by the gestures library:
+    GesturesTimer* createTimer();
+    void setDeadline(GesturesTimer* timer, nsecs_t delay, GesturesTimerCallback callback,
+                     void* callbackData);
+    void cancelTimer(GesturesTimer* timer);
+    void freeTimer(GesturesTimer* timer);
+
+protected:
+    // A wrapper for the system clock, to allow tests to override it.
+    virtual nsecs_t getCurrentTime();
+
+private:
+    void setDeadlineWithoutRequestingTimeout(GesturesTimer* timer, nsecs_t delay,
+                                             GesturesTimerCallback callback, void* callbackData);
+    // Requests a timeout from the InputReader for the nearest deadline in mDeadlines. Must be
+    // called whenever mDeadlines is modified.
+    void requestTimeout();
+
+    InputReaderContext& mReaderContext;
+    int mNextTimerId = 0;
+    std::vector<std::unique_ptr<GesturesTimer>> mTimers;
+
+    struct Deadline {
+        Deadline(std::function<void(nsecs_t)> callback, int timerId)
+              : callback(callback), timerId(timerId) {}
+        const std::function<void(nsecs_t)> callback;
+        const int timerId;
+    };
+
+    std::multimap<nsecs_t /*time*/, Deadline> mDeadlines;
+};
+
+} // namespace android
+
+// Represents a "timer" registered by the gestures library. In practice, this just means a set of
+// deadlines that can be cancelled as a group. The library's API requires this to be in the
+// top-level namespace.
+struct GesturesTimer {
+    int id = -1;
+};
\ No newline at end of file
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 6410046..d87a5a7 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -63,6 +63,7 @@
         "PropertyProvider_test.cpp",
         "SlopController_test.cpp",
         "SyncQueue_test.cpp",
+        "TimerProvider_test.cpp",
         "TestInputListener.cpp",
         "TestInputListenerMatchers.cpp",
         "TouchpadInputMapper_test.cpp",
diff --git a/services/inputflinger/tests/TimerProvider_test.cpp b/services/inputflinger/tests/TimerProvider_test.cpp
new file mode 100644
index 0000000..cb28823
--- /dev/null
+++ b/services/inputflinger/tests/TimerProvider_test.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2023 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 "gestures/TimerProvider.h"
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "InterfaceMocks.h"
+#include "TestConstants.h"
+#include "include/gestures.h"
+
+namespace android {
+
+namespace {
+
+class TestTimerProvider : public TimerProvider {
+public:
+    TestTimerProvider(InputReaderContext& context) : TimerProvider(context) {}
+
+    void setCurrentTime(nsecs_t time) { mCurrentTime = time; }
+
+protected:
+    nsecs_t getCurrentTime() override { return mCurrentTime; }
+
+private:
+    nsecs_t mCurrentTime = 0;
+};
+
+stime_t pushTimeOntoVector(stime_t triggerTime, void* data) {
+    std::vector<stime_t>* times = static_cast<std::vector<stime_t>*>(data);
+    times->push_back(triggerTime);
+    return NO_DEADLINE;
+}
+
+stime_t copyTimeToVariable(stime_t triggerTime, void* data) {
+    stime_t* time = static_cast<stime_t*>(data);
+    *time = triggerTime;
+    return NO_DEADLINE;
+}
+
+stime_t incrementInt(stime_t triggerTime, void* data) {
+    int* count = static_cast<int*>(data);
+    *count += 1;
+    return NO_DEADLINE;
+}
+
+} // namespace
+
+using testing::AtLeast;
+
+class TimerProviderTest : public testing::Test {
+public:
+    TimerProviderTest() : mProvider(mMockContext) {}
+
+protected:
+    void triggerCallbacksWithFakeTime(nsecs_t time) {
+        mProvider.setCurrentTime(time);
+        mProvider.triggerCallbacks(time);
+    }
+
+    MockInputReaderContext mMockContext;
+    TestTimerProvider mProvider;
+};
+
+TEST_F(TimerProviderTest, SingleDeadlineTriggersWhenTimeoutIsExactlyOnTime) {
+    GesturesTimer* timer = mProvider.createTimer();
+    std::vector<stime_t> callTimes;
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(1'000'000'000)).Times(3);
+
+    // Call through kGestureTimerProvider in this test case, so that we cover the stime_t to nsecs_t
+    // conversion code. This is why the delay is 1.0 rather than 1'000'000'000 here.
+    kGestureTimerProvider.set_fn(&mProvider, timer, 1.0, &pushTimeOntoVector, &callTimes);
+
+    triggerCallbacksWithFakeTime(900'000'000);
+    triggerCallbacksWithFakeTime(999'999'999);
+    EXPECT_EQ(0u, callTimes.size());
+    triggerCallbacksWithFakeTime(1'000'000'000);
+    ASSERT_EQ(1u, callTimes.size());
+    EXPECT_NEAR(1.0, callTimes[0], EPSILON);
+
+    // Now that the timer has triggered, it shouldn't trigger again if we get another timeout from
+    // InputReader.
+    triggerCallbacksWithFakeTime(1'300'000'000);
+    EXPECT_EQ(1u, callTimes.size());
+}
+
+TEST_F(TimerProviderTest, SingleDeadlineTriggersWhenTimeoutIsLate) {
+    GesturesTimer* timer = mProvider.createTimer();
+    stime_t callTime = -1.0;
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(1'000'000'000)).Times(1);
+    mProvider.setDeadline(timer, 1'000'000'000, &copyTimeToVariable, &callTime);
+
+    triggerCallbacksWithFakeTime(1'010'000'000);
+    EXPECT_NEAR(1.01, callTime, EPSILON);
+}
+
+TEST_F(TimerProviderTest, SingleRescheduledDeadlineTriggers) {
+    GesturesTimer* timer = mProvider.createTimer();
+    std::vector<stime_t> callTimes;
+    auto callback = [](stime_t triggerTime, void* callbackData) {
+        std::vector<stime_t>* times = static_cast<std::vector<stime_t>*>(callbackData);
+        times->push_back(triggerTime);
+        if (times->size() < 2) {
+            return 1.0;
+        } else {
+            return NO_DEADLINE;
+        }
+    };
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(1'000'000'000)).Times(1);
+    // The deadline should be rescheduled for 2.01s, since the first triggerCallbacks call is 0.01s
+    // late.
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(2'010'000'000)).Times(1);
+
+    mProvider.setDeadline(timer, 1'000'000'000, callback, &callTimes);
+
+    triggerCallbacksWithFakeTime(1'010'000'000);
+    ASSERT_EQ(1u, callTimes.size());
+    EXPECT_NEAR(1.01, callTimes[0], EPSILON);
+
+    triggerCallbacksWithFakeTime(2'020'000'000);
+    ASSERT_EQ(2u, callTimes.size());
+    EXPECT_NEAR(1.01, callTimes[0], EPSILON);
+    EXPECT_NEAR(2.02, callTimes[1], EPSILON);
+
+    triggerCallbacksWithFakeTime(3'000'000'000);
+    EXPECT_EQ(2u, callTimes.size());
+}
+
+TEST_F(TimerProviderTest, MultipleDeadlinesTriggerWithMultipleTimeouts) {
+    GesturesTimer* timer = mProvider.createTimer();
+    std::vector<stime_t> callTimes1;
+    std::vector<stime_t> callTimes2;
+
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(1'000'000'000)).Times(AtLeast(1));
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(1'500'000'000)).Times(1);
+
+    mProvider.setDeadline(timer, 1'000'000'000, &pushTimeOntoVector, &callTimes1);
+    mProvider.setDeadline(timer, 1'500'000'000, &pushTimeOntoVector, &callTimes2);
+
+    EXPECT_EQ(0u, callTimes1.size());
+    EXPECT_EQ(0u, callTimes2.size());
+
+    triggerCallbacksWithFakeTime(1'010'000'000);
+    ASSERT_EQ(1u, callTimes1.size());
+    EXPECT_NEAR(1.01, callTimes1[0], EPSILON);
+    EXPECT_EQ(0u, callTimes2.size());
+
+    triggerCallbacksWithFakeTime(1'500'000'000);
+    EXPECT_EQ(1u, callTimes1.size());
+    ASSERT_EQ(1u, callTimes2.size());
+    EXPECT_NEAR(1.5, callTimes2[0], EPSILON);
+}
+
+TEST_F(TimerProviderTest, MultipleDeadlinesTriggerWithOneLateTimeout) {
+    GesturesTimer* timer = mProvider.createTimer();
+    stime_t callTime1 = -1.0;
+    stime_t callTime2 = -1.0;
+
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(1'000'000'000)).Times(AtLeast(1));
+
+    mProvider.setDeadline(timer, 1'000'000'000, &copyTimeToVariable, &callTime1);
+    mProvider.setDeadline(timer, 1'500'000'000, &copyTimeToVariable, &callTime2);
+
+    triggerCallbacksWithFakeTime(1'510'000'000);
+    EXPECT_NEAR(1.51, callTime1, EPSILON);
+    EXPECT_NEAR(1.51, callTime2, EPSILON);
+}
+
+TEST_F(TimerProviderTest, MultipleDeadlinesAtSameTimeTriggerTogether) {
+    GesturesTimer* timer = mProvider.createTimer();
+    stime_t callTime1 = -1.0;
+    stime_t callTime2 = -1.0;
+
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(1'000'000'000)).Times(AtLeast(1));
+
+    mProvider.setDeadline(timer, 1'000'000'000, &copyTimeToVariable, &callTime1);
+    mProvider.setDeadline(timer, 1'000'000'000, &copyTimeToVariable, &callTime2);
+
+    triggerCallbacksWithFakeTime(1'000'000'000);
+    EXPECT_NEAR(1.0, callTime1, EPSILON);
+    EXPECT_NEAR(1.0, callTime2, EPSILON);
+}
+
+TEST_F(TimerProviderTest, MultipleTimersTriggerCorrectly) {
+    GesturesTimer* timer1 = mProvider.createTimer();
+    GesturesTimer* timer2 = mProvider.createTimer();
+    std::vector<stime_t> callTimes1;
+    std::vector<stime_t> callTimes2;
+
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(500'000'000)).Times(AtLeast(1));
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(1'250'000'000)).Times(1);
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(1'500'000'000)).Times(1);
+
+    mProvider.setDeadline(timer1, 500'000'000, &pushTimeOntoVector, &callTimes1);
+    mProvider.setDeadline(timer1, 1'250'000'000, &pushTimeOntoVector, &callTimes1);
+    mProvider.setDeadline(timer1, 1'500'000'000, &pushTimeOntoVector, &callTimes1);
+    mProvider.setDeadline(timer2, 750'000'000, &pushTimeOntoVector, &callTimes2);
+    mProvider.setDeadline(timer2, 1'250'000'000, &pushTimeOntoVector, &callTimes2);
+
+    triggerCallbacksWithFakeTime(800'000'000);
+    ASSERT_EQ(1u, callTimes1.size());
+    EXPECT_NEAR(0.8, callTimes1[0], EPSILON);
+    ASSERT_EQ(1u, callTimes2.size());
+    EXPECT_NEAR(0.8, callTimes2[0], EPSILON);
+
+    triggerCallbacksWithFakeTime(1'250'000'000);
+    ASSERT_EQ(2u, callTimes1.size());
+    EXPECT_NEAR(1.25, callTimes1[1], EPSILON);
+    ASSERT_EQ(2u, callTimes2.size());
+    EXPECT_NEAR(1.25, callTimes2[1], EPSILON);
+
+    triggerCallbacksWithFakeTime(1'501'000'000);
+    ASSERT_EQ(3u, callTimes1.size());
+    EXPECT_NEAR(1.501, callTimes1[2], EPSILON);
+    EXPECT_EQ(2u, callTimes2.size());
+}
+
+TEST_F(TimerProviderTest, CancelledTimerDoesntTrigger) {
+    GesturesTimer* timer = mProvider.createTimer();
+    int numCalls = 0;
+
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(500'000'000)).Times(AtLeast(1));
+    mProvider.setDeadline(timer, 500'000'000, &incrementInt, &numCalls);
+    mProvider.setDeadline(timer, 1'000'000'000, &incrementInt, &numCalls);
+    mProvider.cancelTimer(timer);
+
+    triggerCallbacksWithFakeTime(1'100'000'000);
+    EXPECT_EQ(0, numCalls);
+}
+
+TEST_F(TimerProviderTest, CancellingOneTimerDoesntAffectOthers) {
+    GesturesTimer* timer1 = mProvider.createTimer();
+    GesturesTimer* timer2 = mProvider.createTimer();
+    int numCalls1 = 0;
+    int numCalls2 = 0;
+
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(500'000'000)).Times(AtLeast(1));
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(1'000'000'000)).Times(1);
+
+    mProvider.setDeadline(timer1, 500'000'000, &incrementInt, &numCalls1);
+    mProvider.setDeadline(timer2, 500'000'000, &incrementInt, &numCalls2);
+    mProvider.setDeadline(timer2, 1'000'000'000, &incrementInt, &numCalls2);
+    mProvider.cancelTimer(timer1);
+
+    triggerCallbacksWithFakeTime(501'000'000);
+    EXPECT_EQ(0, numCalls1);
+    EXPECT_EQ(1, numCalls2);
+
+    triggerCallbacksWithFakeTime(1'000'000'000);
+    EXPECT_EQ(0, numCalls1);
+    EXPECT_EQ(2, numCalls2);
+}
+
+TEST_F(TimerProviderTest, CancellingOneTimerCausesNewTimeoutRequestForAnother) {
+    GesturesTimer* timer1 = mProvider.createTimer();
+    GesturesTimer* timer2 = mProvider.createTimer();
+    auto callback = [](stime_t, void*) { return NO_DEADLINE; };
+
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(500'000'000)).Times(AtLeast(1));
+
+    mProvider.setDeadline(timer1, 500'000'000, callback, nullptr);
+    mProvider.setDeadline(timer2, 1'000'000'000, callback, nullptr);
+
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(1'000'000'000)).Times(1);
+    mProvider.cancelTimer(timer1);
+}
+
+TEST_F(TimerProviderTest, CancelledTimerCanBeReused) {
+    GesturesTimer* timer = mProvider.createTimer();
+    int numCallsBeforeCancellation = 0;
+    int numCallsAfterCancellation = 0;
+
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(500'000'000)).Times(1);
+    EXPECT_CALL(mMockContext, requestTimeoutAtTime(1'000'000'000)).Times(1);
+
+    mProvider.setDeadline(timer, 500'000'000, &incrementInt, &numCallsBeforeCancellation);
+    mProvider.cancelTimer(timer);
+    mProvider.setDeadline(timer, 1'000'000'000, &incrementInt, &numCallsAfterCancellation);
+
+    triggerCallbacksWithFakeTime(1'000'000'000);
+    EXPECT_EQ(0, numCallsBeforeCancellation);
+    EXPECT_EQ(1, numCallsAfterCancellation);
+}
+
+TEST_F(TimerProviderTest, FreeingTimerCancelsFirst) {
+    GesturesTimer* timer = mProvider.createTimer();
+    int numCalls = 0;
+
+    mProvider.setDeadline(timer, 1'000'000'000, &incrementInt, &numCalls);
+    mProvider.freeTimer(timer);
+
+    triggerCallbacksWithFakeTime(1'000'000'000);
+    EXPECT_EQ(0, numCalls);
+}
+
+} // namespace android
\ No newline at end of file