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