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

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

rpc_binder: Refactor C/Rust wrappers for RpcSession

Existing wrappers around RpcSession do not allow to configure parameters
such as the number of incoming/outgoing threads and FD transport mode.
Since these need to be set before the connection is initiated, it is
necessary to split the existing wrappers into creating the RpcSession
object and calling the methods that initiate the connection.

To this end, follow the same structure as C/Rust wrappers for RpcServer.
An opaque handle is passed to the caller and the refcount of the
underlying C++ object is kept incremented until the caller frees its
handle.

Bug: 245727626
Test: builds
Change-Id: I384eb53ea881a2831569c5f4de8c0d5aa724f254
parent b96737d6
Loading
Loading
Loading
Loading
+41 −7
Original line number Diff line number Diff line
@@ -23,6 +23,13 @@ extern "C" {

struct AIBinder;
struct ARpcServer;
struct ARpcSession;

enum class ARpcSession_FileDescriptorTransportMode {
    None,
    Unix,
    Trusty,
};

// Starts an RPC server on a given port and a given root IBinder object.
// The server will only accept connections from the given CID.
@@ -40,6 +47,12 @@ struct ARpcServer;
// could not be started.
[[nodiscard]] ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name);

// Sets the list of supported file descriptor transport modes of this RPC server.
void ARpcServer_setSupportedFileDescriptorTransportModes(
        ARpcServer* handle,
        const ARpcSession_FileDescriptorTransportMode modes[],
        size_t modes_len);

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

@@ -57,19 +70,40 @@ void ARpcServer_shutdown(ARpcServer* server);
// This automatically calls ARpcServer_shutdown().
void ARpcServer_free(ARpcServer* server);

AIBinder* VsockRpcClient(unsigned int cid, unsigned int port);
// Allocates a new RpcSession object and returns an opaque handle to it.
[[nodiscard]] ARpcSession* ARpcSession_new();

// Connects to an RPC server over vsock at a given CID on a given port.
// Returns the root Binder object of the server.
AIBinder* ARpcSession_setupVsockClient(ARpcSession* session, unsigned int cid,
                                       unsigned int port);

// 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`.
AIBinder* UnixDomainRpcClient(const char* name);
// Connects to an RPC server over a Unix Domain Socket of the given name.
// The final Unix Domain Socket path name is /dev/socket/`name`.
// Returns the root Binder object of the server.
AIBinder* ARpcSession_setupUnixDomainClient(ARpcSession* session, const char* name);

// Connect to an RPC server with preconnected file descriptors.
// Connects to an RPC server with preconnected file descriptors.
//
// requestFd should connect to the server and return a valid file descriptor, or
// -1 if connection fails.
//
// param will be passed to requestFd. Callers can use param to pass contexts to
// the requestFd function.
AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param);
AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* session,
                                              int (*requestFd)(void* param),
                                              void* param);

// Sets the file descriptor transport mode for this session.
void ARpcSession_setFileDescriptorTransportMode(ARpcSession* session,
                                                ARpcSession_FileDescriptorTransportMode mode);

// Sets the maximum number of incoming threads.
void ARpcSession_setMaxIncomingThreads(ARpcSession* session, size_t threads);

// Sets the maximum number of outgoing threads.
void ARpcSession_setMaxOutgoingThreads(ARpcSession* session, size_t threads);

// Decrements the refcount of the underlying RpcSession object.
void ARpcSession_free(ARpcSession* session);
}
+78 −19
Original line number Diff line number Diff line
@@ -35,22 +35,44 @@ using android::base::unique_fd;
// Opaque handle for RpcServer.
struct ARpcServer {};

static sp<RpcServer> toRpcServer(ARpcServer* handle) {
    auto ref = reinterpret_cast<RpcServer*>(handle);
    return sp<RpcServer>::fromExisting(ref);
}
// Opaque handle for RpcSession.
struct ARpcSession {};

static ARpcServer* createRpcServerHandle(sp<RpcServer>& server) {
template <typename A, typename T>
static A* createObjectHandle(sp<T>& server) {
    auto ref = server.get();
    ref->incStrong(ref);
    return reinterpret_cast<ARpcServer*>(ref);
    return reinterpret_cast<A*>(ref);
}

static void freeRpcServerHandle(ARpcServer* handle) {
    auto ref = reinterpret_cast<RpcServer*>(handle);
template <typename T, typename A>
static void freeObjectHandle(A* handle) {
    LOG_ALWAYS_FATAL_IF(handle == nullptr, "Handle cannot be null");
    auto ref = reinterpret_cast<T*>(handle);
    ref->decStrong(ref);
}

template <typename T, typename A>
static sp<T> handleToStrongPointer(A* handle) {
    LOG_ALWAYS_FATAL_IF(handle == nullptr, "Handle cannot be null");
    auto ref = reinterpret_cast<T*>(handle);
    return sp<T>::fromExisting(ref);
}

RpcSession::FileDescriptorTransportMode toTransportMode(
        ARpcSession_FileDescriptorTransportMode mode) {
    switch (mode) {
        case ARpcSession_FileDescriptorTransportMode::None:
            return RpcSession::FileDescriptorTransportMode::NONE;
        case ARpcSession_FileDescriptorTransportMode::Unix:
            return RpcSession::FileDescriptorTransportMode::UNIX;
        case ARpcSession_FileDescriptorTransportMode::Trusty:
            return RpcSession::FileDescriptorTransportMode::TRUSTY;
        default:
            return RpcSession::FileDescriptorTransportMode::NONE;
    }
}

extern "C" {

ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port) {
@@ -80,7 +102,7 @@ ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned in
        });
    }
    server->setRootObject(AIBinder_toPlatformBinder(service));
    return createRpcServerHandle(server);
    return createObjectHandle<ARpcServer>(server);
}

ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name) {
@@ -96,27 +118,47 @@ ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name) {
        return nullptr;
    }
    server->setRootObject(AIBinder_toPlatformBinder(service));
    return createRpcServerHandle(server);
    return createObjectHandle<ARpcServer>(server);
}

void ARpcServer_setSupportedFileDescriptorTransportModes(
        ARpcServer* handle, const ARpcSession_FileDescriptorTransportMode modes[],
        size_t modes_len) {
    auto server = handleToStrongPointer<RpcServer>(handle);
    std::vector<RpcSession::FileDescriptorTransportMode> modevec;
    for (size_t i = 0; i < modes_len; i++) {
        modevec.push_back(toTransportMode(modes[i]));
    }
    server->setSupportedFileDescriptorTransportModes(modevec);
}

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

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

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

void ARpcServer_free(ARpcServer* handle) {
    freeRpcServerHandle(handle);
    freeObjectHandle<RpcServer>(handle);
}

AIBinder* VsockRpcClient(unsigned int cid, unsigned int port) {
ARpcSession* ARpcSession_new() {
    auto session = RpcSession::make();
    return createObjectHandle<ARpcSession>(session);
}

void ARpcSession_free(ARpcSession* handle) {
    freeObjectHandle<RpcSession>(handle);
}

AIBinder* ARpcSession_setupVsockClient(ARpcSession* handle, unsigned int cid, unsigned int port) {
    auto session = handleToStrongPointer<RpcSession>(handle);
    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();
@@ -125,10 +167,10 @@ AIBinder* VsockRpcClient(unsigned int cid, unsigned int port) {
    return AIBinder_fromPlatformBinder(session->getRootObject());
}

AIBinder* UnixDomainRpcClient(const char* name) {
AIBinder* ARpcSession_setupUnixDomainClient(ARpcSession* handle, const char* name) {
    std::string pathname(name);
    pathname = ANDROID_SOCKET_DIR "/" + pathname;
    auto session = RpcSession::make();
    auto session = handleToStrongPointer<RpcSession>(handle);
    if (status_t status = session->setupUnixDomainClient(pathname.c_str()); status != OK) {
        LOG(ERROR) << "Failed to set up Unix Domain RPC client with path: " << pathname
                   << " error: " << statusToString(status).c_str();
@@ -137,8 +179,9 @@ AIBinder* UnixDomainRpcClient(const char* name) {
    return AIBinder_fromPlatformBinder(session->getRootObject());
}

AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param) {
    auto session = RpcSession::make();
AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* handle, int (*requestFd)(void* param),
                                              void* param) {
    auto session = handleToStrongPointer<RpcSession>(handle);
    auto request = [=] { return unique_fd{requestFd(param)}; };
    if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) {
        LOG(ERROR) << "Failed to set up vsock client. error: " << statusToString(status).c_str();
@@ -146,4 +189,20 @@ AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param) {
    }
    return AIBinder_fromPlatformBinder(session->getRootObject());
}

void ARpcSession_setFileDescriptorTransportMode(ARpcSession* handle,
                                                ARpcSession_FileDescriptorTransportMode mode) {
    auto session = handleToStrongPointer<RpcSession>(handle);
    session->setFileDescriptorTransportMode(toTransportMode(mode));
}

void ARpcSession_setMaxIncomingThreads(ARpcSession* handle, size_t threads) {
    auto session = handleToStrongPointer<RpcSession>(handle);
    session->setMaxIncomingThreads(threads);
}

void ARpcSession_setMaxOutgoingThreads(ARpcSession* handle, size_t threads) {
    auto session = handleToStrongPointer<RpcSession>(handle);
    session->setMaxOutgoingThreads(threads);
}
}
+3 −0
Original line number Diff line number Diff line
@@ -68,10 +68,13 @@ rust_bindgen {
    visibility: [":__subpackages__"],
    source_stem: "bindings",
    bindgen_flags: [
        "--size_t-is-usize",
        "--blocklist-type",
        "AIBinder",
        "--raw-line",
        "use binder_ndk_sys::AIBinder;",
        "--rustified-enum",
        "ARpcSession_FileDescriptorTransportMode",
    ],
    rustlibs: [
        "libbinder_ndk_sys",
+0 −111
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use binder::{unstable_api::new_spibinder, FromIBinder, SpIBinder, StatusCode, Strong};
use std::ffi::CString;
use std::os::{
    raw::{c_int, c_void},
    unix::io::RawFd,
};

/// Connects to an RPC Binder server over vsock.
pub fn get_vsock_rpc_service(cid: u32, port: u32) -> Option<SpIBinder> {
    // SAFETY: AIBinder returned by VsockRpcClient has correct reference count,
    // and the ownership can safely be taken by new_spibinder.
    unsafe { new_spibinder(binder_rpc_unstable_bindgen::VsockRpcClient(cid, port)) }
}

/// Connects to an RPC Binder server for a particular interface over vsock.
pub fn get_vsock_rpc_interface<T: FromIBinder + ?Sized>(
    cid: u32,
    port: u32,
) -> Result<Strong<T>, StatusCode> {
    interface_cast(get_vsock_rpc_service(cid, port))
}

/// Connects to an RPC Binder server over Unix domain socket.
pub fn get_unix_domain_rpc_service(socket_name: &str) -> Option<SpIBinder> {
    let socket_name = match CString::new(socket_name) {
        Ok(s) => s,
        Err(e) => {
            log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e);
            return None;
        }
    };
    // SAFETY: AIBinder returned by UnixDomainRpcClient has correct reference count,
    // and the ownership can safely be taken by new_spibinder.
    unsafe { new_spibinder(binder_rpc_unstable_bindgen::UnixDomainRpcClient(socket_name.as_ptr())) }
}

/// Connects to an RPC Binder server for a particular interface over Unix domain socket.
pub fn get_unix_domain_rpc_interface<T: FromIBinder + ?Sized>(
    socket_name: &str,
) -> Result<Strong<T>, StatusCode> {
    interface_cast(get_unix_domain_rpc_service(socket_name))
}

/// Connects to an RPC Binder server, using the given callback to get (and take ownership of)
/// file descriptors already connected to it.
pub fn get_preconnected_rpc_service(
    mut request_fd: impl FnMut() -> Option<RawFd>,
) -> Option<SpIBinder> {
    // Double reference the factory because trait objects aren't FFI safe.
    let mut request_fd_ref: RequestFd = &mut request_fd;
    let param = &mut request_fd_ref as *mut RequestFd as *mut c_void;

    // SAFETY: AIBinder returned by RpcPreconnectedClient has correct reference count, and the
    // ownership can be safely taken by new_spibinder. RpcPreconnectedClient does not take ownership
    // of param, only passing it to request_fd_wrapper.
    unsafe {
        new_spibinder(binder_rpc_unstable_bindgen::RpcPreconnectedClient(
            Some(request_fd_wrapper),
            param,
        ))
    }
}

type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>;

unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int {
    // SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the
    // BinderFdFactory reference, with param being a properly aligned non-null pointer to an
    // initialized instance.
    let request_fd_ptr = param as *mut RequestFd;
    let request_fd = request_fd_ptr.as_mut().unwrap();
    if let Some(fd) = request_fd() {
        fd
    } else {
        -1
    }
}

/// Connects to an RPC Binder server for a particular interface, using the given callback to get
/// (and take ownership of) file descriptors already connected to it.
pub fn get_preconnected_rpc_interface<T: FromIBinder + ?Sized>(
    request_fd: impl FnMut() -> Option<RawFd>,
) -> Result<Strong<T>, StatusCode> {
    interface_cast(get_preconnected_rpc_service(request_fd))
}

fn interface_cast<T: FromIBinder + ?Sized>(
    service: Option<SpIBinder>,
) -> Result<Strong<T>, StatusCode> {
    if let Some(service) = service {
        FromIBinder::try_from(service)
    } else {
        Err(StatusCode::NAME_NOT_FOUND)
    }
}
+2 −5
Original line number Diff line number Diff line
@@ -16,11 +16,8 @@

//! API for RPC Binder services.

mod client;
mod server;
mod session;

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::{RpcServer, RpcServerRef};
pub use session::{FileDescriptorTransportMode, RpcSession, RpcSessionRef};
Loading