Loading libs/binder/rust/Android.bp +19 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ rust_library { "libbinder_ndk_sys", "libdowncast_rs", "liblibc", "liblog_rust", "libnix", ], host_supported: true, vendor_available: true, Loading Loading @@ -79,6 +81,9 @@ rust_library { shared_libs: [ "libbinder_ndk", ], rustlibs: [ "liblibc", ], host_supported: true, vendor_available: true, product_available: true, Loading Loading @@ -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, Loading Loading @@ -185,6 +199,8 @@ rust_test { "libbinder_ndk_sys", "libdowncast_rs", "liblibc", "liblog_rust", "libnix", ], } Loading @@ -196,4 +212,7 @@ rust_test { auto_gen_config: true, clippy_lints: "none", lints: "none", rustlibs: [ "liblibc", ], } libs/binder/rust/src/lib.rs +4 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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>; Loading libs/binder/rust/src/system_only.rs 0 → 100644 +187 −0 Original line number Diff line number Diff line /* * 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); } } } libs/binder/rust/sys/BinderBindings.hpp +1 −0 Original line number Diff line number Diff line Loading @@ -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> Loading libs/binder/rust/tests/integration.rs +39 −2 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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"; Loading Loading
libs/binder/rust/Android.bp +19 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ rust_library { "libbinder_ndk_sys", "libdowncast_rs", "liblibc", "liblog_rust", "libnix", ], host_supported: true, vendor_available: true, Loading Loading @@ -79,6 +81,9 @@ rust_library { shared_libs: [ "libbinder_ndk", ], rustlibs: [ "liblibc", ], host_supported: true, vendor_available: true, product_available: true, Loading Loading @@ -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, Loading Loading @@ -185,6 +199,8 @@ rust_test { "libbinder_ndk_sys", "libdowncast_rs", "liblibc", "liblog_rust", "libnix", ], } Loading @@ -196,4 +212,7 @@ rust_test { auto_gen_config: true, clippy_lints: "none", lints: "none", rustlibs: [ "liblibc", ], }
libs/binder/rust/src/lib.rs +4 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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>; Loading
libs/binder/rust/src/system_only.rs 0 → 100644 +187 −0 Original line number Diff line number Diff line /* * 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); } } }
libs/binder/rust/sys/BinderBindings.hpp +1 −0 Original line number Diff line number Diff line Loading @@ -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> Loading
libs/binder/rust/tests/integration.rs +39 −2 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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"; Loading