diff options
author | 2024-08-09 23:20:03 +0000 | |
---|---|---|
committer | 2024-09-27 18:18:28 +0000 | |
commit | bbd71b11d8980c1c382799595016e089491ecafe (patch) | |
tree | bf3440a27e8164e88e9ff5a62b6715757b58bf8f | |
parent | 02401d9743d3f029737b7a32466ed62b88d2b8f7 (diff) |
Add a libbinder_rs wrapper around the new ARpc APIs
This allows rust clients to set up client connections to binder RPC
services underneath the libbinder_rs service manager APIs.
Test: atest binderRpcTest vm_accessor_test
Bug: 358427181
Change-Id: I05f1c5ff4e3447262eaf9df8250c9d99e3414559
-rw-r--r-- | libs/binder/rust/Android.bp | 19 | ||||
-rw-r--r-- | libs/binder/rust/src/lib.rs | 4 | ||||
-rw-r--r-- | libs/binder/rust/src/system_only.rs | 187 | ||||
-rw-r--r-- | libs/binder/rust/sys/BinderBindings.hpp | 1 | ||||
-rw-r--r-- | libs/binder/rust/tests/integration.rs | 41 |
5 files changed, 250 insertions, 2 deletions
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index 2deb25457e..4545d7bb2e 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -15,6 +15,8 @@ rust_library { "libbinder_ndk_sys", "libdowncast_rs", "liblibc", + "liblog_rust", + "libnix", ], host_supported: true, vendor_available: true, @@ -79,6 +81,9 @@ rust_library { shared_libs: [ "libbinder_ndk", ], + rustlibs: [ + "liblibc", + ], host_supported: true, vendor_available: true, product_available: true, @@ -129,9 +134,18 @@ rust_bindgen { // rustified "libbinder_ndk_bindgen_flags.txt", ], + bindgen_flags: [ + "--blocklist-type", + "sockaddr", + "--raw-line", + "use libc::sockaddr;", + ], shared_libs: [ "libbinder_ndk", ], + rustlibs: [ + "liblibc", + ], host_supported: true, vendor_available: true, product_available: true, @@ -185,6 +199,8 @@ rust_test { "libbinder_ndk_sys", "libdowncast_rs", "liblibc", + "liblog_rust", + "libnix", ], } @@ -196,4 +212,7 @@ rust_test { auto_gen_config: true, clippy_lints: "none", lints: "none", + rustlibs: [ + "liblibc", + ], } diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index e70f4f0232..9d4ed3331b 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -104,6 +104,8 @@ mod proxy; mod service; #[cfg(not(trusty))] mod state; +#[cfg(not(any(android_vendor, android_vndk)))] +mod system_only; use binder_ndk_sys as sys; @@ -120,6 +122,8 @@ pub use service::{ }; #[cfg(not(trusty))] pub use state::{ProcessState, ThreadState}; +#[cfg(not(any(android_vendor, android_vndk)))] +pub use system_only::{Accessor, ConnectionInfo}; /// Binder result containing a [`Status`] on error. pub type Result<T> = std::result::Result<T, Status>; diff --git a/libs/binder/rust/src/system_only.rs b/libs/binder/rust/src/system_only.rs new file mode 100644 index 0000000000..a91d84d865 --- /dev/null +++ b/libs/binder/rust/src/system_only.rs @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2024 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 crate::proxy::SpIBinder; +use crate::sys; + +use std::ffi::{c_void, CStr, CString}; +use std::os::raw::c_char; + +use libc::sockaddr; +use nix::sys::socket::{SockaddrLike, UnixAddr, VsockAddr}; +use std::sync::Arc; +use std::{fmt, ptr}; + +/// Rust wrapper around ABinderRpc_Accessor objects for RPC binder service management. +/// +/// Dropping the `Accessor` will drop the underlying object and the binder it owns. +pub struct Accessor { + accessor: *mut sys::ABinderRpc_Accessor, +} + +impl fmt::Debug for Accessor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ABinderRpc_Accessor({:p})", self.accessor) + } +} + +/// Socket connection info required for libbinder to connect to a service. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ConnectionInfo { + /// For vsock connection + Vsock(VsockAddr), + /// For unix domain socket connection + Unix(UnixAddr), +} + +/// Safety: A `Accessor` is a wrapper around `ABinderRpc_Accessor` which is +/// `Sync` and `Send`. As +/// `ABinderRpc_Accessor` is threadsafe, this structure is too. +/// The Fn owned the Accessor has `Sync` and `Send` properties +unsafe impl Send for Accessor {} + +/// Safety: A `Accessor` is a wrapper around `ABinderRpc_Accessor` which is +/// `Sync` and `Send`. As `ABinderRpc_Accessor` is threadsafe, this structure is too. +/// The Fn owned the Accessor has `Sync` and `Send` properties +unsafe impl Sync for Accessor {} + +impl Accessor { + /// Create a new accessor that will call the given callback when its + /// connection info is required. + /// The callback object and all objects it captures are owned by the Accessor + /// and will be deleted some time after the Accessor is Dropped. If the callback + /// is being called when the Accessor is Dropped, the callback will not be deleted + /// immediately. + pub fn new<F>(instance: &str, callback: F) -> Accessor + where + F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static, + { + let callback: *mut c_void = Arc::into_raw(Arc::new(callback)) as *mut c_void; + let inst = CString::new(instance).unwrap(); + + // Safety: The function pointer is a valid connection_info callback. + // This call returns an owned `ABinderRpc_Accessor` pointer which + // must be destroyed via `ABinderRpc_Accessor_delete` when no longer + // needed. + // When the underlying ABinderRpc_Accessor is deleted, it will call + // the cookie_decr_refcount callback to release its strong ref. + let accessor = unsafe { + sys::ABinderRpc_Accessor_new( + inst.as_ptr(), + Some(Self::connection_info::<F>), + callback, + Some(Self::cookie_decr_refcount::<F>), + ) + }; + + Accessor { accessor } + } + + /// Get the underlying binder for this Accessor for when it needs to be either + /// registered with service manager or sent to another process. + pub fn as_binder(&self) -> Option<SpIBinder> { + // Safety: `ABinderRpc_Accessor_asBinder` returns either a null pointer or a + // valid pointer to an owned `AIBinder`. Either of these values is safe to + // pass to `SpIBinder::from_raw`. + unsafe { SpIBinder::from_raw(sys::ABinderRpc_Accessor_asBinder(self.accessor)) } + } + + /// Callback invoked from C++ when the connection info is needed. + /// + /// # Safety + /// + /// The `instance` parameter must be a non-null pointer to a valid C string for + /// CStr::from_ptr. The memory must contain a valid null terminator at the end of + /// the string within isize::MAX from the pointer. The memory must not be mutated for + /// the duration of this function call and must be valid for reads from the pointer + /// to the null terminator. + /// The `cookie` parameter must be the cookie for an `Arc<F>` and + /// the caller must hold a ref-count to it. + unsafe extern "C" fn connection_info<F>( + instance: *const c_char, + cookie: *mut c_void, + ) -> *mut binder_ndk_sys::ABinderRpc_ConnectionInfo + where + F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static, + { + if cookie.is_null() || instance.is_null() { + log::error!("Cookie({cookie:p}) or instance({instance:p}) is null!"); + return ptr::null_mut(); + } + // Safety: The caller promises that `cookie` is for an Arc<F>. + let callback = unsafe { (cookie as *const F).as_ref().unwrap() }; + + // Safety: The caller in libbinder_ndk will have already verified this is a valid + // C string + let inst = unsafe { + match CStr::from_ptr(instance).to_str() { + Ok(s) => s, + Err(err) => { + log::error!("Failed to get a valid C string! {err:?}"); + return ptr::null_mut(); + } + } + }; + + let connection = match callback(inst) { + Some(con) => con, + None => { + return ptr::null_mut(); + } + }; + + match connection { + ConnectionInfo::Vsock(addr) => { + // Safety: The sockaddr is being copied in the NDK API + unsafe { sys::ABinderRpc_ConnectionInfo_new(addr.as_ptr(), addr.len()) } + } + ConnectionInfo::Unix(addr) => { + // Safety: The sockaddr is being copied in the NDK API + // The cast is from sockaddr_un* to sockaddr*. + unsafe { + sys::ABinderRpc_ConnectionInfo_new(addr.as_ptr() as *const sockaddr, addr.len()) + } + } + } + } + + /// Callback that decrements the ref-count. + /// This is invoked from C++ when a binder is unlinked. + /// + /// # Safety + /// + /// The `cookie` parameter must be the cookie for an `Arc<F>` and + /// the owner must give up a ref-count to it. + unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void) + where + F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static, + { + // Safety: The caller promises that `cookie` is for an Arc<F>. + unsafe { Arc::decrement_strong_count(cookie as *const F) }; + } +} + +impl Drop for Accessor { + fn drop(&mut self) { + // Safety: `self.accessor` is always a valid, owned + // `ABinderRpc_Accessor` pointer returned by + // `ABinderRpc_Accessor_new` when `self` was created. This delete + // method can only be called once when `self` is dropped. + unsafe { + sys::ABinderRpc_Accessor_delete(self.accessor); + } + } +} diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp index 65fa2ca905..bd666fe0d7 100644 --- a/libs/binder/rust/sys/BinderBindings.hpp +++ b/libs/binder/rust/sys/BinderBindings.hpp @@ -20,6 +20,7 @@ #include <android/binder_parcel.h> #include <android/binder_parcel_platform.h> #include <android/binder_process.h> +#include <android/binder_rpc.h> #include <android/binder_shell.h> #include <android/binder_stability.h> #include <android/binder_status.h> diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs index 5359832da1..bdb7e4a8d8 100644 --- a/libs/binder/rust/tests/integration.rs +++ b/libs/binder/rust/tests/integration.rs @@ -384,8 +384,8 @@ mod tests { use std::time::Duration; use binder::{ - BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode, - Strong, + Accessor, BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, + StatusCode, Strong, }; // Import from impl API for testing only, should not be necessary as long as // you are using AIDL. @@ -908,6 +908,43 @@ mod tests { assert_eq!(service.test().unwrap(), service_name); } + struct ToBeDeleted { + deleted: Arc<AtomicBool>, + } + + impl Drop for ToBeDeleted { + fn drop(&mut self) { + assert!(!self.deleted.load(Ordering::Relaxed)); + self.deleted.store(true, Ordering::Relaxed); + } + } + + #[test] + fn test_accessor_callback_destruction() { + let deleted: Arc<AtomicBool> = Arc::new(AtomicBool::new(false)); + { + let accessor: Accessor; + { + let helper = ToBeDeleted { deleted: deleted.clone() }; + let get_connection_info = move |_instance: &str| { + // Capture this object so we can see it get destructed + // after the parent scope + let _ = &helper; + None + }; + accessor = Accessor::new("foo.service", get_connection_info); + } + + match accessor.as_binder() { + Some(_) => { + assert!(!deleted.load(Ordering::Relaxed)); + } + None => panic!("failed to get that accessor binder"), + } + } + assert!(deleted.load(Ordering::Relaxed)); + } + #[tokio::test] async fn reassociate_rust_binder_async() { let service_name = "testing_service"; |