Add wait_for_interface and wait_for_service.
Bug: 183582651
Test: atest rustBinderTest rustBinderTestService
Test: atest VirtualizationTestCases
Change-Id: I7e25b61ac6717338642ff4d5588e6d34c656382d
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 2694cba..7c0584b 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -115,14 +115,14 @@
pub use native::add_service;
pub use native::Binder;
pub use parcel::Parcel;
-pub use proxy::{get_interface, get_service};
+pub use proxy::{get_interface, get_service, wait_for_interface, wait_for_service};
pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder};
pub use state::{ProcessState, ThreadState};
/// The public API usable outside AIDL-generated interface crates.
pub mod public_api {
pub use super::parcel::ParcelFileDescriptor;
- pub use super::{add_service, get_interface};
+ pub use super::{add_service, get_interface, wait_for_interface};
pub use super::{
BinderFeatures, DeathRecipient, ExceptionCode, IBinder, Interface, ProcessState, SpIBinder,
Status, StatusCode, Strong, ThreadState, Weak, WpIBinder,
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 52036f5..4a6d118 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -653,6 +653,18 @@
}
}
+/// Retrieve an existing service, or start it if it is configured as a dynamic
+/// service and isn't yet started.
+pub fn wait_for_service(name: &str) -> Option<SpIBinder> {
+ let name = CString::new(name).ok()?;
+ unsafe {
+ // Safety: `AServiceManager_waitforService` 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`.
+ SpIBinder::from_raw(sys::AServiceManager_waitForService(name.as_ptr()))
+ }
+}
+
/// Retrieve an existing service for a particular interface, blocking for a few
/// seconds if it doesn't yet exist.
pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
@@ -663,6 +675,16 @@
}
}
+/// Retrieve an existing service for a particular interface, or start it if it
+/// is configured as a dynamic service and isn't yet started.
+pub fn wait_for_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
+ let service = wait_for_service(name);
+ match service {
+ Some(service) => FromIBinder::try_from(service),
+ None => Err(StatusCode::NAME_NOT_FOUND),
+ }
+}
+
/// # 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 0332007..10b77f4 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -274,6 +274,20 @@
}
#[test]
+ fn check_wait_for_service() {
+ let mut sm =
+ binder::wait_for_service("manager").expect("Did not get manager binder service");
+ assert!(sm.is_binder_alive());
+ assert!(sm.ping_binder().is_ok());
+
+ // The service manager service isn't an ITest, so this must fail.
+ assert_eq!(
+ binder::wait_for_interface::<dyn ITest>("manager").err(),
+ Some(StatusCode::BAD_TYPE)
+ );
+ }
+
+ #[test]
fn trivial_client() {
let service_name = "trivial_client_test";
let _process = ScopedServiceProcess::new(service_name);
@@ -283,6 +297,15 @@
}
#[test]
+ fn wait_for_trivial_client() {
+ let service_name = "wait_for_trivial_client_test";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Strong<dyn ITest> =
+ binder::wait_for_interface(service_name).expect("Did not get manager binder service");
+ assert_eq!(test_client.test().unwrap(), "wait_for_trivial_client_test");
+ }
+
+ #[test]
fn get_selinux_context() {
let service_name = "get_selinux_context";
let _process = ScopedServiceProcess::new(service_name);