summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2022-02-24 01:47:37 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2022-02-24 01:47:37 +0000
commit02ece05bcfc5b23c573684816e64490e74ef18cf (patch)
treecb8a9a4ac7f22a08b925c59aeacd9324718629b1
parent0f899ce8dcff7f47dbbe20a9fae27534f1fc19ef (diff)
parent098bbc92950cd6c734642e65882ced0e28bc4be7 (diff)
downloadnative-02ece05bcfc5b23c573684816e64490e74ef18cf.tar.gz
Merge "[binder_rs] Add get_declared_instances and is_declared APIs"
-rw-r--r--libs/binder/rust/src/lib.rs4
-rw-r--r--libs/binder/rust/src/proxy.rs58
-rw-r--r--libs/binder/rust/tests/integration.rs15
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);