Fix batching logic in InputConsumerNoResampling.cpp
Fixed batching logic in InputConsumerNoResampling.cpp because it was
violating resampling frameTime preconditions.
Bug: 297226446
Flag: EXEMPT bugfix
Test: TEST=libinput_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST --gtest_filter="InputConsumerTest*"
Change-Id: I1b658d5b9ac7a56a8a3917a49c2b97b60641c0d5
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index eb41918..de55828 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -37,6 +37,8 @@
namespace {
+using std::chrono::nanoseconds;
+
/**
* Log debug messages relating to the consumer end of the transport channel.
* Enable this via "adb shell setprop log.tag.InputTransportConsumer DEBUG" (requires restart)
@@ -485,7 +487,12 @@
std::queue<InputMessage>& messages) {
std::unique_ptr<MotionEvent> motionEvent;
std::optional<uint32_t> firstSeqForBatch;
- while (!messages.empty() && !(messages.front().body.motion.eventTime > frameTime)) {
+ const nanoseconds resampleLatency =
+ (mResampler != nullptr) ? mResampler->getResampleLatency() : nanoseconds{0};
+ const nanoseconds adjustedFrameTime = nanoseconds{frameTime} - resampleLatency;
+
+ while (!messages.empty() &&
+ (messages.front().body.motion.eventTime <= adjustedFrameTime.count())) {
if (motionEvent == nullptr) {
motionEvent = createMotionEvent(messages.front());
firstSeqForBatch = messages.front().header.seq;
@@ -504,8 +511,7 @@
if (!messages.empty()) {
futureSample = &messages.front();
}
- mResampler->resampleMotionEvent(static_cast<std::chrono::nanoseconds>(frameTime),
- *motionEvent, futureSample);
+ mResampler->resampleMotionEvent(nanoseconds{frameTime}, *motionEvent, futureSample);
}
return std::make_pair(std::move(motionEvent), firstSeqForBatch);
}
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
index b535ff4..51fadf8 100644
--- a/libs/input/Resampler.cpp
+++ b/libs/input/Resampler.cpp
@@ -241,6 +241,10 @@
motionEvent.getId());
}
+nanoseconds LegacyResampler::getResampleLatency() const {
+ return RESAMPLE_LATENCY;
+}
+
void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& motionEvent,
const InputMessage* futureSample) {
if (mPreviousDeviceId && *mPreviousDeviceId != motionEvent.getDeviceId()) {
diff --git a/libs/input/tests/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp
index c30f243..55be453 100644
--- a/libs/input/tests/InputConsumer_test.cpp
+++ b/libs/input/tests/InputConsumer_test.cpp
@@ -30,14 +30,21 @@
namespace android {
+namespace {
+
+using std::chrono::nanoseconds;
+
+} // namespace
+
class InputConsumerTest : public testing::Test, public InputConsumerCallbacks {
protected:
InputConsumerTest()
: mClientTestChannel{std::make_shared<TestInputChannel>("TestChannel")},
mTestLooper{std::make_shared<TestLooper>()} {
Looper::setForThread(mTestLooper->getLooper());
- mConsumer = std::make_unique<InputConsumerNoResampling>(mClientTestChannel, mTestLooper,
- *this, /*resampler=*/nullptr);
+ mConsumer =
+ std::make_unique<InputConsumerNoResampling>(mClientTestChannel, mTestLooper, *this,
+ std::make_unique<LegacyResampler>());
}
void assertOnBatchedInputEventPendingWasCalled();
@@ -54,7 +61,7 @@
BlockingQueue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents;
private:
- size_t onBatchedInputEventPendingInvocationCount{0};
+ size_t mOnBatchedInputEventPendingInvocationCount{0};
// InputConsumerCallbacks interface
void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override {
@@ -69,7 +76,7 @@
if (!mConsumer->probablyHasInput()) {
ADD_FAILURE() << "should deterministically have input because there is a batch";
}
- ++onBatchedInputEventPendingInvocationCount;
+ ++mOnBatchedInputEventPendingInvocationCount;
};
void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override {
mFocusEvents.push(std::move(event));
@@ -90,18 +97,24 @@
};
void InputConsumerTest::assertOnBatchedInputEventPendingWasCalled() {
- ASSERT_GT(onBatchedInputEventPendingInvocationCount, 0UL)
+ ASSERT_GT(mOnBatchedInputEventPendingInvocationCount, 0UL)
<< "onBatchedInputEventPending has not been called.";
- --onBatchedInputEventPendingInvocationCount;
+ --mOnBatchedInputEventPendingInvocationCount;
}
TEST_F(InputConsumerTest, MessageStreamBatchedInMotionEvent) {
- mClientTestChannel->enqueueMessage(
- InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}.build());
- mClientTestChannel->enqueueMessage(
- InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}.build());
- mClientTestChannel->enqueueMessage(
- InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}.build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
+ .eventTime(nanoseconds{0ms}.count())
+ .action(AMOTION_EVENT_ACTION_DOWN)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
+ .eventTime(nanoseconds{5ms}.count())
+ .action(AMOTION_EVENT_ACTION_MOVE)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
+ .eventTime(nanoseconds{10ms}.count())
+ .action(AMOTION_EVENT_ACTION_MOVE)
+ .build());
mClientTestChannel->assertNoSentMessages();
@@ -111,13 +124,62 @@
mConsumer->consumeBatchedInputEvents(std::nullopt);
- std::unique_ptr<MotionEvent> batchedMotionEvent = mMotionEvents.pop();
- ASSERT_NE(batchedMotionEvent, nullptr);
+ std::unique_ptr<MotionEvent> downMotionEvent = mMotionEvents.pop();
+ ASSERT_NE(downMotionEvent, nullptr);
+
+ std::unique_ptr<MotionEvent> moveMotionEvent = mMotionEvents.pop();
+ ASSERT_NE(moveMotionEvent, nullptr);
+ EXPECT_EQ(moveMotionEvent->getHistorySize() + 1, 3UL);
mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+}
- EXPECT_EQ(batchedMotionEvent->getHistorySize() + 1, 3UL);
+TEST_F(InputConsumerTest, LastBatchedSampleIsLessThanResampleTime) {
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
+ .eventTime(nanoseconds{0ms}.count())
+ .action(AMOTION_EVENT_ACTION_DOWN)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
+ .eventTime(nanoseconds{5ms}.count())
+ .action(AMOTION_EVENT_ACTION_MOVE)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
+ .eventTime(nanoseconds{10ms}.count())
+ .action(AMOTION_EVENT_ACTION_MOVE)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/3}
+ .eventTime(nanoseconds{15ms}.count())
+ .action(AMOTION_EVENT_ACTION_MOVE)
+ .build());
+
+ mClientTestChannel->assertNoSentMessages();
+
+ mTestLooper->invokeCallback(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT);
+
+ assertOnBatchedInputEventPendingWasCalled();
+
+ mConsumer->consumeBatchedInputEvents(16'000'000 /*ns*/);
+
+ std::unique_ptr<MotionEvent> downMotionEvent = mMotionEvents.pop();
+ ASSERT_NE(downMotionEvent, nullptr);
+
+ std::unique_ptr<MotionEvent> moveMotionEvent = mMotionEvents.pop();
+ ASSERT_NE(moveMotionEvent, nullptr);
+ const size_t numSamples = moveMotionEvent->getHistorySize() + 1;
+ EXPECT_LT(moveMotionEvent->getHistoricalEventTime(numSamples - 2),
+ moveMotionEvent->getEventTime());
+
+ // Consume all remaining events before ending the test. Otherwise, the smart pointer that owns
+ // consumer is set to null before destroying consumer. This leads to a member function call on a
+ // null object.
+ // TODO(b/332613662): Remove this workaround.
+ mConsumer->consumeBatchedInputEvents(std::nullopt);
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/0, true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, true);
}
} // namespace android