Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 95d15e56 authored by Yifan Hong's avatar Yifan Hong
Browse files

binder: retry connect() on ECONNRESET for non-blocking sockets.

For non-blocking sockets, if connect() returns EAGAIN / EINPROGRESS, the
code calls getsockopt() to get the real error if connect() were called
with a blocking socket fd. If ECONNRESET is seen, also retry.

Test: binderRpcTest
Fixes: 197162885

Change-Id: I7c7b8cb105d0d334b75b883ffcff3b0c62337cf4
parent ab281ec6
Loading
Loading
Loading
Loading
+29 −27
Original line number Diff line number Diff line
@@ -484,16 +484,8 @@ status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr,
        }

        if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
            if (errno == ECONNRESET) {
                ALOGW("Connection reset on %s", addr.toString().c_str());
                continue;
            }
            if (errno != EAGAIN && errno != EINPROGRESS) {
                int savedErrno = errno;
                ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(),
                      strerror(savedErrno));
                return -savedErrno;
            }
            int connErrno = errno;
            if (connErrno == EAGAIN || connErrno == EINPROGRESS) {
                // For non-blocking sockets, connect() may return EAGAIN (for unix domain socket) or
                // EINPROGRESS (for others). Call poll() and getsockopt() to get the error.
                status_t pollStatus = mShutdownTrigger->triggerablePoll(serverFd, POLLOUT);
@@ -502,19 +494,29 @@ status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr,
                          statusToString(pollStatus).c_str());
                    return pollStatus;
                }
            int soError;
            socklen_t soErrorLen = sizeof(soError);
            int ret = getsockopt(serverFd.get(), SOL_SOCKET, SO_ERROR, &soError, &soErrorLen);
                // Set connErrno to the errno that connect() would have set if the fd were blocking.
                socklen_t connErrnoLen = sizeof(connErrno);
                int ret =
                        getsockopt(serverFd.get(), SOL_SOCKET, SO_ERROR, &connErrno, &connErrnoLen);
                if (ret == -1) {
                    int savedErrno = errno;
                ALOGE("Could not getsockopt() after connect() on non-blocking socket: %s",
                      strerror(savedErrno));
                    ALOGE("Could not getsockopt() after connect() on non-blocking socket: %s. "
                          "(Original error from connect() is: %s)",
                          strerror(savedErrno), strerror(connErrno));
                    return -savedErrno;
                }
            if (soError != 0) {
                ALOGE("After connect(), getsockopt() returns error for socket at %s: %s",
                      addr.toString().c_str(), strerror(soError));
                return -soError;
                // Retrieved the real connErrno as if connect() was called with a blocking socket
                // fd. Continue checking connErrno.
            }
            if (connErrno == ECONNRESET) {
                ALOGW("Connection reset on %s", addr.toString().c_str());
                continue;
            }
            // connErrno could be zero if getsockopt determines so. Hence zero-check again.
            if (connErrno != 0) {
                ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(),
                      strerror(connErrno));
                return -connErrno;
            }
        }
        LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());