[libtrusty-rs] Add recv variant that uses Vec<u8>

* Add new `recv` method that takes a `Vec<u8>` and automatically
  allocates extra buffer space and retries the read call if the buffer
  does not have enough capacity.
* Rename the existing `recv` method to `recv_no_alloc`, and update docs
  to clarify the behaviors of both methods.
* Add tests for the new `recv` method and update existing tests to use
  `recv_no_alloc` instead.

Test: tipc-test-rs -m 1024 -t echo
Bug: 226659377
Change-Id: Ic437b617751e865da119fe0c4ef8aa456a63bf3c
diff --git a/trusty/libtrusty-rs/Android.bp b/trusty/libtrusty-rs/Android.bp
index 47c64b7..bc1dcf6 100644
--- a/trusty/libtrusty-rs/Android.bp
+++ b/trusty/libtrusty-rs/Android.bp
@@ -24,6 +24,7 @@
     ],
     rustlibs: [
         "libnix",
+        "liblibc",
     ],
 }
 
@@ -33,5 +34,6 @@
     srcs: ["tests/test.rs"],
     rustlibs: [
         "libtrusty-rs",
+        "liblibc",
     ]
 }
diff --git a/trusty/libtrusty-rs/src/lib.rs b/trusty/libtrusty-rs/src/lib.rs
index b3102f8..28ea075 100644
--- a/trusty/libtrusty-rs/src/lib.rs
+++ b/trusty/libtrusty-rs/src/lib.rs
@@ -51,8 +51,8 @@
 //!
 //! chann.send("Hello, world!".as_bytes()).unwrap();
 //!
-//! let mut read_buf = [0u8; 1024];
-//! let read_len = stream.read(&mut read_buf[..]).unwrap();
+//! let mut read_buf = Vec::new();
+//! let read_len = stream.recv(&mut read_buf).unwrap();
 //!
 //! let response = std::str::from_utf8(&read_buf[..read_len]).unwrap();
 //! assert_eq!("Hello, world!", response);
@@ -63,8 +63,8 @@
 use crate::sys::tipc_connect;
 use std::ffi::CString;
 use std::fs::File;
-use std::io;
 use std::io::prelude::*;
+use std::io::{ErrorKind, Result};
 use std::os::unix::prelude::AsRawFd;
 use std::path::Path;
 
@@ -73,6 +73,12 @@
 /// The default filesystem path for the Trusty IPC device.
 pub const DEFAULT_DEVICE: &str = "/dev/trusty-ipc-dev0";
 
+/// The maximum size an incoming TIPC message can be.
+///
+/// This can be used to pre-allocate buffer space in order to ensure that your
+/// read buffer can always hold an incoming message.
+pub const MAX_MESSAGE_SIZE: usize = 4096;
+
 /// A channel for communicating with a Trusty service.
 ///
 /// See the [crate-level documentation][crate] for usage details and examples.
@@ -92,7 +98,7 @@
     /// bytes. This is handled with a panic because the service names are all
     /// hard-coded constants, and so such an error should always be indicative of a
     /// bug in the calling code.
-    pub fn connect(device: impl AsRef<Path>, service: &str) -> io::Result<Self> {
+    pub fn connect(device: impl AsRef<Path>, service: &str) -> Result<Self> {
         let file = File::options().read(true).write(true).open(device)?;
 
         let srv_name = CString::new(service).expect("Service name contained null bytes");
@@ -107,7 +113,7 @@
     ///
     /// The entire contents of `buf` will be sent as a single message to the
     /// connected service.
-    pub fn send(&mut self, buf: &[u8]) -> io::Result<()> {
+    pub fn send(&mut self, buf: &[u8]) -> Result<()> {
         let write_len = self.0.write(buf)?;
 
         // Verify that the expected number of bytes were written. The entire message
@@ -125,19 +131,91 @@
         Ok(())
     }
 
-    /// Receives a message from the connected service.
+    /// Reads the next incoming message.
     ///
-    /// Returns the number of bytes in the received message, or any error that
-    /// occurred when reading the message. Blocks until there is a message to
-    /// receive if none is already ready to read.
+    /// Attempts to read the next incoming message from the connected service if any
+    /// exist. If the initial capacity of `buf` is not enough to hold the incoming
+    /// message the function repeatedly attempts to reserve additional space until
+    /// it is able to fully read the message.
+    ///
+    /// Blocks until there is an incoming message if there is not already a message
+    /// ready to be received.
     ///
     /// # Errors
     ///
-    /// Returns an error with native error code 90 (`EMSGSIZE`) if `buf` isn't large
+    /// If this function encounters an error of the kind [`ErrorKind::Interrupted`]
+    /// then the error is ignored and the operation will be tried again.
+    ///
+    /// If this function encounters an error with the error code `EMSGSIZE` then
+    /// additional space will be reserved in `buf` and the operation will be tried
+    /// again.
+    ///
+    /// If any other read error is encountered then this function immediately
+    /// returns the error to the caller, and the length of `buf` is set to 0.
+    pub fn recv(&mut self, buf: &mut Vec<u8>) -> Result<()> {
+        // If no space has been allocated in the buffer reserve enough space to hold any
+        // incoming message.
+        if buf.capacity() == 0 {
+            buf.reserve(MAX_MESSAGE_SIZE);
+        }
+
+        loop {
+            // Resize the vec to make its full capacity available to write into.
+            buf.resize(buf.capacity(), 0);
+
+            match self.0.read(buf.as_mut_slice()) {
+                Ok(len) => {
+                    buf.truncate(len);
+                    return Ok(());
+                }
+
+                Err(err) => {
+                    if let Some(libc::EMSGSIZE) = err.raw_os_error() {
+                        // Ensure that we didn't get `EMSGSIZE` when we already had enough capacity
+                        // to contain the maximum message size. This should never happen, but if it
+                        // does we don't want to hang by looping infinitely.
+                        assert!(
+                            buf.capacity() < MAX_MESSAGE_SIZE,
+                            "Received `EMSGSIZE` error when buffer capacity was already at maximum",
+                        );
+
+                        // If we didn't have enough space to hold the incoming message, reserve
+                        // enough space to fit the maximum message size regardless of how much
+                        // capacity the buffer already had.
+                        buf.reserve(MAX_MESSAGE_SIZE - buf.capacity());
+                    } else if err.kind() == ErrorKind::Interrupted {
+                        // If we get an interrupted error the operation can be retried as-is, i.e.
+                        // we don't need to allocate additional space.
+                        continue;
+                    } else {
+                        buf.truncate(0);
+                        return Err(err);
+                    }
+                }
+            }
+        }
+    }
+
+    /// Reads the next incoming message without allocating.
+    ///
+    /// Returns the number of bytes in the received message, or any error that
+    /// occurred when reading the message.
+    ///
+    /// Blocks until there is an incoming message if there is not already a message
+    /// ready to be received.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error with native error code `EMSGSIZE` if `buf` isn't large
     /// enough to contain the incoming message. Use
     /// [`raw_os_error`][std::io::Error::raw_os_error] to check the error code to
-    /// determine if you need to increase the size of `buf`.
-    pub fn recv(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+    /// determine if you need to increase the size of `buf`. If error code
+    /// `EMSGSIZE` is returned the incoming message will not be dropped, and a
+    /// subsequent call to `recv_no_alloc` can still read it.
+    ///
+    /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read
+    /// operation should be retried if there is nothing else to do.
+    pub fn recv_no_alloc(&mut self, buf: &mut [u8]) -> Result<usize> {
         self.0.read(buf)
     }
 
diff --git a/trusty/libtrusty-rs/tests/test.rs b/trusty/libtrusty-rs/tests/test.rs
index a6f1370..6bff479 100644
--- a/trusty/libtrusty-rs/tests/test.rs
+++ b/trusty/libtrusty-rs/tests/test.rs
@@ -3,7 +3,7 @@
 const ECHO_NAME: &str = "com.android.ipc-unittest.srv.echo";
 
 #[test]
-fn echo() {
+fn recv_no_alloc() {
     let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
         .expect("Failed to connect to Trusty service");
 
@@ -11,9 +11,10 @@
     let send_buf = [7u8; 32];
     connection.send(send_buf.as_slice()).unwrap();
 
-    // Receive the response message from the TA.
+    // Receive the response message from the TA. The response message will be the
+    // same as the message we just sent.
     let mut recv_buf = [0u8; 32];
-    let read_len = connection.recv(&mut recv_buf).expect("Failed to read from connection");
+    let read_len = connection.recv_no_alloc(recv_buf.as_mut_slice()).unwrap();
 
     assert_eq!(
         send_buf.len(),
@@ -24,3 +25,62 @@
     );
     assert_eq!(send_buf, recv_buf, "Received data does not match sent data");
 }
+
+#[test]
+fn recv_small_buf() {
+    let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
+        .expect("Failed to connect to Trusty service");
+
+    // Send a long message to the echo service so that we can test receiving a long
+    // message.
+    let send_buf = [7u8; 2048];
+    connection.send(send_buf.as_slice()).unwrap();
+
+    // Attempt to receive the response message with a buffer that is too small to
+    // contain the message.
+    let mut recv_buf = [0u8; 32];
+    let err = connection.recv_no_alloc(recv_buf.as_mut_slice()).unwrap_err();
+
+    assert_eq!(
+        Some(libc::EMSGSIZE),
+        err.raw_os_error(),
+        "Unexpected error err when receiving incoming message: {:?}",
+        err,
+    );
+}
+
+#[test]
+fn recv_empty_vec() {
+    let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
+        .expect("Failed to connect to Trusty service");
+
+    // Send a message to the echo TA.
+    let send_buf = [7u8; 2048];
+    connection.send(send_buf.as_slice()).unwrap();
+
+    // Receive the response message. `recv_buf` is initially empty, and `recv` is
+    // responsible for allocating enough space to hold the message.
+    let mut recv_buf = Vec::new();
+    connection.recv(&mut recv_buf).unwrap();
+
+    assert_eq!(send_buf.as_slice(), recv_buf, "Received data does not match sent data");
+}
+
+#[test]
+fn recv_vec_existing_capacity() {
+    let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
+        .expect("Failed to connect to Trusty service");
+
+    // Send a message to the echo TA.
+    let send_buf = [7u8; 2048];
+    connection.send(send_buf.as_slice()).unwrap();
+
+    // Receive the response message into a buffer that already has enough capacity
+    // to hold the message. No additional capacity should be allocated when
+    // receiving the message.
+    let mut recv_buf = Vec::with_capacity(2048);
+    connection.recv(&mut recv_buf).unwrap();
+
+    assert_eq!(send_buf.as_slice(), recv_buf, "Received data does not match sent data");
+    assert_eq!(2048, recv_buf.capacity(), "Additional capacity was allocated when not needed");
+}