Skip to content

Commit bc72309

Browse files
committed
Do not invoke callbacks when InputConsumer is destroyed
Before this CL, the InputConsumer would try to finish all of the pending events that haven't been received by the app by forcefully consuming everything in the destructor. Unfortunately, this leads to the following crash: the callbacks (app) is storing the consumer inside unique_ptr. When the destructor of consumer runs, the unique_ptr will first assign the raw pointer to nullptr and then run the destructor. Next, inside the destructor, the callbacks are invoked, and the app may try to finish an input event that it receives. However, to finish, app needs to call back into the consumer, which is being destroyed. Since the only way the app can talk to the consumer is via unique_ptr, that pointer has already been set to nullptr, and this causes an NPE. To fix this, we will no longer be invoking the callbacks when the consumer destructor is invoked (this is done in this CL). The logic is as follows: - All events that have already been delivered to the app should still be finished by the app (this piece is debatable. we can fix this later if this becomes a problem). - Events that are batched, and not yet delivered to the app, will be automatically finished by the consumer, without sending them to the app. Since the app is destroying the consumer, it's unlikely that it cares about handling these events, anyways. If we want to finish _all_ pending messages (the messages that app hasn't yet called "finish" on), then we should probably iterate through mConsumeTimes, which is the only struct that keeps track of _all_ events that have been sent to the app, and not yet finished. However, to do this, we would need to rename this data structure, since it will no longer be solely used for keeping track of consume times, but also for "unprocessed events" that were read from the channel. Bug: 332613662 Flag: EXEMPT refactor Test: TEST=libinput_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST Change-Id: I9b0128291f6197f099cb9e3c305f9717c0198c90
1 parent ccd7147 commit bc72309

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)