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