Merge changes Ie725df5a,I18786a33

* changes:
  pvmfw: Add top-level Error reporting
  vmbase: Make BASE_ADDRESS public
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
index 46b9e77..0eb1257 100644
--- a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
+++ b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
@@ -17,26 +17,15 @@
 package com.android.virt.fs;
 
 import static com.android.microdroid.test.host.CommandResultSubject.assertThat;
-import static com.android.microdroid.test.host.LogArchiver.archiveLogThenDelete;
-import static com.android.tradefed.device.TestDevice.MicrodroidBuilder;
-import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
 
 import android.platform.test.annotations.RootPermissionTest;
 
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.compatibility.common.util.PollingCheck;
 import com.android.microdroid.test.host.CommandRunner;
-import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.device.TestDevice;
 import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -45,19 +34,10 @@
 import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
 import com.android.tradefed.util.CommandResult;
 
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.TestName;
 import org.junit.runner.RunWith;
 
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicBoolean;
-
 @RootPermissionTest
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class AuthFsHostTest extends BaseHostJUnit4Test {
@@ -68,39 +48,15 @@
     /** Output directory where the test can generate output on Android */
     private static final String TEST_OUTPUT_DIR = "/data/local/tmp/authfs/output_dir";
 
-    /** VM's log file */
-    private static final String LOG_PATH = TEST_OUTPUT_DIR + "/log.txt";
-
-    /** File name of the test APK */
-    private static final String TEST_APK_NAME = "MicrodroidTestApp.apk";
-
-    /** VM config entry path in the test APK */
-    private static final String VM_CONFIG_PATH_IN_APK = "assets/vm_config.json";
-
-    /** Path to open_then_run on Android */
-    private static final String OPEN_THEN_RUN_BIN = "/data/local/tmp/open_then_run";
-
     /** Path to fsverity on Android */
     private static final String FSVERITY_BIN = "/data/local/tmp/fsverity";
 
     /** Mount point of authfs on Microdroid during the test */
     private static final String MOUNT_DIR = "/data/local/tmp";
 
-    /** Path to fd_server on Android */
-    private static final String FD_SERVER_BIN = "/apex/com.android.virt/bin/fd_server";
-
-    /** Path to authfs on Microdroid */
-    private static final String AUTHFS_BIN = "/system/bin/authfs";
-
     /** Input manifest path in the VM. */
     private static final String INPUT_MANIFEST_PATH = "/mnt/apk/assets/input_manifest.pb";
 
-    /** Plenty of time for authfs to get ready */
-    private static final int AUTHFS_INIT_TIMEOUT_MS = 3000;
-
-    /** FUSE's magic from statfs(2) */
-    private static final String FUSE_SUPER_MAGIC_HEX = "65735546";
-
     // fs-verity digest (sha256) of testdata/input.{4k, 4k1, 4m}
     private static final String DIGEST_4K =
             "sha256-9828cd65f4744d6adda216d3a63d8205375be485bfa261b3b8153d3358f5a576";
@@ -113,86 +69,20 @@
 
     private static CommandRunner sAndroid;
     private static CommandRunner sMicrodroid;
-    private static boolean sAssumptionFailed;
 
-    private ExecutorService mThreadPool = Executors.newCachedThreadPool();
-
-    @Rule public TestLogData mTestLogs = new TestLogData();
-    @Rule public TestName mTestName = new TestName();
+    @Rule public final AuthFsTestRule mAuthFsTestRule = new AuthFsTestRule();
 
     @BeforeClassWithInfo
     public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
-        assertNotNull(testInfo.getDevice());
-        if (!(testInfo.getDevice() instanceof TestDevice)) {
-            CLog.w("Unexpected type of ITestDevice. Skipping.");
-            return;
-        }
-        TestDevice androidDevice = (TestDevice) testInfo.getDevice();
-        sAndroid = new CommandRunner(androidDevice);
-
-        // NB: We can't use assumeTrue because the assumption exception is NOT handled by the test
-        // infra when it is thrown from a class method (see b/37502066). We need to skip both here
-        // and in setUp.
-        if (!androidDevice.supportsMicrodroid()) {
-            CLog.i("Microdroid not supported. Skipping.");
-            return;
-        }
-
-        // For each test case, boot and adb connect to a new Microdroid
-        CLog.i("Starting the shared VM");
-        ITestDevice microdroidDevice =
-                MicrodroidBuilder
-                        .fromFile(findTestApk(testInfo.getBuildInfo()), VM_CONFIG_PATH_IN_APK)
-                        .debugLevel("full")
-                        .build((TestDevice) androidDevice);
-
-        // From this point on, we need to tear down the Microdroid instance
-        sMicrodroid = new CommandRunner(microdroidDevice);
-
-        // Root because authfs (started from shell in this test) currently require root to open
-        // /dev/fuse and mount the FUSE.
-        assertThat(microdroidDevice.enableAdbRoot()).isTrue();
+        AuthFsTestRule.setUpClass(testInfo);
+        sAndroid = AuthFsTestRule.getAndroid();
+        sMicrodroid = AuthFsTestRule.getMicrodroid();
     }
 
     @AfterClassWithInfo
     public static void afterClassWithDevice(TestInformation testInfo)
             throws DeviceNotAvailableException {
-        assertNotNull(sAndroid);
-
-        if (sMicrodroid != null) {
-            CLog.i("Shutting down shared VM");
-            ((TestDevice) testInfo.getDevice()).shutdownMicrodroid(sMicrodroid.getDevice());
-            sMicrodroid = null;
-        }
-
-        sAndroid = null;
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        assumeTrue(((TestDevice) getDevice()).supportsMicrodroid());
-        sAndroid.run("mkdir " + TEST_OUTPUT_DIR);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        if (sMicrodroid != null) {
-            sMicrodroid.tryRun("killall authfs");
-            sMicrodroid.tryRun("umount " + MOUNT_DIR);
-        }
-
-        assertNotNull(sAndroid);
-        sAndroid.tryRun("killall fd_server");
-
-        // Even though we only run one VM for the whole class, and could have collect the VM log
-        // after all tests are done, TestLogData doesn't seem to work at class level. Hence,
-        // collect recent logs manually for each test method.
-        String vmRecentLog = TEST_OUTPUT_DIR + "/vm_recent.log";
-        sAndroid.tryRun("tail -n 50 " + LOG_PATH + " > " + vmRecentLog);
-        archiveLogThenDelete(mTestLogs, getDevice(), vmRecentLog,
-                "vm_recent.log-" + mTestName.getMethodName());
-
-        sAndroid.run("rm -rf " + TEST_OUTPUT_DIR);
+        AuthFsTestRule.tearDownClass(testInfo);
     }
 
     @Test
@@ -534,7 +424,7 @@
         sAndroid.run("test ! -d " + androidOutputDir + "/dir/dir2");
         // Can only delete a directory if empty
         assertThat(sMicrodroid.runForResult("rmdir " + authfsOutputDir + "/dir")).isFailed();
-        sMicrodroid.run("test -d " + authfsOutputDir + "/dir");  // still there
+        sMicrodroid.run("test -d " + authfsOutputDir + "/dir"); // still there
         sMicrodroid.run("rm " + authfsOutputDir + "/dir/file");
         sMicrodroid.run("rmdir " + authfsOutputDir + "/dir");
         sMicrodroid.run("test ! -d " + authfsOutputDir + "/dir");
@@ -560,9 +450,9 @@
         assertThat(sMicrodroid.runForResult("mkdir " + authfsOutputDir + "/some_file")).isFailed();
         assertThat(sMicrodroid.runForResult("mkdir " + authfsOutputDir + "/some_dir")).isFailed();
         assertThat(sMicrodroid.runForResult("mkdir " + authfsOutputDir + "/some_dir/file"))
-            .isFailed();
+                .isFailed();
         assertThat(sMicrodroid.runForResult("mkdir " + authfsOutputDir + "/some_dir/dir"))
-            .isFailed();
+                .isFailed();
     }
 
     @Test
@@ -741,16 +631,8 @@
         // Verify
         // Magic matches. Has only 2 inodes (root and "/3").
         assertEquals(
-                FUSE_SUPER_MAGIC_HEX + " 2", sMicrodroid.run("stat -f -c '%t %c' " + MOUNT_DIR));
-    }
-
-    private static File findTestApk(IBuildInfo buildInfo) {
-        try {
-            return (new CompatibilityBuildHelper(buildInfo)).getTestFile(TEST_APK_NAME);
-        } catch (FileNotFoundException e) {
-            fail("Missing test file: " + TEST_APK_NAME);
-            return null;
-        }
+                mAuthFsTestRule.FUSE_SUPER_MAGIC_HEX + " 2",
+                sMicrodroid.run("stat -f -c '%t %c' " + MOUNT_DIR));
     }
 
     private void expectBackingFileConsistency(
@@ -832,66 +714,11 @@
     }
 
     private void runAuthFsOnMicrodroid(String flags) {
-        String cmd = AUTHFS_BIN + " " + MOUNT_DIR + " " + flags;
-
-        AtomicBoolean starting = new AtomicBoolean(true);
-        mThreadPool.submit(
-                () -> {
-                    // authfs may fail to start if fd_server is not yet listening on the vsock
-                    // ("Error: Invalid raw AIBinder"). Just restart if that happens.
-                    while (starting.get()) {
-                        try {
-                            CLog.i("Starting authfs");
-                            CommandResult result = sMicrodroid.runForResult(cmd);
-                            CLog.w("authfs has stopped: " + result);
-                        } catch (DeviceNotAvailableException e) {
-                            CLog.e("Error running authfs", e);
-                            throw new RuntimeException(e);
-                        }
-                    }
-                });
-        try {
-            PollingCheck.waitFor(
-                    AUTHFS_INIT_TIMEOUT_MS, () -> isMicrodroidDirectoryOnFuse(MOUNT_DIR));
-        } catch (Exception e) {
-            // Convert the broad Exception into an unchecked exception to avoid polluting all other
-            // methods. waitFor throws Exception because the callback, Callable#call(), has a
-            // signature to throw an Exception.
-            throw new RuntimeException(e);
-        } finally {
-            starting.set(false);
-        }
+        mAuthFsTestRule.runAuthFsOnMicrodroid(flags);
     }
 
     private void runFdServerOnAndroid(String helperFlags, String fdServerFlags)
             throws DeviceNotAvailableException {
-        String cmd =
-                "cd "
-                        + TEST_DIR
-                        + " && "
-                        + OPEN_THEN_RUN_BIN
-                        + " "
-                        + helperFlags
-                        + " -- "
-                        + FD_SERVER_BIN
-                        + " "
-                        + fdServerFlags;
-
-        mThreadPool.submit(
-                () -> {
-                    try {
-                        CLog.i("Starting fd_server");
-                        CommandResult result = sAndroid.runForResult(cmd);
-                        CLog.w("fd_server has stopped: " + result);
-                    } catch (DeviceNotAvailableException e) {
-                        CLog.e("Error running fd_server", e);
-                        throw new RuntimeException(e);
-                    }
-                });
-    }
-
-    private boolean isMicrodroidDirectoryOnFuse(String path) throws DeviceNotAvailableException {
-        String fs_type = sMicrodroid.tryRun("stat -f -c '%t' " + path);
-        return FUSE_SUPER_MAGIC_HEX.equals(fs_type);
+        mAuthFsTestRule.runFdServerOnAndroid(helperFlags, fdServerFlags);
     }
 }
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsTestRule.java b/authfs/tests/java/src/com/android/fs/AuthFsTestRule.java
new file mode 100644
index 0000000..e6081f7
--- /dev/null
+++ b/authfs/tests/java/src/com/android/fs/AuthFsTestRule.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.virt.fs;
+
+import static com.android.microdroid.test.host.LogArchiver.archiveLogThenDelete;
+import static com.android.tradefed.device.TestDevice.MicrodroidBuilder;
+import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.microdroid.test.host.CommandRunner;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.device.TestDevice;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.CommandResult;
+
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/** Custom TestRule for AuthFs tests. */
+public class AuthFsTestRule extends TestLogData {
+    /** FUSE's magic from statfs(2) */
+    static final String FUSE_SUPER_MAGIC_HEX = "65735546";
+
+    /** VM config entry path in the test APK */
+    private static final String VM_CONFIG_PATH_IN_APK = "assets/vm_config.json";
+
+    /** Test directory on Android where data are located */
+    private static final String TEST_DIR = "/data/local/tmp/authfs";
+
+    /** File name of the test APK */
+    private static final String TEST_APK_NAME = "MicrodroidTestApp.apk";
+
+    /** Output directory where the test can generate output on Android */
+    private static final String TEST_OUTPUT_DIR = "/data/local/tmp/authfs/output_dir";
+
+    /** Mount point of authfs on Microdroid during the test */
+    private static final String MOUNT_DIR = "/data/local/tmp";
+
+    /** VM's log file */
+    private static final String LOG_PATH = TEST_OUTPUT_DIR + "/log.txt";
+
+    /** Path to open_then_run on Android */
+    private static final String OPEN_THEN_RUN_BIN = "/data/local/tmp/open_then_run";
+
+    /** Path to fd_server on Android */
+    private static final String FD_SERVER_BIN = "/apex/com.android.virt/bin/fd_server";
+
+    /** Path to authfs on Microdroid */
+    private static final String AUTHFS_BIN = "/system/bin/authfs";
+
+    /** Plenty of time for authfs to get ready */
+    private static final int AUTHFS_INIT_TIMEOUT_MS = 3000;
+
+    private static TestInformation sTestInfo;
+    private static CommandRunner sAndroid;
+    private static CommandRunner sMicrodroid;
+
+    private final ExecutorService mThreadPool = Executors.newCachedThreadPool();
+
+    static void setUpClass(TestInformation testInfo) throws Exception {
+        assertNotNull(testInfo.getDevice());
+        if (!(testInfo.getDevice() instanceof TestDevice)) {
+            CLog.w("Unexpected type of ITestDevice. Skipping.");
+            return;
+        }
+        sTestInfo = testInfo;
+        TestDevice androidDevice = getDevice();
+        sAndroid = new CommandRunner(androidDevice);
+
+        // NB: We can't use assumeTrue because the assumption exception is NOT handled by the test
+        // infra when it is thrown from a class method (see b/37502066). We need to skip both here
+        // and in setUp.
+        if (!androidDevice.supportsMicrodroid()) {
+            CLog.i("Microdroid not supported. Skipping.");
+            return;
+        }
+
+        // For each test case, boot and adb connect to a new Microdroid
+        CLog.i("Starting the shared VM");
+        ITestDevice microdroidDevice =
+                MicrodroidBuilder.fromFile(
+                                findTestApk(testInfo.getBuildInfo()), VM_CONFIG_PATH_IN_APK)
+                        .debugLevel("full")
+                        .build((TestDevice) androidDevice);
+
+        // From this point on, we need to tear down the Microdroid instance
+        sMicrodroid = new CommandRunner(microdroidDevice);
+
+        // Root because authfs (started from shell in this test) currently require root to open
+        // /dev/fuse and mount the FUSE.
+        assertThat(microdroidDevice.enableAdbRoot()).isTrue();
+    }
+
+    static void tearDownClass(TestInformation testInfo) throws DeviceNotAvailableException {
+        assertNotNull(sAndroid);
+
+        if (sMicrodroid != null) {
+            CLog.i("Shutting down shared VM");
+            ((TestDevice) testInfo.getDevice()).shutdownMicrodroid(sMicrodroid.getDevice());
+            sMicrodroid = null;
+        }
+
+        sAndroid = null;
+    }
+
+    /** This method is supposed to be called after {@link #setUpTest()}. */
+    static CommandRunner getAndroid() {
+        assertThat(sAndroid).isNotNull();
+        return sAndroid;
+    }
+
+    /** This method is supposed to be called after {@link #setUpTest()}. */
+    static CommandRunner getMicrodroid() {
+        assertThat(sMicrodroid).isNotNull();
+        return sMicrodroid;
+    }
+
+    @Override
+    public Statement apply(final Statement base, Description description) {
+        return super.apply(
+                new Statement() {
+                    @Override
+                    public void evaluate() throws Throwable {
+                        setUpTest();
+                        base.evaluate();
+                        tearDownTest(description.getMethodName());
+                    }
+                },
+                description);
+    }
+
+    void runFdServerOnAndroid(String helperFlags, String fdServerFlags)
+            throws DeviceNotAvailableException {
+        String cmd =
+                "cd "
+                        + TEST_DIR
+                        + " && "
+                        + OPEN_THEN_RUN_BIN
+                        + " "
+                        + helperFlags
+                        + " -- "
+                        + FD_SERVER_BIN
+                        + " "
+                        + fdServerFlags;
+        Future<?> unusedFuture = mThreadPool.submit(() -> runForResult(sAndroid, cmd, "fd_server"));
+    }
+
+    void runAuthFsOnMicrodroid(String flags) {
+        String cmd = AUTHFS_BIN + " " + MOUNT_DIR + " " + flags;
+
+        AtomicBoolean starting = new AtomicBoolean(true);
+        Future<?> unusedFuture =
+                mThreadPool.submit(
+                        () -> {
+                            // authfs may fail to start if fd_server is not yet listening on the
+                            // vsock
+                            // ("Error: Invalid raw AIBinder"). Just restart if that happens.
+                            while (starting.get()) {
+                                runForResult(sMicrodroid, cmd, "authfs");
+                            }
+                        });
+        try {
+            PollingCheck.waitFor(
+                    AUTHFS_INIT_TIMEOUT_MS, () -> isMicrodroidDirectoryOnFuse(MOUNT_DIR));
+        } catch (Exception e) {
+            // Convert the broad Exception into an unchecked exception to avoid polluting all other
+            // methods. waitFor throws Exception because the callback, Callable#call(), has a
+            // signature to throw an Exception.
+            throw new RuntimeException(e);
+        } finally {
+            starting.set(false);
+        }
+    }
+
+    private static File findTestApk(IBuildInfo buildInfo) {
+        try {
+            return (new CompatibilityBuildHelper(buildInfo)).getTestFile(TEST_APK_NAME);
+        } catch (FileNotFoundException e) {
+            fail("Missing test file: " + TEST_APK_NAME);
+            return null;
+        }
+    }
+
+    private static TestDevice getDevice() {
+        return (TestDevice) sTestInfo.getDevice();
+    }
+
+    private void runForResult(CommandRunner cmdRunner, String cmd, String serviceName) {
+        try {
+            CLog.i("Starting " + serviceName);
+            CommandResult result = cmdRunner.runForResult(cmd);
+            CLog.w(serviceName + " has stopped: " + result);
+        } catch (DeviceNotAvailableException e) {
+            CLog.e("Error running " + serviceName, e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    private boolean isMicrodroidDirectoryOnFuse(String path) throws DeviceNotAvailableException {
+        String fs_type = sMicrodroid.tryRun("stat -f -c '%t' " + path);
+        return FUSE_SUPER_MAGIC_HEX.equals(fs_type);
+    }
+
+    private void setUpTest() throws Exception {
+        assumeTrue(getDevice().supportsMicrodroid());
+        sAndroid.run("mkdir -p " + TEST_OUTPUT_DIR);
+    }
+
+    private void tearDownTest(String testName) throws Exception {
+        if (sMicrodroid != null) {
+            sMicrodroid.tryRun("killall authfs");
+            sMicrodroid.tryRun("umount " + MOUNT_DIR);
+        }
+
+        assertNotNull(sAndroid);
+        sAndroid.tryRun("killall fd_server");
+
+        // Even though we only run one VM for the whole class, and could have collect the VM log
+        // after all tests are done, TestLogData doesn't seem to work at class level. Hence,
+        // collect recent logs manually for each test method.
+        String vmRecentLog = TEST_OUTPUT_DIR + "/vm_recent.log";
+        sAndroid.tryRun("tail -n 50 " + LOG_PATH + " > " + vmRecentLog);
+        archiveLogThenDelete(this, getDevice(), vmRecentLog, "vm_recent.log-" + testName);
+
+        sAndroid.run("rm -rf " + TEST_OUTPUT_DIR);
+    }
+}
diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md
index 7fddc5a..34a990d 100644
--- a/docs/getting_started/index.md
+++ b/docs/getting_started/index.md
@@ -118,7 +118,7 @@
 on pVM. You can manually run the demo app on top of Microdroid as follows:
 
 ```shell
-TARGET_BUILD_APPS=MicrodroidDemoApp m apps_only dist
+UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true TARGET_BUILD_APPS=MicrodroidDemoApp m apps_only dist
 adb shell mkdir -p /data/local/tmp/virt
 adb push out/dist/MicrodroidDemoApp.apk /data/local/tmp/virt/
 adb shell /apex/com.android.virt/bin/vm run-app \
diff --git a/microdroid/init.rc b/microdroid/init.rc
index 47002c9..8b2bbdb 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -177,7 +177,7 @@
     mkdir /data/local/tmp 0771 shell shell
 
 service tombstone_transmit /system/bin/tombstone_transmit.microdroid -cid 2 -port 2000 -remove_tombstones_after_transmitting
-    user root
+    user system
     group system
     shutdown critical
 
@@ -186,12 +186,14 @@
     group system
     oneshot
     disabled
+    capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER SYS_ADMIN
 
 service ueventd /system/bin/ueventd
     class core
     critical
     seclabel u:r:ueventd:s0
     shutdown critical
+    capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID MKNOD NET_ADMIN SETGID SETUID SYS_MODULE SYS_RAWIO
 
 service console /system/bin/sh
     class core
diff --git a/microdroid/payload/metadata.proto b/microdroid/payload/metadata.proto
index 229d03f..06cbbf4 100644
--- a/microdroid/payload/metadata.proto
+++ b/microdroid/payload/metadata.proto
@@ -68,12 +68,8 @@
   // Path to the payload binary file inside the APK.
   string payload_binary_path = 1;
 
-  // Required.
-  // Whether tombstones from crashes inside the VM should be exported to the host.
-  bool export_tombstones = 2;
-
   // Optional.
   // Arguments to be passed to the payload.
   // TODO(b/249064104): Remove this
-  repeated string args = 3;
+  repeated string args = 2;
 }
diff --git a/microdroid/vm_payload/Android.bp b/microdroid/vm_payload/Android.bp
new file mode 100644
index 0000000..0424fc1
--- /dev/null
+++ b/microdroid/vm_payload/Android.bp
@@ -0,0 +1,19 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_ffi_static {
+    name: "libvm_payload",
+    crate_name: "vm_payload",
+    srcs: ["src/*.rs"],
+    include_dirs: ["include"],
+    prefer_rlib: true,
+    rustlibs: [
+        "android.system.virtualmachineservice-rust",
+        "libandroid_logger",
+        "libanyhow",
+        "libbinder_rs",
+        "liblog_rust",
+        "librpcbinder_rs",
+    ],
+}
diff --git a/microdroid/vm_payload/include/vm_payload.h b/microdroid/vm_payload/include/vm_payload.h
new file mode 100644
index 0000000..36480da
--- /dev/null
+++ b/microdroid/vm_payload/include/vm_payload.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Notifies the host that the payload is ready.
+/// Returns true if the notification succeeds else false.
+bool notify_payload_ready();
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/microdroid/vm_payload/src/lib.rs b/microdroid/vm_payload/src/lib.rs
new file mode 100644
index 0000000..394578a
--- /dev/null
+++ b/microdroid/vm_payload/src/lib.rs
@@ -0,0 +1,19 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Library for payload to communicate with the VM service.
+
+mod vm_service;
+
+pub use vm_service::notify_payload_ready;
diff --git a/microdroid/vm_payload/src/vm_service.rs b/microdroid/vm_payload/src/vm_service.rs
new file mode 100644
index 0000000..e5a6b9a
--- /dev/null
+++ b/microdroid/vm_payload/src/vm_service.rs
@@ -0,0 +1,52 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! This module handles the interaction with virtual machine service.
+
+use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
+    VM_BINDER_SERVICE_PORT, IVirtualMachineService};
+use anyhow::{Context, Result};
+use binder::Strong;
+use log::{error, info, Level};
+use rpcbinder::get_vsock_rpc_interface;
+
+/// The CID representing the host VM
+const VMADDR_CID_HOST: u32 = 2;
+
+/// Notifies the host that the payload is ready.
+/// Returns true if the notification succeeds else false.
+#[no_mangle]
+pub extern "C" fn notify_payload_ready() -> bool {
+    android_logger::init_once(
+        android_logger::Config::default().with_tag("vm_payload").with_min_level(Level::Debug),
+    );
+    if let Err(e) = try_notify_payload_ready() {
+        error!("Failed to notify ready: {}", e);
+        false
+    } else {
+        info!("Notified host payload ready successfully");
+        true
+    }
+}
+
+/// Notifies the host that the payload is ready.
+/// Returns a `Result` containing error information if failed.
+fn try_notify_payload_ready() -> Result<()> {
+    get_vm_service()?.notifyPayloadReady().context("Cannot notify payload ready")
+}
+
+fn get_vm_service() -> Result<Strong<dyn IVirtualMachineService>> {
+    get_vsock_rpc_interface(VMADDR_CID_HOST, VM_BINDER_SERVICE_PORT as u32)
+        .context("Connecting to IVirtualMachineService")
+}
diff --git a/microdroid_manager/microdroid_manager.rc b/microdroid_manager/microdroid_manager.rc
index 60d8ab7..74a219d 100644
--- a/microdroid_manager/microdroid_manager.rc
+++ b/microdroid_manager/microdroid_manager.rc
@@ -4,3 +4,5 @@
     setenv RUST_LOG info
     # TODO(jooyung) remove this when microdroid_manager becomes a daemon
     oneshot
+    # SYS_BOOT is required to exec kexecload from microdroid_manager
+    capabilities AUDIT_CONTROL SYS_ADMIN SYS_BOOT
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 90cabb6..b4b2c8b 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -30,12 +30,12 @@
 use anyhow::{anyhow, bail, ensure, Context, Error, Result};
 use apkverify::{get_public_key_der, verify, V4Signature};
 use binder::{wait_for_interface, Strong};
-use diced_utils::cbor::encode_header;
+use diced_utils::cbor::{encode_header, encode_number};
 use glob::glob;
 use itertools::sorted;
 use log::{error, info};
 use microdroid_metadata::{write_metadata, Metadata, PayloadMetadata};
-use microdroid_payload_config::{Task, TaskType, VmPayloadConfig};
+use microdroid_payload_config::{Task, TaskType, VmPayloadConfig, OsConfig};
 use openssl::sha::Sha512;
 use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
 use rand::Fill;
@@ -191,7 +191,10 @@
     }
 }
 
-fn dice_derivation(verified_data: &MicrodroidData, payload_config_path: &str) -> Result<()> {
+fn dice_derivation(
+    verified_data: &MicrodroidData,
+    payload_metadata: &PayloadMetadata,
+) -> Result<()> {
     // Calculate compound digests of code and authorities
     let mut code_hash_ctx = Sha512::new();
     let mut authority_hash_ctx = Sha512::new();
@@ -210,15 +213,33 @@
 
     // {
     //   -70002: "Microdroid payload",
-    //   -71000: payload_config_path
+    //   ? -71000: tstr // payload_config_path
+    //   ? -71001: PayloadConfig
     // }
+    // PayloadConfig = {
+    //   1: tstr // payload_binary_path
+    //   // TODO(b/249064104 Either include args, or deprecate them
+    // }
+
     let mut config_desc = vec![
-        0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72, 0x6f,
-        0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01, 0x15, 0x57,
+        0xa2, // map(2)
+        0x3a, 0x00, 0x01, 0x11, 0x71, // -70002
+        0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x70, 0x61, 0x79,
+        0x6c, 0x6f, 0x61, 0x64, // "Microdroid payload"
     ];
-    let config_path_bytes = payload_config_path.as_bytes();
-    encode_header(3, config_path_bytes.len().try_into().unwrap(), &mut config_desc)?;
-    config_desc.extend_from_slice(config_path_bytes);
+
+    match payload_metadata {
+        PayloadMetadata::config_path(payload_config_path) => {
+            encode_negative_number(-71000, &mut config_desc)?;
+            encode_tstr(payload_config_path, &mut config_desc)?;
+        }
+        PayloadMetadata::config(payload_config) => {
+            encode_negative_number(-71001, &mut config_desc)?;
+            encode_header(5, 1, &mut config_desc)?; // map(1)
+            encode_number(1, &mut config_desc)?;
+            encode_tstr(&payload_config.payload_binary_path, &mut config_desc)?;
+        }
+    }
 
     // Check app debuggability, conervatively assuming it is debuggable
     let app_debuggable = system_properties::read_bool(APP_DEBUGGABLE_PROP, true)?;
@@ -240,6 +261,19 @@
     Ok(())
 }
 
+fn encode_tstr(tstr: &str, buffer: &mut Vec<u8>) -> Result<()> {
+    let bytes = tstr.as_bytes();
+    encode_header(3, bytes.len().try_into().unwrap(), buffer)?;
+    buffer.extend_from_slice(bytes);
+    Ok(())
+}
+
+fn encode_negative_number(n: i64, buffer: &mut dyn Write) -> Result<()> {
+    ensure!(n < 0);
+    let n = -1 - n;
+    encode_header(1, n.try_into().unwrap(), buffer)
+}
+
 fn is_strict_boot() -> bool {
     Path::new(AVF_STRICT_BOOT).exists()
 }
@@ -301,14 +335,13 @@
         verified_data
     };
 
-    let payload_config_path = match metadata.payload {
-        Some(PayloadMetadata::config_path(p)) => p,
-        _ => bail!("Unsupported payload config"),
-    };
+    let payload_metadata = metadata.payload.ok_or_else(|| {
+        MicrodroidError::InvalidConfig("No payload config in metadata".to_string())
+    })?;
 
     // To minimize the exposure to untrusted data, derive dice profile as soon as possible.
     info!("DICE derivation for payload");
-    dice_derivation(&verified_data, &payload_config_path)?;
+    dice_derivation(&verified_data, &payload_metadata)?;
 
     // Before reading a file from the APK, start zipfuse
     let noexec = false;
@@ -320,12 +353,7 @@
     )
     .context("Failed to run zipfuse")?;
 
-    ensure!(
-        !payload_config_path.is_empty(),
-        MicrodroidError::InvalidConfig("No payload_config_path in metadata".to_string())
-    );
-
-    let config = load_config(Path::new(&payload_config_path))?;
+    let config = load_config(payload_metadata)?;
 
     let task = config
         .task
@@ -334,7 +362,7 @@
 
     if config.extra_apks.len() != verified_data.extra_apks_data.len() {
         return Err(anyhow!(
-            "config expects {} extra apks, but found only {}",
+            "config expects {} extra apks, but found {}",
             config.extra_apks.len(),
             verified_data.extra_apks_data.len()
         ));
@@ -620,10 +648,31 @@
     }
 }
 
-fn load_config(path: &Path) -> Result<VmPayloadConfig> {
-    info!("loading config from {:?}...", path);
-    let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
-    Ok(serde_json::from_reader(file)?)
+fn load_config(payload_metadata: PayloadMetadata) -> Result<VmPayloadConfig> {
+    match payload_metadata {
+        PayloadMetadata::config_path(path) => {
+            let path = Path::new(&path);
+            info!("loading config from {:?}...", path);
+            let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
+            Ok(serde_json::from_reader(file)?)
+        }
+        PayloadMetadata::config(payload_config) => {
+            let task = Task {
+                type_: TaskType::MicrodroidLauncher,
+                command: payload_config.payload_binary_path,
+                args: payload_config.args.into_vec(),
+            };
+            Ok(VmPayloadConfig {
+                os: OsConfig { name: "microdroid".to_owned() },
+                task: Some(task),
+                apexes: vec![],
+                extra_apks: vec![],
+                prefer_staged: false,
+                export_tombstones: false,
+                enable_authfs: false,
+            })
+        }
+    }
 }
 
 /// Loads the crashkernel into memory using kexec if the VM is loaded with `crashkernel=' parameter
diff --git a/tests/helper/src/java/com/android/microdroid/test/common/ProcessUtil.java b/tests/helper/src/java/com/android/microdroid/test/common/ProcessUtil.java
index 611a572..d85929d 100644
--- a/tests/helper/src/java/com/android/microdroid/test/common/ProcessUtil.java
+++ b/tests/helper/src/java/com/android/microdroid/test/common/ProcessUtil.java
@@ -36,14 +36,14 @@
     public static List<SMapEntry> getProcessSmaps(int pid, Function<String, String> shellExecutor)
             throws IOException {
         String path = "/proc/" + pid + "/smaps";
-        return parseSmaps(shellExecutor.apply("cat " + path + " || true"));
+        return parseMemoryInfo(shellExecutor.apply("cat " + path + " || true"));
     }
 
     /** Gets metrics key and values mapping of specified process id */
     public static Map<String, Long> getProcessSmapsRollup(
             int pid, Function<String, String> shellExecutor) throws IOException {
         String path = "/proc/" + pid + "/smaps_rollup";
-        List<SMapEntry> entries = parseSmaps(shellExecutor.apply("cat " + path + " || true"));
+        List<SMapEntry> entries = parseMemoryInfo(shellExecutor.apply("cat " + path + " || true"));
         if (entries.size() > 1) {
             throw new RuntimeException(
                     "expected at most one entry in smaps_rollup, got " + entries.size());
@@ -54,6 +54,21 @@
         return new HashMap<String, Long>();
     }
 
+    /** Gets global memory metrics key and values mapping */
+    public static Map<String, Long> getProcessMemoryMap(
+            Function<String, String> shellExecutor) throws IOException {
+        // The input file of parseMemoryInfo need a header string as the key of output entries.
+        // /proc/meminfo doesn't have this line so add one as the key.
+        String header = "device memory info\n";
+        List<SMapEntry> entries = parseMemoryInfo(header
+                + shellExecutor.apply("cat /proc/meminfo"));
+        if (entries.size() != 1) {
+            throw new RuntimeException(
+                    "expected one entry in /proc/meminfo, got " + entries.size());
+        }
+        return entries.get(0).metrics;
+    }
+
     /** Gets process id and process name mapping of the device */
     public static Map<Integer, String> getProcessMap(Function<String, String> shellExecutor)
             throws IOException {
@@ -77,7 +92,7 @@
     // To ensures that only one object is created at a time.
     private ProcessUtil() {}
 
-    private static List<SMapEntry> parseSmaps(String file) {
+    private static List<SMapEntry> parseMemoryInfo(String file) {
         List<SMapEntry> entries = new ArrayList<SMapEntry>();
         for (String line : file.split("\n")) {
             line = line.trim();
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
index 7a71254..9dd2a15 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
@@ -71,7 +71,6 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -684,34 +683,6 @@
         shutdownMicrodroid(getDevice(), cid);
     }
 
-    /**
-     * TODO(b/249409434): to be replaced by ProcessUtil
-     *
-     * @deprecated use ProcessUtil instead.
-     */
-    @Deprecated
-    private Map<String, Long> parseMemInfo(String file) {
-        Map<String, Long> stats = new HashMap<>();
-        file.lines().forEach(line -> {
-            if (line.endsWith(" kB")) line = line.substring(0, line.length() - 3);
-
-            String[] elems = line.split(":");
-            assertThat(elems.length).isEqualTo(2);
-            stats.put(elems[0].trim(), Long.parseLong(elems[1].trim()));
-        });
-        return stats;
-    }
-
-    /**
-     * TODO(b/249409434): to be replaced by ProcessUtil
-     *
-     * @deprecated use ProcessUtil instead.
-     */
-    @Deprecated
-    private Map<String, Long> getProcMemInfo() {
-        return parseMemInfo(runOnMicrodroid("cat", "/proc/meminfo"));
-    }
-
     @Test
     public void testMicrodroidRamUsage() throws Exception {
         final String configPath = "assets/vm_config.json";
@@ -729,7 +700,8 @@
         waitForBootComplete();
         rootMicrodroid();
 
-        for (Map.Entry<String, Long> stat : getProcMemInfo().entrySet()) {
+        for (Map.Entry<String, Long> stat :
+                ProcessUtil.getProcessMemoryMap(cmd -> runOnMicrodroid(cmd)).entrySet()) {
             mMetrics.addTestMetric(
                     mMetricPrefix + "meminfo/" + stat.getKey().toLowerCase(),
                     stat.getValue().toString());
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 47116eb..9e6958c 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -33,7 +33,6 @@
     srcs: ["src/native/testbinary.cpp"],
     shared_libs: [
         "android.security.dice-ndk",
-        "android.system.virtualmachineservice-ndk",
         "com.android.microdroid.testservice-ndk",
         "libbase",
         "libbinder_ndk",
@@ -44,6 +43,7 @@
         "libfsverity_digests_proto_cc",
         "liblog",
         "libprotobuf-cpp-lite-ndk",
+        "libvm_payload",
     ],
 }
 
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index b4fee86..fd8e776 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 #include <aidl/android/security/dice/IDiceNode.h>
-#include <aidl/android/system/virtualmachineservice/IVirtualMachineService.h>
 #include <aidl/com/android/microdroid/testservice/BnTestService.h>
 #include <android-base/file.h>
 #include <android-base/properties.h>
@@ -33,11 +32,11 @@
 #include <binder_rpc_unstable.hpp>
 #include <string>
 
+#include "vm_payload.h"
+
 using aidl::android::hardware::security::dice::BccHandover;
 using aidl::android::security::dice::IDiceNode;
 
-using aidl::android::system::virtualmachineservice::IVirtualMachineService;
-
 using android::base::ErrnoError;
 using android::base::Error;
 using android::base::Result;
@@ -133,23 +132,11 @@
     auto testService = ndk::SharedRefBase::make<TestService>();
 
     auto callback = []([[maybe_unused]] void* param) {
-        // Tell microdroid_manager that we're ready.
-        // If we can't, abort in order to fail fast - the host won't proceed without
-        // receiving the onReady signal.
-        ndk::SpAIBinder binder(
-                RpcClient(VMADDR_CID_HOST, IVirtualMachineService::VM_BINDER_SERVICE_PORT));
-        auto virtualMachineService = IVirtualMachineService::fromBinder(binder);
-        if (virtualMachineService == nullptr) {
-            std::cerr << "failed to connect VirtualMachineService\n";
-            abort();
-        }
-        if (auto status = virtualMachineService->notifyPayloadReady(); !status.isOk()) {
-            std::cerr << "failed to notify payload ready to virtualizationservice: "
-                      << status.getDescription() << std::endl;
+        if (!notify_payload_ready()) {
+            std::cerr << "failed to notify payload ready to virtualizationservice" << std::endl;
             abort();
         }
     };
-
     if (!RunRpcServerCallback(testService->asBinder().get(), testService->SERVICE_PORT, callback,
                               nullptr)) {
         return Error() << "RPC Server failed to run";
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 2f4f731..0551229 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -27,6 +27,7 @@
         "libandroid_logger",
         "libanyhow",
         "libapkverify",
+        "libbase_rust",
         "libbinder_rs",
         "libcommand_fds",
         "libdisk",
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachinePayloadConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachinePayloadConfig.aidl
index 0c83349..4d37848 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachinePayloadConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachinePayloadConfig.aidl
@@ -24,11 +24,6 @@
     @utf8InCpp String payloadPath;
 
     /**
-     * Whether to export tombstones (VM crash details) from the VM to the host.
-     */
-    boolean exportTombstones;
-
-    /**
      * Command-line style arguments to be passed to the payload when it is executed.
      * TODO(b/249064104): Remove this
      */
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 22418b9..563fab0 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -667,7 +667,7 @@
         apexes: vec![],
         extra_apks: vec![],
         prefer_staged: false,
-        export_tombstones: payload_config.exportTombstones,
+        export_tombstones: false,
         enable_authfs: false,
     }
 }
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index 82a9e78..ff1116a 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -41,6 +41,9 @@
 use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
 use tombstoned_client::{TombstonedConnection, DebuggerdDumpType};
 
+/// external/crosvm
+use base::UnixSeqpacketListener;
+
 const CROSVM_PATH: &str = "/apex/com.android.virt/bin/crosvm";
 
 /// Version of the platform that crosvm currently implements. The format follows SemVer. This
@@ -139,7 +142,8 @@
             let (failure_pipe_read, failure_pipe_write) = create_pipe()?;
 
             // If this fails and returns an error, `self` will be left in the `Failed` state.
-            let child = Arc::new(run_vm(config, failure_pipe_write)?);
+            let child =
+                Arc::new(run_vm(config, &instance.temporary_directory, failure_pipe_write)?);
 
             let child_clone = child.clone();
             let instance_clone = instance.clone();
@@ -425,7 +429,11 @@
 }
 
 /// Starts an instance of `crosvm` to manage a new VM.
-fn run_vm(config: CrosvmConfig, failure_pipe_write: File) -> Result<SharedChild, Error> {
+fn run_vm(
+    config: CrosvmConfig,
+    temporary_directory: &Path,
+    failure_pipe_write: File,
+) -> Result<SharedChild, Error> {
     validate_config(&config)?;
 
     let mut command = Command::new(CROSVM_PATH);
@@ -434,6 +442,7 @@
         .arg("--extended-status")
         .arg("run")
         .arg("--disable-sandbox")
+        .arg("--no-balloon")
         .arg("--cid")
         .arg(config.cid.to_string());
 
@@ -514,6 +523,11 @@
         command.arg(add_preserved_fd(&mut preserved_fds, kernel));
     }
 
+    let control_server_socket =
+        UnixSeqpacketListener::bind(temporary_directory.join("crosvm.sock"))
+            .context("failed to create control server")?;
+    command.arg("--socket").arg(add_preserved_fd(&mut preserved_fds, &control_server_socket));
+
     debug!("Preserving FDs {:?}", preserved_fds);
     command.preserved_fds(preserved_fds);
 
@@ -546,7 +560,7 @@
 
 /// Adds the file descriptor for `file` to `preserved_fds`, and returns a string of the form
 /// "/proc/self/fd/N" where N is the file descriptor.
-fn add_preserved_fd(preserved_fds: &mut Vec<RawFd>, file: &File) -> String {
+fn add_preserved_fd(preserved_fds: &mut Vec<RawFd>, file: &dyn AsRawFd) -> String {
     let fd = file.as_raw_fd();
     preserved_fds.push(fd);
     format!("/proc/self/fd/{}", fd)
diff --git a/virtualizationservice/src/payload.rs b/virtualizationservice/src/payload.rs
index 3efd7ac..82aa760 100644
--- a/virtualizationservice/src/payload.rs
+++ b/virtualizationservice/src/payload.rs
@@ -165,7 +165,6 @@
     let payload_metadata = match &app_config.payload {
         Payload::PayloadConfig(payload_config) => PayloadMetadata::config(PayloadConfig {
             payload_binary_path: payload_config.payloadPath.clone(),
-            export_tombstones: payload_config.exportTombstones,
             args: payload_config.args.clone().into(),
             ..Default::default()
         }),