Add method to open vsock connection to VM.

Bug: 195411982
Test: atest VirtualizationTestCases MicrodroidHostTestCases
Change-Id: I37b4073b7faa5a7bd6b9c5b15241c1757408d19b
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl
index 33c9716..081580c 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl
@@ -32,4 +32,7 @@
      * we might miss some events that happen before the registration is done.
      */
     void registerCallback(IVirtualMachineCallback callback);
+
+    /** Open a vsock connection to the CID of the VM on the given port. */
+    ParcelFileDescriptor connectVsock(int port);
 }
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index dc38075..96e3c44 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -517,6 +517,23 @@
         self.instance.callbacks.add(callback.clone());
         Ok(())
     }
+
+    fn connectVsock(&self, port: i32) -> binder::Result<ParcelFileDescriptor> {
+        if !self.instance.running() {
+            return Err(new_binder_exception(
+                ExceptionCode::SERVICE_SPECIFIC,
+                "VM is no longer running",
+            ));
+        }
+        let stream =
+            VsockStream::connect_with_cid_port(self.instance.cid, port as u32).map_err(|e| {
+                new_binder_exception(
+                    ExceptionCode::SERVICE_SPECIFIC,
+                    format!("Failed to connect: {}", e),
+                )
+            })?;
+        Ok(vsock_stream_to_pfd(stream))
+    }
 }
 
 impl Drop for VirtualMachine {
@@ -535,9 +552,7 @@
     /// Call all registered callbacks to notify that the payload has started.
     pub fn notify_payload_started(&self, cid: Cid, stream: VsockStream) {
         let callbacks = &*self.0.lock().unwrap();
-        // SAFETY: ownership is transferred from stream to f
-        let f = unsafe { File::from_raw_fd(stream.into_raw_fd()) };
-        let pfd = ParcelFileDescriptor::new(f);
+        let pfd = vsock_stream_to_pfd(stream);
         for callback in callbacks {
             if let Err(e) = callback.onPayloadStarted(cid as i32, &pfd) {
                 error!("Error notifying payload start event from VM CID {}: {}", cid, e);
@@ -641,6 +656,13 @@
     })
 }
 
+/// Converts a `VsockStream` to a `ParcelFileDescriptor`.
+fn vsock_stream_to_pfd(stream: VsockStream) -> ParcelFileDescriptor {
+    // SAFETY: ownership is transferred from stream to f
+    let f = unsafe { File::from_raw_fd(stream.into_raw_fd()) };
+    ParcelFileDescriptor::new(f)
+}
+
 /// Constructs a new Binder error `Status` with the given `ExceptionCode` and message.
 fn new_binder_exception<T: AsRef<str>>(exception: ExceptionCode, message: T) -> Status {
     Status::new_exception(exception, CString::new(message.as_ref()).ok().as_deref())