Merge "No longer need to use libbinder_rs directly."
diff --git a/authfs/TEST_MAPPING b/authfs/TEST_MAPPING
index d0c0b09..d2dc1d8 100644
--- a/authfs/TEST_MAPPING
+++ b/authfs/TEST_MAPPING
@@ -2,6 +2,9 @@
   "presubmit": [
     {
       "name": "authfs_device_test_src_lib"
+    },
+    {
+      "name": "AuthFsTestCases"
     }
   ]
 }
diff --git a/virtmanager/Android.bp b/virtmanager/Android.bp
index 9fc4f42..1b2aec1 100644
--- a/virtmanager/Android.bp
+++ b/virtmanager/Android.bp
@@ -10,6 +10,7 @@
     rustlibs: [
         "android.system.virtmanager-rust",
         "libandroid_logger",
+        "libbinder_rs", // TODO(dbrazdil): remove once b/182890877 is fixed
         "liblog_rust",
         "libserde_json",
         "libserde",
diff --git a/virtmanager/aidl/android/system/virtmanager/IVirtManager.aidl b/virtmanager/aidl/android/system/virtmanager/IVirtManager.aidl
index 79010da..ab03c18 100644
--- a/virtmanager/aidl/android/system/virtmanager/IVirtManager.aidl
+++ b/virtmanager/aidl/android/system/virtmanager/IVirtManager.aidl
@@ -30,4 +30,17 @@
      * and as such is only permitted from the shell user.
      */
     VirtualMachineDebugInfo[] debugListVms();
+
+    /**
+     * Hold a strong reference to a VM in Virt Manager. This method is only intended for debug
+     * purposes, and as such is only permitted from the shell user.
+     */
+    void debugHoldVmRef(IVirtualMachine vm);
+
+    /**
+     * Drop reference to a VM that is being held by Virt Manager. Returns the reference if VM was
+     * found and null otherwise. This method is only intended for debug purposes, and as such is
+     * only permitted from the shell user.
+     */
+    @nullable IVirtualMachine debugDropVmRef(int cid);
 }
diff --git a/virtmanager/src/aidl.rs b/virtmanager/src/aidl.rs
index 8105051..8c963d2 100644
--- a/virtmanager/src/aidl.rs
+++ b/virtmanager/src/aidl.rs
@@ -17,6 +17,7 @@
 use crate::config::VmConfig;
 use crate::crosvm::VmInstance;
 use crate::{Cid, FIRST_GUEST_CID};
+use ::binder::FromIBinder; // TODO(dbrazdil): remove once b/182890877 is fixed
 use android_system_virtmanager::aidl::android::system::virtmanager::IVirtManager::IVirtManager;
 use android_system_virtmanager::aidl::android::system::virtmanager::IVirtualMachine::{
     BnVirtualMachine, IVirtualMachine,
@@ -82,6 +83,33 @@
             .collect();
         Ok(cids)
     }
+
+    /// Hold a strong reference to a VM in Virt Manager. This method is only intended for debug
+    /// purposes, and as such is only permitted from the shell user.
+    fn debugHoldVmRef(&self, vmref: &dyn IVirtualMachine) -> binder::Result<()> {
+        if !debug_access_allowed() {
+            return Err(StatusCode::PERMISSION_DENIED.into());
+        }
+
+        // Workaround for b/182890877.
+        let vm: Strong<dyn IVirtualMachine> = FromIBinder::try_from(vmref.as_binder()).unwrap();
+
+        let state = &mut *self.state.lock().unwrap();
+        state.debug_hold_vm(vm);
+        Ok(())
+    }
+
+    /// Drop reference to a VM that is being held by Virt Manager. Returns the reference if VM was
+    /// found and None otherwise. This method is only intended for debug purposes, and as such is
+    /// only permitted from the shell user.
+    fn debugDropVmRef(&self, cid: i32) -> binder::Result<Option<Strong<dyn IVirtualMachine>>> {
+        if !debug_access_allowed() {
+            return Err(StatusCode::PERMISSION_DENIED.into());
+        }
+
+        let state = &mut *self.state.lock().unwrap();
+        Ok(state.debug_drop_vm(cid))
+    }
 }
 
 /// Check whether the caller of the current Binder method is allowed to call debug methods.
@@ -123,6 +151,10 @@
     /// Binder client are dropped the weak reference here will become invalid, and will be removed
     /// from the list opportunistically the next time `add_vm` is called.
     vms: Vec<Weak<VmInstance>>,
+
+    /// Vector of strong VM references held on behalf of users that cannot hold them themselves.
+    /// This is only used for debugging purposes.
+    debug_held_vms: Vec<Strong<dyn IVirtualMachine>>,
 }
 
 impl State {
@@ -140,11 +172,22 @@
         // Actually add the new VM.
         self.vms.push(vm);
     }
+
+    /// Store a strong VM reference.
+    fn debug_hold_vm(&mut self, vm: Strong<dyn IVirtualMachine>) {
+        self.debug_held_vms.push(vm);
+    }
+
+    /// Retrieve and remove a strong VM reference.
+    fn debug_drop_vm(&mut self, cid: i32) -> Option<Strong<dyn IVirtualMachine>> {
+        let pos = self.debug_held_vms.iter().position(|vm| vm.getCid() == Ok(cid))?;
+        Some(self.debug_held_vms.swap_remove(pos))
+    }
 }
 
 impl Default for State {
     fn default() -> Self {
-        State { next_cid: FIRST_GUEST_CID, vms: vec![] }
+        State { next_cid: FIRST_GUEST_CID, vms: vec![], debug_held_vms: vec![] }
     }
 }
 
diff --git a/vm/src/main.rs b/vm/src/main.rs
index 6ebfe63..8c2a084 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -39,6 +39,15 @@
         /// Path to VM config JSON
         #[structopt(parse(from_os_str))]
         config: PathBuf,
+
+        /// Detach VM from the terminal and run in the background
+        #[structopt(short, long)]
+        daemonize: bool,
+    },
+    /// Stop a virtual machine running in the background
+    Stop {
+        /// CID of the virtual machine
+        cid: u32,
     },
     /// List running virtual machines
     List,
@@ -55,24 +64,44 @@
         .context("Failed to find Virt Manager service")?;
 
     match opt {
-        Opt::Run { config } => command_run(virt_manager, &config),
+        Opt::Run { config, daemonize } => command_run(virt_manager, &config, daemonize),
+        Opt::Stop { cid } => command_stop(virt_manager, cid),
         Opt::List => command_list(virt_manager),
     }
 }
 
 /// Run a VM from the given configuration file.
-fn command_run(virt_manager: Strong<dyn IVirtManager>, config_path: &PathBuf) -> Result<(), Error> {
+fn command_run(
+    virt_manager: Strong<dyn IVirtManager>,
+    config_path: &PathBuf,
+    daemonize: bool,
+) -> Result<(), Error> {
     let config_filename = config_path.to_str().context("Failed to parse VM config path")?;
     let stdout_file = ParcelFileDescriptor::new(duplicate_stdout()?);
-    let vm =
-        virt_manager.startVm(config_filename, Some(&stdout_file)).context("Failed to start VM")?;
+    let stdout = if daemonize { None } else { Some(&stdout_file) };
+    let vm = virt_manager.startVm(config_filename, stdout).context("Failed to start VM")?;
+
     let cid = vm.getCid().context("Failed to get CID")?;
     println!("Started VM from {} with CID {}.", config_filename, cid);
 
-    // Wait until the VM dies. If we just returned immediately then the IVirtualMachine Binder
-    // object would be dropped and the VM would be killed.
-    wait_for_death(&mut vm.as_binder())?;
-    println!("VM died");
+    if daemonize {
+        // Pass the VM reference back to Virt Manager and have it hold it in the background.
+        virt_manager.debugHoldVmRef(&*vm).context("Failed to pass VM to Virt Manager")
+    } else {
+        // Wait until the VM dies. If we just returned immediately then the IVirtualMachine Binder
+        // object would be dropped and the VM would be killed.
+        wait_for_death(&mut vm.as_binder())?;
+        println!("VM died");
+        Ok(())
+    }
+}
+
+/// Retrieve reference to a previously daemonized VM and stop it.
+fn command_stop(virt_manager: Strong<dyn IVirtManager>, cid: u32) -> Result<(), Error> {
+    virt_manager
+        .debugDropVmRef(cid as i32)
+        .context("Failed to get VM from Virt Manager")?
+        .context("CID does not correspond to a running background VM")?;
     Ok(())
 }