blob: 61a0a98c9e44f943c218e615d0268aab152bb102 [file] [log] [blame]
Paul Ramirez5d59a422024-10-01 15:59:16 +00001
2/*
3 * Copyright (C) 2024 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <input/InputConsumerNoResampling.h>
19
20#include <chrono>
21#include <memory>
22#include <string>
23#include <vector>
24
25#include <TestEventMatchers.h>
26#include <TestInputChannel.h>
27#include <attestation/HmacKeyManager.h>
28#include <gmock/gmock.h>
29#include <gtest/gtest.h>
30#include <input/BlockingQueue.h>
31#include <input/InputEventBuilders.h>
32#include <input/Resampler.h>
33#include <utils/Looper.h>
34#include <utils/StrongPointer.h>
35
36namespace android {
37namespace {
38
39using std::chrono::nanoseconds;
40using namespace std::chrono_literals;
41
42struct Pointer {
43 int32_t id{0};
44 float x{0.0f};
45 float y{0.0f};
46 ToolType toolType{ToolType::FINGER};
47 bool isResampled{false};
48
49 PointerBuilder asPointerBuilder() const {
50 return PointerBuilder{id, toolType}.x(x).y(y).isResampled(isResampled);
51 }
52};
53
54struct InputEventEntry {
55 std::chrono::nanoseconds eventTime{0};
56 std::vector<Pointer> pointers{};
57 int32_t action{-1};
58};
59
60} // namespace
61
62class InputConsumerResamplingTest : public ::testing::Test, public InputConsumerCallbacks {
63protected:
64 InputConsumerResamplingTest()
65 : mClientTestChannel{std::make_shared<TestInputChannel>("TestChannel")},
66 mLooper{sp<Looper>::make(/*allowNonCallbacks=*/false)} {
67 Looper::setForThread(mLooper);
68 mConsumer = std::make_unique<
69 InputConsumerNoResampling>(mClientTestChannel, mLooper, *this,
70 []() { return std::make_unique<LegacyResampler>(); });
71 }
72
73 void invokeLooperCallback() const {
74 sp<LooperCallback> callback;
75 ASSERT_TRUE(mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr,
76 /*events=*/nullptr, &callback, /*data=*/nullptr));
77 ASSERT_NE(callback, nullptr);
78 callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr);
79 }
80
81 InputMessage nextPointerMessage(const InputEventEntry& entry);
82
83 void assertReceivedMotionEvent(const std::vector<InputEventEntry>& expectedEntries);
84
85 std::shared_ptr<TestInputChannel> mClientTestChannel;
86 sp<Looper> mLooper;
87 std::unique_ptr<InputConsumerNoResampling> mConsumer;
88
89 BlockingQueue<std::unique_ptr<KeyEvent>> mKeyEvents;
90 BlockingQueue<std::unique_ptr<MotionEvent>> mMotionEvents;
91 BlockingQueue<std::unique_ptr<FocusEvent>> mFocusEvents;
92 BlockingQueue<std::unique_ptr<CaptureEvent>> mCaptureEvents;
93 BlockingQueue<std::unique_ptr<DragEvent>> mDragEvents;
94 BlockingQueue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents;
95
96private:
97 uint32_t mLastSeq{0};
98 size_t mOnBatchedInputEventPendingInvocationCount{0};
99
100 // InputConsumerCallbacks interface
101 void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override {
102 mKeyEvents.push(std::move(event));
103 mConsumer->finishInputEvent(seq, true);
104 }
105 void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override {
106 mMotionEvents.push(std::move(event));
107 mConsumer->finishInputEvent(seq, true);
108 }
109 void onBatchedInputEventPending(int32_t pendingBatchSource) override {
110 if (!mConsumer->probablyHasInput()) {
111 ADD_FAILURE() << "should deterministically have input because there is a batch";
112 }
113 ++mOnBatchedInputEventPendingInvocationCount;
114 }
115 void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override {
116 mFocusEvents.push(std::move(event));
117 mConsumer->finishInputEvent(seq, true);
118 }
119 void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override {
120 mCaptureEvents.push(std::move(event));
121 mConsumer->finishInputEvent(seq, true);
122 }
123 void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override {
124 mDragEvents.push(std::move(event));
125 mConsumer->finishInputEvent(seq, true);
126 }
127 void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override {
128 mTouchModeEvents.push(std::move(event));
129 mConsumer->finishInputEvent(seq, true);
130 }
131};
132
133InputMessage InputConsumerResamplingTest::nextPointerMessage(const InputEventEntry& entry) {
134 ++mLastSeq;
135 InputMessageBuilder messageBuilder = InputMessageBuilder{InputMessage::Type::MOTION, mLastSeq}
136 .eventTime(entry.eventTime.count())
137 .deviceId(1)
138 .action(entry.action)
139 .downTime(0);
140 for (const Pointer& pointer : entry.pointers) {
141 messageBuilder.pointer(pointer.asPointerBuilder());
142 }
143 return messageBuilder.build();
144}
145
146void InputConsumerResamplingTest::assertReceivedMotionEvent(
147 const std::vector<InputEventEntry>& expectedEntries) {
148 std::unique_ptr<MotionEvent> motionEvent = mMotionEvents.pop();
149 ASSERT_NE(motionEvent, nullptr);
150
151 ASSERT_EQ(motionEvent->getHistorySize() + 1, expectedEntries.size());
152
153 for (size_t sampleIndex = 0; sampleIndex < expectedEntries.size(); ++sampleIndex) {
154 SCOPED_TRACE("sampleIndex: " + std::to_string(sampleIndex));
155 const InputEventEntry& expectedEntry = expectedEntries[sampleIndex];
156 EXPECT_EQ(motionEvent->getHistoricalEventTime(sampleIndex),
157 expectedEntry.eventTime.count());
158 EXPECT_EQ(motionEvent->getPointerCount(), expectedEntry.pointers.size());
159 EXPECT_EQ(motionEvent->getAction(), expectedEntry.action);
160
161 for (size_t pointerIndex = 0; pointerIndex < expectedEntry.pointers.size();
162 ++pointerIndex) {
163 SCOPED_TRACE("pointerIndex: " + std::to_string(pointerIndex));
164 ssize_t eventPointerIndex =
165 motionEvent->findPointerIndex(expectedEntry.pointers[pointerIndex].id);
166 EXPECT_EQ(motionEvent->getHistoricalRawX(eventPointerIndex, sampleIndex),
167 expectedEntry.pointers[pointerIndex].x);
168 EXPECT_EQ(motionEvent->getHistoricalRawY(eventPointerIndex, sampleIndex),
169 expectedEntry.pointers[pointerIndex].y);
170 EXPECT_EQ(motionEvent->getHistoricalX(eventPointerIndex, sampleIndex),
171 expectedEntry.pointers[pointerIndex].x);
172 EXPECT_EQ(motionEvent->getHistoricalY(eventPointerIndex, sampleIndex),
173 expectedEntry.pointers[pointerIndex].y);
174 EXPECT_EQ(motionEvent->isResampled(pointerIndex, sampleIndex),
175 expectedEntry.pointers[pointerIndex].isResampled);
176 }
177 }
178}
179
180/**
181 * Timeline
182 * ---------+------------------+------------------+--------+-----------------+----------------------
183 * 0 ms 10 ms 20 ms 25 ms 35 ms
184 * ACTION_DOWN ACTION_MOVE ACTION_MOVE ^ ^
185 * | |
186 * resampled value |
187 * frameTime
188 * Typically, the prediction is made for time frameTime - RESAMPLE_LATENCY, or 30 ms in this case,
189 * where RESAMPLE_LATENCY equals 5 milliseconds. However, that would be 10 ms later than the last
190 * real sample (which came in at 20 ms). Therefore, the resampling should happen at 20 ms +
191 * RESAMPLE_MAX_PREDICTION = 28 ms, where RESAMPLE_MAX_PREDICTION equals 8 milliseconds. In this
192 * situation, though, resample time is further limited by taking half of the difference between the
193 * last two real events, which would put this time at: 20 ms + (20 ms - 10 ms) / 2 = 25 ms.
194 */
195TEST_F(InputConsumerResamplingTest, EventIsResampled) {
196 // Initial ACTION_DOWN should be separate, because the first consume event will only return
197 // InputEvent with a single action.
198 mClientTestChannel->enqueueMessage(nextPointerMessage(
199 {0ms, {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
200
201 mClientTestChannel->assertNoSentMessages();
202
203 invokeLooperCallback();
204 assertReceivedMotionEvent({InputEventEntry{0ms,
205 {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}},
206 AMOTION_EVENT_ACTION_DOWN}});
207
208 // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
209 mClientTestChannel->enqueueMessage(nextPointerMessage(
210 {10ms, {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
211 mClientTestChannel->enqueueMessage(nextPointerMessage(
212 {20ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
213
214 invokeLooperCallback();
215 mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
216 assertReceivedMotionEvent(
217 {InputEventEntry{10ms,
218 {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}},
219 AMOTION_EVENT_ACTION_MOVE},
220 InputEventEntry{20ms,
221 {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}},
222 AMOTION_EVENT_ACTION_MOVE},
223 InputEventEntry{25ms,
224 {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
225 AMOTION_EVENT_ACTION_MOVE}});
226
227 mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
228 mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
229 mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
230}
231
232/**
233 * Same as above test, but use pointer id=1 instead of 0 to make sure that system does not
234 * have these hardcoded.
235 */
236TEST_F(InputConsumerResamplingTest, EventIsResampledWithDifferentId) {
237 // Initial ACTION_DOWN should be separate, because the first consume event will only return
238 // InputEvent with a single action.
239 mClientTestChannel->enqueueMessage(nextPointerMessage(
240 {0ms, {Pointer{.id = 1, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
241
242 mClientTestChannel->assertNoSentMessages();
243
244 invokeLooperCallback();
245 assertReceivedMotionEvent({InputEventEntry{0ms,
246 {Pointer{.id = 1, .x = 10.0f, .y = 20.0f}},
247 AMOTION_EVENT_ACTION_DOWN}});
248
249 // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
250 mClientTestChannel->enqueueMessage(nextPointerMessage(
251 {10ms, {Pointer{.id = 1, .x = 20.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
252 mClientTestChannel->enqueueMessage(nextPointerMessage(
253 {20ms, {Pointer{.id = 1, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
254
255 invokeLooperCallback();
256 mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
257 assertReceivedMotionEvent(
258 {InputEventEntry{10ms,
259 {Pointer{.id = 1, .x = 20.0f, .y = 30.0f}},
260 AMOTION_EVENT_ACTION_MOVE},
261 InputEventEntry{20ms,
262 {Pointer{.id = 1, .x = 30.0f, .y = 30.0f}},
263 AMOTION_EVENT_ACTION_MOVE},
264 InputEventEntry{25ms,
265 {Pointer{.id = 1, .x = 35.0f, .y = 30.0f, .isResampled = true}},
266 AMOTION_EVENT_ACTION_MOVE}});
267
268 mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
269 mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
270 mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
271}
272
273/**
274 * Stylus pointer coordinates are resampled.
275 */
276TEST_F(InputConsumerResamplingTest, StylusEventIsResampled) {
277 // Initial ACTION_DOWN should be separate, because the first consume event will only return
278 // InputEvent with a single action.
279 mClientTestChannel->enqueueMessage(nextPointerMessage(
280 {0ms,
281 {Pointer{.id = 0, .x = 10.0f, .y = 20.0f, .toolType = ToolType::STYLUS}},
282 AMOTION_EVENT_ACTION_DOWN}));
283
284 mClientTestChannel->assertNoSentMessages();
285
286 invokeLooperCallback();
287 assertReceivedMotionEvent({InputEventEntry{0ms,
288 {Pointer{.id = 0,
289 .x = 10.0f,
290 .y = 20.0f,
291 .toolType = ToolType::STYLUS}},
292 AMOTION_EVENT_ACTION_DOWN}});
293
294 // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
295 mClientTestChannel->enqueueMessage(nextPointerMessage(
296 {10ms,
297 {Pointer{.id = 0, .x = 20.0f, .y = 30.0f, .toolType = ToolType::STYLUS}},
298 AMOTION_EVENT_ACTION_MOVE}));
299 mClientTestChannel->enqueueMessage(nextPointerMessage(
300 {20ms,
301 {Pointer{.id = 0, .x = 30.0f, .y = 30.0f, .toolType = ToolType::STYLUS}},
302 AMOTION_EVENT_ACTION_MOVE}));
303
304 invokeLooperCallback();
305 mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
306 assertReceivedMotionEvent({InputEventEntry{10ms,
307 {Pointer{.id = 0,
308 .x = 20.0f,
309 .y = 30.0f,
310 .toolType = ToolType::STYLUS}},
311 AMOTION_EVENT_ACTION_MOVE},
312 InputEventEntry{20ms,
313 {Pointer{.id = 0,
314 .x = 30.0f,
315 .y = 30.0f,
316 .toolType = ToolType::STYLUS}},
317 AMOTION_EVENT_ACTION_MOVE},
318 InputEventEntry{25ms,
319 {Pointer{.id = 0,
320 .x = 35.0f,
321 .y = 30.0f,
322 .toolType = ToolType::STYLUS,
323 .isResampled = true}},
324 AMOTION_EVENT_ACTION_MOVE}});
325
326 mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
327 mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
328 mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
329}
330
331/**
332 * Mouse pointer coordinates are resampled.
333 */
334TEST_F(InputConsumerResamplingTest, MouseEventIsResampled) {
335 // Initial ACTION_DOWN should be separate, because the first consume event will only return
336 // InputEvent with a single action.
337
338 mClientTestChannel->enqueueMessage(nextPointerMessage(
339 {0ms,
340 {Pointer{.id = 0, .x = 10.0f, .y = 20.0f, .toolType = ToolType::MOUSE}},
341 AMOTION_EVENT_ACTION_DOWN}));
342
343 mClientTestChannel->assertNoSentMessages();
344
345 invokeLooperCallback();
346 assertReceivedMotionEvent({InputEventEntry{0ms,
347 {Pointer{.id = 0,
348 .x = 10.0f,
349 .y = 20.0f,
350 .toolType = ToolType::MOUSE}},
351 AMOTION_EVENT_ACTION_DOWN}});
352
353 // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
354 mClientTestChannel->enqueueMessage(nextPointerMessage(
355 {10ms,
356 {Pointer{.id = 0, .x = 20.0f, .y = 30.0f, .toolType = ToolType::MOUSE}},
357 AMOTION_EVENT_ACTION_MOVE}));
358 mClientTestChannel->enqueueMessage(nextPointerMessage(
359 {20ms,
360 {Pointer{.id = 0, .x = 30.0f, .y = 30.0f, .toolType = ToolType::MOUSE}},
361 AMOTION_EVENT_ACTION_MOVE}));
362
363 invokeLooperCallback();
364 mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
365 assertReceivedMotionEvent({InputEventEntry{10ms,
366 {Pointer{.id = 0,
367 .x = 20.0f,
368 .y = 30.0f,
369 .toolType = ToolType::MOUSE}},
370 AMOTION_EVENT_ACTION_MOVE},
371 InputEventEntry{20ms,
372 {Pointer{.id = 0,
373 .x = 30.0f,
374 .y = 30.0f,
375 .toolType = ToolType::MOUSE}},
376 AMOTION_EVENT_ACTION_MOVE},
377 InputEventEntry{25ms,
378 {Pointer{.id = 0,
379 .x = 35.0f,
380 .y = 30.0f,
381 .toolType = ToolType::MOUSE,
382 .isResampled = true}},
383 AMOTION_EVENT_ACTION_MOVE}});
384
385 mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
386 mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
387 mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
388}
389
390/**
391 * Motion events with palm tool type are not resampled.
392 */
393TEST_F(InputConsumerResamplingTest, PalmEventIsNotResampled) {
394 // Initial ACTION_DOWN should be separate, because the first consume event will only return
395 // InputEvent with a single action.
396 mClientTestChannel->enqueueMessage(nextPointerMessage(
397 {0ms,
398 {Pointer{.id = 0, .x = 10.0f, .y = 20.0f, .toolType = ToolType::PALM}},
399 AMOTION_EVENT_ACTION_DOWN}));
400
401 mClientTestChannel->assertNoSentMessages();
402
403 invokeLooperCallback();
404 assertReceivedMotionEvent(
405 {InputEventEntry{0ms,
406 {Pointer{.id = 0, .x = 10.0f, .y = 20.0f, .toolType = ToolType::PALM}},
407 AMOTION_EVENT_ACTION_DOWN}});
408
409 // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
410 mClientTestChannel->enqueueMessage(nextPointerMessage(
411 {10ms,
412 {Pointer{.id = 0, .x = 20.0f, .y = 30.0f, .toolType = ToolType::PALM}},
413 AMOTION_EVENT_ACTION_MOVE}));
414 mClientTestChannel->enqueueMessage(nextPointerMessage(
415 {20ms,
416 {Pointer{.id = 0, .x = 30.0f, .y = 30.0f, .toolType = ToolType::PALM}},
417 AMOTION_EVENT_ACTION_MOVE}));
418
419 invokeLooperCallback();
420 mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
421 assertReceivedMotionEvent(
422 {InputEventEntry{10ms,
423 {Pointer{.id = 0, .x = 20.0f, .y = 30.0f, .toolType = ToolType::PALM}},
424 AMOTION_EVENT_ACTION_MOVE},
425 InputEventEntry{20ms,
426 {Pointer{.id = 0, .x = 30.0f, .y = 30.0f, .toolType = ToolType::PALM}},
427 AMOTION_EVENT_ACTION_MOVE}});
428
429 mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
430 mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
431 mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
432}
433
434} // namespace android