Merge "Entry point for Rust pvmfw."
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 80d0807..f40da7e 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -21,7 +21,7 @@
   ],
   "postsubmit": [
     {
-      "name": "odsign_e2e_tests"
+      "name": "odsign_e2e_tests_full"
     }
   ],
   "imports": [
diff --git a/apkdmverity/TEST_MAPPING b/apkdmverity/TEST_MAPPING
index 1bbec76..997b3f9 100644
--- a/apkdmverity/TEST_MAPPING
+++ b/apkdmverity/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit" : [
+  "postsubmit" : [
     {
       "name" : "apkdmverity.test"
     }
diff --git a/authfs/src/file/remote_file.rs b/authfs/src/file/remote_file.rs
index 039285f..4c112bd 100644
--- a/authfs/src/file/remote_file.rs
+++ b/authfs/src/file/remote_file.rs
@@ -40,7 +40,6 @@
 }
 
 pub struct RemoteFileReader {
-    // This needs to have Sync trait to be used in fuse::worker::start_message_loop.
     service: VirtFdService,
     file_fd: i32,
 }
@@ -81,7 +80,6 @@
 }
 
 pub struct RemoteMerkleTreeReader {
-    // This needs to be a Sync to be used in fuse::worker::start_message_loop.
     service: VirtFdService,
     file_fd: i32,
 }
@@ -108,7 +106,6 @@
 }
 
 pub struct RemoteFileEditor {
-    // This needs to have Sync trait to be used in fuse::worker::start_message_loop.
     service: VirtFdService,
     file_fd: i32,
 }
diff --git a/authfs/src/fusefs.rs b/authfs/src/fusefs.rs
index 511db68..beb6b30 100644
--- a/authfs/src/fusefs.rs
+++ b/authfs/src/fusefs.rs
@@ -184,10 +184,9 @@
 
 type DirHandleTable = BTreeMap<Handle, Arc<DirEntriesSnapshot>>;
 
-// AuthFS needs to be `Sync` to be accepted by fuse::worker::start_message_loop as a `FileSystem`.
+// AuthFS needs to be `Sync` to be used with the `fuse` crate.
 pub struct AuthFs {
-    /// Table for `Inode` to `InodeState` lookup. This needs to be `Sync` to be used in
-    /// `fuse::worker::start_message_loop`.
+    /// Table for `Inode` to `InodeState` lookup.
     inode_table: RwLock<BTreeMap<Inode, InodeState>>,
 
     /// The next available inode number.
diff --git a/authfs/src/fusefs/mount.rs b/authfs/src/fusefs/mount.rs
index 294c6b1..7f8bac1 100644
--- a/authfs/src/fusefs/mount.rs
+++ b/authfs/src/fusefs/mount.rs
@@ -16,16 +16,18 @@
 
 use fuse::mount::MountOption;
 use std::fs::OpenOptions;
+use std::num::NonZeroU8;
 use std::os::unix::io::AsRawFd;
 use std::path::Path;
 
 use super::AuthFs;
 
-/// Maximum bytes in the write transaction to the FUSE device. This limits the maximum buffer
-/// size in a read request (including FUSE protocol overhead) that the filesystem writes to.
+/// Maximum bytes (excluding the FUSE header) `AuthFs` will receive from the kernel for write
+/// operations by another process.
 pub const MAX_WRITE_BYTES: u32 = 65536;
 
-/// Maximum bytes in a read operation.
+/// Maximum bytes (excluding the FUSE header) `AuthFs` will receive from the kernel for read
+/// operations by another process.
 /// TODO(victorhsieh): This option is deprecated by FUSE. Figure out if we can remove this.
 const MAX_READ_BYTES: u32 = 65536;
 
@@ -34,6 +36,7 @@
     authfs: AuthFs,
     mountpoint: &Path,
     extra_options: &Option<String>,
+    threads: Option<NonZeroU8>,
 ) -> Result<(), fuse::Error> {
     let dev_fuse = OpenOptions::new()
         .read(true)
@@ -61,5 +64,10 @@
     )
     .expect("Failed to mount fuse");
 
-    fuse::worker::start_message_loop(dev_fuse, MAX_WRITE_BYTES, MAX_READ_BYTES, authfs)
+    let mut config = fuse::FuseConfig::new();
+    config.dev_fuse(dev_fuse).max_write(MAX_WRITE_BYTES).max_read(MAX_READ_BYTES);
+    if let Some(num) = threads {
+        config.num_threads(u8::from(num).into());
+    }
+    config.enter_message_loop(authfs)
 }
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index bdca5b4..60318e8 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -37,6 +37,7 @@
 use protobuf::Message;
 use std::convert::TryInto;
 use std::fs::File;
+use std::num::NonZeroU8;
 use std::path::{Path, PathBuf};
 use structopt::StructOpt;
 
@@ -67,6 +68,10 @@
     #[structopt(short = "o")]
     extra_options: Option<String>,
 
+    /// Number of threads to serve FUSE requests.
+    #[structopt(short = "j")]
+    thread_number: Option<NonZeroU8>,
+
     /// A read-only remote file with integrity check. Can be multiple.
     ///
     /// For example, `--remote-ro-file 5:sha256-1234abcd` tells the filesystem to associate the
@@ -312,7 +317,12 @@
     let mut authfs = AuthFs::new(RemoteFsStatsReader::new(service.clone()));
     prepare_root_dir_entries(service, &mut authfs, &args)?;
 
-    fusefs::mount_and_enter_message_loop(authfs, &args.mount_point, &args.extra_options)?;
+    fusefs::mount_and_enter_message_loop(
+        authfs,
+        &args.mount_point,
+        &args.extra_options,
+        args.thread_number,
+    )?;
     bail!("Unexpected exit after the handler loop")
 }
 
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index 9787434..16dc2cf 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -38,12 +38,12 @@
 use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
 use log::{info, warn};
 use rustutils::system_properties;
-use std::fs::File;
+use std::fs::{self, File};
 use std::io::{BufRead, BufReader};
 use std::num::NonZeroU32;
 use std::os::raw;
 use std::os::unix::io::IntoRawFd;
-use std::path::Path;
+use std::path::{Path, PathBuf};
 use std::sync::{Arc, Condvar, Mutex};
 use std::thread;
 
@@ -95,8 +95,8 @@
         let apex_dir = Path::new(COMPOS_APEX_ROOT);
         let data_dir = Path::new(COMPOS_DATA_ROOT);
 
-        let apk_fd = File::open(apex_dir.join("app/CompOSPayloadApp/CompOSPayloadApp.apk"))
-            .context("Failed to open config APK file")?;
+        let config_apk = Self::locate_config_apk(apex_dir)?;
+        let apk_fd = File::open(config_apk).context("Failed to open config APK file")?;
         let apk_fd = ParcelFileDescriptor::new(apk_fd);
         let idsig_fd = prepare_idsig(service, &apk_fd, idsig)?;
 
@@ -166,6 +166,21 @@
         Ok(VmInstance { vm, cid })
     }
 
+    fn locate_config_apk(apex_dir: &Path) -> Result<PathBuf> {
+        // Our config APK will be in a directory under app, but the name of the directory is at the
+        // discretion of the build system. So just look in each sub-directory until we find it.
+        // (In practice there will be exactly one directory, so this shouldn't take long.)
+        let app_dir = apex_dir.join("app");
+        for dir in fs::read_dir(app_dir).context("Reading app dir")? {
+            let apk_file = dir?.path().join("CompOSPayloadApp.apk");
+            if apk_file.is_file() {
+                return Ok(apk_file);
+            }
+        }
+
+        bail!("Failed to locate CompOSPayloadApp.apk")
+    }
+
     /// Create and return an RPC Binder connection to the Comp OS service in the VM.
     pub fn get_service(&self) -> Result<Strong<dyn ICompOsService>> {
         let mut vsock_factory = VsockFactory::new(&*self.vm);
diff --git a/compos/tests/java/android/compos/test/ComposTestCase.java b/compos/tests/java/android/compos/test/ComposTestCase.java
index 6773eb7..eec9e39 100644
--- a/compos/tests/java/android/compos/test/ComposTestCase.java
+++ b/compos/tests/java/android/compos/test/ComposTestCase.java
@@ -182,15 +182,8 @@
     private void killVmAndReconnectAdb() throws Exception {
         CommandRunner android = new CommandRunner(getDevice());
 
-        // When a VM exits, we tend to see adb disconnecting. So we attempt to reconnect
-        // when we kill it to avoid problems. Of course VirtualizationService may exit anyway
-        // (it's an on-demand service and all its clients have gone), taking the VM with it,
-        // which makes this a bit unpredictable.
-        reconnectHostAdb(getDevice());
         android.tryRun("killall", "crosvm");
-        reconnectHostAdb(getDevice());
         android.tryRun("stop", "virtualizationservice");
-        reconnectHostAdb(getDevice());
 
         // Delete stale data
         android.tryRun("rm", "-rf", "/data/misc/virtualizationservice/*");
diff --git a/libs/avb_bindgen/Android.bp b/libs/avb_bindgen/Android.bp
index 1035498..1e62864 100644
--- a/libs/avb_bindgen/Android.bp
+++ b/libs/avb_bindgen/Android.bp
@@ -4,6 +4,7 @@
 
 rust_bindgen {
     name: "libavb_bindgen",
+    host_supported: true,
     wrapper_src: "bindgen/avb.h",
     crate_name: "avb_bindgen",
     source_stem: "bindings",
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index b265d5c..60f8fd4 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -77,6 +77,7 @@
         "linkerconfig",
         "servicemanager.microdroid",
         "tombstoned",
+        "tombstone_transmit.microdroid",
         "cgroups.json",
         "public.libraries.android.txt",
 
@@ -88,11 +89,9 @@
         "microdroid_property_contexts",
         "microdroid_service_contexts",
 
-        // TODO(b/195425111) these four should be added automatically
-        "android.hardware.security.secureclock-V1-ndk",
-        "android.hardware.security.sharedsecret-V1-ndk",
-        "libcrypto",
-        "liblzma",
+        // TODO(b/195425111) these should be added automatically
+        "libcrypto", // used by many (init_second_stage, microdroid_manager, toybox, etc)
+        "liblzma", // used by init_second_stage
     ] + microdroid_shell_and_utilities,
     multilib: {
         common: {
@@ -111,9 +110,6 @@
                 "authfs_service",
                 "microdroid_manager",
                 "zipfuse",
-
-                // TODO(b/184872979): Needed by authfs. Remove once the Rust API is created.
-                "libbinder_rpc_unstable",
             ],
         },
     },
@@ -532,7 +528,7 @@
 genrule {
     name: "microdroid_uboot_env_gen",
     tools: [
-        "mkenvimage_host",
+        "mkenvimage_slim",
         "avbtool",
     ],
     srcs: [
@@ -540,7 +536,7 @@
         ":microdroid_sign_key",
     ],
     out: ["output.img"],
-    cmd: "$(location mkenvimage_host) -s 4096 -o $(out) $(location uboot-env.txt) && " +
+    cmd: "$(location mkenvimage_slim) -output_path $(out) -input_path $(location uboot-env.txt) && " +
         "$(location avbtool) add_hash_footer " +
         "--algorithm SHA256_RSA4096 " +
         "--partition_name uboot_env " +
@@ -612,4 +608,4 @@
     src: "microdroid_event-log-tags",
     filename: "event-log-tags",
     installable: false,
-}
\ No newline at end of file
+}
diff --git a/microdroid/bootconfig.common b/microdroid/bootconfig.common
index eda95a2..c202ba0 100644
--- a/microdroid/bootconfig.common
+++ b/microdroid/bootconfig.common
@@ -1,2 +1,6 @@
 androidboot.first_stage_console = 1
 androidboot.hardware = microdroid
+
+# tombstone_transmit is enabled. Tombstones will be transmitted to the host
+# TODO(b/227443903) : Make this configurable by VM owners
+androidboot.tombstone_transmit.enabled=1
diff --git a/microdroid/init.rc b/microdroid/init.rc
index f6d5092..dbe5ac7 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -173,6 +173,13 @@
     mkdir /data/local 0751 root root
     mkdir /data/local/tmp 0771 shell shell
 
+on init && property:ro.boot.tombstone_transmit.enabled=1
+     start tombstone_transmit
+
+service tombstone_transmit /system/bin/tombstone_transmit.microdroid -cid 2 -port 2000
+    user root
+    group system
+
 service apexd-vm /system/bin/apexd --vm
     user root
     group system
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index d94334c..fb1373d 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 rust_ffi_static {
     name: "libpvmfw",
     crate_name: "pvmfw",
diff --git a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
index 40be248..440ae18 100644
--- a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
+++ b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
@@ -82,28 +82,12 @@
         // disconnect from microdroid
         tryRunOnHost("adb", "disconnect", MICRODROID_SERIAL);
 
-        reconnectHostAdb(androidDevice);
-
         // kill stale VMs and directories
         android.tryRun("killall", "crosvm");
         android.tryRun("stop", "virtualizationservice");
         android.tryRun("rm", "-rf", "/data/misc/virtualizationservice/*");
     }
 
-    public static void reconnectHostAdb(ITestDevice androidDevice)
-            throws DeviceNotAvailableException {
-        CommandRunner android = new CommandRunner(androidDevice);
-
-        // Make sure we're connected to the host adb; this connection seems to get dropped when a VM
-        // exits.
-        for (int retry = 0; retry < 10; ++retry) {
-            if (android.tryRun("true") != null) {
-                break;
-            }
-            androidDevice.waitForDeviceOnline(1000);
-        }
-    }
-
     public static void testIfDeviceIsCapable(ITestDevice androidDevice) throws Exception {
         assumeTrue("Need an actual TestDevice", androidDevice instanceof TestDevice);
         TestDevice testDevice = (TestDevice) androidDevice;
@@ -121,7 +105,7 @@
     }
 
     // Run an arbitrary command in the host side and returns the result
-    private static String runOnHost(String... cmd) {
+    public static String runOnHost(String... cmd) {
         return runOnHostWithTimeout(10000, cmd);
     }
 
@@ -149,6 +133,19 @@
         return result.getStdout().trim();
     }
 
+    // Same as runOnMicrodroid, but keeps retrying on error till timeout
+    private static String runOnMicrodroidRetryingOnFailure(String... cmd) {
+        final long timeoutMs = 30000; // 30 sec. Microdroid is extremely slow on GCE-on-CF.
+        int attempts = (int) MICRODROID_ADB_CONNECT_TIMEOUT_MINUTES * 60 * 1000 / 500;
+        CommandResult result = RunUtil.getDefault()
+                .runTimedCmdRetry(timeoutMs, 500, attempts,
+                        "adb", "-s", MICRODROID_SERIAL, "shell", join(cmd));
+        if (result.getStatus() != CommandStatus.SUCCESS) {
+            fail(join(cmd) + " has failed: " + result);
+        }
+        return result.getStdout().trim();
+    }
+
     // Same as runOnMicrodroid, but returns null on error.
     public static String tryRunOnMicrodroid(String... cmd) {
         CommandResult result = runOnMicrodroidForResult(cmd);
@@ -344,34 +341,20 @@
 
         // Shutdown the VM
         android.run(VIRT_APEX + "bin/vm", "stop", cid);
-
-        // TODO(192660485): Figure out why shutting down the VM disconnects adb on cuttlefish
-        // temporarily. Without this wait, the rest of `runOnAndroid/skipIfFail` fails due to the
-        // connection loss, and results in assumption error exception for the rest of the tests.
-        try {
-            Thread.sleep(1000);
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-        }
     }
 
     public static void rootMicrodroid() {
         runOnHost("adb", "-s", MICRODROID_SERIAL, "root");
-
-        // TODO(192660959): Figure out the root cause and remove the sleep. For unknown reason,
-        // even though `adb root` actually wait-for-disconnect then wait-for-device, the next
-        // `adb -s $MICRODROID_SERIAL shell ...` often fails with "adb: device offline".
-        try {
-            Thread.sleep(1000);
-            runOnHostWithTimeout(
-                    MICRODROID_ADB_CONNECT_TIMEOUT_MINUTES * 60 * 1000,
-                    "adb",
-                    "-s",
-                    MICRODROID_SERIAL,
-                    "wait-for-device");
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-        }
+        runOnHostWithTimeout(
+                MICRODROID_ADB_CONNECT_TIMEOUT_MINUTES * 60 * 1000,
+                "adb",
+                "-s",
+                MICRODROID_SERIAL,
+                "wait-for-device");
+        // There have been tests when adb wait-for-device succeeded but the following command
+        // fails with error: closed. Hence, we run adb shell true in microdroid with retries
+        // before returning.
+        runOnMicrodroidRetryingOnFailure("true");
     }
 
     // Establish an adb connection to microdroid by letting Android forward the connection to
diff --git a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
index 5b71eba..7944245 100644
--- a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
@@ -22,6 +22,7 @@
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -100,6 +101,13 @@
                 false);
     }
 
+    // Wait until logd-init starts. The service is one of the last services that are started in
+    // the microdroid boot procedure. Therefore, waiting for the service means that we wait for
+    // the boot to complete. TODO: we need a better marker eventually.
+    private void waitForLogdInit() {
+        tryRunOnMicrodroid("watch -e \"getprop init.svc.logd-reinit | grep '^$'\"");
+    }
+
     @Test
     public void testCreateVmRequiresPermission() throws Exception {
         // Revoke the MANAGE_VIRTUAL_MACHINE permission for the test app
@@ -350,6 +358,41 @@
     }
 
     @Test
+    public void testTombstonesAreBeingForwarded() throws Exception {
+        // Note this test relies on logcat values being printed by tombstone_transmit on
+        // and the reeceiver on host (virtualization_service)
+        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),
+                        Optional.of(CPU_AFFINITY));
+        adbConnectToMicrodroid(getDevice(), cid);
+        waitForLogdInit();
+        runOnMicrodroid("logcat -c");
+        // We need root permission to write to /data/tombstones/
+        rootMicrodroid();
+        // Write a test tombstone file in /data/tombstones
+        runOnMicrodroid("echo -n \'Test tombstone in VM with 34 bytes\'"
+                    + "> /data/tombstones/transmit.txt");
+        // check if the tombstone have been tranferred from VM
+        assertNotEquals(runOnMicrodroid("timeout 15s logcat | grep -m 1 "
+                            + "'tombstone_transmit.microdroid:.*data/tombstones/transmit.txt'"),
+                "");
+        // Confirm that tombstone is received (from host logcat)
+        assertNotEquals(runOnHost("adb", "-s", getDevice().getSerialNumber(),
+                            "logcat", "-d", "-e",
+                            "Received 34 bytes from guest & wrote to tombstone file.*"),
+                "");
+    }
+
+    @Test
     public void testMicrodroidBoots() throws Exception {
         final String configPath = "assets/vm_config.json"; // path inside the APK
         final String cid =
@@ -364,12 +407,7 @@
                         Optional.of(NUM_VCPUS),
                         Optional.of(CPU_AFFINITY));
         adbConnectToMicrodroid(getDevice(), cid);
-
-        // Wait until logd-init starts. The service is one of the last services that are started in
-        // the microdroid boot procedure. Therefore, waiting for the service means that we wait for
-        // the boot to complete. TODO: we need a better marker eventually.
-        tryRunOnMicrodroid("watch -e \"getprop init.svc.logd-reinit | grep '^$'\"");
-
+        waitForLogdInit();
         // Test writing to /data partition
         runOnMicrodroid("echo MicrodroidTest > /data/local/tmp/test.txt");
         assertThat(runOnMicrodroid("cat /data/local/tmp/test.txt"), is("MicrodroidTest"));
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 36bea72..f7261d3 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -262,11 +262,6 @@
     public void changingDebugLevelInvalidatesVmIdentity()
             throws VirtualMachineException, InterruptedException, IOException {
         assume()
-            .withMessage("Skip on Cuttlefish. b/195765441")
-            .that(android.os.Build.DEVICE)
-            .isNotEqualTo("vsoc_x86_64");
-
-        assume()
             .withMessage("SKip on 5.4 kernel. b/218303240")
             .that(KERNEL_VERSION)
             .isNotEqualTo("5.4");
@@ -344,11 +339,6 @@
     public void instancesOfSameVmHaveDifferentCdis()
             throws VirtualMachineException, InterruptedException {
         assume()
-            .withMessage("Skip on Cuttlefish. b/195765441")
-            .that(android.os.Build.DEVICE)
-            .isNotEqualTo("vsoc_x86_64");
-
-        assume()
             .withMessage("SKip on 5.4 kernel. b/218303240")
             .that(KERNEL_VERSION)
             .isNotEqualTo("5.4");
@@ -368,11 +358,6 @@
     public void sameInstanceKeepsSameCdis()
             throws VirtualMachineException, InterruptedException {
         assume()
-            .withMessage("Skip on Cuttlefish. b/195765441")
-            .that(android.os.Build.DEVICE)
-            .isNotEqualTo("vsoc_x86_64");
-
-        assume()
             .withMessage("SKip on 5.4 kernel. b/218303240")
             .that(KERNEL_VERSION)
             .isNotEqualTo("5.4");
@@ -389,11 +374,6 @@
     public void bccIsSuperficiallyWellFormed()
             throws VirtualMachineException, InterruptedException, CborException {
         assume()
-            .withMessage("Skip on Cuttlefish. b/195765441")
-            .that(android.os.Build.DEVICE)
-            .isNotEqualTo("vsoc_x86_64");
-
-        assume()
             .withMessage("SKip on 5.4 kernel. b/218303240")
             .that(KERNEL_VERSION)
             .isNotEqualTo("5.4");
@@ -529,20 +509,12 @@
     @Test
     public void bootFailsWhenMicrodroidDataIsCompromised()
             throws VirtualMachineException, InterruptedException, IOException {
-        assume().withMessage("Skip on Cuttlefish. b/195765441")
-                .that(android.os.Build.DEVICE)
-                .isNotEqualTo("vsoc_x86_64");
-
         assertThatBootFailsAfterCompromisingPartition(MICRODROID_PARTITION_UUID);
     }
 
     @Test
     public void bootFailsWhenUBootAvbDataIsCompromised()
             throws VirtualMachineException, InterruptedException, IOException {
-        assume().withMessage("Skip on Cuttlefish. b/195765441")
-                .that(android.os.Build.DEVICE)
-                .isNotEqualTo("vsoc_x86_64");
-
         if (mProtectedVm) {
             assertThatBootFailsAfterCompromisingPartition(U_BOOT_AVB_PARTITION_UUID);
         } else {
@@ -554,10 +526,6 @@
     @Test
     public void bootFailsWhenUBootEnvDataIsCompromised()
             throws VirtualMachineException, InterruptedException, IOException {
-        assume().withMessage("Skip on Cuttlefish. b/195765441")
-                .that(android.os.Build.DEVICE)
-                .isNotEqualTo("vsoc_x86_64");
-
         if (mProtectedVm) {
             assertThatBootFailsAfterCompromisingPartition(U_BOOT_ENV_PARTITION_UUID);
         } else {
@@ -569,10 +537,6 @@
     @Test
     public void bootFailsWhenPvmFwDataIsCompromised()
             throws VirtualMachineException, InterruptedException, IOException {
-        assume().withMessage("Skip on Cuttlefish. b/195765441")
-                .that(android.os.Build.DEVICE)
-                .isNotEqualTo("vsoc_x86_64");
-
         if (mProtectedVm) {
             assertThatBootFailsAfterCompromisingPartition(PVM_FW_PARTITION_UUID);
         } else {
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 9b2b740..7a8da96 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -45,6 +45,7 @@
         "libserde_xml_rs",
         "libshared_child",
         "libstatslog_virtualization_rust",
+        "libtombstoned_client_rust",
         "libvmconfig",
         "libzip",
         "libvsock",
diff --git a/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl b/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
index 1a16f2a..dff5d46 100644
--- a/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
+++ b/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
@@ -30,6 +30,12 @@
     const int VM_BINDER_SERVICE_PORT = 5000;
 
     /**
+     * Port number that VirtualMachineService listens on connections from the guest VMs for the
+     * tombtones
+     */
+    const int VM_TOMBSTONES_SERVICE_PORT = 2000;
+
+    /**
      * Notifies that the payload has started.
      */
     void notifyPayloadStarted();
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index d9825dc..a2e856c 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -42,7 +42,7 @@
 use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::{
     IVirtualMachineService::{
         BnVirtualMachineService, IVirtualMachineService, VM_BINDER_SERVICE_PORT,
-        VM_STREAM_SERVICE_PORT,
+        VM_STREAM_SERVICE_PORT, VM_TOMBSTONES_SERVICE_PORT,
     },
 };
 use anyhow::{anyhow, bail, Context, Result};
@@ -57,13 +57,14 @@
 use std::convert::TryInto;
 use std::ffi::CStr;
 use std::fs::{create_dir, File, OpenOptions};
-use std::io::{Error, ErrorKind, Write};
+use std::io::{Error, ErrorKind, Write, Read};
 use std::num::NonZeroU32;
 use std::os::raw;
 use std::os::unix::io::{FromRawFd, IntoRawFd};
 use std::path::{Path, PathBuf};
 use std::ptr::null_mut;
 use std::sync::{Arc, Mutex, Weak};
+use tombstoned_client::{TombstonedConnection, DebuggerdDumpType};
 use vmconfig::VmConfig;
 use vsock::{SockAddr, VsockListener, VsockStream};
 use zip::ZipArchive;
@@ -86,6 +87,8 @@
 /// Version of the instance image format
 const ANDROID_VM_INSTANCE_VERSION: u16 = 1;
 
+const CHUNK_RECV_MAX_LEN: usize = 1024;
+
 /// Implementation of `IVirtualizationService`, the entry point of the AIDL service.
 #[derive(Debug, Default)]
 pub struct VirtualizationService {
@@ -129,139 +132,26 @@
         console_fd: Option<&ParcelFileDescriptor>,
         log_fd: Option<&ParcelFileDescriptor>,
     ) -> binder::Result<Strong<dyn IVirtualMachine>> {
-        check_manage_access()?;
-        let state = &mut *self.state.lock().unwrap();
-        let console_fd = console_fd.map(clone_file).transpose()?;
-        let log_fd = log_fd.map(clone_file).transpose()?;
-        let requester_uid = ThreadState::get_calling_uid();
-        let requester_sid = get_calling_sid()?;
-        let requester_debug_pid = ThreadState::get_calling_pid();
-        let cid = next_cid().or(Err(ExceptionCode::ILLEGAL_STATE))?;
-
-        // Counter to generate unique IDs for temporary image files.
-        let mut next_temporary_image_id = 0;
-        // Files which are referred to from composite images. These must be mapped to the crosvm
-        // child process, and not closed before it is started.
-        let mut indirect_files = vec![];
-
-        // Make directory for temporary files.
-        let temporary_directory: PathBuf = format!("{}/{}", TEMPORARY_DIRECTORY, cid).into();
-        create_dir(&temporary_directory).map_err(|e| {
-            // At this point, we do not know the protected status of Vm
-            // setting it to false, though this may not be correct.
-            write_vm_creation_stats(false, false);
-            error!(
-                "Failed to create temporary directory {:?} for VM files: {}",
-                temporary_directory, e
-            );
-            new_binder_exception(
-                ExceptionCode::SERVICE_SPECIFIC,
-                format!(
-                    "Failed to create temporary directory {:?} for VM files: {}",
-                    temporary_directory, e
-                ),
-            )
-        })?;
-
-        let is_app_config = matches!(config, VirtualMachineConfig::AppConfig(_));
-
-        let config = match config {
-            VirtualMachineConfig::AppConfig(config) => BorrowedOrOwned::Owned(
-                load_app_config(config, &temporary_directory).map_err(|e| {
-                    error!("Failed to load app config from {}: {}", &config.configPath, e);
-                    write_vm_creation_stats(config.protectedVm, false);
-                    new_binder_exception(
-                        ExceptionCode::SERVICE_SPECIFIC,
-                        format!("Failed to load app config from {}: {}", &config.configPath, e),
-                    )
-                })?,
-            ),
-            VirtualMachineConfig::RawConfig(config) => BorrowedOrOwned::Borrowed(config),
-        };
-        let config = config.as_ref();
-        let protected = config.protectedVm;
-
-        // Check if partition images are labeled incorrectly. This is to prevent random images
-        // which are not protected by the Android Verified Boot (e.g. bits downloaded by apps) from
-        // being loaded in a pVM.  Specifically, for images in the raw config, nothing is allowed
-        // to be labeled as app_data_file. For images in the app config, nothing but the instance
-        // partition is allowed to be labeled as such.
-        config
-            .disks
-            .iter()
-            .flat_map(|disk| disk.partitions.iter())
-            .filter(|partition| {
-                if is_app_config {
-                    partition.label != "vm-instance"
-                } else {
-                    true // all partitions are checked
-                }
-            })
-            .try_for_each(check_label_for_partition)
-            .map_err(|e| new_binder_exception(ExceptionCode::SERVICE_SPECIFIC, e.to_string()))?;
-
-        let zero_filler_path = temporary_directory.join("zero.img");
-        write_zero_filler(&zero_filler_path).map_err(|e| {
-            error!("Failed to make composite image: {}", e);
-            write_vm_creation_stats(protected, false);
-            new_binder_exception(
-                ExceptionCode::SERVICE_SPECIFIC,
-                format!("Failed to make composite image: {}", e),
-            )
-        })?;
-
-        // Assemble disk images if needed.
-        let disks = config
-            .disks
-            .iter()
-            .map(|disk| {
-                assemble_disk_image(
-                    disk,
-                    &zero_filler_path,
-                    &temporary_directory,
-                    &mut next_temporary_image_id,
-                    &mut indirect_files,
-                )
-            })
-            .collect::<Result<Vec<DiskFile>, _>>()?;
-
-        // Actually start the VM.
-        let crosvm_config = CrosvmConfig {
-            cid,
-            bootloader: maybe_clone_file(&config.bootloader)?,
-            kernel: maybe_clone_file(&config.kernel)?,
-            initrd: maybe_clone_file(&config.initrd)?,
-            disks,
-            params: config.params.to_owned(),
-            protected,
-            memory_mib: config.memoryMib.try_into().ok().and_then(NonZeroU32::new),
-            cpus: config.numCpus.try_into().ok().and_then(NonZeroU32::new),
-            cpu_affinity: config.cpuAffinity.clone(),
-            console_fd,
-            log_fd,
-            indirect_files,
-            platform_version: parse_platform_version_req(&config.platformVersion)?,
-        };
-        let instance = Arc::new(
-            VmInstance::new(
-                crosvm_config,
-                temporary_directory,
-                requester_uid,
-                requester_sid,
-                requester_debug_pid,
-            )
-            .map_err(|e| {
-                error!("Failed to create VM with config {:?}: {}", config, e);
-                write_vm_creation_stats(protected, false);
-                new_binder_exception(
-                    ExceptionCode::SERVICE_SPECIFIC,
-                    format!("Failed to create VM: {}", e),
-                )
-            })?,
-        );
-        state.add_vm(Arc::downgrade(&instance));
-        write_vm_creation_stats(protected, true);
-        Ok(VirtualMachine::create(instance))
+        let mut is_protected = false;
+        let ret = self.create_vm_internal(config, console_fd, log_fd, &mut is_protected);
+        match ret {
+            Ok(_) => {
+                let ok_status = Status::ok();
+                write_vm_creation_stats(
+                    is_protected,
+                    /*creation_succeeded*/ true,
+                    ok_status.exception_code() as i32,
+                );
+            }
+            Err(ref e) => {
+                write_vm_creation_stats(
+                    is_protected,
+                    /*creation_succeeded*/ false,
+                    e.exception_code() as i32,
+                );
+            }
+        }
+        ret
     }
 
     /// Initialise an empty partition image of the given size to be used as a writable partition.
@@ -371,6 +261,55 @@
     }
 }
 
+fn handle_stream_connection_tombstoned() -> Result<()> {
+    let listener =
+        VsockListener::bind_with_cid_port(VMADDR_CID_HOST, VM_TOMBSTONES_SERVICE_PORT as u32)?;
+    info!("Listening to tombstones from guests ...");
+    for incoming_stream in listener.incoming() {
+        let mut incoming_stream = match incoming_stream {
+            Err(e) => {
+                warn!("invalid incoming connection: {}", e);
+                continue;
+            }
+            Ok(s) => s,
+        };
+        std::thread::spawn(move || {
+            if let Err(e) = handle_tombstone(&mut incoming_stream) {
+                error!("Failed to write tombstone- {:?}", e);
+            }
+        });
+    }
+    Ok(())
+}
+
+fn handle_tombstone(stream: &mut VsockStream) -> Result<()> {
+    if let Ok(SockAddr::Vsock(addr)) = stream.peer_addr() {
+        info!("Vsock Stream connected to cid={} for tombstones", addr.cid());
+    }
+    let tb_connection =
+        TombstonedConnection::connect(std::process::id() as i32, DebuggerdDumpType::Tombstone)
+            .context("Failed to connect to tombstoned")?;
+    let mut text_output = tb_connection
+        .text_output
+        .as_ref()
+        .ok_or_else(|| anyhow!("Could not get file to write the tombstones on"))?;
+    let mut num_bytes_read = 0;
+    loop {
+        let mut chunk_recv = [0; CHUNK_RECV_MAX_LEN];
+        let n = stream
+            .read(&mut chunk_recv)
+            .context("Failed to read tombstone data from Vsock stream")?;
+        if n == 0 {
+            break;
+        }
+        num_bytes_read += n;
+        text_output.write_all(&chunk_recv[0..n]).context("Failed to write guests tombstones")?;
+    }
+    info!("Received {} bytes from guest & wrote to tombstone file", num_bytes_read);
+    tb_connection.notify_completion()?;
+    Ok(())
+}
+
 impl VirtualizationService {
     pub fn init() -> VirtualizationService {
         let service = VirtualizationService::default();
@@ -381,8 +320,15 @@
             handle_stream_connection_from_vm(state).unwrap();
         });
 
+        std::thread::spawn(|| {
+            if let Err(e) = handle_stream_connection_tombstoned() {
+                warn!("Error receiving tombstone from guest or writing them. Error: {}", e);
+            }
+        });
+
         // binder server for vm
-        let mut state = service.state.clone(); // reference to state (not the state itself) is copied
+        // reference to state (not the state itself) is copied
+        let mut state = service.state.clone();
         std::thread::spawn(move || {
             let state_ptr = &mut state as *mut _ as *mut raw::c_void;
 
@@ -407,11 +353,149 @@
         });
         service
     }
+
+    fn create_vm_internal(
+        &self,
+        config: &VirtualMachineConfig,
+        console_fd: Option<&ParcelFileDescriptor>,
+        log_fd: Option<&ParcelFileDescriptor>,
+        is_protected: &mut bool,
+    ) -> binder::Result<Strong<dyn IVirtualMachine>> {
+        check_manage_access()?;
+        let state = &mut *self.state.lock().unwrap();
+        let console_fd = console_fd.map(clone_file).transpose()?;
+        let log_fd = log_fd.map(clone_file).transpose()?;
+        let requester_uid = ThreadState::get_calling_uid();
+        let requester_sid = get_calling_sid()?;
+        let requester_debug_pid = ThreadState::get_calling_pid();
+        let cid = next_cid().or(Err(ExceptionCode::ILLEGAL_STATE))?;
+
+        // Counter to generate unique IDs for temporary image files.
+        let mut next_temporary_image_id = 0;
+        // Files which are referred to from composite images. These must be mapped to the crosvm
+        // child process, and not closed before it is started.
+        let mut indirect_files = vec![];
+
+        // Make directory for temporary files.
+        let temporary_directory: PathBuf = format!("{}/{}", TEMPORARY_DIRECTORY, cid).into();
+        create_dir(&temporary_directory).map_err(|e| {
+            // At this point, we do not know the protected status of Vm
+            // setting it to false, though this may not be correct.
+            error!(
+                "Failed to create temporary directory {:?} for VM files: {}",
+                temporary_directory, e
+            );
+            new_binder_exception(
+                ExceptionCode::SERVICE_SPECIFIC,
+                format!(
+                    "Failed to create temporary directory {:?} for VM files: {}",
+                    temporary_directory, e
+                ),
+            )
+        })?;
+
+        let is_app_config = matches!(config, VirtualMachineConfig::AppConfig(_));
+
+        let config = match config {
+            VirtualMachineConfig::AppConfig(config) => BorrowedOrOwned::Owned(
+                load_app_config(config, &temporary_directory).map_err(|e| {
+                    error!("Failed to load app config from {}: {}", &config.configPath, e);
+                    *is_protected = config.protectedVm;
+                    new_binder_exception(
+                        ExceptionCode::SERVICE_SPECIFIC,
+                        format!("Failed to load app config from {}: {}", &config.configPath, e),
+                    )
+                })?,
+            ),
+            VirtualMachineConfig::RawConfig(config) => BorrowedOrOwned::Borrowed(config),
+        };
+        let config = config.as_ref();
+        *is_protected = config.protectedVm;
+
+        // Check if partition images are labeled incorrectly. This is to prevent random images
+        // which are not protected by the Android Verified Boot (e.g. bits downloaded by apps) from
+        // being loaded in a pVM.  Specifically, for images in the raw config, nothing is allowed
+        // to be labeled as app_data_file. For images in the app config, nothing but the instance
+        // partition is allowed to be labeled as such.
+        config
+            .disks
+            .iter()
+            .flat_map(|disk| disk.partitions.iter())
+            .filter(|partition| {
+                if is_app_config {
+                    partition.label != "vm-instance"
+                } else {
+                    true // all partitions are checked
+                }
+            })
+            .try_for_each(check_label_for_partition)
+            .map_err(|e| new_binder_exception(ExceptionCode::SERVICE_SPECIFIC, e.to_string()))?;
+
+        let zero_filler_path = temporary_directory.join("zero.img");
+        write_zero_filler(&zero_filler_path).map_err(|e| {
+            error!("Failed to make composite image: {}", e);
+            new_binder_exception(
+                ExceptionCode::SERVICE_SPECIFIC,
+                format!("Failed to make composite image: {}", e),
+            )
+        })?;
+
+        // Assemble disk images if needed.
+        let disks = config
+            .disks
+            .iter()
+            .map(|disk| {
+                assemble_disk_image(
+                    disk,
+                    &zero_filler_path,
+                    &temporary_directory,
+                    &mut next_temporary_image_id,
+                    &mut indirect_files,
+                )
+            })
+            .collect::<Result<Vec<DiskFile>, _>>()?;
+
+        // Actually start the VM.
+        let crosvm_config = CrosvmConfig {
+            cid,
+            bootloader: maybe_clone_file(&config.bootloader)?,
+            kernel: maybe_clone_file(&config.kernel)?,
+            initrd: maybe_clone_file(&config.initrd)?,
+            disks,
+            params: config.params.to_owned(),
+            protected: *is_protected,
+            memory_mib: config.memoryMib.try_into().ok().and_then(NonZeroU32::new),
+            cpus: config.numCpus.try_into().ok().and_then(NonZeroU32::new),
+            cpu_affinity: config.cpuAffinity.clone(),
+            console_fd,
+            log_fd,
+            indirect_files,
+            platform_version: parse_platform_version_req(&config.platformVersion)?,
+        };
+        let instance = Arc::new(
+            VmInstance::new(
+                crosvm_config,
+                temporary_directory,
+                requester_uid,
+                requester_sid,
+                requester_debug_pid,
+            )
+            .map_err(|e| {
+                error!("Failed to create VM with config {:?}: {}", config, e);
+                new_binder_exception(
+                    ExceptionCode::SERVICE_SPECIFIC,
+                    format!("Failed to create VM: {}", e),
+                )
+            })?,
+        );
+        state.add_vm(Arc::downgrade(&instance));
+        Ok(VirtualMachine::create(instance))
+    }
 }
 
 /// Write the stats of VMCreation to statsd
-fn write_vm_creation_stats(protected: bool, success: bool) {
-    match stats_write(Hypervisor::Pkvm, protected, success) {
+fn write_vm_creation_stats(is_protected: bool, creation_succeeded: bool, exception_code: i32) {
+    match stats_write(Hypervisor::Pkvm, is_protected, creation_succeeded, exception_code) {
         Err(e) => {
             warn!("statslog_rust failed with error: {}", e);
         }
diff --git a/zipfuse/src/main.rs b/zipfuse/src/main.rs
index a91642c..c3fae69 100644
--- a/zipfuse/src/main.rs
+++ b/zipfuse/src/main.rs
@@ -82,7 +82,9 @@
         libc::MS_NOSUID | libc::MS_NODEV | libc::MS_RDONLY,
         &mount_options,
     )?;
-    Ok(fuse::worker::start_message_loop(dev_fuse, MAX_READ, MAX_WRITE, ZipFuse::new(zip_file)?)?)
+    let mut config = fuse::FuseConfig::new();
+    config.dev_fuse(dev_fuse).max_write(MAX_WRITE).max_read(MAX_READ);
+    Ok(config.enter_message_loop(ZipFuse::new(zip_file)?)?)
 }
 
 struct ZipFuse {