diff options
| -rw-r--r-- | libs/binder/Android.bp | 1 | ||||
| -rw-r--r-- | libs/binder/rust/Android.bp | 28 | ||||
| -rw-r--r-- | libs/binder/rust/rpcbinder/Android.bp | 50 | ||||
| -rw-r--r-- | libs/binder/rust/rpcbinder/src/client.rs | 92 | ||||
| -rw-r--r-- | libs/binder/rust/rpcbinder/src/lib.rs | 26 | ||||
| -rw-r--r-- | libs/binder/rust/rpcbinder/src/server.rs | 124 |
6 files changed, 293 insertions, 28 deletions
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index fabf3ebdae..2cb6821ae7 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -446,6 +446,7 @@ cc_library { // This library is intentionally limited to these targets, and it will be removed later. // Do not expand the visibility. visibility: [ + ":__subpackages__", "//packages/modules/Virtualization:__subpackages__", ], } diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index c0d4487e09..0ec618352a 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -144,24 +144,6 @@ rust_bindgen { min_sdk_version: "Tiramisu", } -// TODO(b/184872979): remove once the Rust API is created. -rust_bindgen { - name: "libbinder_rpc_unstable_bindgen", - wrapper_src: ":libbinder_rpc_unstable_header", - crate_name: "binder_rpc_unstable_bindgen", - visibility: ["//packages/modules/Virtualization:__subpackages__"], - source_stem: "bindings", - shared_libs: [ - "libutils", - ], - apex_available: [ - "com.android.compos", - "com.android.uwb", - "com.android.virt", - ], - min_sdk_version: "Tiramisu", -} - rust_test { name: "libbinder_rs-internal_test", crate_name: "binder", @@ -188,13 +170,3 @@ rust_test { clippy_lints: "none", lints: "none", } - -rust_test { - name: "libbinder_rpc_unstable_bindgen_test", - srcs: [":libbinder_rpc_unstable_bindgen"], - crate_name: "binder_rpc_unstable_bindgen", - test_suites: ["general-tests"], - auto_gen_config: true, - clippy_lints: "none", - lints: "none", -} diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp new file mode 100644 index 0000000000..067ca0dcdd --- /dev/null +++ b/libs/binder/rust/rpcbinder/Android.bp @@ -0,0 +1,50 @@ +rust_library { + name: "librpcbinder_rs", + crate_name: "rpcbinder", + srcs: ["src/lib.rs"], + shared_libs: [ + "libutils", + ], + rustlibs: [ + "libbinder_ndk_sys", + "libbinder_rpc_unstable_bindgen", + "libbinder_rs", + "libdowncast_rs", + "liblibc", + ], + apex_available: [ + "com.android.compos", + "com.android.uwb", + "com.android.virt", + ], + min_sdk_version: "Tiramisu", +} + +// TODO(b/184872979): remove once the RPC Binder API is stabilised. +rust_bindgen { + name: "libbinder_rpc_unstable_bindgen", + wrapper_src: ":libbinder_rpc_unstable_header", + crate_name: "binder_rpc_unstable_bindgen", + visibility: [":__subpackages__"], + source_stem: "bindings", + shared_libs: [ + "libbinder_rpc_unstable", + "libutils", + ], + apex_available: [ + "com.android.compos", + "com.android.uwb", + "com.android.virt", + ], + min_sdk_version: "Tiramisu", +} + +rust_test { + name: "libbinder_rpc_unstable_bindgen_test", + srcs: [":libbinder_rpc_unstable_bindgen"], + crate_name: "binder_rpc_unstable_bindgen", + test_suites: ["general-tests"], + auto_gen_config: true, + clippy_lints: "none", + lints: "none", +} diff --git a/libs/binder/rust/rpcbinder/src/client.rs b/libs/binder/rust/rpcbinder/src/client.rs new file mode 100644 index 0000000000..dfc6f06b14 --- /dev/null +++ b/libs/binder/rust/rpcbinder/src/client.rs @@ -0,0 +1,92 @@ +/* + * 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, AIBinder}, + FromIBinder, SpIBinder, StatusCode, Strong, +}; +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 RpcClient has correct reference count, and the ownership can + // safely be taken by new_spibinder. + unsafe { new_spibinder(binder_rpc_unstable_bindgen::RpcClient(cid, port) as *mut AIBinder) } +} + +/// 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, 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, + ) as *mut AIBinder) + } +} + +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 new file mode 100644 index 0000000000..a5eea61798 --- /dev/null +++ b/libs/binder/rust/rpcbinder/src/lib.rs @@ -0,0 +1,26 @@ +/* + * 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. + */ + +//! API for RPC Binder services. + +mod client; +mod server; + +pub use client::{ + get_preconnected_rpc_interface, get_preconnected_rpc_service, get_vsock_rpc_interface, + get_vsock_rpc_service, +}; +pub use server::{run_rpc_server, run_rpc_server_with_factory}; diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs new file mode 100644 index 0000000000..d98a439da4 --- /dev/null +++ b/libs/binder/rust/rpcbinder/src/server.rs @@ -0,0 +1,124 @@ +/* + * 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::AsNative, SpIBinder}; +use std::{os::raw, ptr::null_mut}; + +/// Runs a binder RPC server, serving the supplied binder service implementation on the given vsock +/// port. +/// +/// If and when the server is ready for connections (it is listening on the port), `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_rpc_server<F>(service: SpIBinder, port: u32, on_ready: F) -> bool +where + F: FnOnce(), +{ + let mut ready_notifier = ReadyNotifier(Some(on_ready)); + ready_notifier.run_server(service, port) +} + +struct ReadyNotifier<F>(Option<F>) +where + F: FnOnce(); + +impl<F> ReadyNotifier<F> +where + F: FnOnce(), +{ + fn run_server(&mut self, mut service: SpIBinder, port: u32) -> bool { + let service = service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder; + 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. + // RunRpcServerCallback 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::RunRpcServerCallback( + service, + port, + Some(Self::ready_callback), + param, + ) + } + } + + fn as_void_ptr(&mut self) -> *mut raw::c_void { + self as *mut _ as *mut raw::c_void + } + + unsafe extern "C" fn ready_callback(param: *mut raw::c_void) { + // SAFETY: This is only ever called by `RunRpcServerCallback`, within the lifetime of the + // `ReadyNotifier`, with `param` taking the value returned by `as_void_ptr` (so a properly + // aligned non-null pointer to an initialized instance). + let ready_notifier = param as *mut Self; + ready_notifier.as_mut().unwrap().notify() + } + + fn notify(&mut self) { + if let Some(on_ready) = self.0.take() { + on_ready(); + } + } +} + +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_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 `RunRpcServerWithFactory`, 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 `RunRpcServerWithFactory` returns). + unsafe { + binder_rpc_unstable_bindgen::RunRpcServerWithFactory(Some(factory_wrapper), context, port) + } +} + +unsafe extern "C" fn factory_wrapper( + cid: u32, + context: *mut raw::c_void, +) -> *mut binder_rpc_unstable_bindgen::AIBinder { + // SAFETY: `context` was created from an `&mut RpcServerFactoryRef` by + // `run_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() as *mut binder_rpc_unstable_bindgen::AIBinder + } else { + null_mut() + } +} |