diff options
-rw-r--r-- | libs/binder/ndk/Android.bp | 2 | ||||
-rw-r--r-- | libs/binder/rust/src/binder.rs | 57 | ||||
-rw-r--r-- | libs/binder/rust/src/lib.rs | 4 | ||||
-rw-r--r-- | libs/binder/rust/tests/integration.rs | 108 |
4 files changed, 167 insertions, 4 deletions
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index ee46fcb884..42895740ea 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -38,7 +38,7 @@ cc_defaults { host: { cflags: [ "-D__INTRODUCED_IN(n)=", - "-D__assert(a,b,c)=", + "-D__assert(a,b,c)=do { syslog(LOG_ERR, a \": \" c); abort(); } while(false)", // We want all the APIs to be available on the host. "-D__ANDROID_API__=10000", ], diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index 3d2eddf611..4d6b294000 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -66,6 +66,35 @@ pub trait Interface: Send + Sync { } } +/// Implemented by sync interfaces to specify what the associated async interface is. +/// Generic to handle the fact that async interfaces are generic over a thread pool. +/// +/// The binder in any object implementing this trait should be compatible with the +/// `Target` associated type, and using `FromIBinder` to convert it to the target +/// should not fail. +pub trait ToAsyncInterface<P> +where + Self: Interface, + Self::Target: FromIBinder, +{ + /// The async interface associated with this sync interface. + type Target: ?Sized; +} + +/// Implemented by async interfaces to specify what the associated sync interface is. +/// +/// The binder in any object implementing this trait should be compatible with the +/// `Target` associated type, and using `FromIBinder` to convert it to the target +/// should not fail. +pub trait ToSyncInterface +where + Self: Interface, + Self::Target: FromIBinder, +{ + /// The sync interface associated with this async interface. + type Target: ?Sized; +} + /// Interface stability promise /// /// An interface can promise to be a stable vendor interface ([`Vintf`]), or @@ -337,6 +366,26 @@ impl<I: FromIBinder + ?Sized> Strong<I> { pub fn downgrade(this: &Strong<I>) -> Weak<I> { Weak::new(this) } + + /// Convert this synchronous binder handle into an asynchronous one. + pub fn into_async<P>(self) -> Strong<<I as ToAsyncInterface<P>>::Target> + where + I: ToAsyncInterface<P>, + { + // By implementing the ToAsyncInterface trait, it is guaranteed that the binder + // object is also valid for the target type. + FromIBinder::try_from(self.0.as_binder()).unwrap() + } + + /// Convert this asynchronous binder handle into a synchronous one. + pub fn into_sync(self) -> Strong<<I as ToSyncInterface>::Target> + where + I: ToSyncInterface, + { + // By implementing the ToSyncInterface trait, it is guaranteed that the binder + // object is also valid for the target type. + FromIBinder::try_from(self.0.as_binder()).unwrap() + } } impl<I: FromIBinder + ?Sized> Clone for Strong<I> { @@ -1017,6 +1066,14 @@ macro_rules! declare_binder_interface { .expect(concat!("Error cloning interface ", stringify!($async_interface))) } } + + impl<P: $crate::BinderAsyncPool> $crate::ToAsyncInterface<P> for dyn $interface { + type Target = dyn $async_interface<P>; + } + + impl<P: $crate::BinderAsyncPool> $crate::ToSyncInterface for dyn $async_interface<P> { + type Target = dyn $interface; + } )? }; } diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index b94dfa137e..7c04a7207b 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -109,8 +109,8 @@ pub mod parcel; pub use crate::binder::{ BinderFeatures, FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Remotable, - Stability, Strong, TransactionCode, TransactionFlags, Weak, FIRST_CALL_TRANSACTION, - FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION, + Stability, Strong, ToAsyncInterface, ToSyncInterface, TransactionCode, TransactionFlags, Weak, + FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION, }; pub use crate::binder_async::{BoxFuture, BinderAsyncPool}; pub use error::{status_t, ExceptionCode, Result, Status, StatusCode}; diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs index 40359b4749..80dc47682c 100644 --- a/libs/binder/rust/tests/integration.rs +++ b/libs/binder/rust/tests/integration.rs @@ -100,6 +100,7 @@ enum TestTransactionCode { Test = FIRST_CALL_TRANSACTION, GetDumpArgs, GetSelinuxContext, + GetIsHandlingTransaction, } impl TryFrom<u32> for TestTransactionCode { @@ -112,6 +113,7 @@ impl TryFrom<u32> for TestTransactionCode { _ if c == TestTransactionCode::GetSelinuxContext as u32 => { Ok(TestTransactionCode::GetSelinuxContext) } + _ if c == TestTransactionCode::GetIsHandlingTransaction as u32 => Ok(TestTransactionCode::GetIsHandlingTransaction), _ => Err(StatusCode::UNKNOWN_TRANSACTION), } } @@ -140,6 +142,10 @@ impl ITest for TestService { ThreadState::with_calling_sid(|sid| sid.map(|s| s.to_string_lossy().into_owned())); sid.ok_or(StatusCode::UNEXPECTED_NULL) } + + fn get_is_handling_transaction(&self) -> binder::Result<bool> { + Ok(binder::is_handling_transaction()) + } } /// Trivial testing binder interface @@ -152,6 +158,9 @@ pub trait ITest: Interface { /// Returns the caller's SELinux context fn get_selinux_context(&self) -> binder::Result<String>; + + /// Returns the value of calling `is_handling_transaction`. + fn get_is_handling_transaction(&self) -> binder::Result<bool>; } /// Async trivial testing binder interface @@ -164,6 +173,9 @@ pub trait IATest<P>: Interface { /// Returns the caller's SELinux context fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>>; + + /// Returns the value of calling `is_handling_transaction`. + fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>>; } declare_binder_interface! { @@ -186,6 +198,7 @@ fn on_transact( TestTransactionCode::Test => reply.write(&service.test()?), TestTransactionCode::GetDumpArgs => reply.write(&service.get_dump_args()?), TestTransactionCode::GetSelinuxContext => reply.write(&service.get_selinux_context()?), + TestTransactionCode::GetIsHandlingTransaction => reply.write(&service.get_is_handling_transaction()?), } } @@ -212,6 +225,15 @@ impl ITest for BpTest { )?; reply.read() } + + fn get_is_handling_transaction(&self) -> binder::Result<bool> { + let reply = self.binder.transact( + TestTransactionCode::GetIsHandlingTransaction as TransactionCode, + 0, + |_| Ok(()), + )?; + reply.read() + } } impl<P: binder::BinderAsyncPool> IATest<P> for BpTest { @@ -238,6 +260,14 @@ impl<P: binder::BinderAsyncPool> IATest<P> for BpTest { |reply| async move { reply?.read() } ) } + + fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>> { + let binder = self.binder.clone(); + P::spawn( + move || binder.transact(TestTransactionCode::GetIsHandlingTransaction as TransactionCode, 0, |_| Ok(())), + |reply| async move { reply?.read() } + ) + } } impl ITest for Binder<BnTest> { @@ -252,6 +282,10 @@ impl ITest for Binder<BnTest> { fn get_selinux_context(&self) -> binder::Result<String> { self.0.get_selinux_context() } + + fn get_is_handling_transaction(&self) -> binder::Result<bool> { + self.0.get_is_handling_transaction() + } } impl<P: binder::BinderAsyncPool> IATest<P> for Binder<BnTest> { @@ -269,6 +303,11 @@ impl<P: binder::BinderAsyncPool> IATest<P> for Binder<BnTest> { let res = self.0.get_selinux_context(); Box::pin(async move { res }) } + + fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>> { + let res = self.0.get_is_handling_transaction(); + Box::pin(async move { res }) + } } /// Trivial testing binder interface @@ -500,7 +539,7 @@ mod tests { #[tokio::test] async fn get_selinux_context_async() { - let service_name = "get_selinux_context"; + let service_name = "get_selinux_context_async"; let _process = ScopedServiceProcess::new(service_name); let test_client: Strong<dyn IATest<Tokio>> = binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service"); @@ -510,6 +549,32 @@ mod tests { ); } + #[tokio::test] + async fn get_selinux_context_sync_to_async() { + let service_name = "get_selinux_context"; + let _process = ScopedServiceProcess::new(service_name); + let test_client: Strong<dyn ITest> = + binder::get_interface(service_name).expect("Did not get manager binder service"); + let test_client = test_client.into_async::<Tokio>(); + assert_eq!( + test_client.get_selinux_context().await.unwrap(), + get_expected_selinux_context() + ); + } + + #[tokio::test] + async fn get_selinux_context_async_to_sync() { + let service_name = "get_selinux_context"; + let _process = ScopedServiceProcess::new(service_name); + let test_client: Strong<dyn IATest<Tokio>> = + binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service"); + let test_client = test_client.into_sync(); + assert_eq!( + test_client.get_selinux_context().unwrap(), + get_expected_selinux_context() + ); + } + struct Bools { binder_died: Arc<AtomicBool>, binder_dealloc: Arc<AtomicBool>, @@ -867,4 +932,45 @@ mod tests { Err(err) => assert_eq!(err, binder::StatusCode::BAD_VALUE), } } + + #[test] + fn get_is_handling_transaction() { + let service_name = "get_is_handling_transaction"; + let _process = ScopedServiceProcess::new(service_name); + let test_client: Strong<dyn ITest> = + binder::get_interface(service_name).expect("Did not get manager binder service"); + // Should be true externally. + assert!(test_client.get_is_handling_transaction().unwrap()); + + // Should be false locally. + assert!(!binder::is_handling_transaction()); + + // Should also be false in spawned thread. + std::thread::spawn(|| { + assert!(!binder::is_handling_transaction()); + }).join().unwrap(); + } + + #[tokio::test] + async fn get_is_handling_transaction_async() { + let service_name = "get_is_handling_transaction_async"; + let _process = ScopedServiceProcess::new(service_name); + let test_client: Strong<dyn IATest<Tokio>> = + binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service"); + // Should be true externally. + assert!(test_client.get_is_handling_transaction().await.unwrap()); + + // Should be false locally. + assert!(!binder::is_handling_transaction()); + + // Should also be false in spawned task. + tokio::spawn(async { + assert!(!binder::is_handling_transaction()); + }).await.unwrap(); + + // And in spawn_blocking task. + tokio::task::spawn_blocking(|| { + assert!(!binder::is_handling_transaction()); + }).await.unwrap(); + } } |