Merge "libbinder_rs: Add Strong<> and Weak<> Binder references"
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 42c1e0a..d53a88f 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -16,12 +16,17 @@
//! Trait definitions for binder objects
-use crate::error::{status_t, Result};
+use crate::error::{status_t, Result, StatusCode};
use crate::parcel::Parcel;
-use crate::proxy::{DeathRecipient, SpIBinder};
+use crate::proxy::{DeathRecipient, SpIBinder, WpIBinder};
use crate::sys;
+use std::borrow::Borrow;
+use std::cmp::Ordering;
use std::ffi::{c_void, CStr, CString};
+use std::fmt;
+use std::marker::PhantomData;
+use std::ops::Deref;
use std::os::raw::c_char;
use std::os::unix::io::AsRawFd;
use std::ptr;
@@ -44,7 +49,7 @@
/// interfaces) must implement this trait.
///
/// This is equivalent `IInterface` in C++.
-pub trait Interface {
+pub trait Interface: Send {
/// Convert this binder object into a generic [`SpIBinder`] reference.
fn as_binder(&self) -> SpIBinder {
panic!("This object was not a Binder object and cannot be converted into an SpIBinder.")
@@ -230,6 +235,132 @@
}
}
+/// Strong reference to a binder object
+pub struct Strong<I: FromIBinder + ?Sized>(Box<I>);
+
+impl<I: FromIBinder + ?Sized> Strong<I> {
+ /// Create a new strong reference to the provided binder object
+ pub fn new(binder: Box<I>) -> Self {
+ Self(binder)
+ }
+
+ /// Construct a new weak reference to this binder
+ pub fn downgrade(this: &Strong<I>) -> Weak<I> {
+ Weak::new(this)
+ }
+}
+
+impl<I: FromIBinder + ?Sized> Clone for Strong<I> {
+ fn clone(&self) -> Self {
+ // Since we hold a strong reference, we should always be able to create
+ // a new strong reference to the same interface type, so try_from()
+ // should never fail here.
+ FromIBinder::try_from(self.0.as_binder()).unwrap()
+ }
+}
+
+impl<I: FromIBinder + ?Sized> Borrow<I> for Strong<I> {
+ fn borrow(&self) -> &I {
+ &self.0
+ }
+}
+
+impl<I: FromIBinder + ?Sized> AsRef<I> for Strong<I> {
+ fn as_ref(&self) -> &I {
+ &self.0
+ }
+}
+
+impl<I: FromIBinder + ?Sized> Deref for Strong<I> {
+ type Target = I;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl<I: FromIBinder + fmt::Debug + ?Sized> fmt::Debug for Strong<I> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Debug::fmt(&**self, f)
+ }
+}
+
+impl<I: FromIBinder + ?Sized> Ord for Strong<I> {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.0.as_binder().cmp(&other.0.as_binder())
+ }
+}
+
+impl<I: FromIBinder + ?Sized> PartialOrd for Strong<I> {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ self.0.as_binder().partial_cmp(&other.0.as_binder())
+ }
+}
+
+impl<I: FromIBinder + ?Sized> PartialEq for Strong<I> {
+ fn eq(&self, other: &Self) -> bool {
+ self.0.as_binder().eq(&other.0.as_binder())
+ }
+}
+
+impl<I: FromIBinder + ?Sized> Eq for Strong<I> {}
+
+/// Weak reference to a binder object
+#[derive(Debug)]
+pub struct Weak<I: FromIBinder + ?Sized> {
+ weak_binder: WpIBinder,
+ interface_type: PhantomData<I>,
+}
+
+impl<I: FromIBinder + ?Sized> Weak<I> {
+ /// Construct a new weak reference from a strong reference
+ fn new(binder: &Strong<I>) -> Self {
+ let weak_binder = binder.as_binder().downgrade();
+ Weak {
+ weak_binder,
+ interface_type: PhantomData,
+ }
+ }
+
+ /// Upgrade this weak reference to a strong reference if the binder object
+ /// is still alive
+ pub fn upgrade(&self) -> Result<Strong<I>> {
+ self.weak_binder
+ .promote()
+ .ok_or(StatusCode::DEAD_OBJECT)
+ .and_then(FromIBinder::try_from)
+ }
+}
+
+impl<I: FromIBinder + ?Sized> Clone for Weak<I> {
+ fn clone(&self) -> Self {
+ Self {
+ weak_binder: self.weak_binder.clone(),
+ interface_type: PhantomData,
+ }
+ }
+}
+
+impl<I: FromIBinder + ?Sized> Ord for Weak<I> {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.weak_binder.cmp(&other.weak_binder)
+ }
+}
+
+impl<I: FromIBinder + ?Sized> PartialOrd for Weak<I> {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ self.weak_binder.partial_cmp(&other.weak_binder)
+ }
+}
+
+impl<I: FromIBinder + ?Sized> PartialEq for Weak<I> {
+ fn eq(&self, other: &Self) -> bool {
+ self.weak_binder == other.weak_binder
+ }
+}
+
+impl<I: FromIBinder + ?Sized> Eq for Weak<I> {}
+
/// Create a function implementing a static getter for an interface class.
///
/// Each binder interface (i.e. local [`Remotable`] service or remote proxy
@@ -354,12 +485,12 @@
/// }
/// }
/// ```
-pub trait FromIBinder {
+pub trait FromIBinder: Interface {
/// Try to interpret a generic Binder object as this interface.
///
/// Returns a trait object for the `Self` interface if this object
/// implements that interface.
- fn try_from(ibinder: SpIBinder) -> Result<Box<Self>>;
+ fn try_from(ibinder: SpIBinder) -> Result<Strong<Self>>;
}
/// Trait for transparent Rust wrappers around android C++ native types.
@@ -534,8 +665,9 @@
impl $native {
/// Create a new binder service.
- pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T) -> impl $interface {
- $crate::Binder::new($native(Box::new(inner)))
+ pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T) -> $crate::Strong<dyn $interface> {
+ let binder = $crate::Binder::new($native(Box::new(inner)));
+ $crate::Strong::new(Box::new(binder))
}
}
@@ -577,7 +709,7 @@
}
impl $crate::FromIBinder for dyn $interface {
- fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<Box<dyn $interface>> {
+ fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $interface>> {
use $crate::AssociateClass;
let existing_class = ibinder.get_class();
@@ -590,7 +722,7 @@
// associated object as remote, because we can't cast it
// into a Rust service object without a matching class
// pointer.
- return Ok(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?));
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
}
}
@@ -600,10 +732,10 @@
if let Ok(service) = service {
// We were able to associate with our expected class and
// the service is local.
- return Ok(Box::new(service));
+ return Ok($crate::Strong::new(Box::new(service)));
} else {
// Service is remote
- return Ok(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?));
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
}
}
@@ -633,9 +765,9 @@
}
}
- // Convert a &dyn $interface to Box<dyn $interface>
+ /// Convert a &dyn $interface to Strong<dyn $interface>
impl std::borrow::ToOwned for dyn $interface {
- type Owned = Box<dyn $interface>;
+ type Owned = $crate::Strong<dyn $interface>;
fn to_owned(&self) -> Self::Owned {
self.as_binder().into_interface()
.expect(concat!("Error cloning interface ", stringify!($interface)))
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index edfb56a..43a237a 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -107,7 +107,8 @@
pub mod parcel;
pub use crate::binder::{
- FromIBinder, IBinder, Interface, InterfaceClass, Remotable, TransactionCode, TransactionFlags,
+ FromIBinder, IBinder, Interface, InterfaceClass, Remotable, Strong, TransactionCode,
+ TransactionFlags, Weak,
};
pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
pub use native::add_service;
@@ -122,7 +123,8 @@
pub use super::parcel::ParcelFileDescriptor;
pub use super::{add_service, get_interface};
pub use super::{
- ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode, WpIBinder,
+ ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode, Strong, ThreadState,
+ Weak, WpIBinder,
};
/// Binder result containing a [`Status`] on error.
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 8d18fb4..f57788b 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-use crate::binder::{AsNative, FromIBinder};
+use crate::binder::{AsNative, FromIBinder, Strong};
use crate::error::{status_result, status_t, Result, Status, StatusCode};
use crate::parcel::Parcel;
use crate::proxy::SpIBinder;
@@ -628,26 +628,26 @@
}
}
-impl<T: Serialize + ?Sized> Serialize for Box<T> {
+impl<T: Serialize + FromIBinder + ?Sized> Serialize for Strong<T> {
fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
Serialize::serialize(&**self, parcel)
}
}
-impl<T: SerializeOption + ?Sized> SerializeOption for Box<T> {
+impl<T: SerializeOption + FromIBinder + ?Sized> SerializeOption for Strong<T> {
fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
SerializeOption::serialize_option(this.map(|b| &**b), parcel)
}
}
-impl<T: FromIBinder + ?Sized> Deserialize for Box<T> {
+impl<T: FromIBinder + ?Sized> Deserialize for Strong<T> {
fn deserialize(parcel: &Parcel) -> Result<Self> {
let ibinder: SpIBinder = parcel.read()?;
FromIBinder::try_from(ibinder)
}
}
-impl<T: FromIBinder + ?Sized> DeserializeOption for Box<T> {
+impl<T: FromIBinder + ?Sized> DeserializeOption for Strong<T> {
fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
let ibinder: Option<SpIBinder> = parcel.read()?;
ibinder.map(FromIBinder::try_from).transpose()
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index e9e74c0..132e075 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -17,7 +17,7 @@
//! Rust API for interacting with a remote binder service.
use crate::binder::{
- AsNative, FromIBinder, IBinder, Interface, InterfaceClass, TransactionCode, TransactionFlags,
+ AsNative, FromIBinder, IBinder, Interface, InterfaceClass, Strong, TransactionCode, TransactionFlags,
};
use crate::error::{status_result, Result, StatusCode};
use crate::parcel::{
@@ -27,6 +27,7 @@
use crate::sys;
use std::convert::TryInto;
+use std::cmp::Ordering;
use std::ffi::{c_void, CString};
use std::fmt;
use std::os::unix::io::AsRawFd;
@@ -99,7 +100,7 @@
///
/// If this object does not implement the expected interface, the error
/// `StatusCode::BAD_TYPE` is returned.
- pub fn into_interface<I: FromIBinder + ?Sized>(self) -> Result<Box<I>> {
+ pub fn into_interface<I: FromIBinder + Interface + ?Sized>(self) -> Result<Strong<I>> {
FromIBinder::try_from(self)
}
@@ -148,6 +149,36 @@
}
}
+impl Ord for SpIBinder {
+ fn cmp(&self, other: &Self) -> Ordering {
+ let less_than = unsafe {
+ // Safety: SpIBinder always holds a valid `AIBinder` pointer, so
+ // this pointer is always safe to pass to `AIBinder_lt` (null is
+ // also safe to pass to this function, but we should never do that).
+ sys::AIBinder_lt(self.0, other.0)
+ };
+ let greater_than = unsafe {
+ // Safety: SpIBinder always holds a valid `AIBinder` pointer, so
+ // this pointer is always safe to pass to `AIBinder_lt` (null is
+ // also safe to pass to this function, but we should never do that).
+ sys::AIBinder_lt(other.0, self.0)
+ };
+ if !less_than && !greater_than {
+ Ordering::Equal
+ } else if less_than {
+ Ordering::Less
+ } else {
+ Ordering::Greater
+ }
+ }
+}
+
+impl PartialOrd for SpIBinder {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
impl PartialEq for SpIBinder {
fn eq(&self, other: &Self) -> bool {
ptr::eq(self.0, other.0)
@@ -326,7 +357,7 @@
// Safety: `SpIBinder` guarantees that `self` always contains a
// valid pointer to an `AIBinder`. `recipient` can always be
// converted into a valid pointer to an
- // `AIBinder_DeatRecipient`. Any value is safe to pass as the
+ // `AIBinder_DeathRecipient`. Any value is safe to pass as the
// cookie, although we depend on this value being set by
// `get_cookie` when the death recipient callback is called.
sys::AIBinder_linkToDeath(
@@ -342,7 +373,7 @@
// Safety: `SpIBinder` guarantees that `self` always contains a
// valid pointer to an `AIBinder`. `recipient` can always be
// converted into a valid pointer to an
- // `AIBinder_DeatRecipient`. Any value is safe to pass as the
+ // `AIBinder_DeathRecipient`. Any value is safe to pass as the
// cookie, although we depend on this value being set by
// `get_cookie` when the death recipient callback is called.
sys::AIBinder_unlinkToDeath(
@@ -430,6 +461,62 @@
}
}
+impl Clone for WpIBinder {
+ fn clone(&self) -> Self {
+ let ptr = unsafe {
+ // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer,
+ // so this pointer is always safe to pass to `AIBinder_Weak_clone`
+ // (although null is also a safe value to pass to this API).
+ //
+ // We get ownership of the returned pointer, so can construct a new
+ // WpIBinder object from it.
+ sys::AIBinder_Weak_clone(self.0)
+ };
+ assert!(!ptr.is_null(), "Unexpected null pointer from AIBinder_Weak_clone");
+ Self(ptr)
+ }
+}
+
+impl Ord for WpIBinder {
+ fn cmp(&self, other: &Self) -> Ordering {
+ let less_than = unsafe {
+ // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer,
+ // so this pointer is always safe to pass to `AIBinder_Weak_lt`
+ // (null is also safe to pass to this function, but we should never
+ // do that).
+ sys::AIBinder_Weak_lt(self.0, other.0)
+ };
+ let greater_than = unsafe {
+ // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer,
+ // so this pointer is always safe to pass to `AIBinder_Weak_lt`
+ // (null is also safe to pass to this function, but we should never
+ // do that).
+ sys::AIBinder_Weak_lt(other.0, self.0)
+ };
+ if !less_than && !greater_than {
+ Ordering::Equal
+ } else if less_than {
+ Ordering::Less
+ } else {
+ Ordering::Greater
+ }
+ }
+}
+
+impl PartialOrd for WpIBinder {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl PartialEq for WpIBinder {
+ fn eq(&self, other: &Self) -> bool {
+ self.cmp(other) == Ordering::Equal
+ }
+}
+
+impl Eq for WpIBinder {}
+
impl Drop for WpIBinder {
fn drop(&mut self) {
unsafe {
@@ -564,7 +651,7 @@
/// 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<Box<T>> {
+pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
let service = get_service(name);
match service {
Some(service) => FromIBinder::try_from(service),
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index bb8c492..719229c 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -209,7 +209,7 @@
use std::thread;
use std::time::Duration;
- use binder::{Binder, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode};
+ use binder::{Binder, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode, Strong};
use super::{BnTest, ITest, ITestSameDescriptor, RUST_SERVICE_BINARY, TestService};
@@ -271,7 +271,7 @@
fn trivial_client() {
let service_name = "trivial_client_test";
let _process = ScopedServiceProcess::new(service_name);
- let test_client: Box<dyn ITest> =
+ let test_client: Strong<dyn ITest> =
binder::get_interface(service_name).expect("Did not get manager binder service");
assert_eq!(test_client.test().unwrap(), "trivial_client_test");
}
@@ -280,7 +280,7 @@
fn get_selinux_context() {
let service_name = "get_selinux_context";
let _process = ScopedServiceProcess::new(service_name);
- let test_client: Box<dyn ITest> =
+ let test_client: Strong<dyn ITest> =
binder::get_interface(service_name).expect("Did not get manager binder service");
let expected_context = unsafe {
let mut out_ptr = ptr::null_mut();
@@ -453,7 +453,7 @@
let extension = maybe_extension.expect("Remote binder did not have an extension");
- let extension: Box<dyn ITest> = FromIBinder::try_from(extension)
+ let extension: Strong<dyn ITest> = FromIBinder::try_from(extension)
.expect("Extension could not be converted to the expected interface");
assert_eq!(extension.test().unwrap(), extension_name);
@@ -479,7 +479,7 @@
// This should succeed although we will have to treat the service as
// remote.
- let _interface: Box<dyn ITestSameDescriptor> = FromIBinder::try_from(service.as_binder())
+ let _interface: Strong<dyn ITestSameDescriptor> = FromIBinder::try_from(service.as_binder())
.expect("Could not re-interpret service as the ITestSameDescriptor interface");
}
@@ -490,9 +490,60 @@
let service_ibinder = BnTest::new_binder(TestService { s: service_name.to_string() })
.as_binder();
- let service: Box<dyn ITest> = service_ibinder.into_interface()
+ let service: Strong<dyn ITest> = service_ibinder.into_interface()
.expect("Could not reassociate the generic ibinder");
assert_eq!(service.test().unwrap(), service_name);
}
+
+ #[test]
+ fn weak_binder_upgrade() {
+ let service_name = "testing_service";
+ let service = BnTest::new_binder(TestService { s: service_name.to_string() });
+
+ let weak = Strong::downgrade(&service);
+
+ let upgraded = weak.upgrade().expect("Could not upgrade weak binder");
+
+ assert_eq!(service, upgraded);
+ }
+
+ #[test]
+ fn weak_binder_upgrade_dead() {
+ let service_name = "testing_service";
+ let weak = {
+ let service = BnTest::new_binder(TestService { s: service_name.to_string() });
+
+ Strong::downgrade(&service)
+ };
+
+ assert_eq!(weak.upgrade(), Err(StatusCode::DEAD_OBJECT));
+ }
+
+ #[test]
+ fn weak_binder_clone() {
+ let service_name = "testing_service";
+ let service = BnTest::new_binder(TestService { s: service_name.to_string() });
+
+ let weak = Strong::downgrade(&service);
+ let cloned = weak.clone();
+ assert_eq!(weak, cloned);
+
+ let upgraded = weak.upgrade().expect("Could not upgrade weak binder");
+ let clone_upgraded = cloned.upgrade().expect("Could not upgrade weak binder");
+
+ assert_eq!(service, upgraded);
+ assert_eq!(service, clone_upgraded);
+ }
+
+ #[test]
+ #[allow(clippy::eq_op)]
+ fn binder_ord() {
+ let service1 = BnTest::new_binder(TestService { s: "testing_service1".to_string() });
+ let service2 = BnTest::new_binder(TestService { s: "testing_service2".to_string() });
+
+ assert!(!(service1 < service1));
+ assert!(!(service1 > service1));
+ assert_eq!(service1 < service2, !(service2 < service1));
+ }
}
diff --git a/libs/binder/rust/tests/ndk_rust_interop.rs b/libs/binder/rust/tests/ndk_rust_interop.rs
index 70a6dc0..ce75ab7 100644
--- a/libs/binder/rust/tests/ndk_rust_interop.rs
+++ b/libs/binder/rust/tests/ndk_rust_interop.rs
@@ -37,7 +37,7 @@
// The Rust class descriptor pointer will not match the NDK one, but the
// descriptor strings match so this needs to still associate.
- let service: Box<dyn IBinderRustNdkInteropTest> = match binder::get_interface(service_name) {
+ let service: binder::Strong<dyn IBinderRustNdkInteropTest> = match binder::get_interface(service_name) {
Err(e) => {
eprintln!("Could not find Ndk service {}: {:?}", service_name, e);
return StatusCode::NAME_NOT_FOUND as c_int;
@@ -53,7 +53,7 @@
}
// Try using the binder service through the wrong interface type
- let wrong_service: Result<Box<dyn IBinderRustNdkInteropTestOther>, StatusCode> =
+ let wrong_service: Result<binder::Strong<dyn IBinderRustNdkInteropTestOther>, StatusCode> =
binder::get_interface(service_name);
match wrong_service {
Err(e) if e == StatusCode::BAD_TYPE => {}