Skip to content

Commit 34313a3

Browse files
Treehugger RobotAndroid (Google) Code Review
authored andcommitted
Merge "Add a unit test for receiving 'finish' message after channel close" into main
2 parents 7238239 + 8f3af08 commit 34313a3

4 files changed

Lines changed: 173 additions & 10 deletions

File tree

include/input/InputTransport.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,4 +454,6 @@ class InputPublisher {
454454
InputVerifier mInputVerifier;
455455
};
456456

457+
std::ostream& operator<<(std::ostream& out, const InputMessage& msg);
458+
457459
} // namespace android

libs/input/InputConsumerNoResampling.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,6 @@ InputMessage createTimelineMessage(int32_t inputEventId, nsecs_t gpuCompletedTim
171171
return msg;
172172
}
173173

174-
std::ostream& operator<<(std::ostream& out, const InputMessage& msg) {
175-
out << ftl::enum_string(msg.header.type);
176-
return out;
177-
}
178-
179174
} // namespace
180175

181176
// --- InputConsumerNoResampling ---

libs/input/InputTransport.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -436,16 +436,29 @@ android::base::Result<InputMessage> InputChannel::receiveMessage() {
436436
if (error == EAGAIN || error == EWOULDBLOCK) {
437437
return android::base::Error(WOULD_BLOCK);
438438
}
439-
if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED) {
440-
return android::base::Error(DEAD_OBJECT);
439+
if (error == EPIPE) {
440+
return android::base::ResultError("Got EPIPE", DEAD_OBJECT);
441+
}
442+
if (error == ENOTCONN) {
443+
return android::base::ResultError("Got ENOTCONN", DEAD_OBJECT);
444+
}
445+
if (error == ECONNREFUSED) {
446+
return android::base::ResultError("Got ECONNREFUSED", DEAD_OBJECT);
447+
}
448+
if (error == ECONNRESET) {
449+
// This means that the client has closed the channel while there was
450+
// still some data in the buffer. In most cases, subsequent reads
451+
// would result in more data. However, that is not guaranteed, so we
452+
// should not return WOULD_BLOCK here to try again.
453+
return android::base::ResultError("Got ECONNRESET", DEAD_OBJECT);
441454
}
442455
return android::base::Error(-error);
443456
}
444457

445458
if (nRead == 0) { // check for EOF
446-
ALOGD_IF(DEBUG_CHANNEL_MESSAGES,
447-
"channel '%s' ~ receive message failed because peer was closed", name.c_str());
448-
return android::base::Error(DEAD_OBJECT);
459+
LOG_IF(INFO, DEBUG_CHANNEL_MESSAGES)
460+
<< "channel '" << name << "' ~ receive message failed because peer was closed";
461+
return android::base::ResultError("::recv returned 0", DEAD_OBJECT);
449462
}
450463

451464
if (!msg.isValid(nRead)) {
@@ -766,4 +779,9 @@ android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveC
766779
return android::base::Error(UNKNOWN_ERROR);
767780
}
768781

782+
std::ostream& operator<<(std::ostream& out, const InputMessage& msg) {
783+
out << ftl::enum_string(msg.header.type);
784+
return out;
785+
}
786+
769787
} // namespace android

libs/input/tests/InputChannel_test.cpp

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <time.h>
2121
#include <errno.h>
2222

23+
#include <android-base/logging.h>
2324
#include <binder/Binder.h>
2425
#include <binder/Parcel.h>
2526
#include <gtest/gtest.h>
@@ -43,6 +44,39 @@ bool operator==(const InputChannel& left, const InputChannel& right) {
4344
return left.getName() == right.getName() &&
4445
left.getConnectionToken() == right.getConnectionToken() && lhs.st_ino == rhs.st_ino;
4546
}
47+
48+
/**
49+
* Read a message from the provided channel. Read will continue until there's data, so only call
50+
* this if there's data in the channel, or it's closed. If there's no data, this will loop forever.
51+
*/
52+
android::base::Result<InputMessage> readMessage(InputChannel& channel) {
53+
while (true) {
54+
// Keep reading until we get something other than 'WOULD_BLOCK'
55+
android::base::Result<InputMessage> result = channel.receiveMessage();
56+
if (!result.ok() && result.error().code() == WOULD_BLOCK) {
57+
// The data is not available yet.
58+
continue; // try again
59+
}
60+
return result;
61+
}
62+
}
63+
64+
InputMessage createFinishedMessage(uint32_t seq) {
65+
InputMessage finish{};
66+
finish.header.type = InputMessage::Type::FINISHED;
67+
finish.header.seq = seq;
68+
finish.body.finished.handled = true;
69+
return finish;
70+
}
71+
72+
InputMessage createKeyMessage(uint32_t seq) {
73+
InputMessage key{};
74+
key.header.type = InputMessage::Type::KEY;
75+
key.header.seq = seq;
76+
key.body.key.action = AKEY_EVENT_ACTION_DOWN;
77+
return key;
78+
}
79+
4680
} // namespace
4781

4882
class InputChannelTest : public testing::Test {
@@ -227,6 +261,120 @@ TEST_F(InputChannelTest, SendAndReceive_MotionClassification) {
227261
}
228262
}
229263

264+
/**
265+
* In this test, server writes 3 key events to the client. The client, upon receiving the first key,
266+
* sends a "finished" signal back to server, and then closes the fd.
267+
*
268+
* Next, we check what the server receives.
269+
*
270+
* In most cases, the server will receive the finish event, and then an 'fd closed' event.
271+
*
272+
* However, sometimes, the 'finish' event will not be delivered to the server. This is communicated
273+
* to the server via 'ECONNRESET', which the InputChannel converts into DEAD_OBJECT.
274+
*
275+
* The server needs to be aware of this behaviour and correctly clean up any state associated with
276+
* the client, even if the client did not end up finishing some of the messages.
277+
*
278+
* This test is written to expose a behaviour on the linux side - occasionally, the
279+
* last events written to the fd by the consumer are not delivered to the server.
280+
*
281+
* When tested on 2025 hardware, ECONNRESET was received approximately 1 out of 40 tries.
282+
* In vast majority (~ 29999 / 30000) of cases, after receiving ECONNRESET, the server could still
283+
* read the client data after receiving ECONNRESET.
284+
*/
285+
TEST_F(InputChannelTest, ReceiveAfterCloseMultiThreaded) {
286+
std::unique_ptr<InputChannel> serverChannel, clientChannel;
287+
status_t result =
288+
InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel);
289+
ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
290+
291+
// Sender / publisher: publish 3 keys
292+
InputMessage key1 = createKeyMessage(/*seq=*/1);
293+
serverChannel->sendMessage(&key1);
294+
// The client should close the fd after it reads this one, but we will send 2 more here.
295+
InputMessage key2 = createKeyMessage(/*seq=*/2);
296+
serverChannel->sendMessage(&key2);
297+
InputMessage key3 = createKeyMessage(/*seq=*/3);
298+
serverChannel->sendMessage(&key3);
299+
300+
std::thread consumer = std::thread([clientChannel = std::move(clientChannel)]() mutable {
301+
// Read the first key
302+
android::base::Result<InputMessage> firstKey = readMessage(*clientChannel);
303+
if (!firstKey.ok()) {
304+
FAIL() << "Did not receive the first key";
305+
}
306+
307+
// Send finish
308+
const InputMessage finish = createFinishedMessage(firstKey->header.seq);
309+
clientChannel->sendMessage(&finish);
310+
// Now close the fd
311+
clientChannel.reset();
312+
});
313+
314+
// Now try to read the finish message, even though client closed the fd
315+
android::base::Result<InputMessage> response = readMessage(*serverChannel);
316+
consumer.join();
317+
if (response.ok()) {
318+
ASSERT_EQ(response->header.type, InputMessage::Type::FINISHED);
319+
} else {
320+
// It's possible that after the client closes the fd, server will receive ECONNRESET.
321+
// In those situations, this error code will be translated into DEAD_OBJECT by the
322+
// InputChannel.
323+
ASSERT_EQ(response.error().code(), DEAD_OBJECT);
324+
// In most cases, subsequent attempts to read the client channel at this
325+
// point would succeed. However, for simplicity, we exit here (since
326+
// it's not guaranteed).
327+
return;
328+
}
329+
330+
// There should not be any more events from the client, since the client closed fd after the
331+
// first key.
332+
android::base::Result<InputMessage> noEvent = serverChannel->receiveMessage();
333+
ASSERT_FALSE(noEvent.ok()) << "Got event " << *noEvent;
334+
}
335+
336+
/**
337+
* Similar test as above, but single-threaded.
338+
*/
339+
TEST_F(InputChannelTest, ReceiveAfterCloseSingleThreaded) {
340+
std::unique_ptr<InputChannel> serverChannel, clientChannel;
341+
status_t result =
342+
InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel);
343+
ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
344+
345+
// Sender / publisher: publish 3 keys
346+
InputMessage key1 = createKeyMessage(/*seq=*/1);
347+
serverChannel->sendMessage(&key1);
348+
// The client should close the fd after it reads this one, but we will send 2 more here.
349+
InputMessage key2 = createKeyMessage(/*seq=*/2);
350+
serverChannel->sendMessage(&key2);
351+
InputMessage key3 = createKeyMessage(/*seq=*/3);
352+
serverChannel->sendMessage(&key3);
353+
354+
// Read the first key
355+
android::base::Result<InputMessage> firstKey = readMessage(*clientChannel);
356+
if (!firstKey.ok()) {
357+
FAIL() << "Did not receive the first key";
358+
}
359+
360+
// Send finish
361+
const InputMessage finish = createFinishedMessage(firstKey->header.seq);
362+
clientChannel->sendMessage(&finish);
363+
// Now close the fd
364+
clientChannel.reset();
365+
366+
// Now try to read the finish message, even though client closed the fd
367+
android::base::Result<InputMessage> response = readMessage(*serverChannel);
368+
ASSERT_FALSE(response.ok());
369+
ASSERT_EQ(response.error().code(), DEAD_OBJECT);
370+
371+
// We can still read the finish event (but in practice, the expectation is that the server will
372+
// not be doing this after getting DEAD_OBJECT).
373+
android::base::Result<InputMessage> finishEvent = serverChannel->receiveMessage();
374+
ASSERT_TRUE(finishEvent.ok());
375+
ASSERT_EQ(finishEvent->header.type, InputMessage::Type::FINISHED);
376+
}
377+
230378
TEST_F(InputChannelTest, DuplicateChannelAndAssertEqual) {
231379
std::unique_ptr<InputChannel> serverChannel, clientChannel;
232380

0 commit comments

Comments
 (0)