diff options
| author | 2022-11-11 22:36:38 +0000 | |
|---|---|---|
| committer | 2022-11-11 22:36:38 +0000 | |
| commit | 213454462ca60ec8af20f69a797bbef19712b85b (patch) | |
| tree | 8d14d6b31c56345293768d884aa781a5111a1627 | |
| parent | bb7e77ba945d566f185c7aef3f00f043a65288ac (diff) | |
| parent | 893a991ac27240178e736214b981910291c1833b (diff) | |
Merge "[rpc_binder] Implement RPC binder over init-managed Unix domain socket"
| -rw-r--r-- | libs/binder/Android.bp | 1 | ||||
| -rw-r--r-- | libs/binder/RpcServer.cpp | 23 | ||||
| -rw-r--r-- | libs/binder/include/binder/RpcServer.h | 10 | ||||
| -rw-r--r-- | libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp | 14 | ||||
| -rw-r--r-- | libs/binder/libbinder_rpc_unstable.cpp | 45 | ||||
| -rw-r--r-- | libs/binder/libbinder_rpc_unstable.map.txt | 2 | ||||
| -rw-r--r-- | libs/binder/rust/rpcbinder/Android.bp | 1 | ||||
| -rw-r--r-- | libs/binder/rust/rpcbinder/src/client.rs | 22 | ||||
| -rw-r--r-- | libs/binder/rust/rpcbinder/src/lib.rs | 8 | ||||
| -rw-r--r-- | libs/binder/rust/rpcbinder/src/server.rs | 52 | ||||
| -rw-r--r-- | libs/binder/tests/BinderRpcTestServerConfig.aidl | 1 | ||||
| -rw-r--r-- | libs/binder/tests/binderRpcTest.cpp | 34 | ||||
| -rw-r--r-- | libs/binder/tests/binderRpcTestCommon.h | 3 | ||||
| -rw-r--r-- | libs/binder/tests/binderRpcTestFixture.h | 3 | ||||
| -rw-r--r-- | libs/binder/tests/binderRpcTestService.cpp | 3 | ||||
| -rw-r--r-- | libs/binder/tests/binderRpcUniversalTests.cpp | 2 |
16 files changed, 206 insertions, 18 deletions
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 23625805c0..28369d6df0 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -501,6 +501,7 @@ cc_library { "libbase", "libbinder", "libbinder_ndk", + "libcutils_sockets", "liblog", "libutils", ], diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp index 83d0de7835..399667d02b 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -564,6 +564,29 @@ status_t RpcServer::setupSocketServer(const RpcSocketAddress& addr) { return OK; } +status_t RpcServer::setupRawSocketServer(base::unique_fd socket_fd) { + RpcTransportFd transportFd(std::move(socket_fd)); + if (!transportFd.fd.ok()) { + int savedErrno = errno; + ALOGE("Could not get initialized Unix socket: %s", strerror(savedErrno)); + return -savedErrno; + } + // Right now, we create all threads at once, making accept4 slow. To avoid hanging the client, + // the backlog is increased to a large number. + // TODO(b/189955605): Once we create threads dynamically & lazily, the backlog can be reduced + // to 1. + if (0 != TEMP_FAILURE_RETRY(listen(transportFd.fd.get(), 50 /*backlog*/))) { + int savedErrno = errno; + ALOGE("Could not listen initialized Unix socket: %s", strerror(savedErrno)); + return -savedErrno; + } + if (status_t status = setupExternalServer(std::move(transportFd.fd)); status != OK) { + ALOGE("Another thread has set up server while calling setupSocketServer. Race?"); + return status; + } + return OK; +} + void RpcServer::onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) { const std::vector<uint8_t>& id = session->mId; LOG_ALWAYS_FATAL_IF(id.empty(), "Server sessions must be initialized with ID"); diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h index 81ae26a344..4ad0a47920 100644 --- a/libs/binder/include/binder/RpcServer.h +++ b/libs/binder/include/binder/RpcServer.h @@ -71,6 +71,16 @@ public: [[nodiscard]] status_t setupUnixDomainServer(const char* path); /** + * Sets up an RPC server with a raw socket file descriptor. + * The socket should be created and bound to a socket address already, e.g. + * the socket can be created in init.rc. + * + * This method is used in the libbinder_rpc_unstable API + * RunInitUnixDomainRpcServer(). + */ + [[nodiscard]] status_t setupRawSocketServer(base::unique_fd socket_fd); + + /** * Creates an RPC server at the current port. */ [[nodiscard]] status_t setupVsockServer(unsigned int port); diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp index e4a9f9975a..dd177aff7f 100644 --- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp +++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp @@ -42,6 +42,20 @@ 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`. +AIBinder* UnixDomainRpcClient(const char* name); + // Connect to an RPC server with preconnected file descriptors. // // requestFd should connect to the server and return a valid file descriptor, or diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index 1f38bb925c..ae07aeecd1 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -19,6 +19,7 @@ #include <android/binder_libbinder.h> #include <binder/RpcServer.h> #include <binder/RpcSession.h> +#include <cutils/sockets.h> #include <linux/vm_sockets.h> using android::OK; @@ -30,6 +31,17 @@ using android::base::unique_fd; extern "C" { +void RunRpcServer(android::sp<RpcServer>& server, AIBinder* service, + void (*readyCallback)(void* param), void* param) { + server->setRootObject(AIBinder_toPlatformBinder(service)); + + if (readyCallback) readyCallback(param); + server->join(); + + // Shutdown any open sessions since server failed. + (void)server->shutdown(); +} + bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context), void* factoryContext, unsigned int port) { auto server = RpcServer::make(); @@ -60,13 +72,7 @@ bool RunVsockRpcServerCallback(AIBinder* service, unsigned int port, << " error: " << statusToString(status).c_str(); return false; } - server->setRootObject(AIBinder_toPlatformBinder(service)); - - if (readyCallback) readyCallback(param); - server->join(); - - // Shutdown any open sessions since server failed. - (void)server->shutdown(); + RunRpcServer(server, service, readyCallback, param); return true; } @@ -84,6 +90,31 @@ AIBinder* VsockRpcClient(unsigned int cid, unsigned int port) { return AIBinder_fromPlatformBinder(session->getRootObject()); } +bool RunInitUnixDomainRpcServer(AIBinder* service, const char* name, + void (*readyCallback)(void* param), void* param) { + auto server = RpcServer::make(); + auto fd = unique_fd(android_get_control_socket(name)); + 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; + } + RunRpcServer(server, service, readyCallback, param); + return true; +} + +AIBinder* UnixDomainRpcClient(const char* name) { + std::string pathname(name); + pathname = ANDROID_SOCKET_DIR "/" + pathname; + auto session = RpcSession::make(); + 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(); + return nullptr; + } + return AIBinder_fromPlatformBinder(session->getRootObject()); +} + AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param) { auto session = RpcSession::make(); auto request = [=] { return unique_fd{requestFd(param)}; }; diff --git a/libs/binder/libbinder_rpc_unstable.map.txt b/libs/binder/libbinder_rpc_unstable.map.txt index 347831a5fc..f9c7bcf117 100644 --- a/libs/binder/libbinder_rpc_unstable.map.txt +++ b/libs/binder/libbinder_rpc_unstable.map.txt @@ -3,6 +3,8 @@ LIBBINDER_RPC_UNSTABLE_SHIM { # platform-only RunVsockRpcServer; RunVsockRpcServerCallback; VsockRpcClient; + RunInitUnixDomainRpcServer; + UnixDomainRpcClient; RpcPreconnectedClient; local: *; diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp index 5ebc27f694..9771cc9a89 100644 --- a/libs/binder/rust/rpcbinder/Android.bp +++ b/libs/binder/rust/rpcbinder/Android.bp @@ -20,6 +20,7 @@ rust_library { "libbinder_rs", "libdowncast_rs", "liblibc", + "liblog_rust", ], apex_available: [ "com.android.compos", diff --git a/libs/binder/rust/rpcbinder/src/client.rs b/libs/binder/rust/rpcbinder/src/client.rs index 4343ff4f83..48c787b2fa 100644 --- a/libs/binder/rust/rpcbinder/src/client.rs +++ b/libs/binder/rust/rpcbinder/src/client.rs @@ -15,6 +15,7 @@ */ 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, @@ -35,6 +36,27 @@ pub fn get_vsock_rpc_interface<T: FromIBinder + ?Sized>( 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( diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs index fb6b90c8e1..89a49a468c 100644 --- a/libs/binder/rust/rpcbinder/src/lib.rs +++ b/libs/binder/rust/rpcbinder/src/lib.rs @@ -20,7 +20,9 @@ mod client; mod server; pub use client::{ - get_preconnected_rpc_interface, get_preconnected_rpc_service, get_vsock_rpc_interface, - get_vsock_rpc_service, + 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, run_vsock_rpc_server_with_factory}; diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs index 80092977fa..b350a133a8 100644 --- a/libs/binder/rust/rpcbinder/src/server.rs +++ b/libs/binder/rust/rpcbinder/src/server.rs @@ -18,7 +18,7 @@ use binder::{ unstable_api::{AIBinder, AsNative}, SpIBinder, }; -use std::{os::raw, ptr::null_mut}; +use std::{ffi::CString, os::raw, ptr::null_mut}; /// Runs a binder RPC server, serving the supplied binder service implementation on the given vsock /// port. @@ -35,7 +35,28 @@ where F: FnOnce(), { let mut ready_notifier = ReadyNotifier(Some(on_ready)); - ready_notifier.run_server(service, port) + ready_notifier.run_vsock_server(service, port) +} + +/// Runs a binder RPC server, serving the supplied binder service implementation on the given +/// socket file name. The socket should be initialized in init.rc with the same name. +/// +/// If and when the server is ready for connections, `on_ready` is called to allow appropriate +/// action to be taken - e.g. to notify clients that they may now attempt to connect. +/// +/// 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_init_unix_domain_rpc_server<F>( + service: SpIBinder, + socket_name: &str, + on_ready: F, +) -> bool +where + F: FnOnce(), +{ + let mut ready_notifier = ReadyNotifier(Some(on_ready)); + ready_notifier.run_init_unix_domain_server(service, socket_name) } struct ReadyNotifier<F>(Option<F>) @@ -46,7 +67,7 @@ impl<F> ReadyNotifier<F> where F: FnOnce(), { - fn run_server(&mut self, mut service: SpIBinder, port: u32) -> bool { + fn run_vsock_server(&mut self, mut service: SpIBinder, port: u32) -> bool { let service = service.as_native_mut(); let param = self.as_void_ptr(); @@ -64,6 +85,31 @@ where } } + fn run_init_unix_domain_server(&mut self, mut service: SpIBinder, socket_name: &str) -> bool { + let socket_name = match CString::new(socket_name) { + Ok(s) => s, + Err(e) => { + log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e); + return false; + } + }; + let service = service.as_native_mut(); + let param = self.as_void_ptr(); + + // SAFETY: Service ownership is transferring to the server and won't be valid afterward. + // Plus the binder objects are threadsafe. + // RunInitUnixDomainRpcServer does not retain a reference to `ready_callback` or `param`; + // it only uses them before it returns, which is during the lifetime of `self`. + unsafe { + binder_rpc_unstable_bindgen::RunInitUnixDomainRpcServer( + service, + socket_name.as_ptr(), + Some(Self::ready_callback), + param, + ) + } + } + fn as_void_ptr(&mut self) -> *mut raw::c_void { self as *mut _ as *mut raw::c_void } diff --git a/libs/binder/tests/BinderRpcTestServerConfig.aidl b/libs/binder/tests/BinderRpcTestServerConfig.aidl index 4cdeac4a97..aac4b04dae 100644 --- a/libs/binder/tests/BinderRpcTestServerConfig.aidl +++ b/libs/binder/tests/BinderRpcTestServerConfig.aidl @@ -22,5 +22,6 @@ parcelable BinderRpcTestServerConfig { int serverVersion; int vsockPort; int unixBootstrapFd; // Inherited from parent + int socketFd; @utf8InCpp String addr; } diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index 004dea317d..79bd9d4993 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -129,6 +129,15 @@ static unsigned int allocateVsockPort() { return vsockPort++; } +static base::unique_fd initUnixSocket(std::string addr) { + auto socket_addr = UnixSocketAddress(addr.c_str()); + base::unique_fd fd( + TEMP_FAILURE_RETRY(socket(socket_addr.addr()->sa_family, SOCK_STREAM, AF_UNIX))); + CHECK(fd.ok()); + CHECK_EQ(0, TEMP_FAILURE_RETRY(bind(fd.get(), socket_addr.addr(), socket_addr.addrSize()))); + return fd; +} + // Destructors need to be defined, even if pure virtual ProcessSession::~ProcessSession() {} @@ -243,13 +252,18 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( singleThreaded ? "_single_threaded" : "", noKernel ? "_no_kernel" : ""); - base::unique_fd bootstrapClientFd, bootstrapServerFd; + base::unique_fd bootstrapClientFd, bootstrapServerFd, socketFd; // Do not set O_CLOEXEC, bootstrapServerFd needs to survive fork/exec. // This is because we cannot pass ParcelFileDescriptor over a pipe. if (!base::Socketpair(SOCK_STREAM, &bootstrapClientFd, &bootstrapServerFd)) { int savedErrno = errno; LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno); } + auto addr = allocateSocketAddress(); + // Initializes the socket before the fork/exec. + if (socketType == SocketType::UNIX_RAW) { + socketFd = initUnixSocket(addr); + } auto ret = std::make_unique<LinuxProcessSession>( Process([=](android::base::borrowed_fd writeEnd, android::base::borrowed_fd readEnd) { @@ -265,8 +279,9 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( serverConfig.rpcSecurity = static_cast<int32_t>(rpcSecurity); serverConfig.serverVersion = serverVersion; serverConfig.vsockPort = allocateVsockPort(); - serverConfig.addr = allocateSocketAddress(); + serverConfig.addr = addr; serverConfig.unixBootstrapFd = bootstrapServerFd.get(); + serverConfig.socketFd = socketFd.get(); for (auto mode : options.serverSupportedFileDescriptorTransportModes) { serverConfig.serverSupportedFileDescriptorTransportModes.push_back( static_cast<int32_t>(mode)); @@ -312,6 +327,7 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( return connectTo(UnixSocketAddress(serverConfig.addr.c_str())); }); break; + case SocketType::UNIX_RAW: case SocketType::UNIX: status = session->setupUnixDomainClient(serverConfig.addr.c_str()); break; @@ -1042,7 +1058,8 @@ static bool testSupportVsockLoopback() { } static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) { - std::vector<SocketType> ret = {SocketType::UNIX, SocketType::UNIX_BOOTSTRAP, SocketType::INET}; + std::vector<SocketType> ret = {SocketType::UNIX, SocketType::UNIX_BOOTSTRAP, SocketType::INET, + SocketType::UNIX_RAW}; if (hasPreconnected) ret.push_back(SocketType::PRECONNECTED); @@ -1284,6 +1301,17 @@ public: mAcceptConnection = &Server::recvmsgServerConnection; mConnectToServer = [this] { return connectToUnixBootstrap(mBootstrapSocket); }; } break; + case SocketType::UNIX_RAW: { + auto addr = allocateSocketAddress(); + auto status = rpcServer->setupRawSocketServer(initUnixSocket(addr)); + if (status != OK) { + return AssertionFailure() + << "setupRawSocketServer: " << statusToString(status); + } + mConnectToServer = [addr] { + return connectTo(UnixSocketAddress(addr.c_str())); + }; + } break; case SocketType::VSOCK: { auto port = allocateVsockPort(); auto status = rpcServer->setupVsockServer(port); diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h index dc7d26491b..654e16c3be 100644 --- a/libs/binder/tests/binderRpcTestCommon.h +++ b/libs/binder/tests/binderRpcTestCommon.h @@ -69,6 +69,7 @@ enum class SocketType { PRECONNECTED, UNIX, UNIX_BOOTSTRAP, + UNIX_RAW, VSOCK, INET, }; @@ -81,6 +82,8 @@ static inline std::string PrintToString(SocketType socketType) { return "unix_domain_socket"; case SocketType::UNIX_BOOTSTRAP: return "unix_domain_socket_bootstrap"; + case SocketType::UNIX_RAW: + return "raw_uds"; case SocketType::VSOCK: return "vm_socket"; case SocketType::INET: diff --git a/libs/binder/tests/binderRpcTestFixture.h b/libs/binder/tests/binderRpcTestFixture.h index 721fbfe1e1..5a78782b9b 100644 --- a/libs/binder/tests/binderRpcTestFixture.h +++ b/libs/binder/tests/binderRpcTestFixture.h @@ -108,7 +108,8 @@ public: bool supportsFdTransport() const { return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS && (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX || - socketType() == SocketType::UNIX_BOOTSTRAP); + socketType() == SocketType::UNIX_BOOTSTRAP || + socketType() == SocketType::UNIX_RAW); } void SetUp() override { diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp index a922b21dd7..cc40995a52 100644 --- a/libs/binder/tests/binderRpcTestService.cpp +++ b/libs/binder/tests/binderRpcTestService.cpp @@ -54,6 +54,9 @@ int main(int argc, const char* argv[]) { case SocketType::UNIX_BOOTSTRAP: CHECK_EQ(OK, server->setupUnixDomainSocketBootstrapServer(std::move(unixBootstrapFd))); break; + case SocketType::UNIX_RAW: + CHECK_EQ(OK, server->setupRawSocketServer(base::unique_fd(serverConfig.socketFd))); + break; case SocketType::VSOCK: CHECK_EQ(OK, server->setupVsockServer(serverConfig.vsockPort)); break; diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp index 1e8d93d81f..f960442859 100644 --- a/libs/binder/tests/binderRpcUniversalTests.cpp +++ b/libs/binder/tests/binderRpcUniversalTests.cpp @@ -86,7 +86,7 @@ TEST_P(BinderRpc, SeparateRootObject) { SocketType type = std::get<0>(GetParam()); if (type == SocketType::PRECONNECTED || type == SocketType::UNIX || - type == SocketType::UNIX_BOOTSTRAP) { + type == SocketType::UNIX_BOOTSTRAP || type == SocketType::UNIX_RAW) { // we can't get port numbers for unix sockets return; } |