Merge "Fix race condition between authfs_service and /data mounted"
diff --git a/compos/compos_key_helper/Android.bp b/compos/compos_key_helper/Android.bp
index cffa1e3..f8dc783 100644
--- a/compos/compos_key_helper/Android.bp
+++ b/compos/compos_key_helper/Android.bp
@@ -29,7 +29,7 @@
         "libcompos_key",
     ],
     shared_libs: [
-        "libvm_payload",
+        "libvm_payload#current",
         "libbinder_ndk",
     ],
 }
diff --git a/javalib/api/system-current.txt b/javalib/api/system-current.txt
index 850bffc..16995c5 100644
--- a/javalib/api/system-current.txt
+++ b/javalib/api/system-current.txt
@@ -3,7 +3,7 @@
 
   public class VirtualMachine implements java.lang.AutoCloseable {
     method public void clearCallback();
-    method public void close() throws android.system.virtualmachine.VirtualMachineException;
+    method public void close();
     method @NonNull public android.os.IBinder connectToVsockServer(int) throws android.system.virtualmachine.VirtualMachineException;
     method @NonNull public android.os.ParcelFileDescriptor connectVsock(int) throws android.system.virtualmachine.VirtualMachineException;
     method public int getCid() throws android.system.virtualmachine.VirtualMachineException;
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index 193d213..dec873f 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -65,6 +65,7 @@
 import android.system.virtualizationservice.VirtualMachineAppConfig;
 import android.system.virtualizationservice.VirtualMachineState;
 import android.util.JsonReader;
+import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -105,6 +106,8 @@
  */
 @SystemApi
 public class VirtualMachine implements AutoCloseable {
+    private static final String TAG = "VirtualMachine";
+
     /** Name of the directory under the files directory where all VMs created for the app exist. */
     private static final String VM_DIR = "vm";
 
@@ -754,7 +757,8 @@
      * computer; the machine halts immediately. Software running on the virtual machine is not
      * notified of the event. A stopped virtual machine can be re-started by calling {@link #run()}.
      *
-     * @throws VirtualMachineException if the virtual machine could not be stopped.
+     * @throws VirtualMachineException if the virtual machine is not running or could not be
+     *     stopped.
      * @hide
      */
     @SystemApi
@@ -775,15 +779,31 @@
     }
 
     /**
-     * Stops this virtual machine. See {@link #stop()}.
+     * Stops this virtual machine, if it is running.
      *
-     * @throws VirtualMachineException if the virtual machine could not be stopped.
+     * @see #stop()
      * @hide
      */
     @SystemApi
     @Override
-    public void close() throws VirtualMachineException {
-        stop();
+    public void close() {
+        synchronized (mLock) {
+            if (mVirtualMachine == null) {
+                return;
+            }
+            try {
+                if (stateToStatus(mVirtualMachine.getState()) == STATUS_RUNNING) {
+                    mVirtualMachine.stop();
+                    mVirtualMachine = null;
+                }
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            } catch (ServiceSpecificException e) {
+                // Deliberately ignored; this almost certainly means the VM exited just as
+                // we tried to stop it.
+                Log.i(TAG, "Ignoring error on close()", e);
+            }
+        }
     }
 
     private static void deleteRecursively(File dir) throws IOException {
diff --git a/tests/benchmark/Android.bp b/tests/benchmark/Android.bp
index 10cdac5..9d2b6c7 100644
--- a/tests/benchmark/Android.bp
+++ b/tests/benchmark/Android.bp
@@ -38,6 +38,6 @@
         "libbase",
         "libbinder_ndk",
         "liblog",
-        "libvm_payload",
+        "libvm_payload#current",
     ],
 }
diff --git a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
index 13e3afd..14a0e39 100644
--- a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
+++ b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
@@ -207,8 +207,7 @@
         for (int i = 0; i < IO_TEST_TRIAL_COUNT; ++i) {
             int port = (mProtectedVm ? 5666 : 6666) + i;
             String vmName = "test_vm_io_" + i;
-            forceCreateNewVirtualMachine(vmName, config);
-            VirtualMachine vm = getVirtualMachineManager().get(vmName);
+            VirtualMachine vm = forceCreateNewVirtualMachine(vmName, config);
             BenchmarkVmListener.create(new VsockListener(transferRates, port)).runToFinish(TAG, vm);
         }
         reportMetrics(transferRates, "vsock/transfer_host_to_vm", "mb_per_sec");
@@ -240,8 +239,7 @@
                 readRates.clear();
             }
             String vmName = "test_vm_io_" + i;
-            forceCreateNewVirtualMachine(vmName, config);
-            VirtualMachine vm = getVirtualMachineManager().get(vmName);
+            VirtualMachine vm = forceCreateNewVirtualMachine(vmName, config);
             BenchmarkVmListener.create(new VirtioBlkListener(readRates, isRand))
                     .runToFinish(TAG, vm);
         }
@@ -290,8 +288,7 @@
                         .setDebugLevel(DEBUG_LEVEL_NONE)
                         .setMemoryMib(256)
                         .build();
-        forceCreateNewVirtualMachine(vmName, config);
-        VirtualMachine vm = getVirtualMachineManager().get(vmName);
+        VirtualMachine vm = forceCreateNewVirtualMachine(vmName, config);
         MemoryUsageListener listener = new MemoryUsageListener(this::executeCommand);
         BenchmarkVmListener.create(listener).runToFinish(TAG, vm);
 
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index df7c6c0..4dc9489 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -39,7 +39,7 @@
     shared_libs: [
         "libbinder_ndk",
         "MicrodroidTestNativeLibSub",
-        "libvm_payload",
+        "libvm_payload#current",
     ],
     static_libs: [
         "com.android.microdroid.testservice-ndk",
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index f6bb7e4..1f5bae9 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -15,6 +15,9 @@
  */
 package com.android.microdroid.test;
 
+import static android.system.virtualmachine.VirtualMachine.STATUS_DELETED;
+import static android.system.virtualmachine.VirtualMachine.STATUS_RUNNING;
+import static android.system.virtualmachine.VirtualMachine.STATUS_STOPPED;
 import static android.system.virtualmachine.VirtualMachineConfig.DEBUG_LEVEL_APP_ONLY;
 import static android.system.virtualmachine.VirtualMachineConfig.DEBUG_LEVEL_FULL;
 import static android.system.virtualmachine.VirtualMachineConfig.DEBUG_LEVEL_NONE;
@@ -102,11 +105,8 @@
     private static final int MIN_MEM_X86_64 = 196;
 
     @Test
-    @CddTest(requirements = {
-            "9.17/C-1-1",
-            "9.17/C-2-1"
-    })
-    public void connectToVmService() throws Exception {
+    @CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-1"})
+    public void createAndConnectToVm() throws Exception {
         assumeSupportedKernel();
 
         VirtualMachineConfig config =
@@ -151,6 +151,36 @@
     }
 
     @Test
+    @CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-1"})
+    public void autoCloseVm() throws Exception {
+        assumeSupportedKernel();
+
+        VirtualMachineConfig config =
+                newVmConfigBuilder()
+                        .setPayloadBinaryPath("MicrodroidTestNativeLib.so")
+                        .setMemoryMib(minMemoryRequired())
+                        .build();
+
+        try (VirtualMachine vm = forceCreateNewVirtualMachine("test_vm", config)) {
+            assertThat(vm.getStatus()).isEqualTo(STATUS_STOPPED);
+            // close() implicitly called on stopped VM.
+        }
+
+        try (VirtualMachine vm = getVirtualMachineManager().get("test_vm")) {
+            vm.run();
+            assertThat(vm.getStatus()).isEqualTo(STATUS_RUNNING);
+            // close() implicitly called on running VM.
+        }
+
+        try (VirtualMachine vm = getVirtualMachineManager().get("test_vm")) {
+            assertThat(vm.getStatus()).isEqualTo(STATUS_STOPPED);
+            getVirtualMachineManager().delete("test_vm");
+            assertThat(vm.getStatus()).isEqualTo(STATUS_DELETED);
+            // close() implicitly called on deleted VM.
+        }
+    }
+
+    @Test
     @CddTest(requirements = {
             "9.17/C-1-1",
             "9.17/C-1-2",
@@ -399,6 +429,7 @@
     })
     public void sameInstanceKeepsSameCdis() throws Exception {
         assumeSupportedKernel();
+        assume().withMessage("Skip on CF. Too Slow. b/257270529").that(isCuttlefish()).isFalse();
 
         grantPermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION);
         VirtualMachineConfig normalConfig =
diff --git a/vm_payload/Android.bp b/vm_payload/Android.bp
index 6be6f22..967d1cf 100644
--- a/vm_payload/Android.bp
+++ b/vm_payload/Android.bp
@@ -2,9 +2,11 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-rust_ffi_shared {
-    name: "libvm_payload",
+// The Rust implementation of the C API.
+rust_ffi_static {
+    name: "libvm_payload_impl",
     crate_name: "vm_payload",
+    visibility: ["//visibility:private"],
     srcs: ["src/*.rs"],
     include_dirs: ["include"],
     prefer_rlib: true,
@@ -19,9 +21,6 @@
         "librpcbinder_rs",
         "libvsock",
     ],
-    apex_available: [
-        "com.android.compos",
-    ],
     // The sanitize section below fixes the fuzzer build in b/256166339.
     // TODO(b/250854486): Remove the sanitize section once the bug is fixed.
     sanitize: {
@@ -29,6 +28,8 @@
     },
 }
 
+// Rust wrappers round the C API for Rust clients.
+// (Yes, this involves going Rust -> C -> Rust.)
 rust_bindgen {
     name: "libvm_payload_bindgen",
     wrapper_src: "include-restricted/vm_payload_restricted.h",
@@ -37,16 +38,38 @@
     apex_available: ["com.android.compos"],
     visibility: ["//packages/modules/Virtualization/compos"],
     shared_libs: [
-        "libvm_payload",
+        "libvm_payload#current",
     ],
 }
 
+// Shared library for clients to link against.
+cc_library_shared {
+    name: "libvm_payload",
+    shared_libs: [
+        "libbinder_ndk",
+        "libbinder_rpc_unstable",
+        "liblog",
+    ],
+    whole_static_libs: ["libvm_payload_impl"],
+    export_static_lib_headers: ["libvm_payload_impl"],
+    installable: false,
+    version_script: "libvm_payload.map.txt",
+    stubs: {
+        symbol_file: "libvm_payload.map.txt",
+        // Implementation is available inside a Microdroid VM.
+        implementation_installable: false,
+    },
+}
+
+// Just the headers. Mostly useful for clients that only want the
+// declaration of AVmPayload_main().
 cc_library_headers {
     name: "vm_payload_headers",
     apex_available: ["com.android.compos"],
     export_include_dirs: ["include"],
 }
 
+// Restricted headers for use by internal clients & associated tests.
 cc_library_headers {
     name: "vm_payload_restricted_headers",
     header_libs: ["vm_payload_headers"],
diff --git a/vm_payload/libvm_payload.map.txt b/vm_payload/libvm_payload.map.txt
new file mode 100644
index 0000000..a2402d1
--- /dev/null
+++ b/vm_payload/libvm_payload.map.txt
@@ -0,0 +1,12 @@
+LIBVM_PAYLOAD {
+  global:
+    AVmPayload_notifyPayloadReady;       # systemapi
+    AVmPayload_runVsockRpcServer;        # systemapi
+    AVmPayload_getVmInstanceSecret;      # systemapi
+    AVmPayload_getDiceAttestationChain;  # systemapi
+    AVmPayload_getDiceAttestationCdi;    # systemapi
+    AVmPayload_getApkContentsPath;       # systemapi
+    AVmPayload_getEncryptedStoragePath;  # systemapi
+  local:
+    *;
+};