[binder] Add support for dump transaction to Rust

Allow Rust Binder services to define a handler for dump transactions.
Binder services that want to handle this special transaction should
override the dump method in Interface, otherwise dump transactions will
be a no-op.

Test: atest rustBinderTest
Bug: 190174171
Change-Id: Ib87ca09a8f7b8eda08f8a1bf59ad8066ea93d5a9
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 10b77f4..da8907d 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -23,6 +23,9 @@
     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 @@
     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 @@
     ));
 }
 
-#[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 @@
     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 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 @@
     /// 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 @@
 ) -> 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 @@
         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 @@
         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 @@
         {
             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 @@
     /// 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 @@
     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 @@
     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 @@
         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 @@
     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 @@
     #[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(),
         );