[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/src/binder.rs b/libs/binder/rust/src/binder.rs
index 695a83e..2a09afc 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -25,6 +25,7 @@
 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 @@
     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 @@
     /// `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 @@
             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 @@
     /// 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 @@
                 }
             }
 
+            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;
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 3920129..5e324b3 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -21,9 +21,13 @@
 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 @@
         // 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 @@
         Ok(())
     }
 
+    fn on_dump(&self, _file: &File, _args: &[&CStr]) -> Result<()> {
+        Ok(())
+    }
+
     binder_fn_get_class!(Binder::<Self>);
 }