@@ -38,6 +38,8 @@ namespace {
3838using std::chrono::nanoseconds;
3939using namespace std ::chrono_literals;
4040
41+ const std::chrono::milliseconds RESAMPLE_LATENCY{5 };
42+
4143struct Pointer {
4244 int32_t id{0 };
4345 float x{0 .0f };
@@ -440,7 +442,7 @@ TEST_F(InputConsumerResamplingTest, SampleTimeEqualsEventTime) {
440442 {20ms, {Pointer{.id = 0 , .x = 30 .0f , .y = 30 .0f }}, AMOTION_EVENT_ACTION_MOVE}));
441443
442444 invokeLooperCallback ();
443- mConsumer ->consumeBatchedInputEvents (nanoseconds{20ms + 5ms /* RESAMPLE_LATENCY*/ }.count ());
445+ mConsumer ->consumeBatchedInputEvents (nanoseconds{20ms + RESAMPLE_LATENCY}.count ());
444446
445447 // MotionEvent should not resampled because the resample time falls exactly on the existing
446448 // event time.
@@ -496,14 +498,15 @@ TEST_F(InputConsumerResamplingTest, ResampledValueIsUsedForIdenticalCoordinates)
496498 {40ms, {Pointer{.id = 0 , .x = 30 .0f , .y = 30 .0f }}, AMOTION_EVENT_ACTION_MOVE}));
497499
498500 invokeLooperCallback ();
499- mConsumer ->consumeBatchedInputEvents (nanoseconds{45ms + 5ms /* RESAMPLE_LATENCY*/ }.count ());
501+ mConsumer ->consumeBatchedInputEvents (nanoseconds{45ms + RESAMPLE_LATENCY}.count ());
502+ // Original and resampled event should be both overwritten.
500503 assertReceivedMotionEvent (
501504 {InputEventEntry{40ms,
502505 {Pointer{.id = 0 , .x = 35 .0f , .y = 30 .0f , .isResampled = true }},
503- AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
506+ AMOTION_EVENT_ACTION_MOVE},
504507 InputEventEntry{45ms,
505508 {Pointer{.id = 0 , .x = 35 .0f , .y = 30 .0f , .isResampled = true }},
506- AMOTION_EVENT_ACTION_MOVE}}); // resampled event, rewritten
509+ AMOTION_EVENT_ACTION_MOVE}});
507510
508511 mClientTestChannel ->assertFinishMessage (/* seq=*/ 1 , /* handled=*/ true );
509512 mClientTestChannel ->assertFinishMessage (/* seq=*/ 2 , /* handled=*/ true );
@@ -552,13 +555,14 @@ TEST_F(InputConsumerResamplingTest, OldEventReceivedAfterResampleOccurs) {
552555
553556 invokeLooperCallback ();
554557 mConsumer ->consumeBatchedInputEvents (nanoseconds{50ms}.count ());
558+ // Original and resampled event should be both overwritten.
555559 assertReceivedMotionEvent (
556560 {InputEventEntry{24ms,
557561 {Pointer{.id = 0 , .x = 35 .0f , .y = 30 .0f , .isResampled = true }},
558- AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
562+ AMOTION_EVENT_ACTION_MOVE},
559563 InputEventEntry{26ms,
560564 {Pointer{.id = 0 , .x = 45 .0f , .y = 30 .0f , .isResampled = true }},
561- AMOTION_EVENT_ACTION_MOVE}}); // resampled event, rewritten
565+ AMOTION_EVENT_ACTION_MOVE}});
562566
563567 mClientTestChannel ->assertFinishMessage (/* seq=*/ 1 , /* handled=*/ true );
564568 mClientTestChannel ->assertFinishMessage (/* seq=*/ 2 , /* handled=*/ true );
@@ -594,4 +598,147 @@ TEST_F(InputConsumerResamplingTest, DoNotResampleWhenFrameTimeIsNotAvailable) {
594598 mClientTestChannel ->assertFinishMessage (/* seq=*/ 3 , /* handled=*/ true );
595599}
596600
601+ TEST_F (InputConsumerResamplingTest, TwoPointersAreResampledIndependently) {
602+ // Full action for when a pointer with index=1 appears (some other pointer must already be
603+ // present)
604+ const int32_t actionPointer1Down =
605+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
606+
607+ // Full action for when a pointer with index=0 disappears (some other pointer must still remain)
608+ const int32_t actionPointer0Up =
609+ AMOTION_EVENT_ACTION_POINTER_UP + (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
610+
611+ mClientTestChannel ->enqueueMessage (nextPointerMessage (
612+ {0ms, {Pointer{.id = 0 , .x = 100 .0f , .y = 100 .0f }}, AMOTION_EVENT_ACTION_DOWN}));
613+
614+ mClientTestChannel ->assertNoSentMessages ();
615+
616+ invokeLooperCallback ();
617+ assertReceivedMotionEvent ({InputEventEntry{0ms,
618+ {Pointer{.id = 0 , .x = 100 .0f , .y = 100 .0f }},
619+ AMOTION_EVENT_ACTION_DOWN}});
620+
621+ mClientTestChannel ->enqueueMessage (nextPointerMessage (
622+ {10ms, {Pointer{.id = 0 , .x = 100 .0f , .y = 100 .0f }}, AMOTION_EVENT_ACTION_MOVE}));
623+
624+ invokeLooperCallback ();
625+ mConsumer ->consumeBatchedInputEvents (nanoseconds{10ms + RESAMPLE_LATENCY}.count ());
626+ // Not resampled value because requestedFrameTime - RESAMPLE_LATENCY == eventTime
627+ assertReceivedMotionEvent ({InputEventEntry{10ms,
628+ {Pointer{.id = 0 , .x = 100 .0f , .y = 100 .0f }},
629+ AMOTION_EVENT_ACTION_MOVE}});
630+
631+ // Second pointer id=1 appears
632+ mClientTestChannel ->enqueueMessage (
633+ nextPointerMessage ({15ms,
634+ {Pointer{.id = 0 , .x = 100 .0f , .y = 100 .0f },
635+ Pointer{.id = 1 , .x = 500 .0f , .y = 500 .0f }},
636+ actionPointer1Down}));
637+
638+ invokeLooperCallback ();
639+ mConsumer ->consumeBatchedInputEvents (nanoseconds{20ms + RESAMPLE_LATENCY}.count ());
640+ // Not resampled value because requestedFrameTime - RESAMPLE_LATENCY == eventTime.
641+ assertReceivedMotionEvent ({InputEventEntry{15ms,
642+ {Pointer{.id = 0 , .x = 100 .0f , .y = 100 .0f },
643+ Pointer{.id = 1 , .x = 500 .0f , .y = 500 .0f }},
644+ actionPointer1Down}});
645+
646+ // Both pointers move
647+ mClientTestChannel ->enqueueMessage (
648+ nextPointerMessage ({30ms,
649+ {Pointer{.id = 0 , .x = 100 .0f , .y = 100 .0f },
650+ Pointer{.id = 1 , .x = 500 .0f , .y = 500 .0f }},
651+ AMOTION_EVENT_ACTION_MOVE}));
652+ mClientTestChannel ->enqueueMessage (
653+ nextPointerMessage ({40ms,
654+ {Pointer{.id = 0 , .x = 120 .0f , .y = 120 .0f },
655+ Pointer{.id = 1 , .x = 600 .0f , .y = 600 .0f }},
656+ AMOTION_EVENT_ACTION_MOVE}));
657+
658+ invokeLooperCallback ();
659+ mConsumer ->consumeBatchedInputEvents (nanoseconds{45ms + RESAMPLE_LATENCY}.count ());
660+ assertReceivedMotionEvent (
661+ {InputEventEntry{30ms,
662+ {Pointer{.id = 0 , .x = 100 .0f , .y = 100 .0f },
663+ Pointer{.id = 1 , .x = 500 .0f , .y = 500 .0f }},
664+ AMOTION_EVENT_ACTION_MOVE},
665+ InputEventEntry{40ms,
666+ {Pointer{.id = 0 , .x = 120 .0f , .y = 120 .0f },
667+ Pointer{.id = 1 , .x = 600 .0f , .y = 600 .0f }},
668+ AMOTION_EVENT_ACTION_MOVE},
669+ InputEventEntry{45ms,
670+ {Pointer{.id = 0 , .x = 130 .0f , .y = 130 .0f , .isResampled = true },
671+ Pointer{.id = 1 , .x = 650 .0f , .y = 650 .0f , .isResampled = true }},
672+ AMOTION_EVENT_ACTION_MOVE}});
673+
674+ // Both pointers move again
675+ mClientTestChannel ->enqueueMessage (
676+ nextPointerMessage ({60ms,
677+ {Pointer{.id = 0 , .x = 120 .0f , .y = 120 .0f },
678+ Pointer{.id = 1 , .x = 600 .0f , .y = 600 .0f }},
679+ AMOTION_EVENT_ACTION_MOVE}));
680+ mClientTestChannel ->enqueueMessage (
681+ nextPointerMessage ({70ms,
682+ {Pointer{.id = 0 , .x = 130 .0f , .y = 130 .0f },
683+ Pointer{.id = 1 , .x = 700 .0f , .y = 700 .0f }},
684+ AMOTION_EVENT_ACTION_MOVE}));
685+
686+ invokeLooperCallback ();
687+ mConsumer ->consumeBatchedInputEvents (nanoseconds{75ms + RESAMPLE_LATENCY}.count ());
688+
689+ /*
690+ * The pointer id 0 at t = 60 should not be equal to 120 because the value was received twice,
691+ * and resampled to 130. Therefore, if we reported 130, then we should continue to report it as
692+ * such. Likewise, with pointer id 1.
693+ */
694+
695+ // Not 120 because it matches a previous real event.
696+ assertReceivedMotionEvent (
697+ {InputEventEntry{60ms,
698+ {Pointer{.id = 0 , .x = 130 .0f , .y = 130 .0f , .isResampled = true },
699+ Pointer{.id = 1 , .x = 650 .0f , .y = 650 .0f , .isResampled = true }},
700+ AMOTION_EVENT_ACTION_MOVE},
701+ InputEventEntry{70ms,
702+ {Pointer{.id = 0 , .x = 130 .0f , .y = 130 .0f },
703+ Pointer{.id = 1 , .x = 700 .0f , .y = 700 .0f }},
704+ AMOTION_EVENT_ACTION_MOVE},
705+ InputEventEntry{75ms,
706+ {Pointer{.id = 0 , .x = 135 .0f , .y = 135 .0f , .isResampled = true },
707+ Pointer{.id = 1 , .x = 750 .0f , .y = 750 .0f , .isResampled = true }},
708+ AMOTION_EVENT_ACTION_MOVE}});
709+
710+ // First pointer id=0 leaves the screen
711+ mClientTestChannel ->enqueueMessage (
712+ nextPointerMessage ({80ms,
713+ {Pointer{.id = 0 , .x = 120 .0f , .y = 120 .0f },
714+ Pointer{.id = 1 , .x = 600 .0f , .y = 600 .0f }},
715+ actionPointer0Up}));
716+
717+ invokeLooperCallback ();
718+ // Not resampled event for ACTION_POINTER_UP
719+ assertReceivedMotionEvent ({InputEventEntry{80ms,
720+ {Pointer{.id = 0 , .x = 120 .0f , .y = 120 .0f },
721+ Pointer{.id = 1 , .x = 600 .0f , .y = 600 .0f }},
722+ actionPointer0Up}});
723+
724+ // Remaining pointer id=1 is still present, but doesn't move
725+ mClientTestChannel ->enqueueMessage (nextPointerMessage (
726+ {90ms, {Pointer{.id = 1 , .x = 600 .0f , .y = 600 .0f }}, AMOTION_EVENT_ACTION_MOVE}));
727+
728+ invokeLooperCallback ();
729+ mConsumer ->consumeBatchedInputEvents (nanoseconds{100ms}.count ());
730+
731+ /*
732+ * The latest event with ACTION_MOVE was at t = 70 with value = 700. Thus, the resampled value
733+ * is 700 + ((95 - 70)/(90 - 70))*(600 - 700) = 575.
734+ */
735+ assertReceivedMotionEvent (
736+ {InputEventEntry{90ms,
737+ {Pointer{.id = 1 , .x = 600 .0f , .y = 600 .0f }},
738+ AMOTION_EVENT_ACTION_MOVE},
739+ InputEventEntry{95ms,
740+ {Pointer{.id = 1 , .x = 575 .0f , .y = 575 .0f , .isResampled = true }},
741+ AMOTION_EVENT_ACTION_MOVE}});
742+ }
743+
597744} // namespace android
0 commit comments