Skip to content

Commit bad8a9e

Browse files
Treehugger RobotAndroid (Google) Code Review
authored andcommitted
Merge "Remove callback at the end of consumer destructor" into main
2 parents 2eb60a8 + 362fa22 commit bad8a9e

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)