Merge "microdroid_manager: print more info when payload fails via signal"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index a6b1f95..43c89d4 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -4,10 +4,15 @@
 bpfmt = true
 clang_format = true
 jsonlint = true
+google_java_format = true
 pylint3 = true
 rustfmt = true
 xmllint = true
 
+[Tool Paths]
+google-java-format = ${REPO_ROOT}/prebuilts/tools/common/google-java-format/google-java-format
+google-java-format-diff = ${REPO_ROOT}/prebuilts/tools/common/google-java-format/google-java-format-diff.py
+
 [Builtin Hooks Options]
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
 rustfmt = --config-path=rustfmt.toml
diff --git a/authfs/tests/benchmarks/src/java/com/android/fs/benchmarks/AuthFsBenchmarks.java b/authfs/tests/benchmarks/src/java/com/android/fs/benchmarks/AuthFsBenchmarks.java
index 428c816..8cee496 100644
--- a/authfs/tests/benchmarks/src/java/com/android/fs/benchmarks/AuthFsBenchmarks.java
+++ b/authfs/tests/benchmarks/src/java/com/android/fs/benchmarks/AuthFsBenchmarks.java
@@ -20,6 +20,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
 import android.cts.host.utils.DeviceJUnit4ClassRunnerWithParameters;
@@ -28,11 +29,11 @@
 
 import com.android.fs.common.AuthFsTestRule;
 import com.android.microdroid.test.common.MetricsProcessor;
+import com.android.microdroid.test.host.MicrodroidHostTestCaseBase;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.metrics.proto.MetricMeasurement.DataType;
 import com.android.tradefed.metrics.proto.MetricMeasurement.Measurements;
 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.After;
 import org.junit.AfterClass;
@@ -52,7 +53,7 @@
 @RootPermissionTest
 @RunWith(DeviceJUnit4Parameterized.class)
 @UseParametersRunnerFactory(DeviceJUnit4ClassRunnerWithParameters.RunnerFactory.class)
-public class AuthFsBenchmarks extends BaseHostJUnit4Test {
+public class AuthFsBenchmarks extends MicrodroidHostTestCaseBase {
     private static final int TRIAL_COUNT = 5;
 
     /** Name of the measure_io binary on host. */
@@ -82,6 +83,7 @@
         AuthFsTestRule.setUpAndroid(getTestInformation());
         mAuthFsTestRule.setUpTest();
         assumeTrue(AuthFsTestRule.getDevice().supportsMicrodroid(mProtectedVm));
+        assumeFalse("Skip on CF; protected VM not supported", isCuttlefish());
         String metricsPrefix =
                 MetricsProcessor.getMetricPrefix(
                         getDevice().getProperty("debug.hypervisor.metrics_tag"));
diff --git a/compos/compos_key_helper/Android.bp b/compos/compos_key_helper/Android.bp
index c9480fc..cffa1e3 100644
--- a/compos/compos_key_helper/Android.bp
+++ b/compos/compos_key_helper/Android.bp
@@ -24,6 +24,7 @@
     defaults: ["compos_key_defaults"],
     srcs: ["compos_key_main.cpp"],
 
+    header_libs: ["vm_payload_restricted_headers"],
     static_libs: [
         "libcompos_key",
     ],
diff --git a/compos/compos_key_helper/compos_key_main.cpp b/compos/compos_key_helper/compos_key_main.cpp
index 4fb0762..9417584 100644
--- a/compos/compos_key_helper/compos_key_main.cpp
+++ b/compos/compos_key_helper/compos_key_main.cpp
@@ -17,7 +17,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <unistd.h>
-#include <vm_payload.h>
+#include <vm_payload_restricted.h>
 
 #include <string_view>
 #include <vector>
diff --git a/demo/java/com/android/microdroid/demo/MainActivity.java b/demo/java/com/android/microdroid/demo/MainActivity.java
index df6f44e..b5ae3d5 100644
--- a/demo/java/com/android/microdroid/demo/MainActivity.java
+++ b/demo/java/com/android/microdroid/demo/MainActivity.java
@@ -278,8 +278,8 @@
                 mVirtualMachine.setCallback(Executors.newSingleThreadExecutor(), callback);
                 mStatus.postValue(mVirtualMachine.getStatus());
 
-                InputStream console = mVirtualMachine.getConsoleOutputStream();
-                InputStream log = mVirtualMachine.getLogOutputStream();
+                InputStream console = mVirtualMachine.getConsoleOutput();
+                InputStream log = mVirtualMachine.getLogOutput();
                 mExecutorService.execute(new Reader("console", mConsoleOutput, console));
                 mExecutorService.execute(new Reader("log", mLogOutput, log));
             } catch (VirtualMachineException e) {
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index e750ae9..7c826b6 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -282,14 +282,7 @@
     @GuardedBy("sCreateLock")
     @NonNull
     private static Map<String, WeakReference<VirtualMachine>> getInstancesMap(Context context) {
-        Map<String, WeakReference<VirtualMachine>> instancesMap;
-        if (sInstances.containsKey(context)) {
-            instancesMap = sInstances.get(context);
-        } else {
-            instancesMap = new HashMap<>();
-            sInstances.put(context, instancesMap);
-        }
-        return instancesMap;
+        return sInstances.computeIfAbsent(context, unused -> new HashMap<>());
     }
 
     @NonNull
@@ -729,7 +722,7 @@
      * @hide
      */
     @NonNull
-    public InputStream getConsoleOutputStream() throws VirtualMachineException {
+    public InputStream getConsoleOutput() throws VirtualMachineException {
         synchronized (mLock) {
             createVmPipes();
             return new FileInputStream(mConsoleReader.getFileDescriptor());
@@ -743,7 +736,7 @@
      * @hide
      */
     @NonNull
-    public InputStream getLogOutputStream() throws VirtualMachineException {
+    public InputStream getLogOutput() throws VirtualMachineException {
         synchronized (mLock) {
             createVmPipes();
             return new FileInputStream(mLogReader.getFileDescriptor());
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index 90b09c8..b814367 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -381,7 +381,7 @@
          * @hide
          */
         public Builder(@NonNull Context context) {
-            mContext = requireNonNull(context);
+            mContext = requireNonNull(context, "context must not be null");
             mDebugLevel = DEBUG_LEVEL_NONE;
             mNumCpus = 1;
         }
diff --git a/microdroid/kernel/README.md b/microdroid/kernel/README.md
index 1f6b2ee..44f828c 100644
--- a/microdroid/kernel/README.md
+++ b/microdroid/kernel/README.md
@@ -24,6 +24,7 @@
 
 For x86\_64,
 ```bash
+tools/bazel clean
 tools/bazel run --config=fast --lto=thin //common-modules/virtual-device:microdroid_x86_64_dist -- --dist_dir=out/dist
 ```
 
@@ -36,7 +37,15 @@
 
 ### Change the kernel configs
 
+For ARM64
+```bash
+tools/bazel run //common-modules/virtual-device:microdroid_aarch64_config -- menuconfig
+```
 
+For x86\_64
+```bash
+tools/bazel run //common-modules/virtual-device:microdroid_x86_64_config -- menuconfig
+```
 
 ## How to update Microdroid kernel prebuilts
 
diff --git a/microdroid/vm_payload/Android.bp b/microdroid/vm_payload/Android.bp
index 8d78444..e153f92 100644
--- a/microdroid/vm_payload/Android.bp
+++ b/microdroid/vm_payload/Android.bp
@@ -29,7 +29,7 @@
 
 rust_bindgen {
     name: "libvm_payload_bindgen",
-    wrapper_src: "include/vm_payload.h",
+    wrapper_src: "include-restricted/vm_payload_restricted.h",
     crate_name: "vm_payload_bindgen",
     source_stem: "bindings",
     apex_available: ["com.android.compos"],
@@ -41,5 +41,15 @@
 
 cc_library_headers {
     name: "vm_payload_headers",
+    apex_available: ["com.android.compos"],
     export_include_dirs: ["include"],
 }
+
+cc_library_headers {
+    name: "vm_payload_restricted_headers",
+    header_libs: ["vm_payload_headers"],
+    export_header_lib_headers: ["vm_payload_headers"],
+    export_include_dirs: ["include-restricted"],
+    apex_available: ["com.android.compos"],
+    visibility: ["//packages/modules/Virtualization:__subpackages__"],
+}
diff --git a/microdroid/vm_payload/include-restricted/vm_payload_restricted.h b/microdroid/vm_payload/include-restricted/vm_payload_restricted.h
new file mode 100644
index 0000000..8170a64
--- /dev/null
+++ b/microdroid/vm_payload/include-restricted/vm_payload_restricted.h
@@ -0,0 +1,56 @@
+/*
+ * 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
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/cdefs.h>
+
+#include "vm_payload.h"
+
+// The functions declared here are restricted to VMs created with a config file;
+// they will fail if called in other VMs. The ability to create such VMs
+// requires the android.permission.USE_CUSTOM_VIRTUAL_MACHINE permission, and is
+// therefore not available to privileged or third party apps.
+
+// These functions can be used by tests, if the permission is granted via shell.
+
+__BEGIN_DECLS
+
+/**
+ * Get the VM's DICE attestation chain.
+ *
+ * \param data pointer to size bytes where the chain is written.
+ * \param size number of bytes that can be written to data.
+ * \param total outputs the total size of the chain if the function succeeds
+ *
+ * \return true on success and false on failure.
+ */
+bool AVmPayload_getDiceAttestationChain(void *data, size_t size, size_t *total);
+
+/**
+ * Get the VM's DICE attestation CDI.
+ *
+ * \param data pointer to size bytes where the CDI is written.
+ * \param size number of bytes that can be written to data.
+ * \param total outputs the total size of the CDI if the function succeeds
+ *
+ * \return true on success and false on failure.
+ */
+bool AVmPayload_getDiceAttestationCdi(void *data, size_t size, size_t *total);
+
+__END_DECLS
diff --git a/microdroid/vm_payload/include/vm_payload.h b/microdroid/vm_payload/include/vm_payload.h
index 2aeb44e..82dbd6d 100644
--- a/microdroid/vm_payload/include/vm_payload.h
+++ b/microdroid/vm_payload/include/vm_payload.h
@@ -18,12 +18,11 @@
 
 #include <stdbool.h>
 #include <stddef.h>
+#include <sys/cdefs.h>
 
 #include "vm_main.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+__BEGIN_DECLS
 
 struct AIBinder;
 typedef struct AIBinder AIBinder;
@@ -71,32 +70,6 @@
                                     size_t size);
 
 /**
- * Get the VM's DICE attestation chain.
- *
- * This function will fail if the use of restricted APIs is not permitted.
- *
- * \param data pointer to size bytes where the chain is written.
- * \param size number of bytes that can be written to data.
- * \param total outputs the total size of the chain if the function succeeds
- *
- * \return true on success and false on failure.
- */
-bool AVmPayload_getDiceAttestationChain(void *data, size_t size, size_t *total);
-
-/**
- * Get the VM's DICE attestation CDI.
- *
- * This function will fail if the use of restricted APIs is not permitted.
- *
- * \param data pointer to size bytes where the CDI is written.
- * \param size number of bytes that can be written to data.
- * \param total outputs the total size of the CDI if the function succeeds
- *
- * \return true on success and false on failure.
- */
-bool AVmPayload_getDiceAttestationCdi(void *data, size_t size, size_t *total);
-
-/**
  * Gets the path to the APK contents. It is a directory, under which are
  * the unzipped contents of the APK containing the payload, all read-only
  * but accessible to the payload.
@@ -107,6 +80,4 @@
  */
 const char *AVmPayload_getApkContentsPath(void);
 
-#ifdef __cplusplus
-} // extern "C"
-#endif
+__END_DECLS
diff --git a/microdroid/vm_payload/src/vm_payload_service.rs b/microdroid/vm_payload/src/vm_payload_service.rs
index b0dd891..098d246 100644
--- a/microdroid/vm_payload/src/vm_payload_service.rs
+++ b/microdroid/vm_payload/src/vm_payload_service.rs
@@ -15,12 +15,12 @@
 //! This module handles the interaction with virtual machine payload service.
 
 use android_system_virtualization_payload::aidl::android::system::virtualization::payload::IVmPayloadService::{
-    IVmPayloadService, VM_PAYLOAD_SERVICE_NAME, VM_APK_CONTENTS_PATH};
+    IVmPayloadService, VM_PAYLOAD_SERVICE_SOCKET_NAME, VM_APK_CONTENTS_PATH};
 use anyhow::{Context, Result};
-use binder::{wait_for_interface, Strong, unstable_api::{AIBinder, new_spibinder}};
+use binder::{Strong, unstable_api::{AIBinder, new_spibinder}};
 use lazy_static::lazy_static;
 use log::{error, info, Level};
-use rpcbinder::run_vsock_rpc_server;
+use rpcbinder::{get_unix_domain_rpc_interface, run_vsock_rpc_server};
 use std::ffi::CString;
 use std::os::raw::{c_char, c_void};
 
@@ -203,6 +203,6 @@
 }
 
 fn get_vm_payload_service() -> Result<Strong<dyn IVmPayloadService>> {
-    wait_for_interface(VM_PAYLOAD_SERVICE_NAME)
-        .context(format!("Failed to connect to service: {}", VM_PAYLOAD_SERVICE_NAME))
+    get_unix_domain_rpc_interface(VM_PAYLOAD_SERVICE_SOCKET_NAME)
+        .context(format!("Failed to connect to service: {}", VM_PAYLOAD_SERVICE_SOCKET_NAME))
 }
diff --git a/microdroid_manager/aidl/android/system/virtualization/payload/IVmPayloadService.aidl b/microdroid_manager/aidl/android/system/virtualization/payload/IVmPayloadService.aidl
index 4823bb8..f8e7d34 100644
--- a/microdroid_manager/aidl/android/system/virtualization/payload/IVmPayloadService.aidl
+++ b/microdroid_manager/aidl/android/system/virtualization/payload/IVmPayloadService.aidl
@@ -21,8 +21,8 @@
  * Microdroid Manager for execution.
  */
 interface IVmPayloadService {
-    /** Name of the service IVmPayloadService. */
-    const String VM_PAYLOAD_SERVICE_NAME = "virtual_machine_payload_service";
+    /** Socket name of the service IVmPayloadService. */
+    const String VM_PAYLOAD_SERVICE_SOCKET_NAME = "vm_payload_service";
 
     /** Path to the APK contents path. */
     const String VM_APK_CONTENTS_PATH = "/mnt/apk";
diff --git a/microdroid_manager/microdroid_manager.rc b/microdroid_manager/microdroid_manager.rc
index 74a219d..c41ee38 100644
--- a/microdroid_manager/microdroid_manager.rc
+++ b/microdroid_manager/microdroid_manager.rc
@@ -1,8 +1,12 @@
 service microdroid_manager /system/bin/microdroid_manager
     disabled
+    # print android log to kmsg
     file /dev/kmsg w
+    # redirect stdout/stderr to kmsg_debug
+    stdio_to_kmsg
     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
+    socket vm_payload_service stream 0666 system system
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 8572a0d..c18dd26 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -31,7 +31,7 @@
 use android_system_virtualization_payload::aidl::android::system::virtualization::payload::IVmPayloadService::VM_APK_CONTENTS_PATH;
 use anyhow::{anyhow, bail, ensure, Context, Error, Result};
 use apkverify::{get_public_key_der, verify, V4Signature};
-use binder::{ProcessState, Strong};
+use binder::Strong;
 use diced_utils::cbor::{encode_header, encode_number};
 use glob::glob;
 use itertools::sorted;
@@ -48,6 +48,7 @@
 use rustutils::system_properties::PropertyWatcher;
 use std::borrow::Cow::{Borrowed, Owned};
 use std::convert::TryInto;
+use std::env;
 use std::fs::{self, create_dir, File, OpenOptions};
 use std::io::Write;
 use std::os::unix::io::{FromRawFd, IntoRawFd};
@@ -154,6 +155,11 @@
 }
 
 fn main() -> Result<()> {
+    // If debuggable, print full backtrace to console log with stdio_to_kmsg
+    if system_properties::read_bool(APP_DEBUGGABLE_PROP, true)? {
+        env::set_var("RUST_BACKTRACE", "full");
+    }
+
     scopeguard::defer! {
         info!("Shutting down...");
         if let Err(e) = system_properties::write("sys.powerctl", "shutdown") {
@@ -399,7 +405,6 @@
     wait_for_property_true(APK_MOUNT_DONE_PROP).context("Failed waiting for APK mount done")?;
 
     register_vm_payload_service(allow_restricted_apis, service.clone(), dice)?;
-    ProcessState::start_thread_pool();
 
     system_properties::write("dev.bootcomplete", "1").context("set dev.bootcomplete")?;
     exec_task(task, service).context("Failed to run payload")
diff --git a/microdroid_manager/src/swap.rs b/microdroid_manager/src/swap.rs
index 0fd1468..d7916db 100644
--- a/microdroid_manager/src/swap.rs
+++ b/microdroid_manager/src/swap.rs
@@ -43,20 +43,21 @@
     let sysfs_size = format!("/sys/{}/size", dev);
     let len = read_to_string(&sysfs_size)?
         .trim()
-        .parse::<u32>()
-        .context(format!("No u32 in {}", &sysfs_size))?
-        * 512;
+        .parse::<u64>()
+        .context(format!("No u64 in {}", &sysfs_size))?
+        .checked_mul(512)
+        .ok_or_else(|| anyhow!("sysfs_size too large"))?;
 
-    let pagesize: libc::c_uint;
     // safe because we give a constant and known-valid sysconf parameter
-    unsafe {
-        pagesize = libc::sysconf(libc::_SC_PAGE_SIZE) as libc::c_uint;
-    }
+    let pagesize = unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) as u64 };
 
     let mut f = OpenOptions::new().read(false).write(true).open(format!("/dev/{}", dev))?;
 
+    let last_page = len / pagesize - 1;
+
     // Write the info fields: [ version, last_page ]
-    let info: [u32; 2] = [1, (len / pagesize) - 1];
+    let info: [u32; 2] = [1, last_page.try_into().context("Number of pages out of range")?];
+
     f.seek(SeekFrom::Start(1024))?;
     f.write_all(&info.iter().flat_map(|v| v.to_ne_bytes()).collect::<Vec<u8>>())?;
 
diff --git a/microdroid_manager/src/vm_payload_service.rs b/microdroid_manager/src/vm_payload_service.rs
index 159bf67..fcfc79d 100644
--- a/microdroid_manager/src/vm_payload_service.rs
+++ b/microdroid_manager/src/vm_payload_service.rs
@@ -16,13 +16,17 @@
 
 use crate::dice::DiceContext;
 use android_system_virtualization_payload::aidl::android::system::virtualization::payload::IVmPayloadService::{
-    BnVmPayloadService, IVmPayloadService, VM_PAYLOAD_SERVICE_NAME};
+    BnVmPayloadService, IVmPayloadService, VM_PAYLOAD_SERVICE_SOCKET_NAME};
 use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
-use anyhow::{Context, Result};
-use binder::{Interface, BinderFeatures, ExceptionCode, Status, Strong, add_service};
-use log::error;
+use anyhow::{bail, Result};
+use binder::{Interface, BinderFeatures, ExceptionCode, Status, Strong};
+use log::{error, info};
 use openssl::hkdf::hkdf;
 use openssl::md::Md;
+use rpcbinder::run_init_unix_domain_rpc_server;
+use std::sync::mpsc;
+use std::thread;
+use std::time::Duration;
 
 /// Implementation of `IVmPayloadService`.
 struct VmPayloadService {
@@ -97,8 +101,29 @@
         VmPayloadService::new(allow_restricted_apis, vm_service, dice),
         BinderFeatures::default(),
     );
-    add_service(VM_PAYLOAD_SERVICE_NAME, vm_payload_binder.as_binder())
-        .with_context(|| format!("Failed to register service {}", VM_PAYLOAD_SERVICE_NAME))?;
-    log::info!("{} is running", VM_PAYLOAD_SERVICE_NAME);
-    Ok(())
+    let (sender, receiver) = mpsc::channel();
+    thread::spawn(move || {
+        let retval = run_init_unix_domain_rpc_server(
+            vm_payload_binder.as_binder(),
+            VM_PAYLOAD_SERVICE_SOCKET_NAME,
+            || {
+                sender.send(()).unwrap();
+            },
+        );
+        if retval {
+            info!(
+                "The RPC server at '{}' has shut down gracefully.",
+                VM_PAYLOAD_SERVICE_SOCKET_NAME
+            );
+        } else {
+            error!("Premature termination of the RPC server '{}'.", VM_PAYLOAD_SERVICE_SOCKET_NAME);
+        }
+    });
+    match receiver.recv_timeout(Duration::from_millis(200)) {
+        Ok(()) => {
+            info!("The RPC server '{}' is running.", VM_PAYLOAD_SERVICE_SOCKET_NAME);
+            Ok(())
+        }
+        _ => bail!("Failed to register service '{}'", VM_PAYLOAD_SERVICE_SOCKET_NAME),
+    }
 }
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index b644905..71bac72 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -9,6 +9,7 @@
     srcs: ["src/main.rs"],
     edition: "2021",
     rustlibs: [
+        "libbuddy_system_allocator",
         "liblog_rust_nostd",
         "libpvmfw_embedded_key",
         "libvmbase",
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index 1ea95bc..dc2087d 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -14,6 +14,7 @@
 
 //! Low-level entry and exit points of pvmfw.
 
+use crate::heap;
 use crate::helpers;
 use crate::mmio_guard;
 use core::arch::asm;
@@ -52,6 +53,10 @@
     // - only access MMIO once (and while) it has been mapped and configured
     // - only perform logging once the logger has been initialized
     // - only access non-pvmfw memory once (and while) it has been mapped
+
+    // SAFETY - This function should and will only be called once, here.
+    unsafe { heap::init() };
+
     logger::init(LevelFilter::Info).map_err(|_| RebootReason::InternalError)?;
 
     const FDT_MAX_SIZE: usize = helpers::SIZE_2MB;
diff --git a/pvmfw/src/heap.rs b/pvmfw/src/heap.rs
new file mode 100644
index 0000000..bfa8320
--- /dev/null
+++ b/pvmfw/src/heap.rs
@@ -0,0 +1,26 @@
+// 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.
+
+//! Heap implementation.
+
+use buddy_system_allocator::LockedHeap;
+
+#[global_allocator]
+static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::new();
+
+static mut HEAP: [u8; 65536] = [0; 65536];
+
+pub unsafe fn init() {
+    HEAP_ALLOCATOR.lock().init(HEAP.as_mut_ptr() as usize, HEAP.len());
+}
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 870172d..c0bb263 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -16,10 +16,12 @@
 
 #![no_main]
 #![no_std]
+#![feature(default_alloc_error_handler)]
 
 mod avb;
 mod entry;
 mod exceptions;
+mod heap;
 mod helpers;
 mod mmio_guard;
 mod smccc;
diff --git a/tests/benchmark/src/native/benchmarkbinary.cpp b/tests/benchmark/src/native/benchmarkbinary.cpp
index 18293f3..6321c25 100644
--- a/tests/benchmark/src/native/benchmarkbinary.cpp
+++ b/tests/benchmark/src/native/benchmarkbinary.cpp
@@ -94,11 +94,12 @@
     Result<double> measure_read_rate(const std::string& filename, int64_t fileSizeBytes,
                                      bool is_rand) {
         const int64_t block_count = fileSizeBytes / kBlockSizeBytes;
-        std::vector<uint64_t> offsets;
+        std::vector<uint64_t> offsets(block_count);
+        for (auto i = 0; i < block_count; ++i) {
+            offsets.push_back(i * kBlockSizeBytes);
+        }
         if (is_rand) {
             std::mt19937 rd{std::random_device{}()};
-            offsets.reserve(block_count);
-            for (auto i = 0; i < block_count; ++i) offsets.push_back(i * kBlockSizeBytes);
             std::shuffle(offsets.begin(), offsets.end(), rd);
         }
         char buf[kBlockSizeBytes];
@@ -109,12 +110,7 @@
             return ErrnoError() << "Read: opening " << filename << " failed";
         }
         for (auto i = 0; i < block_count; ++i) {
-            if (is_rand) {
-                if (lseek(fd.get(), offsets[i], SEEK_SET) == -1) {
-                    return ErrnoError() << "failed to lseek";
-                }
-            }
-            auto bytes = read(fd.get(), buf, kBlockSizeBytes);
+            auto bytes = pread(fd, buf, kBlockSizeBytes, offsets[i]);
             if (bytes == 0) {
                 return Error() << "unexpected end of file";
             } else if (bytes == -1) {
diff --git a/tests/benchmark_hostside/Android.bp b/tests/benchmark_hostside/Android.bp
index c5c7571..e053406 100644
--- a/tests/benchmark_hostside/Android.bp
+++ b/tests/benchmark_hostside/Android.bp
@@ -17,4 +17,7 @@
     test_suites: [
         "general-tests",
     ],
+    data: [
+        ":MicrodroidTestApp",
+    ],
 }
diff --git a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
index 5e57b32..1996f4b 100644
--- a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
+++ b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
@@ -16,6 +16,7 @@
 
 package android.avf.test;
 
+import static com.android.tradefed.device.TestDevice.MicrodroidBuilder;
 import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
 
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -31,6 +32,8 @@
 import com.android.microdroid.test.host.CommandRunner;
 import com.android.microdroid.test.host.MicrodroidHostTestCaseBase;
 import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.device.TestDevice;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.util.CommandResult;
@@ -45,7 +48,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -85,6 +87,9 @@
     @Before
     public void setUp() throws Exception {
         testIfDeviceIsCapable(getDevice());
+
+        getDevice().installPackage(findTestFile(APK_NAME), /* reinstall */ false);
+
         mMetricsProcessor = new MetricsProcessor(getMetricPrefix() + "hostside/");
     }
 
@@ -196,10 +201,6 @@
         return null;
     }
 
-    private void microdroidWaitForBootComplete() {
-        runOnMicrodroidForResult("watch -e \"getprop dev.bootcomplete | grep '^0$'\"");
-    }
-
     private AmStartupTimeCmdParser getColdRunStartupTimes(CommandRunner android, String pkgName)
             throws DeviceNotAvailableException, InterruptedException {
         unlockScreen(android);
@@ -216,9 +217,7 @@
     // and the time measured after running the VM.
     private void getAppStartupTime(String pkgName, StartupTimeMetricCollection metricColector)
             throws Exception {
-        final String configPath = "assets/vm_config.json";
-        final String cid;
-        final int vm_mem_mb;
+        TestDevice device = (TestDevice) getDevice();
 
         // 1. Reboot the device to run the test without stage2 fragmentation
         getDevice().rebootUntilOnline();
@@ -240,30 +239,30 @@
         android.tryRun("rm", "-rf", MicrodroidHostTestCaseBase.TEST_ROOT);
 
         // Donate 80% of the available device memory to the VM
-        vm_mem_mb = getFreeMemoryInfoMb(android) * 80 / 100;
-        cid = startMicrodroid(
-                            getDevice(),
-                            getBuild(),
-                            APK_NAME,
-                            PACKAGE_NAME,
-                            configPath,
-                            true,
-                            vm_mem_mb,
-                            Optional.of(NUM_VCPUS));
-        adbConnectToMicrodroid(getDevice(), cid);
-        microdroidWaitForBootComplete();
+        final String configPath = "assets/vm_config.json";
+        final int vm_mem_mb = getFreeMemoryInfoMb(android) * 80 / 100;
+        ITestDevice microdroidDevice =
+                MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
+                        .debugLevel("full")
+                        .memoryMib(vm_mem_mb)
+                        .numCpus(NUM_VCPUS)
+                        .build(device);
+        microdroidDevice.waitForBootComplete(30000);
+        microdroidDevice.enableAdbRoot();
 
-        rootMicrodroid();
+        CommandRunner microdroid = new CommandRunner(microdroidDevice);
 
-        runOnMicrodroid("mkdir -p /mnt/ramdisk && chmod 777 /mnt/ramdisk");
-        runOnMicrodroid("mount -t tmpfs -o size=32G tmpfs /mnt/ramdisk");
+        microdroid.run("mkdir -p /mnt/ramdisk && chmod 777 /mnt/ramdisk");
+        microdroid.run("mount -t tmpfs -o size=32G tmpfs /mnt/ramdisk");
 
         // Allocate memory for the VM until it fails and make sure that we touch
         // the allocated memory in the guest to be able to create stage2 fragmentation.
         try {
-            runOnMicrodroidForResult(String.format("cd /mnt/ramdisk && truncate -s %dM sprayMemory"
-                    + " && dd if=/dev/zero of=sprayMemory bs=1MB count=%d",
-                    vm_mem_mb , vm_mem_mb));
+            microdroid.tryRun(
+                    String.format(
+                            "cd /mnt/ramdisk && truncate -s %dM sprayMemory"
+                                    + " && dd if=/dev/zero of=sprayMemory bs=1MB count=%d",
+                            vm_mem_mb, vm_mem_mb));
         } catch (Exception ex) {
         }
 
@@ -273,7 +272,7 @@
             metricColector.addStartupTimeMetricDuringVmRun(duringVmStartApp);
         }
 
-        shutdownMicrodroid(getDevice(), cid);
+        device.shutdownMicrodroid(microdroidDevice);
 
         // Run the app after the VM run and collect cold startup time.
         for (int i = 0; i < ROUND_COUNT; i++) {
diff --git a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
index ede838b..9fb7d91 100644
--- a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
+++ b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
@@ -202,8 +202,8 @@
                 throws VirtualMachineException, InterruptedException {
             vm.setCallback(mExecutorService, this);
             vm.run();
-            logVmOutputAndMonitorBootEvents(logTag, vm.getConsoleOutputStream(), "Console");
-            logVmOutput(logTag, vm.getLogOutputStream(), "Log");
+            logVmOutputAndMonitorBootEvents(logTag, vm.getConsoleOutput(), "Console");
+            logVmOutput(logTag, vm.getLogOutput(), "Log");
             mExecutorService.awaitTermination(300, TimeUnit.SECONDS);
         }
 
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
index 1a1dbeb..43fe615 100644
--- a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
+++ b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
@@ -38,13 +38,7 @@
 
 import java.io.File;
 import java.io.FileNotFoundException;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Optional;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 public abstract class MicrodroidHostTestCaseBase extends BaseHostJUnit4Test {
     protected static final String TEST_ROOT = "/data/local/tmp/virt/";
@@ -160,53 +154,12 @@
         return result.getStdout().trim();
     }
 
-    // Same as runOnMicrodroid, but keeps retrying on error for maximum attempts times
-    // Each attempt with timeoutMs
-    public static String runOnMicrodroidRetryingOnFailure(
-            long timeoutMs, int attempts, String... cmd) {
-        CommandResult result = RunUtil.getDefault()
-                .runTimedCmdRetry(timeoutMs, MICRODROID_COMMAND_RETRY_INTERVAL_MILLIS, attempts,
-                        "adb", "-s", MICRODROID_SERIAL, "shell", join(cmd));
-        assertWithMessage("Command `" + Arrays.toString(cmd) + "` has failed")
-                .about(command_results())
-                .that(result)
-                .isSuccess();
-        return result.getStdout().trim();
-    }
-
     public static CommandResult runOnMicrodroidForResult(String... cmd) {
         final long timeoutMs = 30000; // 30 sec. Microdroid is extremely slow on GCE-on-CF.
         return RunUtil.getDefault()
                 .runTimedCmd(timeoutMs, "adb", "-s", MICRODROID_SERIAL, "shell", join(cmd));
     }
 
-    public static void pullMicrodroidFile(String path, File target) {
-        final long timeoutMs = 30000; // 30 sec. Microdroid is extremely slow on GCE-on-CF.
-        CommandResult result =
-                RunUtil.getDefault()
-                        .runTimedCmd(
-                                timeoutMs,
-                                "adb",
-                                "-s",
-                                MICRODROID_SERIAL,
-                                "pull",
-                                path,
-                                target.getPath());
-        assertWithMessage("pulling " + path + " from microdroid")
-                .about(command_results())
-                .that(result)
-                .isSuccess();
-    }
-
-    // Asserts the command will fail on Microdroid.
-    public static void assertFailedOnMicrodroid(String... cmd) {
-        CommandResult result = runOnMicrodroidForResult(cmd);
-        assertWithMessage("Microdroid command `" + join(cmd) + "` did not fail expectedly")
-                .about(command_results())
-                .that(result)
-                .isFailed();
-    }
-
     private static String join(String... strs) {
         return String.join(" ", Arrays.asList(strs));
     }
@@ -240,36 +193,6 @@
         return pathLine.substring("package:".length());
     }
 
-    public static String startMicrodroid(
-            ITestDevice androidDevice,
-            IBuildInfo buildInfo,
-            String apkName,
-            String packageName,
-            String configPath,
-            boolean debug,
-            int memoryMib,
-            Optional<Integer> numCpus)
-            throws DeviceNotAvailableException {
-        return startMicrodroid(androidDevice, buildInfo, apkName, packageName, null, configPath,
-                debug, memoryMib, numCpus);
-    }
-
-    public static String startMicrodroid(
-            ITestDevice androidDevice,
-            IBuildInfo buildInfo,
-            String apkName,
-            String packageName,
-            String[] extraIdsigPaths,
-            String configPath,
-            boolean debug,
-            int memoryMib,
-            Optional<Integer> numCpus)
-            throws DeviceNotAvailableException {
-        return startMicrodroid(androidDevice, buildInfo, apkName, null, packageName,
-                extraIdsigPaths, configPath, debug,
-                memoryMib, numCpus);
-    }
-
     private static void forwardFileToLog(CommandRunner android, String path, String tag)
             throws DeviceNotAvailableException {
         android.runWithTimeout(
@@ -281,93 +204,6 @@
                         + " | sed \\'s/^/" + tag + ": /g\\''\""); // add tags in front of lines
     }
 
-    public static String startMicrodroid(
-            ITestDevice androidDevice,
-            IBuildInfo buildInfo,
-            String apkName,
-            String apkPath,
-            String packageName,
-            String[] extraIdsigPaths,
-            String configPath,
-            boolean debug,
-            int memoryMib,
-            Optional<Integer> numCpus)
-            throws DeviceNotAvailableException {
-        CommandRunner android = new CommandRunner(androidDevice);
-
-        // Install APK if necessary
-        if (apkName != null) {
-            File apkFile = findTestFile(buildInfo, apkName);
-            androidDevice.installPackage(apkFile, /* reinstall */ true);
-        }
-
-        if (apkPath == null) {
-            apkPath = getPathForPackage(androidDevice, packageName);
-        }
-
-        android.run("mkdir", "-p", TEST_ROOT);
-
-        // This file is not what we provide. It will be created by the vm tool.
-        final String outApkIdsigPath = TEST_ROOT + apkName + ".idsig";
-
-        final String instanceImg = TEST_ROOT + INSTANCE_IMG;
-        final String logPath = LOG_PATH;
-        final String consolePath = CONSOLE_PATH;
-        final String debugFlag = debug ? "--debug full" : "";
-
-        // Run the VM
-        ArrayList<String> args = new ArrayList<>(Arrays.asList(
-                VIRT_APEX + "bin/vm",
-                "run-app",
-                "--daemonize",
-                "--log " + logPath,
-                "--console " + consolePath,
-                "--mem " + memoryMib,
-                numCpus.isPresent() ? "--cpus " + numCpus.get() : "",
-                debugFlag,
-                apkPath,
-                outApkIdsigPath,
-                instanceImg,
-                configPath));
-        if (extraIdsigPaths != null) {
-            for (String path : extraIdsigPaths) {
-                args.add("--extra-idsig");
-                args.add(path);
-            }
-        }
-        String ret = android.run(args.toArray(new String[0]));
-
-        // Redirect log.txt and console.txt to logd using logwrapper
-        // Keep redirecting as long as the expecting maximum test time. When an adb
-        // command times out, it may trigger the device recovery process, which
-        // disconnect adb, which terminates any live adb commands. See an example at
-        // b/194974010#comment25.
-        ExecutorService executor = Executors.newFixedThreadPool(2);
-        executor.execute(
-                () -> {
-                    try {
-                        forwardFileToLog(android, logPath, "MicrodroidLog");
-                    } catch (Exception e) {
-                        // Consume
-                    }
-                });
-
-        executor.execute(
-                () -> {
-                    try {
-                        forwardFileToLog(android, consolePath, "MicrodroidConsole");
-                    } catch (Exception e) {
-                        // Consume
-                    }
-                });
-
-        // Retrieve the CID from the vm tool output
-        Pattern pattern = Pattern.compile("with CID (\\d+)");
-        Matcher matcher = pattern.matcher(ret);
-        assertWithMessage("Failed to find CID").that(matcher.find()).isTrue();
-        return matcher.group(1);
-    }
-
     public static void shutdownMicrodroid(ITestDevice androidDevice, String cid)
             throws DeviceNotAvailableException {
         CommandRunner android = new CommandRunner(androidDevice);
@@ -376,17 +212,6 @@
         android.run(VIRT_APEX + "bin/vm", "stop", cid);
     }
 
-    public static void rootMicrodroid() throws InterruptedException {
-        runOnHostRetryingOnFailure(MICRODROID_COMMAND_TIMEOUT_MILLIS,
-                MICRODROID_ADB_CONNECT_MAX_ATTEMPTS, "adb", "-s", MICRODROID_SERIAL, "root");
-        // adbd reboots after root. Some commands (including wait-for-device) following this fails
-        // with error: closed. Hence, we disconnect and re-connect to the device before returning.
-        runOnHostRetryingOnFailure(MICRODROID_COMMAND_TIMEOUT_MILLIS,
-                MICRODROID_ADB_CONNECT_MAX_ATTEMPTS, "adb", "disconnect", MICRODROID_SERIAL);
-        runOnHostRetryingOnFailure(MICRODROID_COMMAND_TIMEOUT_MILLIS,
-                MICRODROID_ADB_CONNECT_MAX_ATTEMPTS, "adb", "connect", MICRODROID_SERIAL);
-    }
-
     // Establish an adb connection to microdroid by letting Android forward the connection to
     // microdroid. Wait until the connection is established and microdroid is booted.
     public static void adbConnectToMicrodroid(ITestDevice androidDevice, String cid) {
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index c9df624..a836559 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -17,12 +17,14 @@
 package com.android.microdroid.test;
 
 import static com.android.microdroid.test.host.CommandResultSubject.command_results;
+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 com.google.common.truth.Truth.assertWithMessage;
 
 import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assume.assumeTrue;
 
@@ -38,6 +40,8 @@
 import com.android.os.AtomsProto;
 import com.android.os.StatsLog;
 import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.device.TestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
 import com.android.tradefed.util.CommandResult;
@@ -64,8 +68,8 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.concurrent.Callable;
+import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -81,12 +85,16 @@
     // Number of vCPUs for testing purpose
     private static final int NUM_VCPUS = 3;
 
+    private static final int BOOT_COMPLETE_TIMEOUT = 30000; // 30 seconds
+
     @Rule public TestLogData mTestLogs = new TestLogData();
     @Rule public TestName mTestName = new TestName();
     @Rule public TestMetrics mMetrics = new TestMetrics();
 
     private String mMetricPrefix;
 
+    private ITestDevice mMicrodroidDevice;
+
     private int minMemorySize() throws DeviceNotAvailableException {
         CommandRunner android = new CommandRunner(getDevice());
         String abi = android.run("getprop", "ro.product.cpu.abi");
@@ -99,10 +107,6 @@
         throw new AssertionError("Unsupported ABI: " + abi);
     }
 
-    private void waitForBootComplete() {
-        runOnMicrodroidForResult("watch -e \"getprop dev.bootcomplete | grep '^0$'\"");
-    }
-
     private static JSONObject newPartition(String label, String path) {
         return new JSONObject(Map.of("label", label, "path", path));
     }
@@ -447,19 +451,23 @@
     private boolean isTombstoneGeneratedWithConfig(String configPath) throws Exception {
         // Note this test relies on logcat values being printed by tombstone_transmit on
         // and the reeceiver on host (virtualization_service)
-        final String cid =
-                startMicrodroid(
-                        getDevice(),
-                        getBuild(),
-                        APK_NAME,
-                        PACKAGE_NAME,
-                        configPath,
-                        /* debug */ true,
-                        minMemorySize(),
-                        Optional.of(NUM_VCPUS));
+        mMicrodroidDevice = MicrodroidBuilder
+                .fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
+                .debugLevel("full")
+                .memoryMib(minMemorySize())
+                .numCpus(NUM_VCPUS)
+                .build((TestDevice) getDevice());
+        mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
+        mMicrodroidDevice.enableAdbRoot();
+
+        CommandRunner microdroid = new CommandRunner(mMicrodroidDevice);
+        microdroid.run("kill", "-SIGSEGV", "$(pidof microdroid_launcher)");
+
         // check until microdroid is shut down
         CommandRunner android = new CommandRunner(getDevice());
-        android.runWithTimeout(15000, "logcat", "-m", "1", "-e", "'crosvm has exited normally'");
+        // TODO: improve crosvm exit check. b/258848245
+        android.runWithTimeout(15000, "logcat", "-m", "1", "-e",
+                              "'virtualizationservice::crosvm.*exited with status exit status: 0'");
         // Check that tombstone is received (from host logcat)
         String result =
                 runOnHost(
@@ -499,22 +507,28 @@
         ConfigUtils.uploadConfigForPushedAtoms(getDevice(), PACKAGE_NAME, atomIds);
 
         // Create VM with microdroid
+        TestDevice device = (TestDevice) getDevice();
         final String configPath = "assets/vm_config_apex.json"; // path inside the APK
-        final String cid =
-                startMicrodroid(
-                        getDevice(),
-                        getBuild(),
-                        APK_NAME,
-                        PACKAGE_NAME,
-                        configPath,
-                        /* debug */ true,
-                        minMemorySize(),
-                        Optional.of(NUM_VCPUS));
+        ITestDevice microdroid =
+                MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
+                        .debugLevel("full")
+                        .memoryMib(minMemorySize())
+                        .numCpus(NUM_VCPUS)
+                        .build(device);
+        microdroid.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
+        device.shutdownMicrodroid(microdroid);
 
-        // Check VmCreationRequested atom and clear the statsd report
-        List<StatsLog.EventMetricData> data;
-        data = ReportUtils.getEventMetricDataList(getDevice());
-        assertThat(data).hasSize(1);
+        List<StatsLog.EventMetricData> data = new ArrayList<>();
+        assertThatEventually(
+                10000,
+                () -> {
+                    data.addAll(ReportUtils.getEventMetricDataList(getDevice()));
+                    return data.size();
+                },
+                is(3)
+        );
+
+        // Check VmCreationRequested atom
         assertThat(data.get(0).getAtom().getPushedCase().getNumber()).isEqualTo(
                 AtomsProto.Atom.VM_CREATION_REQUESTED_FIELD_NUMBER);
         AtomsProto.VmCreationRequested atomVmCreationRequested =
@@ -532,29 +546,16 @@
         assertThat(atomVmCreationRequested.getApexes())
                 .isEqualTo("com.android.art:com.android.compos:com.android.sdkext");
 
-        // Boot VM with microdroid
-        adbConnectToMicrodroid(getDevice(), cid);
-        waitForBootComplete();
-
-        // Check VmBooted atom and clear the statsd report
-        data = ReportUtils.getEventMetricDataList(getDevice());
-        assertThat(data).hasSize(1);
-        assertThat(data.get(0).getAtom().getPushedCase().getNumber())
+        // Check VmBooted atom
+        assertThat(data.get(1).getAtom().getPushedCase().getNumber())
                 .isEqualTo(AtomsProto.Atom.VM_BOOTED_FIELD_NUMBER);
-        AtomsProto.VmBooted atomVmBooted = data.get(0).getAtom().getVmBooted();
+        AtomsProto.VmBooted atomVmBooted = data.get(1).getAtom().getVmBooted();
         assertThat(atomVmBooted.getVmIdentifier()).isEqualTo("VmRunApp");
 
-        // Shutdown VM with microdroid
-        shutdownMicrodroid(getDevice(), cid);
-        // TODO: make sure the VM is completely shut down while 'vm stop' command running.
-        Thread.sleep(1000);
-
-        // Check VmExited atom and clear the statsd report
-        data = ReportUtils.getEventMetricDataList(getDevice());
-        assertThat(data).hasSize(1);
-        assertThat(data.get(0).getAtom().getPushedCase().getNumber())
+        // Check VmExited atom
+        assertThat(data.get(2).getAtom().getPushedCase().getNumber())
                 .isEqualTo(AtomsProto.Atom.VM_EXITED_FIELD_NUMBER);
-        AtomsProto.VmExited atomVmExited = data.get(0).getAtom().getVmExited();
+        AtomsProto.VmExited atomVmExited = data.get(2).getAtom().getVmExited();
         assertThat(atomVmExited.getVmIdentifier()).isEqualTo("VmRunApp");
         assertThat(atomVmExited.getDeathReason()).isEqualTo(AtomsProto.VmExited.DeathReason.KILLED);
 
@@ -569,54 +570,49 @@
     @CddTest(requirements = {"9.17/C-1-1", "9.17/C-1-2", "9.17/C/1-3"})
     public void testMicrodroidBoots() throws Exception {
         final String configPath = "assets/vm_config.json"; // path inside the APK
-        final String cid =
-                startMicrodroid(
-                        getDevice(),
-                        getBuild(),
-                        APK_NAME,
-                        PACKAGE_NAME,
-                        configPath,
-                        /* debug */ true,
-                        minMemorySize(),
-                        Optional.of(NUM_VCPUS));
-        adbConnectToMicrodroid(getDevice(), cid);
-        waitForBootComplete();
+        mMicrodroidDevice =
+                MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
+                        .debugLevel("full")
+                        .memoryMib(minMemorySize())
+                        .numCpus(NUM_VCPUS)
+                        .build((TestDevice) getDevice());
+        mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
+        CommandRunner microdroid = new CommandRunner(mMicrodroidDevice);
+
         // Test writing to /data partition
-        runOnMicrodroid("echo MicrodroidTest > /data/local/tmp/test.txt");
-        assertThat(runOnMicrodroid("cat /data/local/tmp/test.txt")).isEqualTo("MicrodroidTest");
+        microdroid.run("echo MicrodroidTest > /data/local/tmp/test.txt");
+        assertThat(microdroid.run("cat /data/local/tmp/test.txt")).isEqualTo("MicrodroidTest");
 
         // Check if the APK & its idsig partitions exist
         final String apkPartition = "/dev/block/by-name/microdroid-apk";
-        assertThat(runOnMicrodroid("ls", apkPartition)).isEqualTo(apkPartition);
+        assertThat(microdroid.run("ls", apkPartition)).isEqualTo(apkPartition);
         final String apkIdsigPartition = "/dev/block/by-name/microdroid-apk-idsig";
-        assertThat(runOnMicrodroid("ls", apkIdsigPartition)).isEqualTo(apkIdsigPartition);
+        assertThat(microdroid.run("ls", apkIdsigPartition)).isEqualTo(apkIdsigPartition);
         // Check the vm-instance partition as well
         final String vmInstancePartition = "/dev/block/by-name/vm-instance";
-        assertThat(runOnMicrodroid("ls", vmInstancePartition)).isEqualTo(vmInstancePartition);
+        assertThat(microdroid.run("ls", vmInstancePartition)).isEqualTo(vmInstancePartition);
 
         // Check if the native library in the APK is has correct filesystem info
-        final String[] abis = runOnMicrodroid("getprop", "ro.product.cpu.abilist").split(",");
+        final String[] abis = microdroid.run("getprop", "ro.product.cpu.abilist").split(",");
         assertThat(abis).hasLength(1);
         final String testLib = "/mnt/apk/lib/" + abis[0] + "/MicrodroidTestNativeLib.so";
         final String label = "u:object_r:system_file:s0";
-        assertThat(runOnMicrodroid("ls", "-Z", testLib)).isEqualTo(label + " " + testLib);
+        assertThat(microdroid.run("ls", "-Z", testLib)).isEqualTo(label + " " + testLib);
 
         // Check that no denials have happened so far
         CommandRunner android = new CommandRunner(getDevice());
         assertThat(android.tryRun("egrep", "'avc:[[:space:]]{1,2}denied'", LOG_PATH)).isNull();
 
-        assertThat(runOnMicrodroid("cat /proc/cpuinfo | grep processor | wc -l"))
+        assertThat(microdroid.run("cat /proc/cpuinfo | grep processor | wc -l"))
                 .isEqualTo(Integer.toString(NUM_VCPUS));
 
         // Check that selinux is enabled
-        assertThat(runOnMicrodroid("getenforce")).isEqualTo("Enforcing");
+        assertThat(microdroid.run("getenforce")).isEqualTo("Enforcing");
 
         // TODO(b/176805428): adb is broken for nested VM
         if (!isCuttlefish()) {
             // Check neverallow rules on microdroid
-            File policyFile = FileUtil.createTempFile("microdroid_sepolicy", "");
-            pullMicrodroidFile("/sys/fs/selinux/policy", policyFile);
-
+            File policyFile = mMicrodroidDevice.pullFile("/sys/fs/selinux/policy");
             File generalPolicyConfFile = findTestFile("microdroid_general_sepolicy.conf");
             File sepolicyAnalyzeBin = findTestFile("sepolicy-analyze");
 
@@ -635,38 +631,41 @@
                     .that(result)
                     .isSuccess();
         }
-
-        shutdownMicrodroid(getDevice(), cid);
     }
 
     @Test
     public void testMicrodroidRamUsage() throws Exception {
         final String configPath = "assets/vm_config.json";
-        final String cid =
-                startMicrodroid(
-                        getDevice(),
-                        getBuild(),
-                        APK_NAME,
-                        PACKAGE_NAME,
-                        configPath,
-                        /* debug */ true,
-                        minMemorySize(),
-                        Optional.of(NUM_VCPUS));
-        adbConnectToMicrodroid(getDevice(), cid);
-        waitForBootComplete();
-        rootMicrodroid();
+        mMicrodroidDevice = MicrodroidBuilder
+                .fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
+                .debugLevel("full")
+                .memoryMib(minMemorySize())
+                .numCpus(NUM_VCPUS)
+                .build((TestDevice) getDevice());
+        mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
+        mMicrodroidDevice.enableAdbRoot();
+
+        CommandRunner microdroid = new CommandRunner(mMicrodroidDevice);
+        Function<String, String> microdroidExec =
+                (cmd) -> {
+                    try {
+                        return microdroid.run(cmd);
+                    } catch (Exception ex) {
+                        throw new IllegalStateException(ex);
+                    }
+                };
 
         for (Map.Entry<String, Long> stat :
-                ProcessUtil.getProcessMemoryMap(cmd -> runOnMicrodroid(cmd)).entrySet()) {
+                ProcessUtil.getProcessMemoryMap(microdroidExec).entrySet()) {
             mMetrics.addTestMetric(
                     mMetricPrefix + "meminfo/" + stat.getKey().toLowerCase(),
                     stat.getValue().toString());
         }
 
         for (Map.Entry<Integer, String> proc :
-                ProcessUtil.getProcessMap(cmd -> runOnMicrodroid(cmd)).entrySet()) {
+                ProcessUtil.getProcessMap(microdroidExec).entrySet()) {
             for (Map.Entry<String, Long> stat :
-                    ProcessUtil.getProcessSmapsRollup(proc.getKey(), cmd -> runOnMicrodroid(cmd))
+                    ProcessUtil.getProcessSmapsRollup(proc.getKey(), microdroidExec)
                             .entrySet()) {
                 String name = stat.getKey().toLowerCase();
                 mMetrics.addTestMetric(
@@ -674,8 +673,6 @@
                         stat.getValue().toString());
             }
         }
-
-        shutdownMicrodroid(getDevice(), cid);
     }
 
     @Test
@@ -717,6 +714,7 @@
     public void setUp() throws Exception {
         testIfDeviceIsCapable(getDevice());
         mMetricPrefix = getMetricPrefix() + "microdroid/";
+        mMicrodroidDevice = null;
 
         prepareVirtualizationTestSetup(getDevice());
 
@@ -728,6 +726,10 @@
 
     @After
     public void shutdown() throws Exception {
+        if (mMicrodroidDevice != null) {
+            ((TestDevice) getDevice()).shutdownMicrodroid(mMicrodroidDevice);
+        }
+
         cleanUpVirtualizationTestSetup(getDevice());
 
         archiveLogThenDelete(
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 42abbbf..8972046 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -22,7 +22,7 @@
     libs: ["android.system.virtualmachine"],
     jni_libs: [
         "MicrodroidTestNativeLib",
-        "MicrodroidTestNativeCrashLib",
+        "MicrodroidIdleNativeLib",
     ],
     platform_apis: true,
     use_embedded_native_libs: true,
@@ -35,6 +35,7 @@
     name: "MicrodroidTestNativeLib",
     srcs: ["src/native/testbinary.cpp"],
     stl: "libc++_static",
+    header_libs: ["vm_payload_restricted_headers"],
     shared_libs: [
         "libbinder_ndk",
         "MicrodroidTestNativeLibSub",
@@ -50,13 +51,6 @@
 }
 
 cc_library_shared {
-    name: "MicrodroidTestNativeCrashLib",
-    header_libs: ["vm_payload_headers"],
-    srcs: ["src/native/crashbinary.cpp"],
-    stl: "libc++_static",
-}
-
-cc_library_shared {
     name: "MicrodroidTestNativeLibSub",
     srcs: ["src/native/testlib.cpp"],
     stl: "libc++_static",
diff --git a/tests/testapk/assets/vm_config_crash.json b/tests/testapk/assets/vm_config_crash.json
index 2951fdf..3ec34a3 100644
--- a/tests/testapk/assets/vm_config_crash.json
+++ b/tests/testapk/assets/vm_config_crash.json
@@ -4,7 +4,7 @@
     },
     "task": {
       "type": "microdroid_launcher",
-      "command": "MicrodroidTestNativeCrashLib.so"
+      "command": "MicrodroidIdleNativeLib.so"
     },
     "export_tombstones": true
   }
diff --git a/tests/testapk/assets/vm_config_crash_no_tombstone.json b/tests/testapk/assets/vm_config_crash_no_tombstone.json
index 583f87b..9678e38 100644
--- a/tests/testapk/assets/vm_config_crash_no_tombstone.json
+++ b/tests/testapk/assets/vm_config_crash_no_tombstone.json
@@ -4,7 +4,7 @@
     },
     "task": {
       "type": "microdroid_launcher",
-      "command": "MicrodroidTestNativeCrashLib.so"
+      "command": "MicrodroidIdleNativeLib.so"
     },
     "export_tombstones": false
   }
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 dd01867..cc623a8 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -28,6 +28,7 @@
 import android.content.Context;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
+import android.os.ServiceSpecificException;
 import android.os.SystemProperties;
 import android.system.virtualmachine.ParcelVirtualMachine;
 import android.system.virtualmachine.VirtualMachine;
@@ -337,7 +338,10 @@
                     }
                 };
         listener.runToFinish(TAG, vm);
-        assertThat(exception.getNow(null)).isNull();
+        Exception e = exception.getNow(null);
+        if (e != null) {
+            throw e;
+        }
         return vmCdis;
     }
 
@@ -438,6 +442,24 @@
         }
     }
 
+    @Test
+    @CddTest(requirements = {
+            "9.17/C-1-1",
+            "9.17/C-1-2"
+    })
+    public void accessToCdisIsRestricted() throws Exception {
+        assumeSupportedKernel();
+
+        VirtualMachineConfig config = mInner.newVmConfigBuilder()
+                .setPayloadBinaryPath("MicrodroidTestNativeLib.so")
+                .setDebugLevel(DEBUG_LEVEL_FULL)
+                .build();
+        mInner.forceCreateNewVirtualMachine("test_vm", config);
+
+        assertThrows(ServiceSpecificException.class, () -> launchVmAndGetCdis("test_vm"));
+    }
+
+
     private static final UUID MICRODROID_PARTITION_UUID =
             UUID.fromString("cf9afe9a-0662-11ec-a329-c32663a09d75");
     private static final UUID U_BOOT_AVB_PARTITION_UUID =
diff --git a/tests/testapk/src/native/crashbinary.cpp b/tests/testapk/src/native/crashbinary.cpp
deleted file mode 100644
index a0edc40..0000000
--- a/tests/testapk/src/native/crashbinary.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "vm_main.h"
-
-// A VM payload that crashes as soon as it starts, to allow us to exercise that error path.
-extern "C" int AVmPayload_main() {
-    printf("test crash!!!!\n");
-    abort();
-}
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index 1a3e940..48942dc 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -28,7 +28,7 @@
 #include <sys/system_properties.h>
 #include <unistd.h>
 #include <vm_main.h>
-#include <vm_payload.h>
+#include <vm_payload_restricted.h>
 
 #include <string>
 
@@ -79,7 +79,7 @@
             if (!AVmPayload_getVmInstanceSecret(identifier, sizeof(identifier), out->data(),
                                                 out->size())) {
                 return ndk::ScopedAStatus::
-                        fromServiceSpecificErrorWithMessage(0, "Failed to VM instance secret");
+                        fromServiceSpecificErrorWithMessage(0, "Failed to get VM instance secret");
             }
             return ndk::ScopedAStatus::ok();
         }
diff --git a/virtualizationservice/src/atom.rs b/virtualizationservice/src/atom.rs
index eabb4cc..20f88e7 100644
--- a/virtualizationservice/src/atom.rs
+++ b/virtualizationservice/src/atom.rs
@@ -26,7 +26,9 @@
 use binder::{ParcelFileDescriptor, ThreadState};
 use log::{trace, warn};
 use microdroid_payload_config::VmPayloadConfig;
+use rustutils::system_properties;
 use statslog_virtualization_rust::{vm_booted, vm_creation_requested, vm_exited};
+use std::thread;
 use std::time::{Duration, SystemTime};
 use zip::ZipArchive;
 
@@ -86,17 +88,16 @@
             binder_exception_code = e.exception_code() as i32;
         }
     }
-
     let (vm_identifier, config_type, num_cpus, memory_mib, apexes) = match config {
         VirtualMachineConfig::AppConfig(config) => (
-            &config.name,
+            config.name.clone(),
             vm_creation_requested::ConfigType::VirtualMachineAppConfig,
             config.numCpus,
             config.memoryMib,
             get_apex_list(config),
         ),
         VirtualMachineConfig::RawConfig(config) => (
-            &config.name,
+            config.name.clone(),
             vm_creation_requested::ConfigType::VirtualMachineRawConfig,
             config.numCpus,
             config.memoryMib,
@@ -104,105 +105,139 @@
         ),
     };
 
-    let vm_creation_requested = vm_creation_requested::VmCreationRequested {
-        uid: ThreadState::get_calling_uid() as i32,
-        vm_identifier,
-        hypervisor: vm_creation_requested::Hypervisor::Pkvm,
-        is_protected,
-        creation_succeeded,
-        binder_exception_code,
-        config_type,
-        num_cpus,
-        cpu_affinity: "", // deprecated
-        memory_mib,
-        apexes: &apexes,
-        // TODO(seungjaeyoo) Fill information about task_profile
-        // TODO(seungjaeyoo) Fill information about disk_image for raw config
-    };
+    let uid = ThreadState::get_calling_uid() as i32;
+    thread::spawn(move || {
+        let vm_creation_requested = vm_creation_requested::VmCreationRequested {
+            uid,
+            vm_identifier: &vm_identifier,
+            hypervisor: vm_creation_requested::Hypervisor::Pkvm,
+            is_protected,
+            creation_succeeded,
+            binder_exception_code,
+            config_type,
+            num_cpus,
+            cpu_affinity: "", // deprecated
+            memory_mib,
+            apexes: &apexes,
+            // TODO(seungjaeyoo) Fill information about task_profile
+            // TODO(seungjaeyoo) Fill information about disk_image for raw config
+        };
 
-    match vm_creation_requested.stats_write() {
-        Err(e) => {
-            warn!("statslog_rust failed with error: {}", e);
+        wait_for_statsd().unwrap_or_else(|e| warn!("failed to wait for statsd with error: {}", e));
+        match vm_creation_requested.stats_write() {
+            Err(e) => {
+                warn!("statslog_rust failed with error: {}", e);
+            }
+            Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
         }
-        Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
-    }
+    });
 }
 
 /// Write the stats of VM boot to statsd
+/// The function creates a separate thread which waits fro statsd to start to push atom
 pub fn write_vm_booted_stats(
     uid: i32,
-    vm_identifier: &String,
+    vm_identifier: &str,
     vm_start_timestamp: Option<SystemTime>,
 ) {
+    let vm_identifier = vm_identifier.to_owned();
     let duration = get_duration(vm_start_timestamp);
-    let vm_booted = vm_booted::VmBooted {
-        uid,
-        vm_identifier,
-        elapsed_time_millis: duration.as_millis() as i64,
-    };
-    match vm_booted.stats_write() {
-        Err(e) => {
-            warn!("statslog_rust failed with error: {}", e);
+    thread::spawn(move || {
+        let vm_booted = vm_booted::VmBooted {
+            uid,
+            vm_identifier: &vm_identifier,
+            elapsed_time_millis: duration.as_millis() as i64,
+        };
+        wait_for_statsd().unwrap_or_else(|e| warn!("failed to wait for statsd with error: {}", e));
+        match vm_booted.stats_write() {
+            Err(e) => {
+                warn!("statslog_rust failed with error: {}", e);
+            }
+            Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
         }
-        Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
-    }
+    });
 }
 
 /// Write the stats of VM exit to statsd
+/// The function creates a separate thread which waits fro statsd to start to push atom
 pub fn write_vm_exited_stats(
     uid: i32,
-    vm_identifier: &String,
+    vm_identifier: &str,
     reason: DeathReason,
     vm_start_timestamp: Option<SystemTime>,
 ) {
+    let vm_identifier = vm_identifier.to_owned();
     let duration = get_duration(vm_start_timestamp);
-    let vm_exited = vm_exited::VmExited {
-        uid,
-        vm_identifier,
-        elapsed_time_millis: duration.as_millis() as i64,
-        death_reason: match reason {
-            DeathReason::INFRASTRUCTURE_ERROR => vm_exited::DeathReason::InfrastructureError,
-            DeathReason::KILLED => vm_exited::DeathReason::Killed,
-            DeathReason::UNKNOWN => vm_exited::DeathReason::Unknown,
-            DeathReason::SHUTDOWN => vm_exited::DeathReason::Shutdown,
-            DeathReason::ERROR => vm_exited::DeathReason::Error,
-            DeathReason::REBOOT => vm_exited::DeathReason::Reboot,
-            DeathReason::CRASH => vm_exited::DeathReason::Crash,
-            DeathReason::PVM_FIRMWARE_PUBLIC_KEY_MISMATCH => {
-                vm_exited::DeathReason::PvmFirmwarePublicKeyMismatch
+    thread::spawn(move || {
+        let vm_exited = vm_exited::VmExited {
+            uid,
+            vm_identifier: &vm_identifier,
+            elapsed_time_millis: duration.as_millis() as i64,
+            death_reason: match reason {
+                DeathReason::INFRASTRUCTURE_ERROR => vm_exited::DeathReason::InfrastructureError,
+                DeathReason::KILLED => vm_exited::DeathReason::Killed,
+                DeathReason::UNKNOWN => vm_exited::DeathReason::Unknown,
+                DeathReason::SHUTDOWN => vm_exited::DeathReason::Shutdown,
+                DeathReason::ERROR => vm_exited::DeathReason::Error,
+                DeathReason::REBOOT => vm_exited::DeathReason::Reboot,
+                DeathReason::CRASH => vm_exited::DeathReason::Crash,
+                DeathReason::PVM_FIRMWARE_PUBLIC_KEY_MISMATCH => {
+                    vm_exited::DeathReason::PvmFirmwarePublicKeyMismatch
+                }
+                DeathReason::PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED => {
+                    vm_exited::DeathReason::PvmFirmwareInstanceImageChanged
+                }
+                DeathReason::BOOTLOADER_PUBLIC_KEY_MISMATCH => {
+                    vm_exited::DeathReason::BootloaderPublicKeyMismatch
+                }
+                DeathReason::BOOTLOADER_INSTANCE_IMAGE_CHANGED => {
+                    vm_exited::DeathReason::BootloaderInstanceImageChanged
+                }
+                DeathReason::MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE => {
+                    vm_exited::DeathReason::MicrodroidFailedToConnectToVirtualizationService
+                }
+                DeathReason::MICRODROID_PAYLOAD_HAS_CHANGED => {
+                    vm_exited::DeathReason::MicrodroidPayloadHasChanged
+                }
+                DeathReason::MICRODROID_PAYLOAD_VERIFICATION_FAILED => {
+                    vm_exited::DeathReason::MicrodroidPayloadVerificationFailed
+                }
+                DeathReason::MICRODROID_INVALID_PAYLOAD_CONFIG => {
+                    vm_exited::DeathReason::MicrodroidInvalidPayloadConfig
+                }
+                DeathReason::MICRODROID_UNKNOWN_RUNTIME_ERROR => {
+                    vm_exited::DeathReason::MicrodroidUnknownRuntimeError
+                }
+                DeathReason::HANGUP => vm_exited::DeathReason::Hangup,
+                _ => vm_exited::DeathReason::Unknown,
+            },
+        };
+        wait_for_statsd().unwrap_or_else(|e| warn!("failed to wait for statsd with error: {}", e));
+        match vm_exited.stats_write() {
+            Err(e) => {
+                warn!("statslog_rust failed with error: {}", e);
             }
-            DeathReason::PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED => {
-                vm_exited::DeathReason::PvmFirmwareInstanceImageChanged
-            }
-            DeathReason::BOOTLOADER_PUBLIC_KEY_MISMATCH => {
-                vm_exited::DeathReason::BootloaderPublicKeyMismatch
-            }
-            DeathReason::BOOTLOADER_INSTANCE_IMAGE_CHANGED => {
-                vm_exited::DeathReason::BootloaderInstanceImageChanged
-            }
-            DeathReason::MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE => {
-                vm_exited::DeathReason::MicrodroidFailedToConnectToVirtualizationService
-            }
-            DeathReason::MICRODROID_PAYLOAD_HAS_CHANGED => {
-                vm_exited::DeathReason::MicrodroidPayloadHasChanged
-            }
-            DeathReason::MICRODROID_PAYLOAD_VERIFICATION_FAILED => {
-                vm_exited::DeathReason::MicrodroidPayloadVerificationFailed
-            }
-            DeathReason::MICRODROID_INVALID_PAYLOAD_CONFIG => {
-                vm_exited::DeathReason::MicrodroidInvalidPayloadConfig
-            }
-            DeathReason::MICRODROID_UNKNOWN_RUNTIME_ERROR => {
-                vm_exited::DeathReason::MicrodroidUnknownRuntimeError
-            }
-            DeathReason::HANGUP => vm_exited::DeathReason::Hangup,
-            _ => vm_exited::DeathReason::Unknown,
-        },
-    };
-    match vm_exited.stats_write() {
-        Err(e) => {
-            warn!("statslog_rust failed with error: {}", e);
+            Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
         }
-        Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
+    });
+}
+
+fn wait_for_statsd() -> Result<()> {
+    let mut prop = system_properties::PropertyWatcher::new("init.svc.statsd")?;
+    loop {
+        prop.wait()?;
+        match system_properties::read("init.svc.statsd")? {
+            Some(s) => {
+                if s == "running" {
+                    break;
+                }
+            }
+            None => {
+                // This case never really happens because
+                // prop.wait() waits for property to be non-null.
+                break;
+            }
+        }
     }
+    Ok(())
 }
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index 6f646b7..1b8061e 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -546,6 +546,7 @@
     debug!("Preserving FDs {:?}", preserved_fds);
     command.preserved_fds(preserved_fds);
 
+    command.arg("--params").arg("crashkernel=17M");
     print_crosvm_args(&command);
 
     let result = SharedChild::spawn(&mut command)?;