Skip to content

Commit 362fa22

Browse files
committed
Remove callback at the end of consumer destructor
It turns out that 'finishInputEvent' was adding the fd back to the looper. This caused an NPE because we were calling 'finishInputEvent' at the end of the consumer destructor, thus leaving the callback in the looper. The looper would hit an NPE whenever the fd signaled after that. To fix this, we move the call to setFdEvents(0) to the very end of the destructor. This way, we can be sure that the last thing that executes is the removal of the fd from the Looper. There is also a possibility that fd is signaled during the destructor execution. Theoretically, this should be okay because the fd callbacks are processed on the same thread as the one where destructor runs. Therefore, the signals would only be processed after the destructor has completed, which means the callback would be removed before it gets the chance to execute. Bug: 332613662 Test: TEST=libinput_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST --gtest_filter="*InputConsumerTest*" Test: TEST=libutils_test; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST Flag: EXEMPT bugfix Change-Id: If5ac7a8eaf96e842d5d8e44008b9c1bff74e674e
1 parent 1c375bb commit 362fa22

3 files changed

Lines changed: 44 additions & 11 deletions

File tree

include/input/InputConsumerNoResampling.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ class InputConsumerNoResampling final {
141141
}
142142

143143
private:
144-
std::function<int(int events)> mCallback;
144+
const std::function<int(int events)> mCallback;
145145
};
146146
sp<LooperEventCallback> mCallback;
147147
/**

libs/input/InputConsumerNoResampling.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,6 @@ InputConsumerNoResampling::InputConsumerNoResampling(
193193

194194
InputConsumerNoResampling::~InputConsumerNoResampling() {
195195
ensureCalledOnLooperThread(__func__);
196-
while (!mOutboundQueue.empty()) {
197-
processOutboundEvents();
198-
// This is our last chance to ack the events. If we don't ack them here, we will get an ANR,
199-
// so keep trying to send the events as long as they are present in the queue.
200-
}
201-
202-
setFdEvents(0);
203196
// If there are any remaining unread batches, send an ack for them and don't deliver
204197
// them to callbacks.
205198
for (auto& [_, batches] : mBatches) {
@@ -208,6 +201,12 @@ InputConsumerNoResampling::~InputConsumerNoResampling() {
208201
batches.pop();
209202
}
210203
}
204+
205+
while (!mOutboundQueue.empty()) {
206+
processOutboundEvents();
207+
// This is our last chance to ack the events. If we don't ack them here, we will get an ANR,
208+
// so keep trying to send the events as long as they are present in the queue.
209+
}
211210
// However, it is still up to the app to finish any events that have already been delivered
212211
// to the callbacks. If we wanted to change that behaviour and auto-finish all unfinished events
213212
// that were already sent to callbacks, we could potentially loop through "mConsumeTimes"
@@ -216,6 +215,10 @@ InputConsumerNoResampling::~InputConsumerNoResampling() {
216215
const size_t unfinishedEvents = mConsumeTimes.size();
217216
LOG_IF(INFO, unfinishedEvents != 0)
218217
<< getName() << " has " << unfinishedEvents << " unfinished event(s)";
218+
// Remove the fd from epoll, so that Looper does not call 'handleReceiveCallback' anymore.
219+
// This must be done at the end of the destructor; otherwise, some of the other functions may
220+
// call 'setFdEvents' as a side-effect, thus adding the fd back to the epoll set of the looper.
221+
setFdEvents(0);
219222
}
220223

221224
int InputConsumerNoResampling::handleReceiveCallback(int events) {

libs/input/tests/InputConsumer_test.cpp

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,20 @@ class InputConsumerTest : public testing::Test, public InputConsumerCallbacks {
7070
[]() { return std::make_unique<LegacyResampler>(); });
7171
}
7272

73-
void invokeLooperCallback() const {
73+
bool invokeLooperCallback() const {
7474
sp<LooperCallback> callback;
75-
ASSERT_TRUE(mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr,
76-
/*events=*/nullptr, &callback, /*data=*/nullptr));
75+
const bool found =
76+
mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr,
77+
/*events=*/nullptr, &callback, /*data=*/nullptr);
78+
if (!found) {
79+
return false;
80+
}
81+
if (callback == nullptr) {
82+
LOG(FATAL) << "Looper has the fd of interest, but the callback is null!";
83+
return false;
84+
}
7785
callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr);
86+
return true;
7887
}
7988

8089
void assertOnBatchedInputEventPendingWasCalled() {
@@ -270,6 +279,27 @@ TEST_F(InputConsumerTest, UnhandledEventsNotFinishedInDestructor) {
270279
mClientTestChannel->assertNoSentMessages();
271280
}
272281

282+
/**
283+
* Check what happens when looper invokes callback after consumer has been destroyed.
284+
* This reproduces a crash where the LooperEventCallback was added back to the Looper during
285+
* destructor, thus allowing the looper callback to be invoked onto a null consumer object.
286+
*/
287+
TEST_F(InputConsumerTest, LooperCallbackInvokedAfterConsumerDestroyed) {
288+
mClientTestChannel->enqueueMessage(
289+
InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}.action(ACTION_DOWN).build());
290+
mClientTestChannel->enqueueMessage(
291+
InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}.action(ACTION_MOVE).build());
292+
ASSERT_TRUE(invokeLooperCallback());
293+
assertOnBatchedInputEventPendingWasCalled();
294+
assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
295+
mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
296+
297+
// Now, destroy the consumer and invoke the looper callback again after it's been destroyed.
298+
mConsumer.reset();
299+
mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/false);
300+
ASSERT_FALSE(invokeLooperCallback());
301+
}
302+
273303
/**
274304
* Send an event to the InputConsumer, but do not invoke "consumeBatchedInputEvents", thus leaving
275305
* the input event unconsumed by the callbacks. Ensure that no crash occurs when the consumer is

0 commit comments

Comments
 (0)