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