1 /*
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 <chrono>
18 #include <vector>
19
20 #include <attestation/HmacKeyManager.h>
21 #include <gtest/gtest.h>
22 #include <input/InputConsumer.h>
23 #include <input/InputTransport.h>
24
25 using namespace std::chrono_literals;
26
27 namespace android {
28
29 namespace {
30
31 struct Pointer {
32 int32_t id;
33 float x;
34 float y;
35 ToolType toolType = ToolType::FINGER;
36 bool isResampled = false;
37 };
38
39 struct InputEventEntry {
40 std::chrono::nanoseconds eventTime;
41 std::vector<Pointer> pointers;
42 int32_t action;
43 };
44
45 } // namespace
46
47 class TouchResamplingTest : public testing::Test {
48 protected:
49 std::unique_ptr<InputPublisher> mPublisher;
50 std::unique_ptr<InputConsumer> mConsumer;
51 PreallocatedInputEventFactory mEventFactory;
52
53 uint32_t mSeq = 1;
54
SetUp()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),
63 /*enableTouchResampling=*/true);
64 }
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
publishSimpleMotionEventWithCoords(int32_t action,nsecs_t eventTime,const std::vector<PointerProperties> & properties,const std::vector<PointerCoords> & coords)77 status_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 }
86 return mPublisher->publishMotionEvent(mSeq++, InputEvent::nextId(), /*deviceId=*/1,
87 AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
88 INVALID_HMAC, action, /*actionButton=*/0, /*flags=*/0,
89 /*edgeFlags=*/0, AMETA_NONE, /*buttonState=*/0,
90 MotionClassification::NONE, identityTransform,
91 /*xPrecision=*/0, /*yPrecision=*/0,
92 AMOTION_EVENT_INVALID_CURSOR_POSITION,
93 AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
94 downTime, eventTime, properties.size(), properties.data(),
95 coords.data());
96 }
97
publishSimpleMotionEvent(int32_t action,nsecs_t eventTime,const std::vector<Pointer> & pointers)98 void 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;
107 properties.back().toolType = pointer.toolType;
108
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 */
publishInputEventEntries(const std::vector<InputEventEntry> & entries)123 void 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 */
receiveResponseUntilSequence(uint32_t seq)139 void 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 */
consumeInputEventEntries(const std::vector<InputEventEntry> & entries,std::chrono::nanoseconds frameTime)162 void 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
169 status_t status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, frameTime.count(),
170 &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));
199 ASSERT_EQ(entry.pointers[p].isResampled,
200 motionEvent->isResampled(motionEventPointerIndex, i));
201 }
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 */
TEST_F(TouchResamplingTest,EventIsResampled)225 TEST_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},
255 {25ms, {{0, 35, 30, .isResampled = true}}, AMOTION_EVENT_ACTION_MOVE},
256 };
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 */
TEST_F(TouchResamplingTest,EventIsResampledWithDifferentId)264 TEST_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},
294 {25ms, {{1, 35, 30, .isResampled = true}}, AMOTION_EVENT_ACTION_MOVE},
295 };
296 consumeInputEventEntries(expectedEntries, frameTime);
297 }
298
299 /**
300 * Stylus pointer coordinates are resampled.
301 */
TEST_F(TouchResamplingTest,StylusEventIsResampled)302 TEST_F(TouchResamplingTest, StylusEventIsResampled) {
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 {25ms,
333 {{0, 35, 30, .toolType = ToolType::STYLUS, .isResampled = true}},
334 AMOTION_EVENT_ACTION_MOVE},
335 };
336 consumeInputEventEntries(expectedEntries, frameTime);
337 }
338
339 /**
340 * Mouse pointer coordinates are resampled.
341 */
TEST_F(TouchResamplingTest,MouseEventIsResampled)342 TEST_F(TouchResamplingTest, MouseEventIsResampled) {
343 std::chrono::nanoseconds frameTime;
344 std::vector<InputEventEntry> entries, expectedEntries;
345
346 // Initial ACTION_DOWN should be separate, because the first consume event will only return
347 // InputEvent with a single action.
348 entries = {
349 // id x y
350 {0ms, {{0, 10, 20, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_DOWN},
351 };
352 publishInputEventEntries(entries);
353 frameTime = 5ms;
354 expectedEntries = {
355 // id x y
356 {0ms, {{0, 10, 20, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_DOWN},
357 };
358 consumeInputEventEntries(expectedEntries, frameTime);
359
360 // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
361 entries = {
362 // id x y
363 {10ms, {{0, 20, 30, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_MOVE},
364 {20ms, {{0, 30, 30, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_MOVE},
365 };
366 publishInputEventEntries(entries);
367 frameTime = 35ms;
368 expectedEntries = {
369 // id x y
370 {10ms, {{0, 20, 30, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_MOVE},
371 {20ms, {{0, 30, 30, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_MOVE},
372 {25ms,
373 {{0, 35, 30, .toolType = ToolType::MOUSE, .isResampled = true}},
374 AMOTION_EVENT_ACTION_MOVE},
375 };
376 consumeInputEventEntries(expectedEntries, frameTime);
377 }
378
379 /**
380 * Motion events with palm tool type are not resampled.
381 */
TEST_F(TouchResamplingTest,PalmEventIsNotResampled)382 TEST_F(TouchResamplingTest, PalmEventIsNotResampled) {
383 std::chrono::nanoseconds frameTime;
384 std::vector<InputEventEntry> entries, expectedEntries;
385
386 // Initial ACTION_DOWN should be separate, because the first consume event will only return
387 // InputEvent with a single action.
388 entries = {
389 // id x y
390 {0ms, {{0, 10, 20, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_DOWN},
391 };
392 publishInputEventEntries(entries);
393 frameTime = 5ms;
394 expectedEntries = {
395 // id x y
396 {0ms, {{0, 10, 20, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_DOWN},
397 };
398 consumeInputEventEntries(expectedEntries, frameTime);
399
400 // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
401 entries = {
402 // id x y
403 {10ms, {{0, 20, 30, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_MOVE},
404 {20ms, {{0, 30, 30, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_MOVE},
405 };
406 publishInputEventEntries(entries);
407 frameTime = 35ms;
408 expectedEntries = {
409 // id x y
410 {10ms, {{0, 20, 30, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_MOVE},
411 {20ms, {{0, 30, 30, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_MOVE},
412 };
413 consumeInputEventEntries(expectedEntries, frameTime);
414 }
415
416 /**
417 * Event should not be resampled when sample time is equal to event time.
418 */
TEST_F(TouchResamplingTest,SampleTimeEqualsEventTime)419 TEST_F(TouchResamplingTest, SampleTimeEqualsEventTime) {
420 std::chrono::nanoseconds frameTime;
421 std::vector<InputEventEntry> entries, expectedEntries;
422
423 // Initial ACTION_DOWN should be separate, because the first consume event will only return
424 // InputEvent with a single action.
425 entries = {
426 // id x y
427 {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN},
428 };
429 publishInputEventEntries(entries);
430 frameTime = 5ms;
431 expectedEntries = {
432 // id x y
433 {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN},
434 };
435 consumeInputEventEntries(expectedEntries, frameTime);
436
437 // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
438 entries = {
439 // id x y
440 {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE},
441 {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
442 };
443 publishInputEventEntries(entries);
444 frameTime = 20ms + 5ms /*RESAMPLE_LATENCY*/;
445 expectedEntries = {
446 // id x y
447 {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE},
448 {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
449 // no resampled event because the time of resample falls exactly on the existing event
450 };
451 consumeInputEventEntries(expectedEntries, frameTime);
452 }
453
454 /**
455 * Once we send a resampled value to the app, we should continue to "lie" if the pointer
456 * does not move. So, if the pointer keeps the same coordinates, resampled value should continue
457 * to be used.
458 */
TEST_F(TouchResamplingTest,ResampledValueIsUsedForIdenticalCoordinates)459 TEST_F(TouchResamplingTest, ResampledValueIsUsedForIdenticalCoordinates) {
460 std::chrono::nanoseconds frameTime;
461 std::vector<InputEventEntry> entries, expectedEntries;
462
463 // Initial ACTION_DOWN should be separate, because the first consume event will only return
464 // InputEvent with a single action.
465 entries = {
466 // id x y
467 {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN},
468 };
469 publishInputEventEntries(entries);
470 frameTime = 5ms;
471 expectedEntries = {
472 // id x y
473 {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN},
474 };
475 consumeInputEventEntries(expectedEntries, frameTime);
476
477 // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
478 entries = {
479 // id x y
480 {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE},
481 {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
482 };
483 publishInputEventEntries(entries);
484 frameTime = 35ms;
485 expectedEntries = {
486 // id x y
487 {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE},
488 {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
489 {25ms, {{0, 35, 30, .isResampled = true}}, AMOTION_EVENT_ACTION_MOVE},
490 };
491 consumeInputEventEntries(expectedEntries, frameTime);
492
493 // Coordinate value 30 has been resampled to 35. When a new event comes in with value 30 again,
494 // the system should still report 35.
495 entries = {
496 // id x y
497 {40ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
498 };
499 publishInputEventEntries(entries);
500 frameTime = 45ms + 5ms /*RESAMPLE_LATENCY*/;
501 expectedEntries = {
502 // id x y
503 {40ms,
504 {{0, 35, 30, .isResampled = true}},
505 AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
506 {45ms,
507 {{0, 35, 30, .isResampled = true}},
508 AMOTION_EVENT_ACTION_MOVE}, // resampled event, rewritten
509 };
510 consumeInputEventEntries(expectedEntries, frameTime);
511 }
512
TEST_F(TouchResamplingTest,OldEventReceivedAfterResampleOccurs)513 TEST_F(TouchResamplingTest, OldEventReceivedAfterResampleOccurs) {
514 std::chrono::nanoseconds frameTime;
515 std::vector<InputEventEntry> entries, expectedEntries;
516
517 // Initial ACTION_DOWN should be separate, because the first consume event will only return
518 // InputEvent with a single action.
519 entries = {
520 // id x y
521 {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN},
522 };
523 publishInputEventEntries(entries);
524 frameTime = 5ms;
525 expectedEntries = {
526 // id x y
527 {0ms, {{0, 10, 20}}, AMOTION_EVENT_ACTION_DOWN},
528 };
529 consumeInputEventEntries(expectedEntries, frameTime);
530
531 // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
532 entries = {
533 // id x y
534 {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE},
535 {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
536 };
537 publishInputEventEntries(entries);
538 frameTime = 35ms;
539 expectedEntries = {
540 // id x y
541 {10ms, {{0, 20, 30}}, AMOTION_EVENT_ACTION_MOVE},
542 {20ms, {{0, 30, 30}}, AMOTION_EVENT_ACTION_MOVE},
543 {25ms, {{0, 35, 30, .isResampled = true}}, AMOTION_EVENT_ACTION_MOVE},
544 };
545 consumeInputEventEntries(expectedEntries, frameTime);
546 // Above, the resampled event is at 25ms rather than at 30 ms = 35ms - RESAMPLE_LATENCY
547 // because we are further bound by how far we can extrapolate by the "last time delta".
548 // That's 50% of (20 ms - 10ms) => 5ms. So we can't predict more than 5 ms into the future
549 // from the event at 20ms, which is why the resampled event is at t = 25 ms.
550
551 // We resampled the event to 25 ms. Now, an older 'real' event comes in.
552 entries = {
553 // id x y
554 {24ms, {{0, 40, 30}}, AMOTION_EVENT_ACTION_MOVE},
555 };
556 publishInputEventEntries(entries);
557 frameTime = 50ms;
558 expectedEntries = {
559 // id x y
560 {24ms,
561 {{0, 35, 30, .isResampled = true}},
562 AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
563 {26ms,
564 {{0, 45, 30, .isResampled = true}},
565 AMOTION_EVENT_ACTION_MOVE}, // resampled event, rewritten
566 };
567 consumeInputEventEntries(expectedEntries, frameTime);
568 }
569
TEST_F(TouchResamplingTest,TwoPointersAreResampledIndependently)570 TEST_F(TouchResamplingTest, TwoPointersAreResampledIndependently) {
571 std::chrono::nanoseconds frameTime;
572 std::vector<InputEventEntry> entries, expectedEntries;
573
574 // full action for when a pointer with id=1 appears (some other pointer must already be present)
575 constexpr int32_t actionPointer1Down =
576 AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
577
578 // full action for when a pointer with id=0 disappears (some other pointer must still remain)
579 constexpr int32_t actionPointer0Up =
580 AMOTION_EVENT_ACTION_POINTER_UP + (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
581
582 // Initial ACTION_DOWN should be separate, because the first consume event will only return
583 // InputEvent with a single action.
584 entries = {
585 // id x y
586 {0ms, {{0, 100, 100}}, AMOTION_EVENT_ACTION_DOWN},
587 };
588 publishInputEventEntries(entries);
589 frameTime = 5ms;
590 expectedEntries = {
591 // id x y
592 {0ms, {{0, 100, 100}}, AMOTION_EVENT_ACTION_DOWN},
593 };
594 consumeInputEventEntries(expectedEntries, frameTime);
595
596 entries = {
597 // id x y
598 {10ms, {{0, 100, 100}}, AMOTION_EVENT_ACTION_MOVE},
599 };
600 publishInputEventEntries(entries);
601 frameTime = 10ms + 5ms /*RESAMPLE_LATENCY*/;
602 expectedEntries = {
603 // id x y
604 {10ms, {{0, 100, 100}}, AMOTION_EVENT_ACTION_MOVE},
605 // no resampled value because frameTime - RESAMPLE_LATENCY == eventTime
606 };
607 consumeInputEventEntries(expectedEntries, frameTime);
608
609 // Second pointer id=1 appears
610 entries = {
611 // id x y
612 {15ms, {{0, 100, 100}, {1, 500, 500}}, actionPointer1Down},
613 };
614 publishInputEventEntries(entries);
615 frameTime = 20ms + 5ms /*RESAMPLE_LATENCY*/;
616 expectedEntries = {
617 // id x y
618 {15ms, {{0, 100, 100}, {1, 500, 500}}, actionPointer1Down},
619 // no resampled value because frameTime - RESAMPLE_LATENCY == eventTime
620 };
621 consumeInputEventEntries(expectedEntries, frameTime);
622
623 // Both pointers move
624 entries = {
625 // id x y
626 {30ms, {{0, 100, 100}, {1, 500, 500}}, AMOTION_EVENT_ACTION_MOVE},
627 {40ms, {{0, 120, 120}, {1, 600, 600}}, AMOTION_EVENT_ACTION_MOVE},
628 };
629 publishInputEventEntries(entries);
630 frameTime = 45ms + 5ms /*RESAMPLE_LATENCY*/;
631 expectedEntries = {
632 // id x y
633 {30ms, {{0, 100, 100}, {1, 500, 500}}, AMOTION_EVENT_ACTION_MOVE},
634 {40ms, {{0, 120, 120}, {1, 600, 600}}, AMOTION_EVENT_ACTION_MOVE},
635 {45ms,
636 {{0, 130, 130, .isResampled = true}, {1, 650, 650, .isResampled = true}},
637 AMOTION_EVENT_ACTION_MOVE},
638 };
639 consumeInputEventEntries(expectedEntries, frameTime);
640
641 // Both pointers move again
642 entries = {
643 // id x y
644 {60ms, {{0, 120, 120}, {1, 600, 600}}, AMOTION_EVENT_ACTION_MOVE},
645 {70ms, {{0, 130, 130}, {1, 700, 700}}, AMOTION_EVENT_ACTION_MOVE},
646 };
647 publishInputEventEntries(entries);
648 frameTime = 75ms + 5ms /*RESAMPLE_LATENCY*/;
649 /**
650 * The sample at t = 60, pointer id 0 is not equal to 120, because this value of 120 was
651 * received twice, and resampled to 130. So if we already reported it as "130", we continue
652 * to report it as such. Similar with pointer id 1.
653 */
654 expectedEntries = {
655 {60ms,
656 {{0, 130, 130, .isResampled = true}, // not 120! because it matches previous real event
657 {1, 650, 650, .isResampled = true}},
658 AMOTION_EVENT_ACTION_MOVE},
659 {70ms, {{0, 130, 130}, {1, 700, 700}}, AMOTION_EVENT_ACTION_MOVE},
660 {75ms,
661 {{0, 135, 135, .isResampled = true}, {1, 750, 750, .isResampled = true}},
662 AMOTION_EVENT_ACTION_MOVE},
663 };
664 consumeInputEventEntries(expectedEntries, frameTime);
665
666 // First pointer id=0 leaves the screen
667 entries = {
668 // id x y
669 {80ms, {{0, 120, 120}, {1, 600, 600}}, actionPointer0Up},
670 };
671 publishInputEventEntries(entries);
672 frameTime = 90ms;
673 expectedEntries = {
674 // id x y
675 {80ms, {{0, 120, 120}, {1, 600, 600}}, actionPointer0Up},
676 // no resampled event for ACTION_POINTER_UP
677 };
678 consumeInputEventEntries(expectedEntries, frameTime);
679
680 // Remaining pointer id=1 is still present, but doesn't move
681 entries = {
682 // id x y
683 {90ms, {{1, 600, 600}}, AMOTION_EVENT_ACTION_MOVE},
684 };
685 publishInputEventEntries(entries);
686 frameTime = 100ms;
687 expectedEntries = {
688 // id x y
689 {90ms, {{1, 600, 600}}, AMOTION_EVENT_ACTION_MOVE},
690 /**
691 * The latest event with ACTION_MOVE was at t = 70, coord = 700.
692 * Use that value for resampling here: (600 - 700) / (90 - 70) * 5 + 600
693 */
694 {95ms, {{1, 575, 575, .isResampled = true}}, AMOTION_EVENT_ACTION_MOVE},
695 };
696 consumeInputEventEntries(expectedEntries, frameTime);
697 }
698
699 } // namespace android
700