blob: 0b0bb63a8140b4cfd6a58c86b1e28f9de644c542 [file] [log] [blame]
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -08001/*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -080017#include <chrono>
18#include <vector>
19
20#include <attestation/HmacKeyManager.h>
21#include <gtest/gtest.h>
22#include <input/InputTransport.h>
23
24using namespace std::chrono_literals;
25
26namespace android {
27
Siarhei Vishniakou6252af72023-09-20 09:00:38 -070028namespace {
29
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -080030struct Pointer {
31 int32_t id;
32 float x;
33 float y;
Philip Quinn4e955a22023-09-26 12:09:40 -070034 ToolType toolType = ToolType::FINGER;
Philip Quinnafb31282022-12-20 18:17:55 -080035 bool isResampled = false;
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -080036};
37
38struct InputEventEntry {
39 std::chrono::nanoseconds eventTime;
40 std::vector<Pointer> pointers;
41 int32_t action;
42};
43
Siarhei Vishniakou6252af72023-09-20 09:00:38 -070044} // namespace
45
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -080046class TouchResamplingTest : public testing::Test {
47protected:
48 std::unique_ptr<InputPublisher> mPublisher;
49 std::unique_ptr<InputConsumer> mConsumer;
50 PreallocatedInputEventFactory mEventFactory;
51
52 uint32_t mSeq = 1;
53
54 void SetUp() override {
55 std::unique_ptr<InputChannel> serverChannel, clientChannel;
56 status_t result =
57 InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel);
58 ASSERT_EQ(OK, result);
59
60 mPublisher = std::make_unique<InputPublisher>(std::move(serverChannel));
61 mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel),
Harry Cutts82c791c2023-03-10 17:15:07 +000062 /*enableTouchResampling=*/true);
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -080063 }
64
65 status_t publishSimpleMotionEventWithCoords(int32_t action, nsecs_t eventTime,
66 const std::vector<PointerProperties>& properties,
67 const std::vector<PointerCoords>& coords);
68 void publishSimpleMotionEvent(int32_t action, nsecs_t eventTime,
69 const std::vector<Pointer>& pointers);
70 void publishInputEventEntries(const std::vector<InputEventEntry>& entries);
71 void consumeInputEventEntries(const std::vector<InputEventEntry>& entries,
72 std::chrono::nanoseconds frameTime);
73 void receiveResponseUntilSequence(uint32_t seq);
74};
75
76status_t TouchResamplingTest::publishSimpleMotionEventWithCoords(
77 int32_t action, nsecs_t eventTime, const std::vector<PointerProperties>& properties,
78 const std::vector<PointerCoords>& coords) {
79 const ui::Transform identityTransform;
80 const nsecs_t downTime = 0;
81
82 if (action == AMOTION_EVENT_ACTION_DOWN && eventTime != 0) {
83 ADD_FAILURE() << "Downtime should be equal to 0 (hardcoded for convenience)";
84 }
Harry Cutts82c791c2023-03-10 17:15:07 +000085 return mPublisher->publishMotionEvent(mSeq++, InputEvent::nextId(), /*deviceId=*/1,
86 AINPUT_SOURCE_TOUCHSCREEN, /*displayId=*/0, INVALID_HMAC,
87 action, /*actionButton=*/0, /*flags=*/0, /*edgeFlags=*/0,
88 AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
89 identityTransform, /*xPrecision=*/0, /*yPrecision=*/0,
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -080090 AMOTION_EVENT_INVALID_CURSOR_POSITION,
91 AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
92 downTime, eventTime, properties.size(), properties.data(),
93 coords.data());
94}
95
96void TouchResamplingTest::publishSimpleMotionEvent(int32_t action, nsecs_t eventTime,
97 const std::vector<Pointer>& pointers) {
98 std::vector<PointerProperties> properties;
99 std::vector<PointerCoords> coords;
100
101 for (const Pointer& pointer : pointers) {
102 properties.push_back({});
103 properties.back().clear();
104 properties.back().id = pointer.id;
Philip Quinn4e955a22023-09-26 12:09:40 -0700105 properties.back().toolType = pointer.toolType;
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -0800106
107 coords.push_back({});
108 coords.back().clear();
109 coords.back().setAxisValue(AMOTION_EVENT_AXIS_X, pointer.x);
110 coords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, pointer.y);
111 }
112
113 status_t result = publishSimpleMotionEventWithCoords(action, eventTime, properties, coords);
114 ASSERT_EQ(OK, result);
115}
116
117/**
118 * Each entry is published separately, one entry at a time. As a result, action is used here
119 * on a per-entry basis.
120 */
121void TouchResamplingTest::publishInputEventEntries(const std::vector<InputEventEntry>& entries) {
122 for (const InputEventEntry& entry : entries) {
123 publishSimpleMotionEvent(entry.action, entry.eventTime.count(), entry.pointers);
124 }
125}
126
127/**
128 * Inside the publisher, read responses repeatedly until the desired sequence number is returned.
129 *
130 * Sometimes, when you call 'sendFinishedSignal', you would be finishing a batch which is comprised
131 * of several input events. As a result, consumer will generate multiple 'finish' signals on your
132 * behalf.
133 *
134 * In this function, we call 'receiveConsumerResponse' in a loop until the desired sequence number
135 * is returned.
136 */
137void TouchResamplingTest::receiveResponseUntilSequence(uint32_t seq) {
138 size_t consumedEvents = 0;
139 while (consumedEvents < 100) {
140 android::base::Result<InputPublisher::ConsumerResponse> response =
141 mPublisher->receiveConsumerResponse();
142 ASSERT_TRUE(response.ok());
143 ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*response));
144 const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*response);
145 ASSERT_TRUE(finish.handled)
146 << "publisher receiveFinishedSignal should have set handled to consumer's reply";
147 if (finish.seq == seq) {
148 return;
149 }
150 consumedEvents++;
151 }
152 FAIL() << "Got " << consumedEvents << "events, but still no event with seq=" << seq;
153}
154
155/**
156 * All entries are compared against a single MotionEvent, but the same data structure
157 * InputEventEntry is used here for simpler code. As a result, the entire array of InputEventEntry
158 * must contain identical values for the action field.
159 */
160void TouchResamplingTest::consumeInputEventEntries(const std::vector<InputEventEntry>& entries,
161 std::chrono::nanoseconds frameTime) {
162 ASSERT_GE(entries.size(), 1U) << "Must have at least 1 InputEventEntry to compare against";
163
164 uint32_t consumeSeq;
165 InputEvent* event;
166
Harry Cutts82c791c2023-03-10 17:15:07 +0000167 status_t status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, frameTime.count(),
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -0800168 &consumeSeq, &event);
169 ASSERT_EQ(OK, status);
170 MotionEvent* motionEvent = static_cast<MotionEvent*>(event);
171
172 ASSERT_EQ(entries.size() - 1, motionEvent->getHistorySize());
173 for (size_t i = 0; i < entries.size(); i++) { // most recent sample is last
174 SCOPED_TRACE(i);
175 const InputEventEntry& entry = entries[i];
176 ASSERT_EQ(entry.action, motionEvent->getAction());
177 ASSERT_EQ(entry.eventTime.count(), motionEvent->getHistoricalEventTime(i));
178 ASSERT_EQ(entry.pointers.size(), motionEvent->getPointerCount());
179
180 for (size_t p = 0; p < motionEvent->getPointerCount(); p++) {
181 SCOPED_TRACE(p);
182 // The pointers can be in any order, both in MotionEvent as well as InputEventEntry
183 ssize_t motionEventPointerIndex = motionEvent->findPointerIndex(entry.pointers[p].id);
184 ASSERT_GE(motionEventPointerIndex, 0) << "Pointer must be present in MotionEvent";
185 ASSERT_EQ(entry.pointers[p].x,
186 motionEvent->getHistoricalAxisValue(AMOTION_EVENT_AXIS_X,
187 motionEventPointerIndex, i));
188 ASSERT_EQ(entry.pointers[p].x,
189 motionEvent->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_X,
190 motionEventPointerIndex, i));
191 ASSERT_EQ(entry.pointers[p].y,
192 motionEvent->getHistoricalAxisValue(AMOTION_EVENT_AXIS_Y,
193 motionEventPointerIndex, i));
194 ASSERT_EQ(entry.pointers[p].y,
195 motionEvent->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y,
196 motionEventPointerIndex, i));
Philip Quinnafb31282022-12-20 18:17:55 -0800197 ASSERT_EQ(entry.pointers[p].isResampled,
198 motionEvent->isResampled(motionEventPointerIndex, i));
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -0800199 }
200 }
201
202 status = mConsumer->sendFinishedSignal(consumeSeq, true);
203 ASSERT_EQ(OK, status);
204
205 receiveResponseUntilSequence(consumeSeq);
206}
207
208/**
209 * Timeline
210 * ---------+------------------+------------------+--------+-----------------+----------------------
211 * 0 ms 10 ms 20 ms 25 ms 35 ms
212 * ACTION_DOWN ACTION_MOVE ACTION_MOVE ^ ^
213 * | |
214 * resampled value |
215 * frameTime
216 * Typically, the prediction is made for time frameTime - RESAMPLE_LATENCY, or 30 ms in this case
217 * However, that would be 10 ms later than the last real sample (which came in at 20 ms).
218 * Therefore, the resampling should happen at 20 ms + RESAMPLE_MAX_PREDICTION = 28 ms.
219 * In this situation, though, resample time is further limited by taking half of the difference
220 * between the last two real events, which would put this time at:
221 * 20 ms + (20 ms - 10 ms) / 2 = 25 ms.
222 */
223TEST_F(TouchResamplingTest, EventIsResampled) {
224 std::chrono::nanoseconds frameTime;
225 std::vector<InputEventEntry> entries, expectedEntries;
226
227 // Initial ACTION_DOWN should be separate, because the first consume event will only return
228 // InputEvent with a single action.
229 entries = {
230 // id x y
231 {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN},
232 };
233 publishInputEventEntries(entries);
234 frameTime = 5ms;
235 expectedEntries = {
236 // id x y
237 {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN},
238 };
239 consumeInputEventEntries(expectedEntries, frameTime);
240
241 // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
242 entries = {
243 // id x y
244 {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE},
245 {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
246 };
247 publishInputEventEntries(entries);
248 frameTime = 35ms;
249 expectedEntries = {
250 // id x y
251 {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE},
252 {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
Philip Quinnafb31282022-12-20 18:17:55 -0800253 {25ms, {{0, 35, 30, .isResampled = true}}, AMOTION_EVENT_ACTION_MOVE},
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -0800254 };
255 consumeInputEventEntries(expectedEntries, frameTime);
256}
257
258/**
259 * Same as above test, but use pointer id=1 instead of 0 to make sure that system does not
260 * have these hardcoded.
261 */
262TEST_F(TouchResamplingTest, EventIsResampledWithDifferentId) {
263 std::chrono::nanoseconds frameTime;
264 std::vector<InputEventEntry> entries, expectedEntries;
265
266 // Initial ACTION_DOWN should be separate, because the first consume event will only return
267 // InputEvent with a single action.
268 entries = {
269 // id x y
270 {0ms, {{1, 10, 20}}, AMOTION_EVENT_ACTION_DOWN},
271 };
272 publishInputEventEntries(entries);
273 frameTime = 5ms;
274 expectedEntries = {
275 // id x y
276 {0ms, {{1, 10, 20}}, AMOTION_EVENT_ACTION_DOWN},
277 };
278 consumeInputEventEntries(expectedEntries, frameTime);
279
280 // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
281 entries = {
282 // id x y
283 {10ms, {{1, 20, 30}}, AMOTION_EVENT_ACTION_MOVE},
284 {20ms, {{1, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
285 };
286 publishInputEventEntries(entries);
287 frameTime = 35ms;
288 expectedEntries = {
289 // id x y
290 {10ms, {{1, 20, 30}}, AMOTION_EVENT_ACTION_MOVE},
291 {20ms, {{1, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
Philip Quinnafb31282022-12-20 18:17:55 -0800292 {25ms, {{1, 35, 30, .isResampled = true}}, AMOTION_EVENT_ACTION_MOVE},
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -0800293 };
294 consumeInputEventEntries(expectedEntries, frameTime);
295}
296
297/**
Philip Quinn4e955a22023-09-26 12:09:40 -0700298 * Stylus pointer coordinates are not resampled, but an event is still generated for the batch with
299 * a resampled timestamp and should be marked as such.
300 */
301TEST_F(TouchResamplingTest, StylusCoordinatesNotResampledFor) {
302 std::chrono::nanoseconds frameTime;
303 std::vector<InputEventEntry> entries, expectedEntries;
304
305 // Initial ACTION_DOWN should be separate, because the first consume event will only return
306 // InputEvent with a single action.
307 entries = {
308 // id x y
309 {0ms, {{0, 10, 20, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_DOWN},
310 };
311 publishInputEventEntries(entries);
312 frameTime = 5ms;
313 expectedEntries = {
314 // id x y
315 {0ms, {{0, 10, 20, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_DOWN},
316 };
317 consumeInputEventEntries(expectedEntries, frameTime);
318
319 // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
320 entries = {
321 // id x y
322 {10ms, {{0, 20, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
323 {20ms, {{0, 30, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
324 };
325 publishInputEventEntries(entries);
326 frameTime = 35ms;
327 expectedEntries = {
328 // id x y
329 {10ms, {{0, 20, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
330 {20ms, {{0, 30, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
331 // A resampled event is generated, but the stylus coordinates are not resampled.
332 {25ms,
333 {{0, 30, 30, .toolType = ToolType::STYLUS, .isResampled = true}},
334 AMOTION_EVENT_ACTION_MOVE},
335 };
336 consumeInputEventEntries(expectedEntries, frameTime);
337}
338
339/**
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -0800340 * Event should not be resampled when sample time is equal to event time.
341 */
342TEST_F(TouchResamplingTest, SampleTimeEqualsEventTime) {
343 std::chrono::nanoseconds frameTime;
344 std::vector<InputEventEntry> entries, expectedEntries;
345
346 // Initial ACTION_DOWN should be separate, because the first consume event will only return
347 // InputEvent with a single action.
348 entries = {
349 // id x y
350 {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN},
351 };
352 publishInputEventEntries(entries);
353 frameTime = 5ms;
354 expectedEntries = {
355 // id x y
356 {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN},
357 };
358 consumeInputEventEntries(expectedEntries, frameTime);
359
360 // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
361 entries = {
362 // id x y
363 {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE},
364 {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
365 };
366 publishInputEventEntries(entries);
367 frameTime = 20ms + 5ms /*RESAMPLE_LATENCY*/;
368 expectedEntries = {
369 // id x y
370 {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE},
371 {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
372 // no resampled event because the time of resample falls exactly on the existing event
373 };
374 consumeInputEventEntries(expectedEntries, frameTime);
375}
376
377/**
378 * Once we send a resampled value to the app, we should continue to "lie" if the pointer
379 * does not move. So, if the pointer keeps the same coordinates, resampled value should continue
380 * to be used.
381 */
382TEST_F(TouchResamplingTest, ResampledValueIsUsedForIdenticalCoordinates) {
383 std::chrono::nanoseconds frameTime;
384 std::vector<InputEventEntry> entries, expectedEntries;
385
386 // Initial ACTION_DOWN should be separate, because the first consume event will only return
387 // InputEvent with a single action.
388 entries = {
389 // id x y
390 {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN},
391 };
392 publishInputEventEntries(entries);
393 frameTime = 5ms;
394 expectedEntries = {
395 // id x y
396 {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN},
397 };
398 consumeInputEventEntries(expectedEntries, frameTime);
399
400 // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
401 entries = {
402 // id x y
403 {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE},
404 {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
405 };
406 publishInputEventEntries(entries);
407 frameTime = 35ms;
408 expectedEntries = {
409 // id x y
410 {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE},
411 {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
Philip Quinnafb31282022-12-20 18:17:55 -0800412 {25ms, {{0, 35, 30, .isResampled = true}}, AMOTION_EVENT_ACTION_MOVE},
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -0800413 };
414 consumeInputEventEntries(expectedEntries, frameTime);
415
416 // Coordinate value 30 has been resampled to 35. When a new event comes in with value 30 again,
417 // the system should still report 35.
418 entries = {
419 // id x y
420 {40ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
421 };
422 publishInputEventEntries(entries);
423 frameTime = 45ms + 5ms /*RESAMPLE_LATENCY*/;
424 expectedEntries = {
425 // id x y
Philip Quinnafb31282022-12-20 18:17:55 -0800426 {40ms,
427 {{0, 35, 30, .isResampled = true}},
428 AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
429 {45ms,
430 {{0, 35, 30, .isResampled = true}},
431 AMOTION_EVENT_ACTION_MOVE}, // resampled event, rewritten
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -0800432 };
433 consumeInputEventEntries(expectedEntries, frameTime);
434}
435
436TEST_F(TouchResamplingTest, OldEventReceivedAfterResampleOccurs) {
437 std::chrono::nanoseconds frameTime;
438 std::vector<InputEventEntry> entries, expectedEntries;
439
440 // Initial ACTION_DOWN should be separate, because the first consume event will only return
441 // InputEvent with a single action.
442 entries = {
443 // id x y
444 {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN},
445 };
446 publishInputEventEntries(entries);
447 frameTime = 5ms;
448 expectedEntries = {
449 // id x y
450 {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN},
451 };
452 consumeInputEventEntries(expectedEntries, frameTime);
453
454 // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
455 entries = {
456 // id x y
457 {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE},
458 {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
459 };
460 publishInputEventEntries(entries);
461 frameTime = 35ms;
462 expectedEntries = {
463 // id x y
464 {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE},
465 {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
Philip Quinnafb31282022-12-20 18:17:55 -0800466 {25ms, {{0, 35, 30, .isResampled = true}}, AMOTION_EVENT_ACTION_MOVE},
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -0800467 };
468 consumeInputEventEntries(expectedEntries, frameTime);
469 // Above, the resampled event is at 25ms rather than at 30 ms = 35ms - RESAMPLE_LATENCY
470 // because we are further bound by how far we can extrapolate by the "last time delta".
471 // That's 50% of (20 ms - 10ms) => 5ms. So we can't predict more than 5 ms into the future
472 // from the event at 20ms, which is why the resampled event is at t = 25 ms.
473
474 // We resampled the event to 25 ms. Now, an older 'real' event comes in.
475 entries = {
476 // id x y
477 {24ms, {{0, 40, 30}}, AMOTION_EVENT_ACTION_MOVE},
478 };
479 publishInputEventEntries(entries);
480 frameTime = 50ms;
481 expectedEntries = {
482 // id x y
Philip Quinnafb31282022-12-20 18:17:55 -0800483 {24ms,
484 {{0, 35, 30, .isResampled = true}},
485 AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
486 {26ms,
487 {{0, 45, 30, .isResampled = true}},
488 AMOTION_EVENT_ACTION_MOVE}, // resampled event, rewritten
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -0800489 };
490 consumeInputEventEntries(expectedEntries, frameTime);
491}
492
493TEST_F(TouchResamplingTest, TwoPointersAreResampledIndependently) {
494 std::chrono::nanoseconds frameTime;
495 std::vector<InputEventEntry> entries, expectedEntries;
496
497 // full action for when a pointer with id=1 appears (some other pointer must already be present)
498 constexpr int32_t actionPointer1Down =
499 AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
500
501 // full action for when a pointer with id=0 disappears (some other pointer must still remain)
502 constexpr int32_t actionPointer0Up =
503 AMOTION_EVENT_ACTION_POINTER_UP + (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
504
505 // Initial ACTION_DOWN should be separate, because the first consume event will only return
506 // InputEvent with a single action.
507 entries = {
508 // id x y
509 {0ms, {{0, 100, 100}}, AMOTION_EVENT_ACTION_DOWN},
510 };
511 publishInputEventEntries(entries);
512 frameTime = 5ms;
513 expectedEntries = {
514 // id x y
515 {0ms, {{0, 100, 100}}, AMOTION_EVENT_ACTION_DOWN},
516 };
517 consumeInputEventEntries(expectedEntries, frameTime);
518
519 entries = {
520 // id x y
521 {10ms, {{0, 100, 100}}, AMOTION_EVENT_ACTION_MOVE},
522 };
523 publishInputEventEntries(entries);
524 frameTime = 10ms + 5ms /*RESAMPLE_LATENCY*/;
525 expectedEntries = {
526 // id x y
527 {10ms, {{0, 100, 100}}, AMOTION_EVENT_ACTION_MOVE},
528 // no resampled value because frameTime - RESAMPLE_LATENCY == eventTime
529 };
530 consumeInputEventEntries(expectedEntries, frameTime);
531
532 // Second pointer id=1 appears
533 entries = {
534 // id x y
535 {15ms, {{0, 100, 100}, {1, 500, 500}}, actionPointer1Down},
536 };
537 publishInputEventEntries(entries);
538 frameTime = 20ms + 5ms /*RESAMPLE_LATENCY*/;
539 expectedEntries = {
540 // id x y
541 {15ms, {{0, 100, 100}, {1, 500, 500}}, actionPointer1Down},
542 // no resampled value because frameTime - RESAMPLE_LATENCY == eventTime
543 };
544 consumeInputEventEntries(expectedEntries, frameTime);
545
546 // Both pointers move
547 entries = {
548 // id x y
549 {30ms, {{0, 100, 100}, {1, 500, 500}}, AMOTION_EVENT_ACTION_MOVE},
550 {40ms, {{0, 120, 120}, {1, 600, 600}}, AMOTION_EVENT_ACTION_MOVE},
551 };
552 publishInputEventEntries(entries);
553 frameTime = 45ms + 5ms /*RESAMPLE_LATENCY*/;
554 expectedEntries = {
555 // id x y
556 {30ms, {{0, 100, 100}, {1, 500, 500}}, AMOTION_EVENT_ACTION_MOVE},
557 {40ms, {{0, 120, 120}, {1, 600, 600}}, AMOTION_EVENT_ACTION_MOVE},
Philip Quinnafb31282022-12-20 18:17:55 -0800558 {45ms,
559 {{0, 130, 130, .isResampled = true}, {1, 650, 650, .isResampled = true}},
560 AMOTION_EVENT_ACTION_MOVE},
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -0800561 };
562 consumeInputEventEntries(expectedEntries, frameTime);
563
564 // Both pointers move again
565 entries = {
566 // id x y
567 {60ms, {{0, 120, 120}, {1, 600, 600}}, AMOTION_EVENT_ACTION_MOVE},
568 {70ms, {{0, 130, 130}, {1, 700, 700}}, AMOTION_EVENT_ACTION_MOVE},
569 };
570 publishInputEventEntries(entries);
571 frameTime = 75ms + 5ms /*RESAMPLE_LATENCY*/;
572 /**
573 * The sample at t = 60, pointer id 0 is not equal to 120, because this value of 120 was
574 * received twice, and resampled to 130. So if we already reported it as "130", we continue
575 * to report it as such. Similar with pointer id 1.
576 */
577 expectedEntries = {
578 {60ms,
Philip Quinnafb31282022-12-20 18:17:55 -0800579 {{0, 130, 130, .isResampled = true}, // not 120! because it matches previous real event
580 {1, 650, 650, .isResampled = true}},
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -0800581 AMOTION_EVENT_ACTION_MOVE},
582 {70ms, {{0, 130, 130}, {1, 700, 700}}, AMOTION_EVENT_ACTION_MOVE},
Philip Quinnafb31282022-12-20 18:17:55 -0800583 {75ms,
584 {{0, 135, 135, .isResampled = true}, {1, 750, 750, .isResampled = true}},
585 AMOTION_EVENT_ACTION_MOVE},
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -0800586 };
587 consumeInputEventEntries(expectedEntries, frameTime);
588
589 // First pointer id=0 leaves the screen
590 entries = {
591 // id x y
Siarhei Vishniakou6252af72023-09-20 09:00:38 -0700592 {80ms, {{0, 120, 120}, {1, 600, 600}}, actionPointer0Up},
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -0800593 };
594 publishInputEventEntries(entries);
595 frameTime = 90ms;
596 expectedEntries = {
597 // id x y
Siarhei Vishniakou6252af72023-09-20 09:00:38 -0700598 {80ms, {{0, 120, 120}, {1, 600, 600}}, actionPointer0Up},
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -0800599 // no resampled event for ACTION_POINTER_UP
600 };
601 consumeInputEventEntries(expectedEntries, frameTime);
602
603 // Remaining pointer id=1 is still present, but doesn't move
604 entries = {
605 // id x y
606 {90ms, {{1, 600, 600}}, AMOTION_EVENT_ACTION_MOVE},
607 };
608 publishInputEventEntries(entries);
609 frameTime = 100ms;
610 expectedEntries = {
611 // id x y
612 {90ms, {{1, 600, 600}}, AMOTION_EVENT_ACTION_MOVE},
613 /**
614 * The latest event with ACTION_MOVE was at t = 70, coord = 700.
615 * Use that value for resampling here: (600 - 700) / (90 - 70) * 5 + 600
616 */
Philip Quinnafb31282022-12-20 18:17:55 -0800617 {95ms, {{1, 575, 575, .isResampled = true}}, AMOTION_EVENT_ACTION_MOVE},
Siarhei Vishniakou0ced3cc2017-11-21 15:33:17 -0800618 };
619 consumeInputEventEntries(expectedEntries, frameTime);
620}
621
622} // namespace android