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

Commit efb56835 authored by David Brazdil's avatar David Brazdil
Browse files

rpc_binder: Refactor C/Rust bindings to control server lifetime

The RpcServer C/Rust bindings have been written such that the calling
thread blocks until the server has returned. This is technically not
necessary because the server runs in a different thread anyway, and it
required a callback function to notify the caller that the server is
ready. This construct complicated the caller code.

Another limitation of this construct is that it is impossible for the
caller to shutdown the server when it's not needed anymore. The only way
of killing it is to kill either the thread or the process, neither of
which does proper cleanup.

To address this, refactor the C bindings to return an opaque handle for
the created RpcServer, and only destroy the object when
ARpcServer_shutdown(handle) is called. The caller can also invoke
ARpcServer_join(handle) to join the server thread.

The Rust bindings are modified accordingly - the handle is wrapped in
a RpcServer struct.

RunVsockRpcServerWithFactory and the corresponding Rust wrapper are left
unchanged. The lifetime of the factory reference is quite complex and
the only user of this function will be removed soon. When that happens,
we will remove RunVsockRpcServerWithFactory as well.

Test: atest -p packages/modules/Virtualization:avf-presubmit
Change-Id: I98fa0a97eb169de95ef8649c43f37a9c7c4cd96e
parent 3cc0776c
Loading
Loading
Loading
Loading
+28 −16
Original line number Diff line number Diff line
@@ -17,20 +17,41 @@
#pragma once

#include <sys/socket.h>
#include <stdint.h>

extern "C" {

struct AIBinder;
struct ARpcServer;

// Starts an RPC server on a given port and a given root IBinder object.
// This function sets up the server and joins before returning.
bool RunVsockRpcServer(AIBinder* service, unsigned int port);
// Returns an opaque handle to the running server instance, or null if the server
// could not be started.
[[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int port);

// Starts an RPC server on a given port and a given root IBinder object.
// This function sets up the server, calls readyCallback with a given param, and
// then joins before returning.
bool RunVsockRpcServerCallback(AIBinder* service, unsigned int port,
                               void (*readyCallback)(void* param), void* param);
// Starts a Unix domain RPC server with a given init-managed Unix domain `name`
// and a given root IBinder object.
// The socket should be created in init.rc with the same `name`.
// Returns an opaque handle to the running server instance, or null if the server
// could not be started.
[[nodiscard]] ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name);

// Runs ARpcServer_join() in a background thread. Immediately returns.
void ARpcServer_start(ARpcServer* server);

// Joins the thread of a running RpcServer instance. At any given point, there
// can only be one thread calling ARpcServer_join().
// If a client needs to actively terminate join, call ARpcServer_shutdown() in
// a separate thread.
void ARpcServer_join(ARpcServer* server);

// Shuts down any running ARpcServer_join().
void ARpcServer_shutdown(ARpcServer* server);

// Frees the ARpcServer handle and drops the reference count on the underlying
// RpcServer instance. The handle must not be reused afterwards.
// This automatically calls ARpcServer_shutdown().
void ARpcServer_free(ARpcServer* server);

// Starts an RPC server on a given port and a given root IBinder factory.
// RunVsockRpcServerWithFactory acts like RunVsockRpcServerCallback, but instead of
@@ -42,15 +63,6 @@ bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* c

AIBinder* VsockRpcClient(unsigned int cid, unsigned int port);

// Starts a Unix domain RPC server with a given init-managed Unix domain `name` and
// a given root IBinder object.
// The socket should be created in init.rc with the same `name`.
//
// This function sets up the server, calls readyCallback with a given param, and
// then joins before returning.
bool RunInitUnixDomainRpcServer(AIBinder* service, const char* name,
                                void (*readyCallback)(void* param), void* param);

// Gets the service via the RPC binder with Unix domain socket with the given
// Unix socket `name`.
// The final Unix domain socket path name is /dev/socket/`name`.
+53 −32
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@
 * limitations under the License.
 */

#include <binder_rpc_unstable.hpp>

#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <android/binder_libbinder.h>
@@ -25,23 +27,32 @@
using android::OK;
using android::RpcServer;
using android::RpcSession;
using android::sp;
using android::status_t;
using android::statusToString;
using android::base::unique_fd;

extern "C" {
// Opaque handle for RpcServer.
struct ARpcServer {};

void RunRpcServer(android::sp<RpcServer>& server, AIBinder* service,
                  void (*readyCallback)(void* param), void* param) {
    server->setRootObject(AIBinder_toPlatformBinder(service));
static sp<RpcServer> toRpcServer(ARpcServer* handle) {
    auto ref = reinterpret_cast<RpcServer*>(handle);
    return sp<RpcServer>::fromExisting(ref);
}

    if (readyCallback) readyCallback(param);
    server->join();
static ARpcServer* createRpcServerHandle(sp<RpcServer>& server) {
    auto ref = server.get();
    ref->incStrong(ref);
    return reinterpret_cast<ARpcServer*>(ref);
}

    // Shutdown any open sessions since server failed.
    (void)server->shutdown();
static void freeRpcServerHandle(ARpcServer* handle) {
    auto ref = reinterpret_cast<RpcServer*>(handle);
    ref->decStrong(ref);
}

extern "C" {

bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
                                  void* factoryContext, unsigned int port) {
    auto server = RpcServer::make();
@@ -64,47 +75,57 @@ bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* c
    return true;
}

bool RunVsockRpcServerCallback(AIBinder* service, unsigned int port,
                               void (*readyCallback)(void* param), void* param) {
ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int port) {
    auto server = RpcServer::make();
    if (status_t status = server->setupVsockServer(port); status != OK) {
        LOG(ERROR) << "Failed to set up vsock server with port " << port
                   << " error: " << statusToString(status).c_str();
        return false;
    }
    RunRpcServer(server, service, readyCallback, param);
    return true;
}

bool RunVsockRpcServer(AIBinder* service, unsigned int port) {
    return RunVsockRpcServerCallback(service, port, nullptr, nullptr);
}

AIBinder* VsockRpcClient(unsigned int cid, unsigned int port) {
    auto session = RpcSession::make();
    if (status_t status = session->setupVsockClient(cid, port); status != OK) {
        LOG(ERROR) << "Failed to set up vsock client with CID " << cid << " and port " << port
                   << " error: " << statusToString(status).c_str();
        return nullptr;
    }
    return AIBinder_fromPlatformBinder(session->getRootObject());
    server->setRootObject(AIBinder_toPlatformBinder(service));
    return createRpcServerHandle(server);
}

bool RunInitUnixDomainRpcServer(AIBinder* service, const char* name,
                                void (*readyCallback)(void* param), void* param) {
ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name) {
    auto server = RpcServer::make();
    auto fd = unique_fd(android_get_control_socket(name));
    if (!fd.ok()) {
        LOG(ERROR) << "Failed to get fd for the socket:" << name;
        return false;
        return nullptr;
    }
    if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) {
        LOG(ERROR) << "Failed to set up Unix Domain RPC server with name " << name
                   << " error: " << statusToString(status).c_str();
        return false;
        return nullptr;
    }
    RunRpcServer(server, service, readyCallback, param);
    return true;
    server->setRootObject(AIBinder_toPlatformBinder(service));
    return createRpcServerHandle(server);
}

void ARpcServer_start(ARpcServer* handle) {
    toRpcServer(handle)->start();
}

void ARpcServer_join(ARpcServer* handle) {
    toRpcServer(handle)->join();
}

void ARpcServer_shutdown(ARpcServer* handle) {
    toRpcServer(handle)->shutdown();
}

void ARpcServer_free(ARpcServer* handle) {
    freeRpcServerHandle(handle);
}

AIBinder* VsockRpcClient(unsigned int cid, unsigned int port) {
    auto session = RpcSession::make();
    if (status_t status = session->setupVsockClient(cid, port); status != OK) {
        LOG(ERROR) << "Failed to set up vsock client with CID " << cid << " and port " << port
                   << " error: " << statusToString(status).c_str();
        return nullptr;
    }
    return AIBinder_fromPlatformBinder(session->getRootObject());
}

AIBinder* UnixDomainRpcClient(const char* name) {
+6 −3
Original line number Diff line number Diff line
LIBBINDER_RPC_UNSTABLE_SHIM { # platform-only
  global:
    RunVsockRpcServer;
    RunVsockRpcServerCallback;
    ARpcServer_free;
    ARpcServer_join;
    ARpcServer_newInitUnixDomain;
    ARpcServer_newVsock;
    ARpcServer_shutdown;
    ARpcServer_start;
    VsockRpcClient;
    RunInitUnixDomainRpcServer;
    UnixDomainRpcClient;
    RpcPreconnectedClient;
  local:
+1 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ rust_library {
        "libbinder_rpc_unstable_bindgen_sys",
        "libbinder_rs",
        "libdowncast_rs",
        "libforeign_types",
        "liblibc",
        "liblog_rust",
    ],
+1 −3
Original line number Diff line number Diff line
@@ -23,6 +23,4 @@ pub use client::{
    get_preconnected_rpc_interface, get_preconnected_rpc_service, get_unix_domain_rpc_interface,
    get_unix_domain_rpc_service, get_vsock_rpc_interface, get_vsock_rpc_service,
};
pub use server::{
    run_init_unix_domain_rpc_server, run_vsock_rpc_server, run_vsock_rpc_server_with_factory,
};
pub use server::{run_vsock_rpc_server_with_factory, RpcServer, RpcServerRef};
Loading