Stop VM immediately with stop() call

Rather than waiting GC to drop the handle, immediately stops the VM by
sending signal to crosvm process.

Bug: 207766487
Test: atest MicrodroidTests
Change-Id: I892d8a60238e7156609e21e9939d4e90038c396d
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index 8d74f5e..0ce7991 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -484,8 +484,13 @@
      * #run()}.
      */
     public void stop() throws VirtualMachineException {
-        // Dropping the IVirtualMachine handle stops the VM
-        mVirtualMachine = null;
+        if (mVirtualMachine == null) return;
+        try {
+            mVirtualMachine.stop();
+            mVirtualMachine = null;
+        } catch (RemoteException e) {
+            throw new VirtualMachineException(e);
+        }
     }
 
     /**
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl
index 6f3d4f0..d9d9a61 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl
@@ -34,6 +34,13 @@
     /** Starts running the VM. */
     void start();
 
+    /**
+     * Stops this virtual machine. Stopping a virtual machine is like pulling the plug on a real
+     * computer; the machine halts immediately. Software running on the virtual machine is not
+     * notified with the event.
+     */
+    void stop();
+
     /** 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 4135253..0078736 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -823,6 +823,13 @@
         })
     }
 
+    fn stop(&self) -> binder::Result<()> {
+        self.instance.kill().map_err(|e| {
+            error!("Error stopping VM with CID {}: {:?}", self.instance.cid, e);
+            new_binder_exception(ExceptionCode::SERVICE_SPECIFIC, e.to_string())
+        })
+    }
+
     fn connectVsock(&self, port: i32) -> binder::Result<ParcelFileDescriptor> {
         if !matches!(&*self.instance.vm_state.lock().unwrap(), VmState::Running { .. }) {
             return Err(new_binder_exception(ExceptionCode::SERVICE_SPECIFIC, "VM is not running"));
@@ -841,7 +848,9 @@
 impl Drop for VirtualMachine {
     fn drop(&mut self) {
         debug!("Dropping {:?}", self);
-        self.instance.kill();
+        if let Err(e) = self.instance.kill() {
+            debug!("Error stopping dropped VM with CID {}: {:?}", self.instance.cid, e);
+        }
     }
 }
 
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index 46ad6b3..b6e62fe 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -241,7 +241,9 @@
                     "Microdroid failed to start payload within {} secs timeout. Shutting down",
                     BOOT_HANGUP_TIMEOUT.as_secs()
                 );
-                self.kill();
+                if let Err(e) = self.kill() {
+                    error!("Error stopping timed-out VM with CID {}: {:?}", self.cid, e);
+                }
                 true
             } else {
                 false
@@ -304,15 +306,19 @@
     }
 
     /// Kills the crosvm instance, if it is running.
-    pub fn kill(&self) {
+    pub fn kill(&self) -> Result<(), Error> {
         let vm_state = &*self.vm_state.lock().unwrap();
         if let VmState::Running { child } = vm_state {
             let id = child.id();
             debug!("Killing crosvm({})", id);
             // TODO: Talk to crosvm to shutdown cleanly.
             if let Err(e) = child.kill() {
-                error!("Error killing crosvm({}) instance: {}", id, e);
+                bail!("Error killing crosvm({}) instance: {}", id, e);
+            } else {
+                Ok(())
             }
+        } else {
+            bail!("VM is not running")
         }
     }