diff --git a/adb/services.cpp b/adb/services.cpp index 75cbe5dd1..2eef1c265 100644 --- a/adb/services.cpp +++ b/adb/services.cpp @@ -49,6 +49,7 @@ #include "remount_service.h" #include "services.h" #include "shell_service.h" +#include "sysdeps.h" #include "transport.h" struct stinfo { @@ -369,12 +370,21 @@ static void wait_for_state(int fd, void* data) { std::string error = "unknown error"; const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : NULL; atransport* t = acquire_one_transport(sinfo->transport_type, serial, &is_ambiguous, &error); - if (t != nullptr && t->connection_state == sinfo->state) { SendOkay(fd); break; } else if (!is_ambiguous) { - adb_sleep_ms(1000); + adb_pollfd pfd = {.fd = fd, .events = POLLIN }; + int rc = adb_poll(&pfd, 1, 1000); + if (rc < 0) { + SendFail(fd, error); + break; + } else if (rc > 0 && (pfd.revents & POLLHUP) != 0) { + // The other end of the socket is closed, probably because the other side was + // terminated, bail out. + break; + } + // Try again... } else { SendFail(fd, error); diff --git a/adb/sysdeps_test.cpp b/adb/sysdeps_test.cpp index 253d62fc2..3904cc0b5 100644 --- a/adb/sysdeps_test.cpp +++ b/adb/sysdeps_test.cpp @@ -112,8 +112,12 @@ class sysdeps_poll : public ::testing::Test { } void TearDown() override { - ASSERT_EQ(0, adb_close(fds[0])); - ASSERT_EQ(0, adb_close(fds[1])); + if (fds[0] >= 0) { + ASSERT_EQ(0, adb_close(fds[0])); + } + if (fds[1] >= 0) { + ASSERT_EQ(0, adb_close(fds[1])); + } } }; @@ -190,3 +194,20 @@ TEST_F(sysdeps_poll, duplicate_fd) { EXPECT_EQ(POLLRDNORM, pfd[0].revents); EXPECT_EQ(POLLRDNORM, pfd[1].revents); } + +TEST_F(sysdeps_poll, disconnect) { + adb_pollfd pfd; + pfd.fd = fds[0]; + pfd.events = POLLIN; + + EXPECT_EQ(0, adb_poll(&pfd, 1, 0)); + EXPECT_EQ(0, pfd.revents); + + EXPECT_EQ(0, adb_close(fds[1])); + fds[1] = -1; + + EXPECT_EQ(1, adb_poll(&pfd, 1, 100)); + + // Linux returns POLLIN | POLLHUP, Windows returns just POLLHUP. + EXPECT_EQ(POLLHUP, pfd.revents & POLLHUP); +}