Merge changes I7ef5fa4a,Ife9ed8bb

* changes:
  Update kernel to builds 8751530
  Update kernel to builds 8751530
diff --git a/apex/Android.bp b/apex/Android.bp
index 4698088..923c378 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -3,12 +3,16 @@
 }
 
 microdroid_filesystem_images = [
-    "microdroid_super",
     "microdroid_boot",
+    "microdroid_bootconfig_app_debuggable",
+    "microdroid_bootconfig_full_debuggable",
+    "microdroid_bootconfig_normal",
     "microdroid_init_boot",
-    "microdroid_vendor_boot",
+    "microdroid_super",
+    "microdroid_uboot_env",
     "microdroid_vbmeta",
     "microdroid_vbmeta_bootconfig",
+    "microdroid_vendor_boot",
 ]
 
 apex {
@@ -60,12 +64,8 @@
     prebuilts: [
         "com.android.virt.init.rc",
         "microdroid.json",
-        "microdroid_uboot_env",
         "microdroid_bootloader",
         "microdroid_bootloader.avbpubkey",
-        "microdroid_bootconfig_normal",
-        "microdroid_bootconfig_app_debuggable",
-        "microdroid_bootconfig_full_debuggable",
     ],
     file_contexts: ":com.android.virt-file_contexts",
     canned_fs_config: "canned_fs_config",
diff --git a/apex/sign_virt_apex.py b/apex/sign_virt_apex.py
index e314b71..df95323 100644
--- a/apex/sign_virt_apex.py
+++ b/apex/sign_virt_apex.py
@@ -360,17 +360,17 @@
 # dict of (key, file) for re-sign/verification. keys are un-versioned for readability.
 virt_apex_files = {
     'bootloader.pubkey': 'etc/microdroid_bootloader.avbpubkey',
-    'bootloader': 'etc/microdroid_bootloader',
+    'bootloader': 'etc/fs/microdroid_bootloader',
     'boot.img': 'etc/fs/microdroid_boot.img',
     'vendor_boot.img': 'etc/fs/microdroid_vendor_boot.img',
     'init_boot.img': 'etc/fs/microdroid_init_boot.img',
     'super.img': 'etc/fs/microdroid_super.img',
     'vbmeta.img': 'etc/fs/microdroid_vbmeta.img',
     'vbmeta_bootconfig.img': 'etc/fs/microdroid_vbmeta_bootconfig.img',
-    'bootconfig.normal': 'etc/microdroid_bootconfig.normal',
-    'bootconfig.app_debuggable': 'etc/microdroid_bootconfig.app_debuggable',
-    'bootconfig.full_debuggable': 'etc/microdroid_bootconfig.full_debuggable',
-    'uboot_env.img': 'etc/uboot_env.img'
+    'bootconfig.normal': 'etc/fs/microdroid_bootconfig.normal',
+    'bootconfig.app_debuggable': 'etc/fs/microdroid_bootconfig.app_debuggable',
+    'bootconfig.full_debuggable': 'etc/fs/microdroid_bootconfig.full_debuggable',
+    'uboot_env.img': 'etc/fs/uboot_env.img'
 }
 
 
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index ed2c2a1..de44b63 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -439,6 +439,7 @@
                         }
                         @Override
                         public void onDied(int cid, int reason) {
+                            // TODO(b/236811123) translate `reason` into a stable reason numbers
                             service.asBinder().unlinkToDeath(deathRecipient, 0);
                             if (onDiedCalled.compareAndSet(false, true)) {
                                 executeCallback((cb) -> cb.onDied(VirtualMachine.this, reason));
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineCallback.java b/javalib/src/android/system/virtualmachine/VirtualMachineCallback.java
index a49c9be..54d0701 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineCallback.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineCallback.java
@@ -66,7 +66,8 @@
         DEATH_REASON_SHUTDOWN,
         DEATH_REASON_ERROR,
         DEATH_REASON_REBOOT,
-        DEATH_REASON_CRASH
+        DEATH_REASON_CRASH,
+        DEATH_REASON_HANGUP,
     })
     @interface DeathReason {}
 
@@ -97,6 +98,36 @@
     /** The VM or crosvm crashed. */
     int DEATH_REASON_CRASH = 6;
 
+    /** The pVM firmware failed to verify the VM because the public key doesn't match. */
+    int DEATH_REASON_PVM_FIRMWARE_PUBLIC_KEY_MISMATCH = 7;
+
+    /** The pVM firmware failed to verify the VM because the instance image changed. */
+    int DEATH_REASON_PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED = 8;
+
+    /** The bootloader failed to verify the VM because the public key doesn't match. */
+    int DEATH_REASON_BOOTLOADER_PUBLIC_KEY_MISMATCH = 9;
+
+    /** The bootloader failed to verify the VM because the instance image changed. */
+    int DEATH_REASON_BOOTLOADER_INSTANCE_IMAGE_CHANGED = 10;
+
+    /** The microdroid failed to connect to VirtualizationService's RPC server. */
+    int DEATH_REASON_MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE = 11;
+
+    /** The payload for microdroid is changed. */
+    int DEATH_REASON_MICRODROID_PAYLOAD_HAS_CHANGED = 12;
+
+    /** The microdroid failed to verify given payload APK. */
+    int DEATH_REASON_MICRODROID_PAYLOAD_VERIFICATION_FAILED = 13;
+
+    /** The VM config for microdroid is invalid (e.g. missing tasks). */
+    int DEATH_REASON_MICRODROID_INVALID_PAYLOAD_CONFIG = 14;
+
+    /** There was a runtime error while running microdroid manager. */
+    int DEATH_REASON_MICRODROID_UNKNOWN_RUNTIME_ERROR = 15;
+
+    /** The VM killed due to hangup */
+    int DEATH_REASON_HANGUP = 16;
+
     /** Called when the payload starts in the VM. */
     void onPayloadStarted(@NonNull VirtualMachine vm, @Nullable ParcelFileDescriptor stream);
 
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 7f240f6..ba3da08 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -167,6 +167,7 @@
         "grep ro\\.build\\.version\\.release= $(location :buildinfo.prop) && " +
         "grep ro\\.build\\.version\\.sdk= $(location :buildinfo.prop) && " +
         "grep ro\\.build\\.version\\.security_patch= $(location :buildinfo.prop) && " +
+        "grep ro\\.build\\.version\\.known_codenames= $(location :buildinfo.prop) && " +
         "cat $(location build.prop) && " +
         "echo ro.product.cpu.abilist=x86_64) > $(out)",
 }
@@ -183,6 +184,7 @@
         "grep ro\\.build\\.version\\.release= $(location :buildinfo.prop) && " +
         "grep ro\\.build\\.version\\.sdk= $(location :buildinfo.prop) && " +
         "grep ro\\.build\\.version\\.security_patch= $(location :buildinfo.prop) && " +
+        "grep ro\\.build\\.version\\.known_codenames= $(location :buildinfo.prop) && " +
         "cat $(location build.prop) && " +
         "echo ro.product.cpu.abilist=arm64-v8a) > $(out)",
 }
@@ -387,84 +389,34 @@
     ],
 }
 
-// See external/avb/avbtool.py
-// MAX_VBMETA_SIZE=64KB, MAX_FOOTER_SIZE=4KB
-avb_hash_footer_kb = "68"
-
-prebuilt_etc {
-    name: "microdroid_bootconfig_normal",
-    src: ":microdroid_bootconfig_normal_gen",
-    filename: "microdroid_bootconfig.normal",
-}
-
-prebuilt_etc {
-    name: "microdroid_bootconfig_app_debuggable",
-    src: ":microdroid_bootconfig_app_debuggable_gen",
-    filename: "microdroid_bootconfig.app_debuggable",
-}
-
-prebuilt_etc {
-    name: "microdroid_bootconfig_full_debuggable",
-    src: ":microdroid_bootconfig_full_debuggable_gen",
-    filename: "microdroid_bootconfig.full_debuggable",
-}
-
 // python -c "import hashlib; print(hashlib.sha256(b'bootconfig').hexdigest())"
 bootconfig_salt = "e158851fbebb402e1f18ea9372ea2f76b4dea23eceb5c4b92e5b27ade8537f5b"
 
-// TODO(jiyong): make a new module type that does the avb signing
-genrule {
-    name: "microdroid_bootconfig_normal_gen",
-    tools: ["avbtool"],
-    srcs: [
-        "bootconfig.normal",
-        ":microdroid_sign_key",
-    ],
-    out: ["microdroid_bootconfig.normal"],
-    cmd: "cp $(location bootconfig.normal) $(out) && " +
-        "$(location avbtool) add_hash_footer " +
-        "--algorithm SHA256_RSA4096 " +
-        "--salt " + bootconfig_salt + " " +
-        "--partition_name bootconfig " +
-        "--key $(location :microdroid_sign_key) " +
-        "--partition_size $$(( " + avb_hash_footer_kb + " * 1024 + ( $$(stat --format=%s $(out)) + 4096 - 1 ) / 4096 * 4096 )) " +
-        "--image $(out)",
+avb_add_hash_footer {
+    name: "microdroid_bootconfig_normal",
+    src: "bootconfig.normal",
+    filename: "microdroid_bootconfig.normal",
+    partition_name: "bootconfig",
+    private_key: ":microdroid_sign_key",
+    salt: bootconfig_salt,
 }
 
-genrule {
-    name: "microdroid_bootconfig_app_debuggable_gen",
-    tools: ["avbtool"],
-    srcs: [
-        "bootconfig.app_debuggable",
-        ":microdroid_sign_key",
-    ],
-    out: ["microdroid_bootconfig.app_debuggable"],
-    cmd: "cp $(location bootconfig.app_debuggable) $(out) && " +
-        "$(location avbtool) add_hash_footer " +
-        "--algorithm SHA256_RSA4096 " +
-        "--salt " + bootconfig_salt + " " +
-        "--partition_name bootconfig " +
-        "--key $(location :microdroid_sign_key) " +
-        "--partition_size $$(( " + avb_hash_footer_kb + " * 1024 + ( $$(stat --format=%s $(out)) + 4096 - 1 ) / 4096 * 4096 )) " +
-        "--image $(out)",
+avb_add_hash_footer {
+    name: "microdroid_bootconfig_app_debuggable",
+    src: "bootconfig.app_debuggable",
+    filename: "microdroid_bootconfig.app_debuggable",
+    partition_name: "bootconfig",
+    private_key: ":microdroid_sign_key",
+    salt: bootconfig_salt,
 }
 
-genrule {
-    name: "microdroid_bootconfig_full_debuggable_gen",
-    tools: ["avbtool"],
-    srcs: [
-        "bootconfig.full_debuggable",
-        ":microdroid_sign_key",
-    ],
-    out: ["microdroid_bootconfig.full_debuggable"],
-    cmd: "cp $(location bootconfig.full_debuggable) $(out) && " +
-        "$(location avbtool) add_hash_footer " +
-        "--algorithm SHA256_RSA4096 " +
-        "--salt " + bootconfig_salt + " " +
-        "--partition_name bootconfig " +
-        "--key $(location :microdroid_sign_key) " +
-        "--partition_size $$(( " + avb_hash_footer_kb + " * 1024 + ( $$(stat --format=%s $(out)) + 4096 - 1 ) / 4096 * 4096 )) " +
-        "--image $(out)",
+avb_add_hash_footer {
+    name: "microdroid_bootconfig_full_debuggable",
+    src: "bootconfig.full_debuggable",
+    filename: "microdroid_bootconfig.full_debuggable",
+    partition_name: "bootconfig",
+    private_key: ":microdroid_sign_key",
+    salt: bootconfig_salt,
 }
 
 prebuilt_etc {
@@ -476,7 +428,7 @@
 
 prebuilt_etc {
     name: "microdroid_bootloader",
-    src: ":microdroid_bootloader_gen",
+    src: ":microdroid_bootloader_signed",
     arch: {
         x86_64: {
             // For unknown reason, the signed bootloader doesn't work on x86_64. Until the problem
@@ -485,36 +437,20 @@
             src: ":microdroid_bootloader_pubkey_replaced",
         },
     },
+    relative_install_path: "fs",
     filename: "microdroid_bootloader",
 }
 
 // python -c "import hashlib; print(hashlib.sha256(b'bootloader').hexdigest())"
 bootloader_salt = "3b4a12881d11f33cff968a24d7c53723a8232cde9a8d91e29fdbd6a95ae6adf0"
 
-genrule {
-    name: "microdroid_bootloader_gen",
-    tools: ["avbtool"],
-    srcs: [
-        ":microdroid_bootloader_pubkey_replaced",
-        ":microdroid_sign_key",
-    ],
-    out: ["bootloader-signed"],
-    // 1. Copy the input to the output becaise avbtool modifies --image in
-    // place.
-    // 2. Check if the file is big enough. For arm and x86 we have fake
-    // bootloader file whose size is 1. It can't pass avbtool.
-    // 3. Add the hash footer. The partition size is set to (image size + 68KB)
-    // rounded up to 4KB boundary.
-    cmd: "cp $(location :microdroid_bootloader_pubkey_replaced) $(out) && " +
-        "if [ $$(stat --format=%s $(out)) -gt 4096 ]; then " +
-        "$(location avbtool) add_hash_footer " +
-        "--algorithm SHA256_RSA4096 " +
-        "--salt " + bootloader_salt + " " +
-        "--partition_name bootloader " +
-        "--key $(location :microdroid_sign_key) " +
-        "--partition_size $$(( " + avb_hash_footer_kb + " * 1024 + ( $$(stat --format=%s $(out)) + 4096 - 1 ) / 4096 * 4096 )) " +
-        "--image $(out)" +
-        "; fi",
+avb_add_hash_footer {
+    name: "microdroid_bootloader_signed",
+    src: ":microdroid_bootloader_pubkey_replaced",
+    filename: "microdroid_bootloader",
+    partition_name: "bootloader",
+    private_key: ":microdroid_sign_key",
+    salt: bootloader_salt,
 }
 
 // Replace avbpubkey of prebuilt bootloader with the avbpubkey of the signing key
@@ -528,15 +464,11 @@
     ],
     out: ["bootloader-pubkey-replaced"],
     // 1. Copy the input to the output (replace_bytes modifies the file in-place)
-    // 2. Check if the file is big enough. For arm and x86 we have fake
-    // bootloader file whose size is 1. (replace_bytes fails if key not found)
-    // 3. Replace embedded pubkey with new one.
+    // 2. Replace embedded pubkey with new one.
     cmd: "cp $(location :microdroid_crosvm_bootloader) $(out) && " +
-        "if [ $$(stat --format=%s $(out)) -gt 4096 ]; then " +
         "$(location replace_bytes) $(out) " +
         "$(location :microdroid_crosvm_bootloader.avbpubkey) " +
-        "$(location :microdroid_bootloader_avbpubkey_gen)" +
-        "; fi",
+        "$(location :microdroid_bootloader_avbpubkey_gen)",
 }
 
 // Apex keeps a copy of avbpubkey embedded in bootloader so that embedded avbpubkey can be replaced
@@ -557,34 +489,24 @@
         "--output $(out)",
 }
 
-prebuilt_etc {
-    name: "microdroid_uboot_env",
-    src: ":microdroid_uboot_env_gen",
-    filename: "uboot_env.img",
-}
-
 // python -c "import hashlib; print(hashlib.sha256(b'uboot_env').hexdigest())"
 uboot_env_salt = "cbf2d76827ece5ca8d176a40c94ac6355edcf6511b4b887364a8c0e05850df10"
 
+avb_add_hash_footer {
+    name: "microdroid_uboot_env",
+    src: ":microdroid_uboot_env_gen",
+    filename: "uboot_env.img",
+    partition_name: "uboot_env",
+    private_key: ":microdroid_sign_key",
+    salt: uboot_env_salt,
+}
+
 genrule {
     name: "microdroid_uboot_env_gen",
-    tools: [
-        "mkenvimage_slim",
-        "avbtool",
-    ],
-    srcs: [
-        "uboot-env.txt",
-        ":microdroid_sign_key",
-    ],
+    tools: ["mkenvimage_slim"],
+    srcs: ["uboot-env.txt"],
     out: ["output.img"],
-    cmd: "$(location mkenvimage_slim) -output_path $(out) -input_path $(location uboot-env.txt) && " +
-        "$(location avbtool) add_hash_footer " +
-        "--algorithm SHA256_RSA4096 " +
-        "--salt " + uboot_env_salt + " " +
-        "--partition_name uboot_env " +
-        "--key $(location :microdroid_sign_key) " +
-        "--partition_size $$(( " + avb_hash_footer_kb + " * 1024 + ( $$(stat --format=%s $(out)) + 4096 - 1 ) / 4096 * 4096 )) " +
-        "--image $(out)",
+    cmd: "$(location mkenvimage_slim) -output_path $(out) -input_path $(location uboot-env.txt)",
 }
 
 // Note that keys can be different for filesystem images even though we're using the same key
diff --git a/microdroid/README.md b/microdroid/README.md
index 681c23a..6e5d709 100644
--- a/microdroid/README.md
+++ b/microdroid/README.md
@@ -162,6 +162,8 @@
 ## ADB
 
 On userdebug builds, you can have an adb connection to microdroid. To do so,
+first, add the `--debug=full` flag to the `/apex/com.android.virt/bin/vm
+run-app` command, and then
 
 ```sh
 adb forward tcp:8000 vsock:$CID:5555
diff --git a/microdroid/microdroid.json b/microdroid/microdroid.json
index bf8d93e..f02dcbf 100644
--- a/microdroid/microdroid.json
+++ b/microdroid/microdroid.json
@@ -1,5 +1,5 @@
 {
-  "bootloader": "/apex/com.android.virt/etc/microdroid_bootloader",
+  "bootloader": "/apex/com.android.virt/etc/fs/microdroid_bootloader",
   "disks": [
     {
       "partitions": [
@@ -30,7 +30,7 @@
       "partitions": [
         {
           "label": "uboot_env",
-          "path": "/apex/com.android.virt/etc/uboot_env.img",
+          "path": "/apex/com.android.virt/etc/fs/uboot_env.img",
           "writable": false
         }
       ],
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index 8343691..d1afc14 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -34,6 +34,7 @@
         "libopenssl",
         "libprotobuf",
         "librustutils",
+        "libscopeguard",
         "libserde",
         "libserde_cbor",
         "libserde_json",
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 998b94b..15d6663 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -155,17 +155,21 @@
     }
 }
 
-fn main() {
-    if let Err(e) = try_main() {
-        error!("Failed with {:?}. Shutting down...", e);
-        if let Err(e) = write_death_reason_to_serial(&e) {
-            error!("Failed to write death reason {:?}", e);
-        }
+fn main() -> Result<()> {
+    scopeguard::defer! {
+        info!("Shutting down...");
         if let Err(e) = system_properties::write("sys.powerctl", "shutdown") {
             error!("failed to shutdown {:?}", e);
         }
-        std::process::exit(1);
     }
+
+    try_main().map_err(|e| {
+        error!("Failed with {:?}.", e);
+        if let Err(e) = write_death_reason_to_serial(&e) {
+            error!("Failed to write death reason {:?}", e);
+        }
+        e
+    })
 }
 
 fn try_main() -> Result<()> {
diff --git a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
index ec2afaa..bb27913 100644
--- a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
@@ -254,7 +254,7 @@
 
         // Add partitions to the second disk
         final String vbmetaPath = TEST_ROOT + "etc/fs/microdroid_vbmeta_bootconfig.img";
-        final String bootconfigPath = TEST_ROOT + "etc/microdroid_bootconfig.full_debuggable";
+        final String bootconfigPath = TEST_ROOT + "etc/fs/microdroid_bootconfig.full_debuggable";
         disks.getJSONObject(1).getJSONArray("partitions")
                 .put(newPartition("vbmeta", vbmetaPath))
                 .put(newPartition("bootconfig", bootconfigPath))
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 7a8da96..791da24 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -31,6 +31,7 @@
         "libcommand_fds",
         "libdisk",
         "libidsig",
+        "liblazy_static",
         "liblog_rust",
         "libmicrodroid_metadata",
         "libmicrodroid_payload_config",
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/DeathReason.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/DeathReason.aidl
index 577e868..dceabf1 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/DeathReason.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/DeathReason.aidl
@@ -52,4 +52,6 @@
     MICRODROID_INVALID_PAYLOAD_CONFIG = 14,
     /** There was a runtime error while running microdroid manager. */
     MICRODROID_UNKNOWN_RUNTIME_ERROR = 15,
+    /** The VM killed due to hangup */
+    HANGUP = 16,
 }
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index f0b3fb4..24f3706 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -479,6 +479,7 @@
             log_fd,
             indirect_files,
             platform_version: parse_platform_version_req(&config.platformVersion)?,
+            detect_hangup: is_app_config,
         };
         let instance = Arc::new(
             VmInstance::new(
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index fcc09c6..7ca802f 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -18,10 +18,13 @@
 use crate::Cid;
 use anyhow::{bail, Error};
 use command_fds::CommandFdExt;
+use lazy_static::lazy_static;
 use log::{debug, error, info};
 use semver::{Version, VersionReq};
 use nix::{fcntl::OFlag, unistd::pipe2};
+use rustutils::system_properties;
 use shared_child::SharedChild;
+use std::borrow::Cow;
 use std::fs::{remove_dir_all, File};
 use std::io::{self, Read};
 use std::mem;
@@ -29,7 +32,8 @@
 use std::os::unix::io::{AsRawFd, RawFd, FromRawFd};
 use std::path::PathBuf;
 use std::process::{Command, ExitStatus};
-use std::sync::{Arc, Mutex};
+use std::sync::{Arc, Condvar, Mutex};
+use std::time::Duration;
 use std::thread;
 use vsock::VsockStream;
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::DeathReason::DeathReason;
@@ -51,6 +55,25 @@
 /// The exit status which crosvm returns when it crashes due to an error.
 const CROSVM_CRASH_STATUS: i32 = 33;
 
+fn is_nested_virtualization() -> bool {
+    //  Check if we are running on vsoc as a proxy for this.
+    matches!(
+        system_properties::read("ro.build.product").unwrap().as_deref(),
+        Some("vsoc_x86_64") | Some("vsoc_x86")
+    )
+}
+
+lazy_static! {
+    /// If the VM doesn't move to the Started state within this amount time, a hang-up error is
+    /// triggered.
+    static ref BOOT_HANGUP_TIMEOUT: Duration = if is_nested_virtualization() {
+        // Nested virtualization is slow, so we need a longer timeout.
+        Duration::from_secs(100)
+    } else {
+        Duration::from_secs(10)
+    };
+}
+
 /// Configuration for a VM to run with crosvm.
 #[derive(Debug)]
 pub struct CrosvmConfig {
@@ -69,6 +92,7 @@
     pub log_fd: Option<File>,
     pub indirect_files: Vec<File>,
     pub platform_version: VersionReq,
+    pub detect_hangup: bool,
 }
 
 /// A disk image to pass to crosvm for a VM.
@@ -116,6 +140,7 @@
     fn start(&mut self, instance: Arc<VmInstance>) -> Result<(), Error> {
         let state = mem::replace(self, VmState::Failed);
         if let VmState::NotStarted { config } = state {
+            let detect_hangup = config.detect_hangup;
             let (failure_pipe_read, failure_pipe_write) = create_pipe()?;
 
             // If this fails and returns an error, `self` will be left in the `Failed` state.
@@ -123,7 +148,7 @@
 
             let child_clone = child.clone();
             thread::spawn(move || {
-                instance.monitor(child_clone, failure_pipe_read);
+                instance.monitor(child_clone, failure_pipe_read, detect_hangup);
             });
 
             // If it started correctly, update the state.
@@ -162,6 +187,8 @@
     pub vm_service: Mutex<Option<Strong<dyn IVirtualMachineService>>>,
     /// The latest lifecycle state which the payload reported itself to be in.
     payload_state: Mutex<PayloadState>,
+    /// Represents the condition that payload_state becomes Started
+    payload_started: Condvar,
 }
 
 impl VmInstance {
@@ -188,6 +215,7 @@
             stream: Mutex::new(None),
             vm_service: Mutex::new(None),
             payload_state: Mutex::new(PayloadState::Starting),
+            payload_started: Condvar::new(),
         })
     }
 
@@ -198,11 +226,38 @@
     }
 
     /// Waits for the crosvm child process to finish, then marks the VM as no longer running and
-    /// calls any callbacks.
+    /// calls any callbacks. If `detect_hangup` is optionally set to true, waits for the start of
+    /// payload in the crosvm process. If that doesn't occur within a BOOT_HANGUP_TIMEOUT, declare
+    /// it as a hangup and forcibly kill the process.
     ///
     /// This takes a separate reference to the `SharedChild` rather than using the one in
     /// `self.vm_state` to avoid holding the lock on `vm_state` while it is running.
-    fn monitor(&self, child: Arc<SharedChild>, mut failure_pipe_read: File) {
+    fn monitor(&self, child: Arc<SharedChild>, mut failure_pipe_read: File, detect_hangup: bool) {
+        let hungup = if detect_hangup {
+            // Wait until payload is started or the crosvm process terminates. The checking of the
+            // child process is needed because otherwise we will be waiting for a condition that
+            // will never be satisfied (because crosvm is the one who can make the condition true).
+            let state = self.payload_state.lock().unwrap();
+            let (_, result) = self
+                .payload_started
+                .wait_timeout_while(state, *BOOT_HANGUP_TIMEOUT, |state| {
+                    *state < PayloadState::Started && child.try_wait().is_ok()
+                })
+                .unwrap();
+            if result.timed_out() {
+                error!(
+                    "Microdroid failed to start payload within {} secs timeout. Shutting down",
+                    BOOT_HANGUP_TIMEOUT.as_secs()
+                );
+                self.kill();
+                true
+            } else {
+                false
+            }
+        } else {
+            false
+        };
+
         let result = child.wait();
         match &result {
             Err(e) => error!("Error waiting for crosvm({}) instance to die: {}", child.id(), e),
@@ -214,14 +269,17 @@
         // Ensure that the mutex is released before calling the callbacks.
         drop(vm_state);
 
-        let mut failure_string = String::new();
-        let failure_read_result = failure_pipe_read.read_to_string(&mut failure_string);
-        if let Err(e) = &failure_read_result {
-            error!("Error reading VM failure reason from pipe: {}", e);
-        }
-        if !failure_string.is_empty() {
-            info!("VM returned failure reason '{}'", failure_string);
-        }
+        let failure_string = if hungup {
+            Cow::from("HANGUP")
+        } else {
+            let mut s = String::new();
+            match failure_pipe_read.read_to_string(&mut s) {
+                Err(e) => error!("Error reading VM failure reason from pipe: {}", e),
+                Ok(len) if len > 0 => info!("VM returned failure reason '{}'", &s),
+                _ => (),
+            };
+            Cow::from(s)
+        };
 
         self.callbacks.callback_on_died(self.cid, death_reason(&result, &failure_string));
 
@@ -243,6 +301,9 @@
         // the other direction.
         if new_state > *state_locked {
             *state_locked = new_state;
+            if new_state >= PayloadState::Started {
+                self.payload_started.notify_all();
+            }
             Ok(())
         } else {
             bail!("Invalid payload state transition from {:?} to {:?}", *state_locked, new_state)
@@ -289,6 +350,7 @@
             "MICRODROID_UNKNOWN_RUNTIME_ERROR" => {
                 return DeathReason::MICRODROID_UNKNOWN_RUNTIME_ERROR
             }
+            "HANGUP" => return DeathReason::HANGUP,
             _ => {}
         }
         match status.code() {
diff --git a/virtualizationservice/src/payload.rs b/virtualizationservice/src/payload.rs
index 7b8cb7f..8378df3 100644
--- a/virtualizationservice/src/payload.rs
+++ b/virtualizationservice/src/payload.rs
@@ -365,7 +365,7 @@
         )?),
         writable: false,
     });
-    let bootconfig_image = "/apex/com.android.virt/etc/microdroid_bootconfig.".to_owned()
+    let bootconfig_image = "/apex/com.android.virt/etc/fs/microdroid_bootconfig.".to_owned()
         + match config.debugLevel {
             DebugLevel::NONE => "normal",
             DebugLevel::APP_ONLY => "app_debuggable",
diff --git a/vmclient/src/death_reason.rs b/vmclient/src/death_reason.rs
index 657eaa2..b976f6f 100644
--- a/vmclient/src/death_reason.rs
+++ b/vmclient/src/death_reason.rs
@@ -44,6 +44,18 @@
     BootloaderPublicKeyMismatch,
     /// The bootloader failed to verify the VM because the instance image changed.
     BootloaderInstanceImageChanged,
+    /// The microdroid failed to connect to VirtualizationService's RPC server.
+    MicrodroidFailedToConnectToVirtualizationService,
+    /// The payload for microdroid is changed.
+    MicrodroidPayloadHasChanged,
+    /// The microdroid failed to verify given payload APK.
+    MicrodroidPayloadVerificationFailed,
+    /// The VM config for microdroid is invalid (e.g. missing tasks).
+    MicrodroidInvalidPayloadConfig,
+    /// There was a runtime error while running microdroid manager.
+    MicrodroidUnknownRuntimeError,
+    /// The VM was killed due to hangup.
+    Hangup,
     /// VirtualizationService sent a death reason which was not recognised by the client library.
     Unrecognised(AidlDeathReason),
 }
@@ -66,6 +78,20 @@
             AidlDeathReason::BOOTLOADER_INSTANCE_IMAGE_CHANGED => {
                 Self::BootloaderInstanceImageChanged
             }
+            AidlDeathReason::MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE => {
+                Self::MicrodroidFailedToConnectToVirtualizationService
+            }
+            AidlDeathReason::MICRODROID_PAYLOAD_HAS_CHANGED => Self::MicrodroidPayloadHasChanged,
+            AidlDeathReason::MICRODROID_PAYLOAD_VERIFICATION_FAILED => {
+                Self::MicrodroidPayloadVerificationFailed
+            }
+            AidlDeathReason::MICRODROID_INVALID_PAYLOAD_CONFIG => {
+                Self::MicrodroidInvalidPayloadConfig
+            }
+            AidlDeathReason::MICRODROID_UNKNOWN_RUNTIME_ERROR => {
+                Self::MicrodroidUnknownRuntimeError
+            }
+            AidlDeathReason::HANGUP => Self::Hangup,
             _ => Self::Unrecognised(reason),
         }
     }
@@ -94,6 +120,20 @@
             Self::BootloaderInstanceImageChanged => {
                 "Bootloader failed to verify the VM because the instance image changed."
             }
+            Self::MicrodroidFailedToConnectToVirtualizationService => {
+                "The microdroid failed to connect to VirtualizationService's RPC server."
+            }
+            Self::MicrodroidPayloadHasChanged => "The payload for microdroid is changed.",
+            Self::MicrodroidPayloadVerificationFailed => {
+                "The microdroid failed to verify given payload APK."
+            }
+            Self::MicrodroidInvalidPayloadConfig => {
+                "The VM config for microdroid is invalid (e.g. missing tasks)."
+            }
+            Self::MicrodroidUnknownRuntimeError => {
+                "There was a runtime error while running microdroid manager."
+            }
+            Self::Hangup => "VM hangup.",
             Self::Unrecognised(reason) => {
                 return write!(f, "Unrecognised death reason {:?}.", reason);
             }