Loading libs/binder/rust/src/binder.rs +28 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ use std::borrow::Borrow; use std::cmp::Ordering; use std::ffi::{c_void, CStr, CString}; use std::fmt; use std::fs::File; use std::marker::PhantomData; use std::ops::Deref; use std::os::raw::c_char; Loading Loading @@ -54,6 +55,14 @@ pub trait Interface: Send { fn as_binder(&self) -> SpIBinder { panic!("This object was not a Binder object and cannot be converted into an SpIBinder.") } /// Dump transaction handler for this Binder object. /// /// This handler is a no-op by default and should be implemented for each /// Binder service struct that wishes to respond to dump transactions. fn dump(&self, _file: &File, _args: &[&CStr]) -> Result<()> { Ok(()) } } /// Interface stability promise Loading Loading @@ -98,6 +107,10 @@ pub trait Remotable: Send + Sync { /// `reply` may be [`None`] if the sender does not expect a reply. fn on_transact(&self, code: TransactionCode, data: &Parcel, reply: &mut Parcel) -> Result<()>; /// Handle a request to invoke the dump transaction on this /// object. fn on_dump(&self, file: &File, args: &[&CStr]) -> Result<()>; /// Retrieve the class of this remote object. /// /// This method should always return the same InterfaceClass for the same Loading Loading @@ -218,7 +231,7 @@ impl InterfaceClass { if class.is_null() { panic!("Expected non-null class pointer from AIBinder_Class_define!"); } sys::AIBinder_Class_setOnDump(class, None); sys::AIBinder_Class_setOnDump(class, Some(I::on_dump)); sys::AIBinder_Class_setHandleShellCommand(class, None); class }; Loading Loading @@ -492,6 +505,16 @@ pub trait InterfaceClassMethods { /// returned by `on_create` for this class. This function takes ownership of /// the provided pointer and destroys it. unsafe extern "C" fn on_destroy(object: *mut c_void); /// Called to handle the `dump` transaction. /// /// # Safety /// /// Must be called with a non-null, valid pointer to a local `AIBinder` that /// contains a `T` pointer in its user data. fd should be a non-owned file /// descriptor, and args must be an array of null-terminated string /// poiinters with length num_args. unsafe extern "C" fn on_dump(binder: *mut sys::AIBinder, fd: i32, args: *mut *const c_char, num_args: u32) -> status_t; } /// Interface for transforming a generic SpIBinder into a specific remote Loading Loading @@ -778,6 +801,10 @@ macro_rules! declare_binder_interface { } } fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> $crate::Result<()> { self.0.dump(file, args) } fn get_class() -> $crate::InterfaceClass { static CLASS_INIT: std::sync::Once = std::sync::Once::new(); static mut CLASS: Option<$crate::InterfaceClass> = None; Loading libs/binder/rust/src/native.rs +40 −1 Original line number Diff line number Diff line Loading @@ -21,9 +21,13 @@ use crate::proxy::SpIBinder; use crate::sys; use std::convert::TryFrom; use std::ffi::{c_void, CString}; use std::ffi::{c_void, CStr, CString}; use std::fs::File; use std::mem::ManuallyDrop; use std::ops::Deref; use std::os::raw::c_char; use std::os::unix::io::FromRawFd; use std::slice; /// Rust wrapper around Binder remotable objects. /// Loading Loading @@ -289,6 +293,37 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { // object created by Box. args } /// Called to handle the `dump` transaction. /// /// # Safety /// /// Must be called with a non-null, valid pointer to a local `AIBinder` that /// contains a `T` pointer in its user data. fd should be a non-owned file /// descriptor, and args must be an array of null-terminated string /// poiinters with length num_args. unsafe extern "C" fn on_dump(binder: *mut sys::AIBinder, fd: i32, args: *mut *const c_char, num_args: u32) -> status_t { if fd < 0 { return StatusCode::UNEXPECTED_NULL as status_t; } // We don't own this file, so we need to be careful not to drop it. let file = ManuallyDrop::new(File::from_raw_fd(fd)); if args.is_null() { return StatusCode::UNEXPECTED_NULL as status_t; } let args = slice::from_raw_parts(args, num_args as usize); let args: Vec<_> = args.iter().map(|s| CStr::from_ptr(*s)).collect(); let object = sys::AIBinder_getUserData(binder); let binder: &T = &*(object as *const T); let res = binder.on_dump(&file, &args); match res { Ok(()) => 0, Err(e) => e as status_t, } } } impl<T: Remotable> Drop for Binder<T> { Loading Loading @@ -409,6 +444,10 @@ impl Remotable for () { Ok(()) } fn on_dump(&self, _file: &File, _args: &[&CStr]) -> Result<()> { Ok(()) } binder_fn_get_class!(Binder::<Self>); } Loading libs/binder/rust/tests/integration.rs +61 −33 Original line number Diff line number Diff line Loading @@ -23,6 +23,9 @@ use binder::{ FIRST_CALL_TRANSACTION, }; use std::convert::{TryFrom, TryInto}; use std::ffi::CStr; use std::fs::File; use std::sync::Mutex; /// Name of service runner. /// Loading Loading @@ -50,13 +53,11 @@ fn main() -> Result<(), &'static str> { let extension_name = args.next(); { let mut service = Binder::new(BnTest(Box::new(TestService { s: service_name.clone(), }))); let mut service = Binder::new(BnTest(Box::new(TestService::new(&service_name)))); service.set_requesting_sid(true); if let Some(extension_name) = extension_name { let extension = BnTest::new_binder(TestService { s: extension_name }, BinderFeatures::default()); BnTest::new_binder(TestService::new(&extension_name), BinderFeatures::default()); service .set_extension(&mut extension.as_binder()) .expect("Could not add extension"); Loading @@ -80,14 +81,24 @@ fn print_usage() { )); } #[derive(Clone)] struct TestService { s: String, dump_args: Mutex<Vec<String>>, } impl TestService { fn new(s: &str) -> Self { Self { s: s.to_string(), dump_args: Mutex::new(Vec::new()), } } } #[repr(u32)] enum TestTransactionCode { Test = FIRST_CALL_TRANSACTION, GetDumpArgs, GetSelinuxContext, } Loading @@ -97,6 +108,7 @@ impl TryFrom<u32> for TestTransactionCode { fn try_from(c: u32) -> Result<Self, Self::Error> { match c { _ if c == TestTransactionCode::Test as u32 => Ok(TestTransactionCode::Test), _ if c == TestTransactionCode::GetDumpArgs as u32 => Ok(TestTransactionCode::GetDumpArgs), _ if c == TestTransactionCode::GetSelinuxContext as u32 => { Ok(TestTransactionCode::GetSelinuxContext) } Loading @@ -105,13 +117,24 @@ impl TryFrom<u32> for TestTransactionCode { } } impl Interface for TestService {} impl Interface for TestService { fn dump(&self, _file: &File, args: &[&CStr]) -> binder::Result<()> { let mut dump_args = self.dump_args.lock().unwrap(); dump_args.extend(args.iter().map(|s| s.to_str().unwrap().to_owned())); Ok(()) } } impl ITest for TestService { fn test(&self) -> binder::Result<String> { Ok(self.s.clone()) } fn get_dump_args(&self) -> binder::Result<Vec<String>> { let args = self.dump_args.lock().unwrap().clone(); Ok(args) } fn get_selinux_context(&self) -> binder::Result<String> { let sid = ThreadState::with_calling_sid(|sid| sid.map(|s| s.to_string_lossy().into_owned())); Loading @@ -124,6 +147,9 @@ pub trait ITest: Interface { /// Returns a test string fn test(&self) -> binder::Result<String>; /// Return the arguments sent via dump fn get_dump_args(&self) -> binder::Result<Vec<String>>; /// Returns the caller's SELinux context fn get_selinux_context(&self) -> binder::Result<String>; } Loading @@ -145,6 +171,7 @@ fn on_transact( ) -> binder::Result<()> { match code.try_into()? { TestTransactionCode::Test => reply.write(&service.test()?), TestTransactionCode::GetDumpArgs => reply.write(&service.get_dump_args()?), TestTransactionCode::GetSelinuxContext => reply.write(&service.get_selinux_context()?), } } Loading @@ -157,6 +184,13 @@ impl ITest for BpTest { reply.read() } fn get_dump_args(&self) -> binder::Result<Vec<String>> { let reply = self.binder .transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(()))?; reply.read() } fn get_selinux_context(&self) -> binder::Result<String> { let reply = self.binder.transact( TestTransactionCode::GetSelinuxContext as TransactionCode, Loading @@ -172,6 +206,10 @@ impl ITest for Binder<BnTest> { self.0.test() } fn get_dump_args(&self) -> binder::Result<Vec<String>> { self.0.get_dump_args() } fn get_selinux_context(&self) -> binder::Result<String> { self.0.get_selinux_context() } Loading Loading @@ -432,18 +470,22 @@ mod tests { { let _process = ScopedServiceProcess::new(service_name); let mut remote = binder::get_service(service_name); let test_client: Strong<dyn ITest> = binder::get_interface(service_name) .expect("Did not get test binder service"); let mut remote = test_client.as_binder(); assert!(remote.is_binder_alive()); remote.ping_binder().expect("Could not ping remote service"); // We're not testing the output of dump here, as that's really a // property of the C++ implementation. There is the risk that the // method just does nothing, but we don't want to depend on any // particular output from the underlying library. let dump_args = ["dump", "args", "for", "testing"]; let null_out = File::open("/dev/null").expect("Could not open /dev/null"); remote .dump(&null_out, &[]) .dump(&null_out, &dump_args) .expect("Could not dump remote service"); let remote_args = test_client.get_dump_args().expect("Could not fetched dumped args"); assert_eq!(dump_args, remote_args[..], "Remote args don't match call to dump"); } // get/set_extensions is tested in test_extensions() Loading Loading @@ -504,9 +546,7 @@ mod tests { /// rust_ndk_interop.rs #[test] fn associate_existing_class() { let service = Binder::new(BnTest(Box::new(TestService { s: "testing_service".to_string(), }))); let service = Binder::new(BnTest(Box::new(TestService::new("testing_service")))); // This should succeed although we will have to treat the service as // remote. Loading @@ -520,9 +560,7 @@ mod tests { fn reassociate_rust_binder() { let service_name = "testing_service"; let service_ibinder = BnTest::new_binder( TestService { s: service_name.to_string(), }, TestService::new(service_name), BinderFeatures::default(), ) .as_binder(); Loading @@ -538,9 +576,7 @@ mod tests { fn weak_binder_upgrade() { let service_name = "testing_service"; let service = BnTest::new_binder( TestService { s: service_name.to_string(), }, TestService::new(service_name), BinderFeatures::default(), ); Loading @@ -556,9 +592,7 @@ mod tests { let service_name = "testing_service"; let weak = { let service = BnTest::new_binder( TestService { s: service_name.to_string(), }, TestService::new(service_name), BinderFeatures::default(), ); Loading @@ -572,9 +606,7 @@ mod tests { fn weak_binder_clone() { let service_name = "testing_service"; let service = BnTest::new_binder( TestService { s: service_name.to_string(), }, TestService::new(service_name), BinderFeatures::default(), ); Loading @@ -593,15 +625,11 @@ mod tests { #[allow(clippy::eq_op)] fn binder_ord() { let service1 = BnTest::new_binder( TestService { s: "testing_service1".to_string(), }, TestService::new("testing_service1"), BinderFeatures::default(), ); let service2 = BnTest::new_binder( TestService { s: "testing_service2".to_string(), }, TestService::new("testing_service2"), BinderFeatures::default(), ); Loading Loading
libs/binder/rust/src/binder.rs +28 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ use std::borrow::Borrow; use std::cmp::Ordering; use std::ffi::{c_void, CStr, CString}; use std::fmt; use std::fs::File; use std::marker::PhantomData; use std::ops::Deref; use std::os::raw::c_char; Loading Loading @@ -54,6 +55,14 @@ pub trait Interface: Send { fn as_binder(&self) -> SpIBinder { panic!("This object was not a Binder object and cannot be converted into an SpIBinder.") } /// Dump transaction handler for this Binder object. /// /// This handler is a no-op by default and should be implemented for each /// Binder service struct that wishes to respond to dump transactions. fn dump(&self, _file: &File, _args: &[&CStr]) -> Result<()> { Ok(()) } } /// Interface stability promise Loading Loading @@ -98,6 +107,10 @@ pub trait Remotable: Send + Sync { /// `reply` may be [`None`] if the sender does not expect a reply. fn on_transact(&self, code: TransactionCode, data: &Parcel, reply: &mut Parcel) -> Result<()>; /// Handle a request to invoke the dump transaction on this /// object. fn on_dump(&self, file: &File, args: &[&CStr]) -> Result<()>; /// Retrieve the class of this remote object. /// /// This method should always return the same InterfaceClass for the same Loading Loading @@ -218,7 +231,7 @@ impl InterfaceClass { if class.is_null() { panic!("Expected non-null class pointer from AIBinder_Class_define!"); } sys::AIBinder_Class_setOnDump(class, None); sys::AIBinder_Class_setOnDump(class, Some(I::on_dump)); sys::AIBinder_Class_setHandleShellCommand(class, None); class }; Loading Loading @@ -492,6 +505,16 @@ pub trait InterfaceClassMethods { /// returned by `on_create` for this class. This function takes ownership of /// the provided pointer and destroys it. unsafe extern "C" fn on_destroy(object: *mut c_void); /// Called to handle the `dump` transaction. /// /// # Safety /// /// Must be called with a non-null, valid pointer to a local `AIBinder` that /// contains a `T` pointer in its user data. fd should be a non-owned file /// descriptor, and args must be an array of null-terminated string /// poiinters with length num_args. unsafe extern "C" fn on_dump(binder: *mut sys::AIBinder, fd: i32, args: *mut *const c_char, num_args: u32) -> status_t; } /// Interface for transforming a generic SpIBinder into a specific remote Loading Loading @@ -778,6 +801,10 @@ macro_rules! declare_binder_interface { } } fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> $crate::Result<()> { self.0.dump(file, args) } fn get_class() -> $crate::InterfaceClass { static CLASS_INIT: std::sync::Once = std::sync::Once::new(); static mut CLASS: Option<$crate::InterfaceClass> = None; Loading
libs/binder/rust/src/native.rs +40 −1 Original line number Diff line number Diff line Loading @@ -21,9 +21,13 @@ use crate::proxy::SpIBinder; use crate::sys; use std::convert::TryFrom; use std::ffi::{c_void, CString}; use std::ffi::{c_void, CStr, CString}; use std::fs::File; use std::mem::ManuallyDrop; use std::ops::Deref; use std::os::raw::c_char; use std::os::unix::io::FromRawFd; use std::slice; /// Rust wrapper around Binder remotable objects. /// Loading Loading @@ -289,6 +293,37 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { // object created by Box. args } /// Called to handle the `dump` transaction. /// /// # Safety /// /// Must be called with a non-null, valid pointer to a local `AIBinder` that /// contains a `T` pointer in its user data. fd should be a non-owned file /// descriptor, and args must be an array of null-terminated string /// poiinters with length num_args. unsafe extern "C" fn on_dump(binder: *mut sys::AIBinder, fd: i32, args: *mut *const c_char, num_args: u32) -> status_t { if fd < 0 { return StatusCode::UNEXPECTED_NULL as status_t; } // We don't own this file, so we need to be careful not to drop it. let file = ManuallyDrop::new(File::from_raw_fd(fd)); if args.is_null() { return StatusCode::UNEXPECTED_NULL as status_t; } let args = slice::from_raw_parts(args, num_args as usize); let args: Vec<_> = args.iter().map(|s| CStr::from_ptr(*s)).collect(); let object = sys::AIBinder_getUserData(binder); let binder: &T = &*(object as *const T); let res = binder.on_dump(&file, &args); match res { Ok(()) => 0, Err(e) => e as status_t, } } } impl<T: Remotable> Drop for Binder<T> { Loading Loading @@ -409,6 +444,10 @@ impl Remotable for () { Ok(()) } fn on_dump(&self, _file: &File, _args: &[&CStr]) -> Result<()> { Ok(()) } binder_fn_get_class!(Binder::<Self>); } Loading
libs/binder/rust/tests/integration.rs +61 −33 Original line number Diff line number Diff line Loading @@ -23,6 +23,9 @@ use binder::{ FIRST_CALL_TRANSACTION, }; use std::convert::{TryFrom, TryInto}; use std::ffi::CStr; use std::fs::File; use std::sync::Mutex; /// Name of service runner. /// Loading Loading @@ -50,13 +53,11 @@ fn main() -> Result<(), &'static str> { let extension_name = args.next(); { let mut service = Binder::new(BnTest(Box::new(TestService { s: service_name.clone(), }))); let mut service = Binder::new(BnTest(Box::new(TestService::new(&service_name)))); service.set_requesting_sid(true); if let Some(extension_name) = extension_name { let extension = BnTest::new_binder(TestService { s: extension_name }, BinderFeatures::default()); BnTest::new_binder(TestService::new(&extension_name), BinderFeatures::default()); service .set_extension(&mut extension.as_binder()) .expect("Could not add extension"); Loading @@ -80,14 +81,24 @@ fn print_usage() { )); } #[derive(Clone)] struct TestService { s: String, dump_args: Mutex<Vec<String>>, } impl TestService { fn new(s: &str) -> Self { Self { s: s.to_string(), dump_args: Mutex::new(Vec::new()), } } } #[repr(u32)] enum TestTransactionCode { Test = FIRST_CALL_TRANSACTION, GetDumpArgs, GetSelinuxContext, } Loading @@ -97,6 +108,7 @@ impl TryFrom<u32> for TestTransactionCode { fn try_from(c: u32) -> Result<Self, Self::Error> { match c { _ if c == TestTransactionCode::Test as u32 => Ok(TestTransactionCode::Test), _ if c == TestTransactionCode::GetDumpArgs as u32 => Ok(TestTransactionCode::GetDumpArgs), _ if c == TestTransactionCode::GetSelinuxContext as u32 => { Ok(TestTransactionCode::GetSelinuxContext) } Loading @@ -105,13 +117,24 @@ impl TryFrom<u32> for TestTransactionCode { } } impl Interface for TestService {} impl Interface for TestService { fn dump(&self, _file: &File, args: &[&CStr]) -> binder::Result<()> { let mut dump_args = self.dump_args.lock().unwrap(); dump_args.extend(args.iter().map(|s| s.to_str().unwrap().to_owned())); Ok(()) } } impl ITest for TestService { fn test(&self) -> binder::Result<String> { Ok(self.s.clone()) } fn get_dump_args(&self) -> binder::Result<Vec<String>> { let args = self.dump_args.lock().unwrap().clone(); Ok(args) } fn get_selinux_context(&self) -> binder::Result<String> { let sid = ThreadState::with_calling_sid(|sid| sid.map(|s| s.to_string_lossy().into_owned())); Loading @@ -124,6 +147,9 @@ pub trait ITest: Interface { /// Returns a test string fn test(&self) -> binder::Result<String>; /// Return the arguments sent via dump fn get_dump_args(&self) -> binder::Result<Vec<String>>; /// Returns the caller's SELinux context fn get_selinux_context(&self) -> binder::Result<String>; } Loading @@ -145,6 +171,7 @@ fn on_transact( ) -> binder::Result<()> { match code.try_into()? { TestTransactionCode::Test => reply.write(&service.test()?), TestTransactionCode::GetDumpArgs => reply.write(&service.get_dump_args()?), TestTransactionCode::GetSelinuxContext => reply.write(&service.get_selinux_context()?), } } Loading @@ -157,6 +184,13 @@ impl ITest for BpTest { reply.read() } fn get_dump_args(&self) -> binder::Result<Vec<String>> { let reply = self.binder .transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(()))?; reply.read() } fn get_selinux_context(&self) -> binder::Result<String> { let reply = self.binder.transact( TestTransactionCode::GetSelinuxContext as TransactionCode, Loading @@ -172,6 +206,10 @@ impl ITest for Binder<BnTest> { self.0.test() } fn get_dump_args(&self) -> binder::Result<Vec<String>> { self.0.get_dump_args() } fn get_selinux_context(&self) -> binder::Result<String> { self.0.get_selinux_context() } Loading Loading @@ -432,18 +470,22 @@ mod tests { { let _process = ScopedServiceProcess::new(service_name); let mut remote = binder::get_service(service_name); let test_client: Strong<dyn ITest> = binder::get_interface(service_name) .expect("Did not get test binder service"); let mut remote = test_client.as_binder(); assert!(remote.is_binder_alive()); remote.ping_binder().expect("Could not ping remote service"); // We're not testing the output of dump here, as that's really a // property of the C++ implementation. There is the risk that the // method just does nothing, but we don't want to depend on any // particular output from the underlying library. let dump_args = ["dump", "args", "for", "testing"]; let null_out = File::open("/dev/null").expect("Could not open /dev/null"); remote .dump(&null_out, &[]) .dump(&null_out, &dump_args) .expect("Could not dump remote service"); let remote_args = test_client.get_dump_args().expect("Could not fetched dumped args"); assert_eq!(dump_args, remote_args[..], "Remote args don't match call to dump"); } // get/set_extensions is tested in test_extensions() Loading Loading @@ -504,9 +546,7 @@ mod tests { /// rust_ndk_interop.rs #[test] fn associate_existing_class() { let service = Binder::new(BnTest(Box::new(TestService { s: "testing_service".to_string(), }))); let service = Binder::new(BnTest(Box::new(TestService::new("testing_service")))); // This should succeed although we will have to treat the service as // remote. Loading @@ -520,9 +560,7 @@ mod tests { fn reassociate_rust_binder() { let service_name = "testing_service"; let service_ibinder = BnTest::new_binder( TestService { s: service_name.to_string(), }, TestService::new(service_name), BinderFeatures::default(), ) .as_binder(); Loading @@ -538,9 +576,7 @@ mod tests { fn weak_binder_upgrade() { let service_name = "testing_service"; let service = BnTest::new_binder( TestService { s: service_name.to_string(), }, TestService::new(service_name), BinderFeatures::default(), ); Loading @@ -556,9 +592,7 @@ mod tests { let service_name = "testing_service"; let weak = { let service = BnTest::new_binder( TestService { s: service_name.to_string(), }, TestService::new(service_name), BinderFeatures::default(), ); Loading @@ -572,9 +606,7 @@ mod tests { fn weak_binder_clone() { let service_name = "testing_service"; let service = BnTest::new_binder( TestService { s: service_name.to_string(), }, TestService::new(service_name), BinderFeatures::default(), ); Loading @@ -593,15 +625,11 @@ mod tests { #[allow(clippy::eq_op)] fn binder_ord() { let service1 = BnTest::new_binder( TestService { s: "testing_service1".to_string(), }, TestService::new("testing_service1"), BinderFeatures::default(), ); let service2 = BnTest::new_binder( TestService { s: "testing_service2".to_string(), }, TestService::new("testing_service2"), BinderFeatures::default(), ); Loading