diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2022-02-24 01:47:37 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2022-02-24 01:47:37 +0000 |
commit | 02ece05bcfc5b23c573684816e64490e74ef18cf (patch) | |
tree | cb8a9a4ac7f22a08b925c59aeacd9324718629b1 | |
parent | 0f899ce8dcff7f47dbbe20a9fae27534f1fc19ef (diff) | |
parent | 098bbc92950cd6c734642e65882ced0e28bc4be7 (diff) | |
download | native-02ece05bcfc5b23c573684816e64490e74ef18cf.tar.gz |
Merge "[binder_rs] Add get_declared_instances and is_declared APIs"
-rw-r--r-- | libs/binder/rust/src/lib.rs | 4 | ||||
-rw-r--r-- | libs/binder/rust/src/proxy.rs | 58 | ||||
-rw-r--r-- | libs/binder/rust/tests/integration.rs | 15 |
3 files changed, 74 insertions, 3 deletions
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index 1d7de98c0d..dbfb1c20a5 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -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}; diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs index 12bfde755e..e3e47305d4 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -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; @@ -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 diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs index 50daf1c0cc..7c5afde85c 100644 --- a/libs/binder/rust/tests/integration.rs +++ b/libs/binder/rust/tests/integration.rs @@ -484,6 +484,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"; let _process = ScopedServiceProcess::new(service_name); |