Skip to content

Commit 7db45d5

Browse files
Treehugger RobotAndroid (Google) Code Review
authored andcommitted
Merge "Do not invoke callbacks when InputConsumer is destroyed" into main
2 parents e4b0e51 + bc72309 commit 7db45d5

2 files changed

Lines changed: 79 additions & 16 deletions

File tree

libs/input/InputConsumerNoResampling.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,29 @@ InputConsumerNoResampling::InputConsumerNoResampling(
193193

194194
InputConsumerNoResampling::~InputConsumerNoResampling() {
195195
ensureCalledOnLooperThread(__func__);
196-
consumeBatchedInputEvents(/*requestedFrameTime=*/std::nullopt);
197196
while (!mOutboundQueue.empty()) {
198197
processOutboundEvents();
199198
// This is our last chance to ack the events. If we don't ack them here, we will get an ANR,
200199
// so keep trying to send the events as long as they are present in the queue.
201200
}
201+
202202
setFdEvents(0);
203+
// If there are any remaining unread batches, send an ack for them and don't deliver
204+
// them to callbacks.
205+
for (auto& [_, batches] : mBatches) {
206+
while (!batches.empty()) {
207+
finishInputEvent(batches.front().header.seq, /*handled=*/false);
208+
batches.pop();
209+
}
210+
}
211+
// However, it is still up to the app to finish any events that have already been delivered
212+
// to the callbacks. If we wanted to change that behaviour and auto-finish all unfinished events
213+
// that were already sent to callbacks, we could potentially loop through "mConsumeTimes"
214+
// instead. We can't use "mBatchedSequenceNumbers" for this purpose, because it only contains
215+
// batchable (i.e., ACTION_MOVE) events that were sent to the callbacks.
216+
const size_t unfinishedEvents = mConsumeTimes.size();
217+
LOG_IF(INFO, unfinishedEvents != 0)
218+
<< getName() << " has " << unfinishedEvents << " unfinished event(s)";
203219
}
204220

205221
int InputConsumerNoResampling::handleReceiveCallback(int events) {

libs/input/tests/InputConsumer_test.cpp

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -112,18 +112,23 @@ class InputConsumerTest : public testing::Test, public InputConsumerCallbacks {
112112
std::queue<std::unique_ptr<DragEvent>> mDragEvents;
113113
std::queue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents;
114114

115+
// Whether or not to automatically call "finish" whenever a motion event is received.
116+
bool mShouldFinishMotions{true};
117+
115118
private:
116119
uint32_t mLastSeq{0};
117120
size_t mOnBatchedInputEventPendingInvocationCount{0};
118121

119122
// InputConsumerCallbacks interface
120123
void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override {
121124
mKeyEvents.push(std::move(event));
122-
mConsumer->finishInputEvent(seq, true);
125+
mConsumer->finishInputEvent(seq, /*handled=*/true);
123126
}
124127
void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override {
125128
mMotionEvents.push(std::move(event));
126-
mConsumer->finishInputEvent(seq, true);
129+
if (mShouldFinishMotions) {
130+
mConsumer->finishInputEvent(seq, /*handled=*/true);
131+
}
127132
}
128133
void onBatchedInputEventPending(int32_t pendingBatchSource) override {
129134
if (!mConsumer->probablyHasInput()) {
@@ -133,19 +138,19 @@ class InputConsumerTest : public testing::Test, public InputConsumerCallbacks {
133138
};
134139
void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override {
135140
mFocusEvents.push(std::move(event));
136-
mConsumer->finishInputEvent(seq, true);
141+
mConsumer->finishInputEvent(seq, /*handled=*/true);
137142
};
138143
void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override {
139144
mCaptureEvents.push(std::move(event));
140-
mConsumer->finishInputEvent(seq, true);
145+
mConsumer->finishInputEvent(seq, /*handled=*/true);
141146
};
142147
void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override {
143148
mDragEvents.push(std::move(event));
144-
mConsumer->finishInputEvent(seq, true);
149+
mConsumer->finishInputEvent(seq, /*handled=*/true);
145150
}
146151
void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override {
147152
mTouchModeEvents.push(std::move(event));
148-
mConsumer->finishInputEvent(seq, true);
153+
mConsumer->finishInputEvent(seq, /*handled=*/true);
149154
};
150155
};
151156

@@ -231,16 +236,58 @@ TEST_F(InputConsumerTest, LastBatchedSampleIsLessThanResampleTime) {
231236
EXPECT_LT(moveMotionEvent->getHistoricalEventTime(numSamples - 2),
232237
moveMotionEvent->getEventTime());
233238

234-
// Consume all remaining events before ending the test. Otherwise, the smart pointer that owns
235-
// consumer is set to null before destroying consumer. This leads to a member function call on a
236-
// null object.
237-
// TODO(b/332613662): Remove this workaround.
238-
mConsumer->consumeBatchedInputEvents(std::nullopt);
239+
mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
240+
mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
241+
mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
242+
// The event with seq=3 remains unconsumed, and therefore finish will not be called for it until
243+
// after the consumer is destroyed.
244+
mConsumer.reset();
245+
mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/false);
246+
mClientTestChannel->assertNoSentMessages();
247+
}
248+
249+
/**
250+
* During normal operation, the user of InputConsumer (callbacks) is expected to call "finish"
251+
* for each input event received in InputConsumerCallbacks.
252+
* If the InputConsumer is destroyed, the events that were already sent to the callbacks will not
253+
* be finished automatically.
254+
*/
255+
TEST_F(InputConsumerTest, UnhandledEventsNotFinishedInDestructor) {
256+
mClientTestChannel->enqueueMessage(
257+
InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}.action(ACTION_DOWN).build());
258+
mClientTestChannel->enqueueMessage(
259+
InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}.action(ACTION_MOVE).build());
260+
mShouldFinishMotions = false;
261+
invokeLooperCallback();
262+
assertOnBatchedInputEventPendingWasCalled();
263+
assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
264+
mClientTestChannel->assertNoSentMessages();
265+
// The "finishInputEvent" was not called by the InputConsumerCallbacks.
266+
// Now, destroy the consumer and check that the "finish" was not called automatically for the
267+
// DOWN event, but was called for the undelivered MOVE event.
268+
mConsumer.reset();
269+
mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/false);
270+
mClientTestChannel->assertNoSentMessages();
271+
}
239272

240-
mClientTestChannel->assertFinishMessage(/*seq=*/0, true);
241-
mClientTestChannel->assertFinishMessage(/*seq=*/1, true);
242-
mClientTestChannel->assertFinishMessage(/*seq=*/2, true);
243-
mClientTestChannel->assertFinishMessage(/*seq=*/3, true);
273+
/**
274+
* Send an event to the InputConsumer, but do not invoke "consumeBatchedInputEvents", thus leaving
275+
* the input event unconsumed by the callbacks. Ensure that no crash occurs when the consumer is
276+
* destroyed.
277+
* This test is similar to the one above, but here we are calling "finish"
278+
* automatically for any event received in the callbacks.
279+
*/
280+
TEST_F(InputConsumerTest, UnconsumedEventDoesNotCauseACrash) {
281+
mClientTestChannel->enqueueMessage(
282+
InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}.action(ACTION_DOWN).build());
283+
invokeLooperCallback();
284+
assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
285+
mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
286+
mClientTestChannel->enqueueMessage(
287+
InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}.action(ACTION_MOVE).build());
288+
invokeLooperCallback();
289+
mConsumer.reset();
290+
mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/false);
244291
}
245292

246293
TEST_F(InputConsumerTest, BatchedEventsMultiDeviceConsumption) {

0 commit comments

Comments
 (0)