Merge "[KM-VM] Add arm64 KM VM kernel targets" into main
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java
index 1abba85..83c6b4c 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java
@@ -29,11 +29,13 @@
 import android.os.RemoteException;
 import android.text.format.Formatter;
 import android.util.Log;
+import android.view.KeyEvent;
 import android.view.View;
 import android.widget.CheckBox;
 import android.widget.TextView;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.virtualization.vmlauncher.InstallUtils;
 
 import com.google.android.material.progressindicator.LinearProgressIndicator;
 import com.google.android.material.snackbar.Snackbar;
@@ -45,7 +47,6 @@
     private static final String TAG = "LinuxInstaller";
 
     private static final long ESTIMATED_IMG_SIZE_BYTES = FileUtils.parseSize("350MB");
-    static final String EXTRA_AUTO_DOWNLOAD = "auto_download";
 
     private ExecutorService mExecutorService;
     private CheckBox mWaitForWifiCheckbox;
@@ -80,17 +81,25 @@
                     requestInstall();
                 });
 
-        if (getIntent().getBooleanExtra(EXTRA_AUTO_DOWNLOAD, false)) {
-            Log.i(TAG, "Auto downloading");
-            requestInstall();
-        }
-
         Intent intent = new Intent(this, InstallerService.class);
         mInstallerServiceConnection = new InstallerServiceConnection(this);
         if (!bindService(intent, mInstallerServiceConnection, Context.BIND_AUTO_CREATE)) {
             handleCriticalError(new Exception("Failed to connect to installer service"));
         }
+    }
 
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        if (Build.isDebuggable() && InstallUtils.payloadFromExternalStorageExists()) {
+            Snackbar.make(
+                            findViewById(android.R.id.content),
+                            "Auto installing",
+                            Snackbar.LENGTH_LONG)
+                    .show();
+            requestInstall();
+        }
     }
 
     @Override
@@ -103,6 +112,15 @@
         super.onDestroy();
     }
 
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_BUTTON_START) {
+            requestInstall();
+            return true;
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+
     @VisibleForTesting
     public boolean waitForInstallCompleted(long timeoutMillis) {
         return mInstallCompleted.block(timeoutMillis);
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
index b785416..f97f16f 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
@@ -17,8 +17,6 @@
 package com.android.virtualization.terminal;
 
 import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
 import android.content.Intent;
@@ -122,16 +120,21 @@
     }
 
     private void requestInstall() {
-        Log.i(TAG, "Installing..");
+        synchronized (mLock) {
+            if (mIsInstalling) {
+                Log.i(TAG, "already installing..");
+                return;
+            } else {
+                Log.i(TAG, "installing..");
+                mIsInstalling = true;
+            }
+        }
 
         // Make service to be long running, even after unbind() when InstallerActivity is destroyed
         // The service will still be destroyed if task is remove.
         startService(new Intent(this, InstallerService.class));
         startForeground(
                 NOTIFICATION_ID, mNotification, ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE);
-        synchronized (mLock) {
-            mIsInstalling = true;
-        }
 
         mExecutorService.execute(
                 () -> {
diff --git a/android/virtmgr/src/crosvm.rs b/android/virtmgr/src/crosvm.rs
index 94379a9..b0944fc 100644
--- a/android/virtmgr/src/crosvm.rs
+++ b/android/virtmgr/src/crosvm.rs
@@ -475,10 +475,23 @@
     fn monitor_vm_exit(
         &self,
         child: Arc<SharedChild>,
-        mut failure_pipe_read: File,
+        failure_pipe_read: File,
         vfio_devices: Vec<VfioDevice>,
         tap: Option<File>,
     ) {
+        let failure_reason_thread = std::thread::spawn(move || {
+            // Read the pipe to see if any failure reason is written
+            let mut failure_reason = String::new();
+            // Arbitrary max size in case of misbehaving guest.
+            const MAX_SIZE: u64 = 50_000;
+            match failure_pipe_read.take(MAX_SIZE).read_to_string(&mut failure_reason) {
+                Err(e) => error!("Error reading VM failure reason from pipe: {}", e),
+                Ok(len) if len > 0 => error!("VM returned failure reason '{}'", &failure_reason),
+                _ => (),
+            };
+            failure_reason
+        });
+
         let result = child.wait();
         match &result {
             Err(e) => error!("Error waiting for crosvm({}) instance to die: {}", child.id(), e),
@@ -492,20 +505,14 @@
             }
         }
 
+        let failure_reason = failure_reason_thread.join().expect("failure_reason_thread panic'd");
+
         let mut vm_state = self.vm_state.lock().unwrap();
         *vm_state = VmState::Dead;
         // Ensure that the mutex is released before calling the callbacks.
         drop(vm_state);
         info!("{} exited", &self);
 
-        // Read the pipe to see if any failure reason is written
-        let mut failure_reason = String::new();
-        match failure_pipe_read.read_to_string(&mut failure_reason) {
-            Err(e) => error!("Error reading VM failure reason from pipe: {}", e),
-            Ok(len) if len > 0 => info!("VM returned failure reason '{}'", &failure_reason),
-            _ => (),
-        };
-
         // In case of hangup, the pipe doesn't give us any information because the hangup can't be
         // detected on the VM side (otherwise, it isn't a hangup), but in the
         // monitor_payload_hangup function below which updates the payload state to Hangup.
diff --git a/build/debian/kokoro/gcp_ubuntu_docker/aarch64/build.sh b/build/debian/kokoro/gcp_ubuntu_docker/aarch64/build.sh
index 7a1523a..130e691 100644
--- a/build/debian/kokoro/gcp_ubuntu_docker/aarch64/build.sh
+++ b/build/debian/kokoro/gcp_ubuntu_docker/aarch64/build.sh
@@ -5,8 +5,7 @@
 cd "${KOKORO_ARTIFACTS_DIR}/git/avf/build/debian/"
 sudo losetup -D
 grep vmx /proc/cpuinfo || true
-sudo ./build.sh
+sudo ./build.sh -r
 sudo mv images.tar.gz ${KOKORO_ARTIFACTS_DIR} || true
-
 mkdir -p ${KOKORO_ARTIFACTS_DIR}/logs
 sudo cp -r /var/log/fai/* ${KOKORO_ARTIFACTS_DIR}/logs || true
diff --git a/build/debian/kokoro/gcp_ubuntu_docker/x86_64/build.sh b/build/debian/kokoro/gcp_ubuntu_docker/x86_64/build.sh
index 66e3d64..50ded7b 100644
--- a/build/debian/kokoro/gcp_ubuntu_docker/x86_64/build.sh
+++ b/build/debian/kokoro/gcp_ubuntu_docker/x86_64/build.sh
@@ -5,7 +5,7 @@
 cd "${KOKORO_ARTIFACTS_DIR}/git/avf/build/debian/"
 sudo losetup -D
 grep vmx /proc/cpuinfo || true
-sudo ./build.sh -a x86_64
+sudo ./build.sh -a x86_64 -r
 sudo mv images.tar.gz ${KOKORO_ARTIFACTS_DIR} || true
 
 mkdir -p ${KOKORO_ARTIFACTS_DIR}/logs
diff --git a/guest/microdroid_manager/src/main.rs b/guest/microdroid_manager/src/main.rs
index fa089fa..4a6887e 100644
--- a/guest/microdroid_manager/src/main.rs
+++ b/guest/microdroid_manager/src/main.rs
@@ -140,10 +140,10 @@
         Owned(format!("MICRODROID_UNKNOWN_RUNTIME_ERROR|{:?}", err))
     };
 
-    for chunk in death_reason.as_bytes().chunks(16) {
-        // TODO(b/220071963): Sometimes, sending more than 16 bytes at once makes MM hang.
-        OpenOptions::new().read(false).write(true).open(FAILURE_SERIAL_DEVICE)?.write_all(chunk)?;
-    }
+    let mut serial_file = OpenOptions::new().read(false).write(true).open(FAILURE_SERIAL_DEVICE)?;
+    serial_file.write_all(death_reason.as_bytes()).context("serial device write_all failed")?;
+    // Block until the serial port trasmits all the data to the host.
+    nix::sys::termios::tcdrain(&serial_file).context("tcdrain failed")?;
 
     Ok(())
 }
@@ -669,7 +669,9 @@
         });
     }
 
-    command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
+    if !is_debuggable()? {
+        command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
+    }
 
     info!("notifying payload started");
     service.notifyPayloadStarted()?;
diff --git a/libs/dice/TEST_MAPPING b/libs/dice/TEST_MAPPING
index 2045ba5..a43d7a2 100644
--- a/libs/dice/TEST_MAPPING
+++ b/libs/dice/TEST_MAPPING
@@ -1,16 +1,18 @@
 {
   "postsubmit": [
     {
-      "name": "libdiced_open_dice.integration_test"
-    },
-    {
-      "name": "libdiced_open_dice_nostd.integration_test"
-    },
-    {
       "name": "libopen_dice_cbor_bindgen_test"
     },
     {
       "name": "libopen_dice_android_bindgen_test"
+    }
+  ],
+  "presubmit": [
+    {
+      "name": "libdiced_open_dice.integration_test"
+    },
+    {
+      "name": "libdiced_open_dice_nostd.integration_test"
     },
     {
       "name": "libdiced_sample_inputs.integration_test"
diff --git a/tests/Terminal/src/com/android/virtualization/terminal/TerminalAppTest.java b/tests/Terminal/src/com/android/virtualization/terminal/TerminalAppTest.java
index 6400438..fd07973 100644
--- a/tests/Terminal/src/com/android/virtualization/terminal/TerminalAppTest.java
+++ b/tests/Terminal/src/com/android/virtualization/terminal/TerminalAppTest.java
@@ -56,7 +56,6 @@
 
         Intent intent = new Intent(mTargetContext, InstallerActivity.class);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        intent.putExtra(InstallerActivity.EXTRA_AUTO_DOWNLOAD, true);
 
         if (mInstr.startActivitySync(intent) instanceof InstallerActivity activity) {
             assertTrue(