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

Commit d074fc41 authored by Alex Vakulenko's avatar Alex Vakulenko
Browse files

libpdx_uds: Improve client connection logic

Handle the case where the service created a socket file but not bound the
socket to it, or not listening for incoming requests yet. Also, if the service
has crashed and left the socket file behind, need to make sure we reconnect
once the service is restarted.

Bug: None
Test: `m -j32` succeeds, device boots and CubeSea app works on Sailfish
Change-Id: I2039cfca6faccd5d1d4b725e454075669484b880
parent f9f18b6b
Loading
Loading
Loading
Loading
+56 −13
Original line number Diff line number Diff line
@@ -6,10 +6,16 @@
#include <sys/un.h>
#include <unistd.h>

#include <chrono>
#include <thread>

#include <uds/channel_manager.h>
#include <uds/client_channel.h>
#include <uds/ipc_helper.h>

using std::chrono::duration_cast;
using std::chrono::steady_clock;

namespace android {
namespace pdx {
namespace uds {
@@ -41,13 +47,11 @@ std::unique_ptr<pdx::ClientChannelFactory> ClientChannelFactory::Create(

Status<std::unique_ptr<pdx::ClientChannel>> ClientChannelFactory::Connect(
    int64_t timeout_ms) const {
  auto status = WaitForEndpoint(endpoint_path_, timeout_ms);
  if (!status)
    return ErrorStatus(status.error());
  Status<void> status;

  LocalHandle socket_fd{socket(AF_UNIX, SOCK_STREAM, 0)};
  if (!socket_fd) {
    ALOGE("ClientChannelFactory::Connect: socket error %s", strerror(errno));
    ALOGE("ClientChannelFactory::Connect: socket error: %s", strerror(errno));
    return ErrorStatus(errno);
  }

@@ -56,16 +60,55 @@ Status<std::unique_ptr<pdx::ClientChannel>> ClientChannelFactory::Connect(
  strncpy(remote.sun_path, endpoint_path_.c_str(), sizeof(remote.sun_path));
  remote.sun_path[sizeof(remote.sun_path) - 1] = '\0';

  bool use_timeout = (timeout_ms >= 0);
  auto now = steady_clock::now();
  auto time_end = now + std::chrono::milliseconds{timeout_ms};

  bool connected = false;
  while (!connected) {
    int64_t timeout = -1;
    if (use_timeout) {
      auto remaining = time_end - now;
      timeout = duration_cast<std::chrono::milliseconds>(remaining).count();
      if (timeout < 0)
        return ErrorStatus(ETIMEDOUT);
    }
    ALOGD("ClientChannelFactory: Waiting for endpoint at %s", remote.sun_path);
    status = WaitForEndpoint(endpoint_path_, timeout);
    if (!status)
      return ErrorStatus(status.error());

    ALOGD("ClientChannelFactory: Connecting to %s", remote.sun_path);
    int ret = RETRY_EINTR(connect(
        socket_fd.Get(), reinterpret_cast<sockaddr*>(&remote), sizeof(remote)));
    if (ret == -1) {
      ALOGD("ClientChannelFactory: Connect error %d: %s", errno,
            strerror(errno));
      if (errno == ECONNREFUSED) {
        // Connection refused can be the result of connecting too early (the
        // service socket is created but not being listened to yet).
        ALOGD("ClientChannelFactory: Connection refused, waiting...");
        using namespace std::literals::chrono_literals;
        std::this_thread::sleep_for(100ms);
      } else if (errno != ENOENT && errno != ENOTDIR) {
        // ENOENT/ENOTDIR might mean that the socket file/directory containing
        // it has been just deleted. Try to wait for its creation and do not
        // return an error immediately.
        ALOGE(
        "ClientChannelFactory::Connect: Failed to initialize connection when "
        "connecting %s",
            "ClientChannelFactory::Connect: Failed to initialize connection "
            "when connecting: %s",
            strerror(errno));
        return ErrorStatus(errno);
      }
    } else {
      connected = true;
    }
    if (use_timeout)
      now = steady_clock::now();
  }  // while (!connected)

  ALOGD("ClientChannelFactory: Connected successfully to %s...",
        remote.sun_path);
  RequestHeader<BorrowedHandle> request;
  InitRequest(&request, opcodes::CHANNEL_OPEN, 0, 0, false);
  status = SendData(socket_fd.Get(), request);