Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a7e2f1b5 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "[binder] Add support for dump transaction to Rust" am: 21e23547

Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/1735009

Change-Id: Ieb4b6ba836978839c1116adee10635e7d05b0dd6
parents 1fad21cd 21e23547
Loading
Loading
Loading
Loading
+28 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ use std::borrow::Borrow;
use std::cmp::Ordering;
use std::ffi::{c_void, CStr, CString};
use std::fmt;
use std::fs::File;
use std::marker::PhantomData;
use std::ops::Deref;
use std::os::raw::c_char;
@@ -54,6 +55,14 @@ pub trait Interface: Send {
    fn as_binder(&self) -> SpIBinder {
        panic!("This object was not a Binder object and cannot be converted into an SpIBinder.")
    }

    /// Dump transaction handler for this Binder object.
    ///
    /// This handler is a no-op by default and should be implemented for each
    /// Binder service struct that wishes to respond to dump transactions.
    fn dump(&self, _file: &File, _args: &[&CStr]) -> Result<()> {
        Ok(())
    }
}

/// Interface stability promise
@@ -98,6 +107,10 @@ pub trait Remotable: Send + Sync {
    /// `reply` may be [`None`] if the sender does not expect a reply.
    fn on_transact(&self, code: TransactionCode, data: &Parcel, reply: &mut Parcel) -> Result<()>;

    /// Handle a request to invoke the dump transaction on this
    /// object.
    fn on_dump(&self, file: &File, args: &[&CStr]) -> Result<()>;

    /// Retrieve the class of this remote object.
    ///
    /// This method should always return the same InterfaceClass for the same
@@ -218,7 +231,7 @@ impl InterfaceClass {
            if class.is_null() {
                panic!("Expected non-null class pointer from AIBinder_Class_define!");
            }
            sys::AIBinder_Class_setOnDump(class, None);
            sys::AIBinder_Class_setOnDump(class, Some(I::on_dump));
            sys::AIBinder_Class_setHandleShellCommand(class, None);
            class
        };
@@ -492,6 +505,16 @@ pub trait InterfaceClassMethods {
    /// returned by `on_create` for this class. This function takes ownership of
    /// the provided pointer and destroys it.
    unsafe extern "C" fn on_destroy(object: *mut c_void);

    /// Called to handle the `dump` transaction.
    ///
    /// # Safety
    ///
    /// Must be called with a non-null, valid pointer to a local `AIBinder` that
    /// contains a `T` pointer in its user data. fd should be a non-owned file
    /// descriptor, and args must be an array of null-terminated string
    /// poiinters with length num_args.
    unsafe extern "C" fn on_dump(binder: *mut sys::AIBinder, fd: i32, args: *mut *const c_char, num_args: u32) -> status_t;
}

/// Interface for transforming a generic SpIBinder into a specific remote
@@ -778,6 +801,10 @@ macro_rules! declare_binder_interface {
                }
            }

            fn on_dump(&self, file: &std::fs::File, args: &[&std::ffi::CStr]) -> $crate::Result<()> {
                self.0.dump(file, args)
            }

            fn get_class() -> $crate::InterfaceClass {
                static CLASS_INIT: std::sync::Once = std::sync::Once::new();
                static mut CLASS: Option<$crate::InterfaceClass> = None;
+40 −1
Original line number Diff line number Diff line
@@ -21,9 +21,13 @@ use crate::proxy::SpIBinder;
use crate::sys;

use std::convert::TryFrom;
use std::ffi::{c_void, CString};
use std::ffi::{c_void, CStr, CString};
use std::fs::File;
use std::mem::ManuallyDrop;
use std::ops::Deref;
use std::os::raw::c_char;
use std::os::unix::io::FromRawFd;
use std::slice;

/// Rust wrapper around Binder remotable objects.
///
@@ -289,6 +293,37 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> {
        // object created by Box.
        args
    }

    /// Called to handle the `dump` transaction.
    ///
    /// # Safety
    ///
    /// Must be called with a non-null, valid pointer to a local `AIBinder` that
    /// contains a `T` pointer in its user data. fd should be a non-owned file
    /// descriptor, and args must be an array of null-terminated string
    /// poiinters with length num_args.
    unsafe extern "C" fn on_dump(binder: *mut sys::AIBinder, fd: i32, args: *mut *const c_char, num_args: u32) -> status_t {
        if fd < 0 {
            return StatusCode::UNEXPECTED_NULL as status_t;
        }
        // We don't own this file, so we need to be careful not to drop it.
        let file = ManuallyDrop::new(File::from_raw_fd(fd));

        if args.is_null() {
            return StatusCode::UNEXPECTED_NULL as status_t;
        }
        let args = slice::from_raw_parts(args, num_args as usize);
        let args: Vec<_> = args.iter().map(|s| CStr::from_ptr(*s)).collect();

        let object = sys::AIBinder_getUserData(binder);
        let binder: &T = &*(object as *const T);
        let res = binder.on_dump(&file, &args);

        match res {
            Ok(()) => 0,
            Err(e) => e as status_t,
        }
    }
}

impl<T: Remotable> Drop for Binder<T> {
@@ -409,6 +444,10 @@ impl Remotable for () {
        Ok(())
    }

    fn on_dump(&self, _file: &File, _args: &[&CStr]) -> Result<()> {
        Ok(())
    }

    binder_fn_get_class!(Binder::<Self>);
}

+61 −33
Original line number Diff line number Diff line
@@ -23,6 +23,9 @@ use binder::{
    FIRST_CALL_TRANSACTION,
};
use std::convert::{TryFrom, TryInto};
use std::ffi::CStr;
use std::fs::File;
use std::sync::Mutex;

/// Name of service runner.
///
@@ -50,13 +53,11 @@ fn main() -> Result<(), &'static str> {
    let extension_name = args.next();

    {
        let mut service = Binder::new(BnTest(Box::new(TestService {
            s: service_name.clone(),
        })));
        let mut service = Binder::new(BnTest(Box::new(TestService::new(&service_name))));
        service.set_requesting_sid(true);
        if let Some(extension_name) = extension_name {
            let extension =
                BnTest::new_binder(TestService { s: extension_name }, BinderFeatures::default());
                BnTest::new_binder(TestService::new(&extension_name), BinderFeatures::default());
            service
                .set_extension(&mut extension.as_binder())
                .expect("Could not add extension");
@@ -80,14 +81,24 @@ fn print_usage() {
    ));
}

#[derive(Clone)]
struct TestService {
    s: String,
    dump_args: Mutex<Vec<String>>,
}

impl TestService {
    fn new(s: &str) -> Self {
        Self {
            s: s.to_string(),
            dump_args: Mutex::new(Vec::new()),
        }
    }
}

#[repr(u32)]
enum TestTransactionCode {
    Test = FIRST_CALL_TRANSACTION,
    GetDumpArgs,
    GetSelinuxContext,
}

@@ -97,6 +108,7 @@ impl TryFrom<u32> for TestTransactionCode {
    fn try_from(c: u32) -> Result<Self, Self::Error> {
        match c {
            _ if c == TestTransactionCode::Test as u32 => Ok(TestTransactionCode::Test),
            _ if c == TestTransactionCode::GetDumpArgs as u32 => Ok(TestTransactionCode::GetDumpArgs),
            _ if c == TestTransactionCode::GetSelinuxContext as u32 => {
                Ok(TestTransactionCode::GetSelinuxContext)
            }
@@ -105,13 +117,24 @@ impl TryFrom<u32> for TestTransactionCode {
    }
}

impl Interface for TestService {}
impl Interface for TestService {
    fn dump(&self, _file: &File, args: &[&CStr]) -> binder::Result<()> {
        let mut dump_args = self.dump_args.lock().unwrap();
        dump_args.extend(args.iter().map(|s| s.to_str().unwrap().to_owned()));
        Ok(())
    }
}

impl ITest for TestService {
    fn test(&self) -> binder::Result<String> {
        Ok(self.s.clone())
    }

    fn get_dump_args(&self) -> binder::Result<Vec<String>> {
        let args = self.dump_args.lock().unwrap().clone();
        Ok(args)
    }

    fn get_selinux_context(&self) -> binder::Result<String> {
        let sid =
            ThreadState::with_calling_sid(|sid| sid.map(|s| s.to_string_lossy().into_owned()));
@@ -124,6 +147,9 @@ pub trait ITest: Interface {
    /// Returns a test string
    fn test(&self) -> binder::Result<String>;

    /// Return the arguments sent via dump
    fn get_dump_args(&self) -> binder::Result<Vec<String>>;

    /// Returns the caller's SELinux context
    fn get_selinux_context(&self) -> binder::Result<String>;
}
@@ -145,6 +171,7 @@ fn on_transact(
) -> binder::Result<()> {
    match code.try_into()? {
        TestTransactionCode::Test => reply.write(&service.test()?),
        TestTransactionCode::GetDumpArgs => reply.write(&service.get_dump_args()?),
        TestTransactionCode::GetSelinuxContext => reply.write(&service.get_selinux_context()?),
    }
}
@@ -157,6 +184,13 @@ impl ITest for BpTest {
        reply.read()
    }

    fn get_dump_args(&self) -> binder::Result<Vec<String>> {
        let reply =
            self.binder
                .transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(()))?;
        reply.read()
    }

    fn get_selinux_context(&self) -> binder::Result<String> {
        let reply = self.binder.transact(
            TestTransactionCode::GetSelinuxContext as TransactionCode,
@@ -172,6 +206,10 @@ impl ITest for Binder<BnTest> {
        self.0.test()
    }

    fn get_dump_args(&self) -> binder::Result<Vec<String>> {
        self.0.get_dump_args()
    }

    fn get_selinux_context(&self) -> binder::Result<String> {
        self.0.get_selinux_context()
    }
@@ -432,18 +470,22 @@ mod tests {
        {
            let _process = ScopedServiceProcess::new(service_name);

            let mut remote = binder::get_service(service_name);
            let test_client: Strong<dyn ITest> =
                binder::get_interface(service_name)
                .expect("Did not get test binder service");
            let mut remote = test_client.as_binder();
            assert!(remote.is_binder_alive());
            remote.ping_binder().expect("Could not ping remote service");

            // We're not testing the output of dump here, as that's really a
            // property of the C++ implementation. There is the risk that the
            // method just does nothing, but we don't want to depend on any
            // particular output from the underlying library.
            let dump_args = ["dump", "args", "for", "testing"];

            let null_out = File::open("/dev/null").expect("Could not open /dev/null");
            remote
                .dump(&null_out, &[])
                .dump(&null_out, &dump_args)
                .expect("Could not dump remote service");

            let remote_args = test_client.get_dump_args().expect("Could not fetched dumped args");
            assert_eq!(dump_args, remote_args[..], "Remote args don't match call to dump");
        }

        // get/set_extensions is tested in test_extensions()
@@ -504,9 +546,7 @@ mod tests {
    /// rust_ndk_interop.rs
    #[test]
    fn associate_existing_class() {
        let service = Binder::new(BnTest(Box::new(TestService {
            s: "testing_service".to_string(),
        })));
        let service = Binder::new(BnTest(Box::new(TestService::new("testing_service"))));

        // This should succeed although we will have to treat the service as
        // remote.
@@ -520,9 +560,7 @@ mod tests {
    fn reassociate_rust_binder() {
        let service_name = "testing_service";
        let service_ibinder = BnTest::new_binder(
            TestService {
                s: service_name.to_string(),
            },
            TestService::new(service_name),
            BinderFeatures::default(),
        )
        .as_binder();
@@ -538,9 +576,7 @@ mod tests {
    fn weak_binder_upgrade() {
        let service_name = "testing_service";
        let service = BnTest::new_binder(
            TestService {
                s: service_name.to_string(),
            },
            TestService::new(service_name),
            BinderFeatures::default(),
        );

@@ -556,9 +592,7 @@ mod tests {
        let service_name = "testing_service";
        let weak = {
            let service = BnTest::new_binder(
                TestService {
                    s: service_name.to_string(),
                },
                TestService::new(service_name),
                BinderFeatures::default(),
            );

@@ -572,9 +606,7 @@ mod tests {
    fn weak_binder_clone() {
        let service_name = "testing_service";
        let service = BnTest::new_binder(
            TestService {
                s: service_name.to_string(),
            },
            TestService::new(service_name),
            BinderFeatures::default(),
        );

@@ -593,15 +625,11 @@ mod tests {
    #[allow(clippy::eq_op)]
    fn binder_ord() {
        let service1 = BnTest::new_binder(
            TestService {
                s: "testing_service1".to_string(),
            },
            TestService::new("testing_service1"),
            BinderFeatures::default(),
        );
        let service2 = BnTest::new_binder(
            TestService {
                s: "testing_service2".to_string(),
            },
            TestService::new("testing_service2"),
            BinderFeatures::default(),
        );