diff options
| -rw-r--r-- | libs/binder/RpcServer.cpp | 3 | ||||
| -rw-r--r-- | libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp | 71 | ||||
| -rw-r--r-- | libs/binder/libbinder_rpc_unstable.cpp | 164 | ||||
| -rw-r--r-- | libs/binder/rust/rpcbinder/Android.bp | 3 | ||||
| -rw-r--r-- | libs/binder/rust/rpcbinder/src/client.rs | 111 | ||||
| -rw-r--r-- | libs/binder/rust/rpcbinder/src/lib.rs | 9 | ||||
| -rw-r--r-- | libs/binder/rust/rpcbinder/src/server.rs | 98 | ||||
| -rw-r--r-- | libs/binder/rust/rpcbinder/src/session.rs | 190 |
8 files changed, 418 insertions, 231 deletions
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp index fedc1d9593..d47e4f044a 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -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) { diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp index 3ec049ebfb..3ebbed6965 100644 --- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp +++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp @@ -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); } diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index 88f8c943a4..78dae4bd1f 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -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); +} } diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp index f70ebfc364..afb73e920f 100644 --- a/libs/binder/rust/rpcbinder/Android.bp +++ b/libs/binder/rust/rpcbinder/Android.bp @@ -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", diff --git a/libs/binder/rust/rpcbinder/src/client.rs b/libs/binder/rust/rpcbinder/src/client.rs deleted file mode 100644 index 48c787b2fa..0000000000 --- a/libs/binder/rust/rpcbinder/src/client.rs +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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) - } -} diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs index 1b719aa7d4..a9573850f1 100644 --- a/libs/binder/rust/rpcbinder/src/lib.rs +++ b/libs/binder/rust/rpcbinder/src/lib.rs @@ -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::{run_vsock_rpc_server_with_factory, RpcServer, RpcServerRef}; +pub use server::{RpcServer, RpcServerRef}; +pub use session::{FileDescriptorTransportMode, RpcSession, RpcSessionRef}; diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs index d5f12195f1..761b306a1e 100644 --- a/libs/binder/rust/rpcbinder/src/server.rs +++ b/libs/binder/rust/rpcbinder/src/server.rs @@ -14,14 +14,13 @@ * limitations under the License. */ -use binder::{ - unstable_api::{AIBinder, AsNative}, - SpIBinder, -}; +use crate::session::FileDescriptorTransportMode; +use binder::{unstable_api::AsNative, SpIBinder}; use binder_rpc_unstable_bindgen::ARpcServer; use foreign_types::{foreign_type, ForeignType, ForeignTypeRef}; +use std::ffi::CString; use std::io::{Error, ErrorKind}; -use std::{ffi::CString, os::raw, ptr::null_mut}; +use std::os::unix::io::{IntoRawFd, OwnedFd}; foreign_type! { type CType = binder_rpc_unstable_bindgen::ARpcServer; @@ -82,6 +81,27 @@ impl RpcServer { } } + /// Creates a binder 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 the server and the other to the client. Multiple client session + /// can be created from the client end of the pair. + pub fn new_unix_domain_bootstrap( + mut service: SpIBinder, + bootstrap_fd: OwnedFd, + ) -> Result<RpcServer, Error> { + let service = service.as_native_mut(); + + // SAFETY: Service ownership is transferring to the server and won't be valid afterward. + // Plus the binder objects are threadsafe. + // The server takes ownership of the bootstrap FD. + unsafe { + Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newUnixDomainBootstrap( + service, + bootstrap_fd.into_raw_fd(), + )) + } + } + unsafe fn checked_from_ptr(ptr: *mut ARpcServer) -> Result<RpcServer, Error> { if ptr.is_null() { return Err(Error::new(ErrorKind::Other, "Failed to start server")); @@ -91,6 +111,22 @@ impl RpcServer { } impl RpcServerRef { + /// Sets the list of file descriptor transport modes supported by this server. + pub fn set_supported_file_descriptor_transport_modes( + &self, + modes: &[FileDescriptorTransportMode], + ) { + // SAFETY - Does not keep the pointer after returning does, nor does it + // read past its boundary. Only passes the 'self' pointer as an opaque handle. + unsafe { + binder_rpc_unstable_bindgen::ARpcServer_setSupportedFileDescriptorTransportModes( + self.as_ptr(), + modes.as_ptr(), + modes.len(), + ) + } + } + /// Starts a new background thread and calls join(). Returns immediately. pub fn start(&self) { unsafe { binder_rpc_unstable_bindgen::ARpcServer_start(self.as_ptr()) }; @@ -104,51 +140,11 @@ impl RpcServerRef { /// Shuts down the running RpcServer. Can be called multiple times and from /// multiple threads. Called automatically during drop(). - pub fn shutdown(&self) { - unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) }; - } -} - -type RpcServerFactoryRef<'a> = &'a mut (dyn FnMut(u32) -> Option<SpIBinder> + Send + Sync); - -/// Runs a binder RPC server, using the given factory function to construct a binder service -/// implementation for each connection. -/// -/// The current thread is joined to the binder thread pool to handle incoming messages. -/// -/// Returns true if the server has shutdown normally, false if it failed in some way. -pub fn run_vsock_rpc_server_with_factory( - port: u32, - mut factory: impl FnMut(u32) -> Option<SpIBinder> + Send + Sync, -) -> bool { - // Double reference the factory because trait objects aren't FFI safe. - // NB: The type annotation is necessary to ensure that we have a `dyn` rather than an `impl`. - let mut factory_ref: RpcServerFactoryRef = &mut factory; - let context = &mut factory_ref as *mut RpcServerFactoryRef as *mut raw::c_void; - - // SAFETY: `factory_wrapper` is only ever called by `RunVsockRpcServerWithFactory`, with context - // taking the pointer value above (so a properly aligned non-null pointer to an initialized - // `RpcServerFactoryRef`), within the lifetime of `factory_ref` (i.e. no more calls will be made - // after `RunVsockRpcServerWithFactory` returns). - unsafe { - binder_rpc_unstable_bindgen::RunVsockRpcServerWithFactory( - Some(factory_wrapper), - context, - port, - ) - } -} - -unsafe extern "C" fn factory_wrapper(cid: u32, context: *mut raw::c_void) -> *mut AIBinder { - // SAFETY: `context` was created from an `&mut RpcServerFactoryRef` by - // `run_vsock_rpc_server_with_factory`, and we are still within the lifetime of the value it is - // pointing to. - let factory_ptr = context as *mut RpcServerFactoryRef; - let factory = factory_ptr.as_mut().unwrap(); - - if let Some(mut service) = factory(cid) { - service.as_native_mut() - } else { - null_mut() + pub fn shutdown(&self) -> Result<(), Error> { + if unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) } { + Ok(()) + } else { + Err(Error::from(ErrorKind::UnexpectedEof)) + } } } diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs new file mode 100644 index 0000000000..62fedb1ffa --- /dev/null +++ b/libs/binder/rust/rpcbinder/src/session.rs @@ -0,0 +1,190 @@ +/* + * 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; +use binder::{FromIBinder, SpIBinder, StatusCode, Strong}; +use foreign_types::{foreign_type, ForeignType, ForeignTypeRef}; +use std::ffi::CString; +use std::os::{ + raw::{c_int, c_void}, + unix::io::{AsRawFd, BorrowedFd, RawFd}, +}; + +pub use binder_rpc_unstable_bindgen::ARpcSession_FileDescriptorTransportMode as FileDescriptorTransportMode; + +foreign_type! { + type CType = binder_rpc_unstable_bindgen::ARpcSession; + fn drop = binder_rpc_unstable_bindgen::ARpcSession_free; + + /// A type that represents a foreign instance of RpcSession. + #[derive(Debug)] + pub struct RpcSession; + /// A borrowed RpcSession. + pub struct RpcSessionRef; +} + +/// SAFETY - The opaque handle can be cloned freely. +unsafe impl Send for RpcSession {} +/// SAFETY - The underlying C++ RpcSession class is thread-safe. +unsafe impl Sync for RpcSession {} + +impl RpcSession { + /// Allocates a new RpcSession object. + pub fn new() -> RpcSession { + // SAFETY - Takes ownership of the returned handle, which has correct refcount. + unsafe { RpcSession::from_ptr(binder_rpc_unstable_bindgen::ARpcSession_new()) } + } +} + +impl Default for RpcSession { + fn default() -> Self { + Self::new() + } +} + +impl RpcSessionRef { + /// Sets the file descriptor transport mode for this session. + pub fn set_file_descriptor_transport_mode(&self, mode: FileDescriptorTransportMode) { + // SAFETY - Only passes the 'self' pointer as an opaque handle. + unsafe { + binder_rpc_unstable_bindgen::ARpcSession_setFileDescriptorTransportMode( + self.as_ptr(), + mode, + ) + }; + } + + /// Sets the maximum number of incoming threads. + pub fn set_max_incoming_threads(&self, threads: usize) { + // SAFETY - Only passes the 'self' pointer as an opaque handle. + unsafe { + binder_rpc_unstable_bindgen::ARpcSession_setMaxIncomingThreads(self.as_ptr(), threads) + }; + } + + /// Sets the maximum number of outgoing threads. + pub fn set_max_outgoing_threads(&self, threads: usize) { + // SAFETY - Only passes the 'self' pointer as an opaque handle. + unsafe { + binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingThreads(self.as_ptr(), threads) + }; + } + + /// Connects to an RPC Binder server over vsock for a particular interface. + pub fn setup_vsock_client<T: FromIBinder + ?Sized>( + &self, + cid: u32, + port: u32, + ) -> Result<Strong<T>, StatusCode> { + // SAFETY: AIBinder returned by ARpcSession_setupVsockClient has correct + // reference count, and the ownership can safely be taken by new_spibinder. + let service = unsafe { + new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupVsockClient( + self.as_ptr(), + cid, + port, + )) + }; + Self::get_interface(service) + } + + /// Connects to an RPC Binder server over a names Unix Domain Socket for + /// a particular interface. + pub fn setup_unix_domain_client<T: FromIBinder + ?Sized>( + &self, + socket_name: &str, + ) -> Result<Strong<T>, StatusCode> { + let socket_name = match CString::new(socket_name) { + Ok(s) => s, + Err(e) => { + log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e); + return Err(StatusCode::NAME_NOT_FOUND); + } + }; + + // SAFETY: AIBinder returned by ARpcSession_setupUnixDomainClient has correct + // reference count, and the ownership can safely be taken by new_spibinder. + let service = unsafe { + new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupUnixDomainClient( + self.as_ptr(), + socket_name.as_ptr(), + )) + }; + Self::get_interface(service) + } + + /// Connects to an RPC Binder server over a bootstrap Unix Domain Socket + /// for a particular interface. + pub fn setup_unix_domain_bootstrap_client<T: FromIBinder + ?Sized>( + &self, + bootstrap_fd: BorrowedFd, + ) -> Result<Strong<T>, StatusCode> { + // SAFETY: ARpcSession_setupUnixDomainBootstrapClient does not take + // ownership of bootstrap_fd. The returned AIBinder has correct + // reference count, and the ownership can safely be taken by new_spibinder. + let service = unsafe { + new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupUnixDomainBootstrapClient( + self.as_ptr(), + bootstrap_fd.as_raw_fd(), + )) + }; + Self::get_interface(service) + } + + /// Connects to an RPC Binder server, using the given callback to get (and + /// take ownership of) file descriptors already connected to it. + pub fn setup_preconnected_client<T: FromIBinder + ?Sized>( + &self, + mut request_fd: impl FnMut() -> Option<RawFd>, + ) -> Result<Strong<T>, StatusCode> { + // 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. + let service = unsafe { + new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupPreconnectedClient( + self.as_ptr(), + Some(request_fd_wrapper), + param, + )) + }; + Self::get_interface(service) + } + + fn get_interface<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) + } + } +} + +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(); + request_fd().unwrap_or(-1) +} |