Loading libs/binder/rust/src/lib.rs +2 −2 Original line number Diff line number Diff line Loading @@ -114,8 +114,8 @@ pub use native::{ }; pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder}; pub use proxy::{ get_interface, get_service, wait_for_interface, wait_for_service, DeathRecipient, SpIBinder, WpIBinder, get_declared_instances, get_interface, get_service, is_declared, wait_for_interface, wait_for_service, DeathRecipient, SpIBinder, WpIBinder, }; pub use state::{ProcessState, ThreadState}; Loading libs/binder/rust/src/proxy.rs +57 −1 Original line number Diff line number Diff line Loading @@ -28,9 +28,10 @@ use crate::sys; use std::cmp::Ordering; use std::convert::TryInto; use std::ffi::{c_void, CString}; use std::ffi::{c_void, CStr, CString}; use std::fmt; use std::mem; use std::os::raw::c_char; use std::os::unix::io::AsRawFd; use std::ptr; use std::sync::Arc; Loading Loading @@ -778,6 +779,61 @@ pub fn wait_for_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong< } } /// Check if a service is declared (e.g. in a VINTF manifest) pub fn is_declared(interface: &str) -> Result<bool> { let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?; unsafe { // Safety: `interface` is a valid null-terminated C-style string and is // only borrowed for the lifetime of the call. The `interface` local // outlives this call as it lives for the function scope. Ok(sys::AServiceManager_isDeclared(interface.as_ptr())) } } /// Retrieve all declared instances for a particular interface /// /// For instance, if 'android.foo.IFoo/foo' is declared, and 'android.foo.IFoo' /// is passed here, then ["foo"] would be returned. pub fn get_declared_instances(interface: &str) -> Result<Vec<String>> { unsafe extern "C" fn callback(instance: *const c_char, opaque: *mut c_void) { // Safety: opaque was a mutable pointer created below from a Vec of // CString, and outlives this callback. The null handling here is just // to avoid the possibility of unwinding across C code if this crate is // ever compiled with panic=unwind. if let Some(instances) = opaque.cast::<Vec<CString>>().as_mut() { // Safety: instance is a valid null-terminated C string with a // lifetime at least as long as this function, and we immediately // copy it into an owned CString. instances.push(CStr::from_ptr(instance).to_owned()); } else { eprintln!("Opaque pointer was null in get_declared_instances callback!"); } } let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?; let mut instances: Vec<CString> = vec![]; unsafe { // Safety: `interface` and `instances` are borrowed for the length of // this call and both outlive the call. `interface` is guaranteed to be // a valid null-terminated C-style string. sys::AServiceManager_forEachDeclaredInstance( interface.as_ptr(), &mut instances as *mut _ as *mut c_void, Some(callback), ); } instances .into_iter() .map(CString::into_string) .collect::<std::result::Result<Vec<String>, _>>() .map_err(|e| { eprintln!("An interface instance name was not a valid UTF-8 string: {}", e); StatusCode::BAD_VALUE }) } /// # Safety /// /// `SpIBinder` guarantees that `binder` always contains a valid pointer to an Loading libs/binder/rust/tests/integration.rs +15 −0 Original line number Diff line number Diff line Loading @@ -483,6 +483,21 @@ mod tests { ); } #[test] fn get_declared_instances() { // At the time of writing this test, there is no good VINTF interface // guaranteed to be on all devices. Cuttlefish has light, so this will // generally test things. let has_lights = binder::is_declared("android.hardware.light.ILights/default") .expect("Could not check for declared interface"); let instances = binder::get_declared_instances("android.hardware.light.ILights") .expect("Could not get declared instances"); let expected_defaults = if has_lights { 1 } else { 0 }; assert_eq!(expected_defaults, instances.iter().filter(|i| i.as_str() == "default").count()); } #[test] fn trivial_client() { let service_name = "trivial_client_test"; Loading Loading
libs/binder/rust/src/lib.rs +2 −2 Original line number Diff line number Diff line Loading @@ -114,8 +114,8 @@ pub use native::{ }; pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder}; pub use proxy::{ get_interface, get_service, wait_for_interface, wait_for_service, DeathRecipient, SpIBinder, WpIBinder, get_declared_instances, get_interface, get_service, is_declared, wait_for_interface, wait_for_service, DeathRecipient, SpIBinder, WpIBinder, }; pub use state::{ProcessState, ThreadState}; Loading
libs/binder/rust/src/proxy.rs +57 −1 Original line number Diff line number Diff line Loading @@ -28,9 +28,10 @@ use crate::sys; use std::cmp::Ordering; use std::convert::TryInto; use std::ffi::{c_void, CString}; use std::ffi::{c_void, CStr, CString}; use std::fmt; use std::mem; use std::os::raw::c_char; use std::os::unix::io::AsRawFd; use std::ptr; use std::sync::Arc; Loading Loading @@ -778,6 +779,61 @@ pub fn wait_for_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong< } } /// Check if a service is declared (e.g. in a VINTF manifest) pub fn is_declared(interface: &str) -> Result<bool> { let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?; unsafe { // Safety: `interface` is a valid null-terminated C-style string and is // only borrowed for the lifetime of the call. The `interface` local // outlives this call as it lives for the function scope. Ok(sys::AServiceManager_isDeclared(interface.as_ptr())) } } /// Retrieve all declared instances for a particular interface /// /// For instance, if 'android.foo.IFoo/foo' is declared, and 'android.foo.IFoo' /// is passed here, then ["foo"] would be returned. pub fn get_declared_instances(interface: &str) -> Result<Vec<String>> { unsafe extern "C" fn callback(instance: *const c_char, opaque: *mut c_void) { // Safety: opaque was a mutable pointer created below from a Vec of // CString, and outlives this callback. The null handling here is just // to avoid the possibility of unwinding across C code if this crate is // ever compiled with panic=unwind. if let Some(instances) = opaque.cast::<Vec<CString>>().as_mut() { // Safety: instance is a valid null-terminated C string with a // lifetime at least as long as this function, and we immediately // copy it into an owned CString. instances.push(CStr::from_ptr(instance).to_owned()); } else { eprintln!("Opaque pointer was null in get_declared_instances callback!"); } } let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?; let mut instances: Vec<CString> = vec![]; unsafe { // Safety: `interface` and `instances` are borrowed for the length of // this call and both outlive the call. `interface` is guaranteed to be // a valid null-terminated C-style string. sys::AServiceManager_forEachDeclaredInstance( interface.as_ptr(), &mut instances as *mut _ as *mut c_void, Some(callback), ); } instances .into_iter() .map(CString::into_string) .collect::<std::result::Result<Vec<String>, _>>() .map_err(|e| { eprintln!("An interface instance name was not a valid UTF-8 string: {}", e); StatusCode::BAD_VALUE }) } /// # Safety /// /// `SpIBinder` guarantees that `binder` always contains a valid pointer to an Loading
libs/binder/rust/tests/integration.rs +15 −0 Original line number Diff line number Diff line Loading @@ -483,6 +483,21 @@ mod tests { ); } #[test] fn get_declared_instances() { // At the time of writing this test, there is no good VINTF interface // guaranteed to be on all devices. Cuttlefish has light, so this will // generally test things. let has_lights = binder::is_declared("android.hardware.light.ILights/default") .expect("Could not check for declared interface"); let instances = binder::get_declared_instances("android.hardware.light.ILights") .expect("Could not get declared instances"); let expected_defaults = if has_lights { 1 } else { 0 }; assert_eq!(expected_defaults, instances.iter().filter(|i| i.as_str() == "default").count()); } #[test] fn trivial_client() { let service_name = "trivial_client_test"; Loading