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