summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/input/Resampler.h6
-rw-r--r--libs/input/Resampler.cpp19
-rw-r--r--libs/input/tests/InputConsumerResampling_test.cpp55
3 files changed, 80 insertions, 0 deletions
diff --git a/include/input/Resampler.h b/include/input/Resampler.h
index f04dfde995..47519c2cfd 100644
--- a/include/input/Resampler.h
+++ b/include/input/Resampler.h
@@ -166,6 +166,12 @@ private:
*/
void overwriteStillPointers(MotionEvent& motionEvent, size_t sampleIndex) const;
+ /**
+ * Overwrites the pointer coordinates of a sample with event time older than
+ * that of mPreviousPrediction.
+ */
+ void overwriteOldPointers(MotionEvent& motionEvent, size_t sampleIndex) const;
+
inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent);
};
} // namespace android
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
index 8fe904f9a4..e2cc6fb174 100644
--- a/libs/input/Resampler.cpp
+++ b/libs/input/Resampler.cpp
@@ -271,6 +271,7 @@ void LegacyResampler::overwriteMotionEventSamples(MotionEvent& motionEvent) cons
const size_t numSamples = motionEvent.getHistorySize() + 1;
for (size_t sampleIndex = 0; sampleIndex < numSamples; ++sampleIndex) {
overwriteStillPointers(motionEvent, sampleIndex);
+ overwriteOldPointers(motionEvent, sampleIndex);
}
}
@@ -289,6 +290,24 @@ void LegacyResampler::overwriteStillPointers(MotionEvent& motionEvent, size_t sa
}
}
+void LegacyResampler::overwriteOldPointers(MotionEvent& motionEvent, size_t sampleIndex) const {
+ if (!mPreviousPrediction.has_value()) {
+ return;
+ }
+ if (nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)} <
+ mPreviousPrediction->eventTime) {
+ LOG_IF(INFO, debugResampling())
+ << "Motion event sample older than predicted sample. Overwriting event time from "
+ << motionEvent.getHistoricalEventTime(sampleIndex) << "ns to "
+ << mPreviousPrediction->eventTime.count() << "ns.";
+ for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount();
+ ++pointerIndex) {
+ setMotionEventPointerCoords(motionEvent, sampleIndex, pointerIndex,
+ mPreviousPrediction->pointers[pointerIndex].coords);
+ }
+ }
+}
+
void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& motionEvent,
const InputMessage* futureSample) {
const nanoseconds resampleTime = frameTime - RESAMPLE_LATENCY;
diff --git a/libs/input/tests/InputConsumerResampling_test.cpp b/libs/input/tests/InputConsumerResampling_test.cpp
index 85311afea9..883ca82fe0 100644
--- a/libs/input/tests/InputConsumerResampling_test.cpp
+++ b/libs/input/tests/InputConsumerResampling_test.cpp
@@ -511,4 +511,59 @@ TEST_F(InputConsumerResamplingTest, ResampledValueIsUsedForIdenticalCoordinates)
mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
}
+TEST_F(InputConsumerResamplingTest, OldEventReceivedAfterResampleOccurs) {
+ // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+ // InputEvent with a single action.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {0ms, {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent({InputEventEntry{0ms,
+ {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}},
+ AMOTION_EVENT_ACTION_DOWN}});
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {10ms, {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {20ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
+ assertReceivedMotionEvent(
+ {InputEventEntry{10ms,
+ {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{20ms,
+ {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{25ms,
+ {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ // Above, the resampled event is at 25ms rather than at 30 ms = 35ms - RESAMPLE_LATENCY
+ // because we are further bound by how far we can extrapolate by the "last time delta".
+ // That's 50% of (20 ms - 10ms) => 5ms. So we can't predict more than 5 ms into the future
+ // from the event at 20ms, which is why the resampled event is at t = 25 ms.
+
+ // We resampled the event to 25 ms. Now, an older 'real' event comes in.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {24ms, {Pointer{.id = 0, .x = 40.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{50ms}.count());
+ assertReceivedMotionEvent(
+ {InputEventEntry{24ms,
+ {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
+ InputEventEntry{26ms,
+ {Pointer{.id = 0, .x = 45.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}}); // resampled event, rewritten
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
+}
+
} // namespace android