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

Commit 0e71a224 authored by David Brazdil's avatar David Brazdil Committed by Automerger Merge Worker
Browse files

Merge changes from topics "rpc-session", "virtmgr-binary" am: 8a1a5f8d am:...

Merge changes from topics "rpc-session", "virtmgr-binary" am: 8a1a5f8d am: 2af9c12e am: a232f1b5

Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/2362491



Change-Id: I38d9bb7bad3b8f204554dfbbcf3077cd7ba05651
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 690a8ae4 a232f1b5
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -50,7 +50,8 @@ using base::unique_fd;

RpcServer::RpcServer(std::unique_ptr<RpcTransportCtx> ctx) : mCtx(std::move(ctx)) {}
RpcServer::~RpcServer() {
    (void)shutdown();
    RpcMutexUniqueLock _l(mLock);
    LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Must call shutdown() before destructor");
}

sp<RpcServer> RpcServer::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
+56 −15
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,22 @@ struct ARpcServer;
// could not be started.
[[nodiscard]] ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name);

// Starts an RPC server that bootstraps sessions using an existing Unix domain
// socket pair, with a given root IBinder object.
// Callers should create a pair of SOCK_STREAM Unix domain sockets, pass one to
// this function and the other to UnixDomainBootstrapClient(). Multiple client
// session can be created from the client end of the pair.
// Does not take ownership of `service`.
// Returns an opaque handle to the running server instance, or null if the server
// could not be started.
[[nodiscard]] ARpcServer* ARpcServer_newUnixDomainBootstrap(AIBinder* service, int bootstrapFd);

// 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);

@@ -50,34 +73,52 @@ void ARpcServer_start(ARpcServer* server);
void ARpcServer_join(ARpcServer* server);

// Shuts down any running ARpcServer_join().
void ARpcServer_shutdown(ARpcServer* server);
[[nodiscard]] bool 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
// assigning single root IBinder object to all connections, factory is called
// whenever a client connects, making it possible to assign unique IBinder
// object to each client.
bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
                                  void* factoryContext, unsigned int port);
// Allocates a new RpcSession object and returns an opaque handle to it.
[[nodiscard]] ARpcSession* ARpcSession_new();

AIBinder* VsockRpcClient(unsigned int cid, unsigned int port);
// 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 over the given bootstrap Unix domain socket.
// Does NOT take ownership of `bootstrapFd`.
AIBinder* ARpcSession_setupUnixDomainBootstrapClient(ARpcSession* session,
                                                     int bootstrapFd);

// 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);
}
+117 −47
Original line number Diff line number Diff line
@@ -35,51 +35,46 @@ 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);
}

static unsigned int cidFromStructAddr(const void* addr, size_t addrlen) {
    LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated");
    const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr);
    LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock");
    return vaddr->svm_cid;
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);
}

extern "C" {

bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
                                  void* factoryContext, unsigned int port) {
    auto server = RpcServer::make();
    if (status_t status = server->setupVsockServer(VMADDR_CID_ANY, port); status != OK) {
        LOG(ERROR) << "Failed to set up vsock server with port " << port
                   << " error: " << statusToString(status).c_str();
        return false;
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;
    }
    server->setPerSessionRootObject([=](const void* addr, size_t addrlen) {
        unsigned int cid = cidFromStructAddr(addr, addrlen);
        return AIBinder_toPlatformBinder(factory(cid, factoryContext));
    });

    server->join();

    // Shutdown any open sessions since server failed.
    (void)server->shutdown();
    return true;
}

extern "C" {

ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port) {
    auto server = RpcServer::make();

@@ -96,16 +91,18 @@ ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned in
    }
    if (cid != VMADDR_CID_ANY) {
        server->setConnectionFilter([=](const void* addr, size_t addrlen) {
            unsigned int remoteCid = cidFromStructAddr(addr, addrlen);
            if (cid != remoteCid) {
                LOG(ERROR) << "Rejected vsock connection from CID " << remoteCid;
            LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated");
            const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr);
            LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock");
            if (cid != vaddr->svm_cid) {
                LOG(ERROR) << "Rejected vsock connection from CID " << vaddr->svm_cid;
                return false;
            }
            return true;
        });
    }
    server->setRootObject(AIBinder_toPlatformBinder(service));
    return createRpcServerHandle(server);
    return createObjectHandle<ARpcServer>(server);
}

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

ARpcServer* ARpcServer_newUnixDomainBootstrap(AIBinder* service, int bootstrapFd) {
    auto server = RpcServer::make();
    auto fd = unique_fd(bootstrapFd);
    if (!fd.ok()) {
        LOG(ERROR) << "Invalid bootstrap fd " << bootstrapFd;
        return nullptr;
    }
    if (status_t status = server->setupUnixDomainSocketBootstrapServer(std::move(fd));
        status != OK) {
        LOG(ERROR) << "Failed to set up Unix Domain RPC server with bootstrap fd " << bootstrapFd
                   << " error: " << statusToString(status).c_str();
        return nullptr;
    }
    server->setRootObject(AIBinder_toPlatformBinder(service));
    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();
bool ARpcServer_shutdown(ARpcServer* handle) {
    return handleToStrongPointer<RpcServer>(handle)->shutdown();
}

void ARpcServer_free(ARpcServer* handle) {
    freeRpcServerHandle(handle);
    // Ignore the result of ARpcServer_shutdown - either it had been called
    // earlier, or the RpcServer destructor will panic.
    (void)ARpcServer_shutdown(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();
@@ -150,10 +187,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();
@@ -162,8 +199,25 @@ AIBinder* UnixDomainRpcClient(const char* name) {
    return AIBinder_fromPlatformBinder(session->getRootObject());
}

AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param) {
    auto session = RpcSession::make();
AIBinder* ARpcSession_setupUnixDomainBootstrapClient(ARpcSession* handle, int bootstrapFd) {
    auto session = handleToStrongPointer<RpcSession>(handle);
    auto fd = unique_fd(dup(bootstrapFd));
    if (!fd.ok()) {
        LOG(ERROR) << "Invalid bootstrap fd " << bootstrapFd;
        return nullptr;
    }
    if (status_t status = session->setupUnixDomainSocketBootstrapClient(std::move(fd));
        status != OK) {
        LOG(ERROR) << "Failed to set up Unix Domain RPC client with bootstrap fd: " << bootstrapFd
                   << " error: " << statusToString(status).c_str();
        return nullptr;
    }
    return AIBinder_fromPlatformBinder(session->getRootObject());
}

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();
@@ -171,4 +225,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)
    }
}
Loading