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/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, ©TimeToVariable, &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, ©TimeToVariable, &callTime1);
+ mProvider.setDeadline(timer, 1'500'000'000, ©TimeToVariable, &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, ©TimeToVariable, &callTime1);
+ mProvider.setDeadline(timer, 1'000'000'000, ©TimeToVariable, &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