Loading include/input/InputConsumerNoResampling.h +1 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ namespace android { /** * An interface to receive batched input events. Even if you don't want batching, you still have to * use this interface, and some of the events will be batched if your implementation is slow to * handle the incoming input. * handle the incoming input. The events received by these callbacks are never null. */ class InputConsumerCallbacks { public: Loading libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp +124 −1 Original line number Diff line number Diff line Loading @@ -52,7 +52,7 @@ struct PublishMotionArgs { const int32_t action; const nsecs_t downTime; const uint32_t seq; const int32_t eventId; int32_t eventId; const int32_t deviceId = 1; const uint32_t source = AINPUT_SOURCE_TOUCHSCREEN; const ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT; Loading Loading @@ -291,6 +291,7 @@ protected: void publishAndConsumeKeyEvent(); void publishAndConsumeMotionStream(); void publishAndConsumeMotionDown(nsecs_t downTime); void publishAndConsumeSinglePointerMultipleSamples(const size_t nSamples); void publishAndConsumeBatchedMotionMove(nsecs_t downTime); void publishAndConsumeFocusEvent(); void publishAndConsumeCaptureEvent(); Loading @@ -298,6 +299,7 @@ protected: void publishAndConsumeTouchModeEvent(); void publishAndConsumeMotionEvent(int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers); void TearDown() override { // Destroy the consumer, flushing any of the pending ack's. sendMessage(LooperMessage::DESTROY_CONSUMER); Loading Loading @@ -519,6 +521,123 @@ void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeMotionDown(nsec {Pointer{.id = 0, .x = 20, .y = 30}}); } /* * Decompose a potential multi-sampled MotionEvent into multiple MotionEvents * with a single sample. */ std::vector<MotionEvent> splitBatchedMotionEvent(const MotionEvent& batchedMotionEvent) { std::vector<MotionEvent> singleMotionEvents; const size_t batchSize = batchedMotionEvent.getHistorySize() + 1; for (size_t i = 0; i < batchSize; ++i) { MotionEvent singleMotionEvent; singleMotionEvent .initialize(batchedMotionEvent.getId(), batchedMotionEvent.getDeviceId(), batchedMotionEvent.getSource(), batchedMotionEvent.getDisplayId(), batchedMotionEvent.getHmac(), batchedMotionEvent.getAction(), batchedMotionEvent.getActionButton(), batchedMotionEvent.getFlags(), batchedMotionEvent.getEdgeFlags(), batchedMotionEvent.getMetaState(), batchedMotionEvent.getButtonState(), batchedMotionEvent.getClassification(), batchedMotionEvent.getTransform(), batchedMotionEvent.getXPrecision(), batchedMotionEvent.getYPrecision(), batchedMotionEvent.getRawXCursorPosition(), batchedMotionEvent.getRawYCursorPosition(), batchedMotionEvent.getRawTransform(), batchedMotionEvent.getDownTime(), batchedMotionEvent.getHistoricalEventTime(/*historicalIndex=*/i), batchedMotionEvent.getPointerCount(), batchedMotionEvent.getPointerProperties(), (batchedMotionEvent.getSamplePointerCoords() + i)); singleMotionEvents.push_back(singleMotionEvent); } return singleMotionEvents; } /* * Simulates a single pointer touching the screen and leaving it there for a period of time. * Publishes a DOWN event and consumes it right away. Then, publishes a sequence of MOVE * samples for the same pointer, and waits until it has been consumed. Splits batched MotionEvents * into individual samples. Checks the consumed MotionEvents against the published ones. * This test is non-deterministic because it depends on the timing of arrival of events to the * socket. * * @param nSamples The number of MOVE samples to publish before attempting consumption. */ void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeSinglePointerMultipleSamples( const size_t nSamples) { const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC); const Pointer pointer(0, 20, 30); const PublishMotionArgs argsDown(AMOTION_EVENT_ACTION_DOWN, downTime, {pointer}, mSeq); const nsecs_t publishTimeOfDown = systemTime(SYSTEM_TIME_MONOTONIC); publishMotionEvent(*mPublisher, argsDown); // Consume the DOWN event. ASSERT_TRUE(mMotionEvents.popWithTimeout(TIMEOUT).has_value()); verifyFinishedSignal(*mPublisher, mSeq, publishTimeOfDown); std::vector<nsecs_t> publishTimes; std::vector<PublishMotionArgs> argsMoves; std::queue<uint32_t> publishedSequenceNumbers; // Block Looper to increase the chance of batching events { std::scoped_lock l(mLock); mLooperMayProceed = false; } sendMessage(LooperMessage::BLOCK_LOOPER); { std::unique_lock l(mLock); mNotifyLooperWaiting.wait(l, [this] { return mLooperIsBlocked; }); } uint32_t firstSampleId; for (size_t i = 0; i < nSamples; ++i) { publishedSequenceNumbers.push(++mSeq); PublishMotionArgs argsMove(AMOTION_EVENT_ACTION_MOVE, downTime, {pointer}, mSeq); // A batched MotionEvent only has a single event id, currently determined when the // MotionEvent is initialized. Therefore, to pass the eventId comparisons inside // verifyArgsEqualToEvent, we need to override the event id of the published args to match // the event id of the first sample inside the MotionEvent. if (i == 0) { firstSampleId = argsMove.eventId; } argsMove.eventId = firstSampleId; publishTimes.push_back(systemTime(SYSTEM_TIME_MONOTONIC)); argsMoves.push_back(argsMove); publishMotionEvent(*mPublisher, argsMove); } std::vector<MotionEvent> singleSampledMotionEvents; // Unblock Looper { std::scoped_lock l(mLock); mLooperMayProceed = true; } mNotifyLooperMayProceed.notify_all(); // We have no control over the socket behavior, so the consumer can receive // the motion as a batched event, or as a sequence of multiple single-sample MotionEvents (or a // mix of those) while (singleSampledMotionEvents.size() != nSamples) { const std::optional<std::unique_ptr<MotionEvent>> batchedMotionEvent = mMotionEvents.popWithTimeout(TIMEOUT); // The events received by these calls are never null std::vector<MotionEvent> splitMotionEvents = splitBatchedMotionEvent(**batchedMotionEvent); singleSampledMotionEvents.insert(singleSampledMotionEvents.end(), splitMotionEvents.begin(), splitMotionEvents.end()); } // Consumer can choose to finish events in any order. For simplicity, // we verify the events in sequence (since that is how the test is implemented). for (size_t i = 0; i < nSamples; ++i) { verifyArgsEqualToEvent(argsMoves[i], singleSampledMotionEvents[i]); verifyFinishedSignal(*mPublisher, publishedSequenceNumbers.front(), publishTimes[i]); publishedSequenceNumbers.pop(); } } void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeBatchedMotionMove( nsecs_t downTime) { uint32_t seq = mSeq++; Loading Loading @@ -814,4 +933,8 @@ TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishMultipleEvents_EndToEnd ASSERT_NO_FATAL_FAILURE(publishAndConsumeTouchModeEvent()); } TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishAndConsumeSinglePointer) { publishAndConsumeSinglePointerMultipleSamples(3); } } // namespace android Loading
include/input/InputConsumerNoResampling.h +1 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ namespace android { /** * An interface to receive batched input events. Even if you don't want batching, you still have to * use this interface, and some of the events will be batched if your implementation is slow to * handle the incoming input. * handle the incoming input. The events received by these callbacks are never null. */ class InputConsumerCallbacks { public: Loading
libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp +124 −1 Original line number Diff line number Diff line Loading @@ -52,7 +52,7 @@ struct PublishMotionArgs { const int32_t action; const nsecs_t downTime; const uint32_t seq; const int32_t eventId; int32_t eventId; const int32_t deviceId = 1; const uint32_t source = AINPUT_SOURCE_TOUCHSCREEN; const ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT; Loading Loading @@ -291,6 +291,7 @@ protected: void publishAndConsumeKeyEvent(); void publishAndConsumeMotionStream(); void publishAndConsumeMotionDown(nsecs_t downTime); void publishAndConsumeSinglePointerMultipleSamples(const size_t nSamples); void publishAndConsumeBatchedMotionMove(nsecs_t downTime); void publishAndConsumeFocusEvent(); void publishAndConsumeCaptureEvent(); Loading @@ -298,6 +299,7 @@ protected: void publishAndConsumeTouchModeEvent(); void publishAndConsumeMotionEvent(int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers); void TearDown() override { // Destroy the consumer, flushing any of the pending ack's. sendMessage(LooperMessage::DESTROY_CONSUMER); Loading Loading @@ -519,6 +521,123 @@ void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeMotionDown(nsec {Pointer{.id = 0, .x = 20, .y = 30}}); } /* * Decompose a potential multi-sampled MotionEvent into multiple MotionEvents * with a single sample. */ std::vector<MotionEvent> splitBatchedMotionEvent(const MotionEvent& batchedMotionEvent) { std::vector<MotionEvent> singleMotionEvents; const size_t batchSize = batchedMotionEvent.getHistorySize() + 1; for (size_t i = 0; i < batchSize; ++i) { MotionEvent singleMotionEvent; singleMotionEvent .initialize(batchedMotionEvent.getId(), batchedMotionEvent.getDeviceId(), batchedMotionEvent.getSource(), batchedMotionEvent.getDisplayId(), batchedMotionEvent.getHmac(), batchedMotionEvent.getAction(), batchedMotionEvent.getActionButton(), batchedMotionEvent.getFlags(), batchedMotionEvent.getEdgeFlags(), batchedMotionEvent.getMetaState(), batchedMotionEvent.getButtonState(), batchedMotionEvent.getClassification(), batchedMotionEvent.getTransform(), batchedMotionEvent.getXPrecision(), batchedMotionEvent.getYPrecision(), batchedMotionEvent.getRawXCursorPosition(), batchedMotionEvent.getRawYCursorPosition(), batchedMotionEvent.getRawTransform(), batchedMotionEvent.getDownTime(), batchedMotionEvent.getHistoricalEventTime(/*historicalIndex=*/i), batchedMotionEvent.getPointerCount(), batchedMotionEvent.getPointerProperties(), (batchedMotionEvent.getSamplePointerCoords() + i)); singleMotionEvents.push_back(singleMotionEvent); } return singleMotionEvents; } /* * Simulates a single pointer touching the screen and leaving it there for a period of time. * Publishes a DOWN event and consumes it right away. Then, publishes a sequence of MOVE * samples for the same pointer, and waits until it has been consumed. Splits batched MotionEvents * into individual samples. Checks the consumed MotionEvents against the published ones. * This test is non-deterministic because it depends on the timing of arrival of events to the * socket. * * @param nSamples The number of MOVE samples to publish before attempting consumption. */ void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeSinglePointerMultipleSamples( const size_t nSamples) { const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC); const Pointer pointer(0, 20, 30); const PublishMotionArgs argsDown(AMOTION_EVENT_ACTION_DOWN, downTime, {pointer}, mSeq); const nsecs_t publishTimeOfDown = systemTime(SYSTEM_TIME_MONOTONIC); publishMotionEvent(*mPublisher, argsDown); // Consume the DOWN event. ASSERT_TRUE(mMotionEvents.popWithTimeout(TIMEOUT).has_value()); verifyFinishedSignal(*mPublisher, mSeq, publishTimeOfDown); std::vector<nsecs_t> publishTimes; std::vector<PublishMotionArgs> argsMoves; std::queue<uint32_t> publishedSequenceNumbers; // Block Looper to increase the chance of batching events { std::scoped_lock l(mLock); mLooperMayProceed = false; } sendMessage(LooperMessage::BLOCK_LOOPER); { std::unique_lock l(mLock); mNotifyLooperWaiting.wait(l, [this] { return mLooperIsBlocked; }); } uint32_t firstSampleId; for (size_t i = 0; i < nSamples; ++i) { publishedSequenceNumbers.push(++mSeq); PublishMotionArgs argsMove(AMOTION_EVENT_ACTION_MOVE, downTime, {pointer}, mSeq); // A batched MotionEvent only has a single event id, currently determined when the // MotionEvent is initialized. Therefore, to pass the eventId comparisons inside // verifyArgsEqualToEvent, we need to override the event id of the published args to match // the event id of the first sample inside the MotionEvent. if (i == 0) { firstSampleId = argsMove.eventId; } argsMove.eventId = firstSampleId; publishTimes.push_back(systemTime(SYSTEM_TIME_MONOTONIC)); argsMoves.push_back(argsMove); publishMotionEvent(*mPublisher, argsMove); } std::vector<MotionEvent> singleSampledMotionEvents; // Unblock Looper { std::scoped_lock l(mLock); mLooperMayProceed = true; } mNotifyLooperMayProceed.notify_all(); // We have no control over the socket behavior, so the consumer can receive // the motion as a batched event, or as a sequence of multiple single-sample MotionEvents (or a // mix of those) while (singleSampledMotionEvents.size() != nSamples) { const std::optional<std::unique_ptr<MotionEvent>> batchedMotionEvent = mMotionEvents.popWithTimeout(TIMEOUT); // The events received by these calls are never null std::vector<MotionEvent> splitMotionEvents = splitBatchedMotionEvent(**batchedMotionEvent); singleSampledMotionEvents.insert(singleSampledMotionEvents.end(), splitMotionEvents.begin(), splitMotionEvents.end()); } // Consumer can choose to finish events in any order. For simplicity, // we verify the events in sequence (since that is how the test is implemented). for (size_t i = 0; i < nSamples; ++i) { verifyArgsEqualToEvent(argsMoves[i], singleSampledMotionEvents[i]); verifyFinishedSignal(*mPublisher, publishedSequenceNumbers.front(), publishTimes[i]); publishedSequenceNumbers.pop(); } } void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeBatchedMotionMove( nsecs_t downTime) { uint32_t seq = mSeq++; Loading Loading @@ -814,4 +933,8 @@ TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishMultipleEvents_EndToEnd ASSERT_NO_FATAL_FAILURE(publishAndConsumeTouchModeEvent()); } TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishAndConsumeSinglePointer) { publishAndConsumeSinglePointerMultipleSamples(3); } } // namespace android