diff options
author | 2024-11-15 23:25:59 +0000 | |
---|---|---|
committer | 2024-11-20 19:22:05 +0000 | |
commit | 7860d408c5d5bd6945cade8268f7cd0d03d7fc58 (patch) | |
tree | 7bfec2220ad8d57a9ad449fe9b9f076243f027d4 | |
parent | 30a4c27daa652a77c54720f65bccc9692a7f9489 (diff) |
Add AccessorProvider to libbinder_rs
This allows Rust clients to inject AccessorProviders into libbinder to
enable the use of service management APIs to get services over sockets.
Test: atest vm_accessor_test
Bug: 358427181
Change-Id: I88bd4df2ece890d74c40a781eab0303182b680cf
-rw-r--r-- | libs/binder/rust/src/lib.rs | 2 | ||||
-rw-r--r-- | libs/binder/rust/src/system_only.rs | 178 | ||||
-rw-r--r-- | libs/binder/rust/tests/integration.rs | 60 |
3 files changed, 228 insertions, 12 deletions
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index 14493db262..1b24b0a1e1 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -126,7 +126,7 @@ pub use service::{get_interface, get_service}; #[cfg(not(any(trusty, android_ndk)))] pub use state::{ProcessState, ThreadState}; #[cfg(not(any(android_vendor, android_vndk, android_ndk)))] -pub use system_only::{delegate_accessor, Accessor, ConnectionInfo}; +pub use system_only::{delegate_accessor, Accessor, AccessorProvider, 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 index 9833cbe4e2..3da59ab811 100644 --- a/libs/binder/rust/src/system_only.rs +++ b/libs/binder/rust/src/system_only.rs @@ -100,17 +100,36 @@ impl Accessor { unsafe { SpIBinder::from_raw(sys::ABinderRpc_Accessor_asBinder(self.accessor)) } } + /// Release the underlying ABinderRpc_Accessor pointer for use with the ndk API + /// This gives up ownership of the ABinderRpc_Accessor and it is the responsibility of + /// the caller to delete it with ABinderRpc_Accessor_delete + /// + /// # Safety + /// + /// - The returned `ABinderRpc_Accessor` pointer is now owned by the caller, who must + /// call `ABinderRpc_Accessor_delete` to delete the object. + /// - This `Accessor` object is now useless after `release` so it can be dropped. + unsafe fn release(mut self) -> *mut sys::ABinderRpc_Accessor { + if self.accessor.is_null() { + log::error!("Attempting to release an Accessor that was already released"); + return ptr::null_mut(); + } + let ptr = self.accessor; + self.accessor = ptr::null_mut(); + ptr + } + /// 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. + /// - 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, @@ -172,8 +191,8 @@ impl Accessor { /// /// # Safety /// - /// The `cookie` parameter must be the cookie for an `Arc<F>` and - /// the owner must give up a ref-count to it. + /// - 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, @@ -185,6 +204,10 @@ impl Accessor { impl Drop for Accessor { fn drop(&mut self) { + if self.accessor.is_null() { + // This Accessor was already released. + return; + } // Safety: `self.accessor` is always a valid, owned // `ABinderRpc_Accessor` pointer returned by // `ABinderRpc_Accessor_new` when `self` was created. This delete @@ -218,3 +241,140 @@ pub fn delegate_accessor(name: &str, mut binder: SpIBinder) -> Result<SpIBinder> // point, so can be safely passed to `SpIBinder::from_raw`. Ok(unsafe { SpIBinder::from_raw(delegator).expect("Expected valid binder at this point") }) } + +/// Rust wrapper around ABinderRpc_AccessorProvider objects for RPC binder service management. +/// +/// Dropping the `AccessorProvider` will drop/unregister the underlying object. +#[derive(Debug)] +pub struct AccessorProvider { + accessor_provider: *mut sys::ABinderRpc_AccessorProvider, +} + +/// Safety: A `AccessorProvider` is a wrapper around `ABinderRpc_AccessorProvider` which is +/// `Sync` and `Send`. As +/// `ABinderRpc_AccessorProvider` is threadsafe, this structure is too. +/// The Fn owned the AccessorProvider has `Sync` and `Send` properties +unsafe impl Send for AccessorProvider {} + +/// Safety: A `AccessorProvider` is a wrapper around `ABinderRpc_AccessorProvider` which is +/// `Sync` and `Send`. As `ABinderRpc_AccessorProvider` is threadsafe, this structure is too. +/// The Fn owned the AccessorProvider has `Sync` and `Send` properties +unsafe impl Sync for AccessorProvider {} + +impl AccessorProvider { + /// Create a new `AccessorProvider` that will give libbinder `Accessors` in order to + /// connect to binder services over sockets. + /// + /// `instances` is a list of all instances that this `AccessorProvider` is responsible for. + /// It is declaring these instances as available to this process and will return + /// `Accessor` objects for them when libbinder calls the `provider` callback. + /// `provider` is the callback that libbinder will call when a service is being requested. + /// The callback takes a `&str` argument representing the service that is being requested. + /// See the `ABinderRpc_AccessorProvider_getAccessorCallback` for the C++ equivalent. + pub fn new<F>(instances: &[String], provider: F) -> Option<AccessorProvider> + where + F: Fn(&str) -> Option<Accessor> + Send + Sync + 'static, + { + let callback: *mut c_void = Arc::into_raw(Arc::new(provider)) as *mut c_void; + let c_str_instances: Vec<CString> = + instances.iter().map(|s| CString::new(s.as_bytes()).unwrap()).collect(); + let mut c_instances: Vec<*const c_char> = + c_str_instances.iter().map(|s| s.as_ptr()).collect(); + let num_instances: usize = c_instances.len(); + // Safety: + // - The function pointer for the first argument is a valid `get_accessor` callback. + // - This call returns an owned `ABinderRpc_AccessorProvider` pointer which + // must be destroyed via `ABinderRpc_unregisterAccessorProvider` when no longer + // needed. + // - When the underlying ABinderRpc_AccessorProvider is deleted, it will call + // the `cookie_decr_refcount` callback on the `callback` pointer to release its + // strong ref. + // - The `c_instances` vector is not modified by the function + let accessor_provider = unsafe { + sys::ABinderRpc_registerAccessorProvider( + Some(Self::get_accessor::<F>), + c_instances.as_mut_ptr(), + num_instances, + callback, + Some(Self::accessor_cookie_decr_refcount::<F>), + ) + }; + + if accessor_provider.is_null() { + return None; + } + Some(AccessorProvider { accessor_provider }) + } + + /// Callback invoked from C++ when an Accessor is needed. + /// + /// # Safety + /// + /// - libbinder guarantees the `instance` argument is a valid C string if it's not null. + /// - The `cookie` pointer is same pointer that we pass to ABinderRpc_registerAccessorProvider + /// in AccessorProvider.new() which is the closure that we will delete with + /// self.accessor_cookie_decr_refcount when unregistering the AccessorProvider. + unsafe extern "C" fn get_accessor<F>( + instance: *const c_char, + cookie: *mut c_void, + ) -> *mut binder_ndk_sys::ABinderRpc_Accessor + where + F: Fn(&str) -> Option<Accessor> + 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() }; + + let inst = { + // Safety: The caller in libbinder_ndk will have already verified this is a valid + // C string + match unsafe { 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(); + } + } + }; + + match callback(inst) { + Some(a) => { + // Safety: This is giving up ownership of this ABinderRpc_Accessor + // to the caller of this function (libbinder) and it is responsible + // for deleting it. + unsafe { a.release() } + } + None => ptr::null_mut(), + } + } + + /// Callback that decrements the ref-count. + /// This is invoked from C++ when the provider is unregistered. + /// + /// # 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 accessor_cookie_decr_refcount<F>(cookie: *mut c_void) + where + F: Fn(&str) -> Option<Accessor> + 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 AccessorProvider { + fn drop(&mut self) { + // Safety: `self.accessor_provider` is always a valid, owned + // `ABinderRpc_AccessorProvider` pointer returned by + // `ABinderRpc_registerAccessorProvider` when `self` was created. This delete + // method can only be called once when `self` is dropped. + unsafe { + sys::ABinderRpc_unregisterAccessorProvider(self.accessor_provider); + } + } +} diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs index 489fa0aa69..0e793e5761 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::{ - Accessor, BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, - StatusCode, Strong, + Accessor, AccessorProvider, 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. @@ -982,6 +982,62 @@ mod tests { assert_eq!(delegator_binder, Err(StatusCode::NAME_NOT_FOUND)); } + #[test] + fn test_accessor_provider_simple() { + let instances: Vec<String> = vec!["foo.service".to_owned(), "foo.other_service".to_owned()]; + let accessor = AccessorProvider::new(&instances, move |_inst: &str| None); + assert!(accessor.is_some()); + } + + #[test] + fn test_accessor_provider_no_instance() { + let instances: Vec<String> = vec![]; + let accessor = AccessorProvider::new(&instances, move |_inst: &str| None); + assert!(accessor.is_none()); + } + + #[test] + fn test_accessor_provider_double_register() { + let instances: Vec<String> = vec!["foo.service".to_owned(), "foo.other_service".to_owned()]; + let accessor = AccessorProvider::new(&instances, move |_inst: &str| None); + assert!(accessor.is_some()); + let accessor2 = AccessorProvider::new(&instances, move |_inst: &str| None); + assert!(accessor2.is_none()); + } + + #[test] + fn test_accessor_provider_register_drop_register() { + let instances: Vec<String> = vec!["foo.service".to_owned(), "foo.other_service".to_owned()]; + { + let accessor = AccessorProvider::new(&instances, move |_inst: &str| None); + assert!(accessor.is_some()); + // accessor drops and unregisters the provider + } + { + let accessor = AccessorProvider::new(&instances, move |_inst: &str| None); + assert!(accessor.is_some()); + } + } + + #[test] + fn test_accessor_provider_callback_destruction() { + let deleted: Arc<AtomicBool> = Arc::new(AtomicBool::new(false)); + let instances: Vec<String> = vec!["foo.service".to_owned(), "foo.other_service".to_owned()]; + { + let accessor: Option<AccessorProvider>; + { + let helper = ToBeDeleted { deleted: deleted.clone() }; + accessor = AccessorProvider::new(&instances, move |_inst: &str| { + let _ = &helper; + None + }); + } + assert!(accessor.is_some()); + assert!(!deleted.load(Ordering::Relaxed)); + } + assert!(deleted.load(Ordering::Relaxed)); + } + #[tokio::test] async fn reassociate_rust_binder_async() { let service_name = "testing_service"; |