Merge changes I6ab7f3c2,I1f905379

* changes:
  Use the unused /dev/hvc1 as the ramdump sink
  VirtualizationService notifies to its client when ramdump is available
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index 1fd939d..23cd505 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -235,6 +235,11 @@
         log::warn!("VM error, cid = {}, error code = {}, message = {}", cid, error_code, message,);
         Ok(())
     }
+
+    fn onRamdump(&self, _cid: i32, _ramdump: &ParcelFileDescriptor) -> BinderResult<()> {
+        // TODO(b/238295267) send this to tombstone?
+        Ok(())
+    }
 }
 
 fn start_logging(pfd: &ParcelFileDescriptor) -> Result<()> {
diff --git a/demo/java/com/android/microdroid/demo/MainActivity.java b/demo/java/com/android/microdroid/demo/MainActivity.java
index e53f95d..747e98c 100644
--- a/demo/java/com/android/microdroid/demo/MainActivity.java
+++ b/demo/java/com/android/microdroid/demo/MainActivity.java
@@ -278,6 +278,13 @@
                             mService.shutdownNow();
                             mStatus.postValue(VirtualMachine.Status.STOPPED);
                         }
+
+                        @Override
+                        public void onRamdump(VirtualMachine vm, ParcelFileDescriptor ramdump) {
+                            if (!mService.isShutdown()) {
+                                mPayloadOutput.postValue("(Kernel panic. Ramdump created)");
+                            }
+                        }
                     };
 
             try {
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index de44b63..8d74f5e 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -445,6 +445,11 @@
                                 executeCallback((cb) -> cb.onDied(VirtualMachine.this, reason));
                             }
                         }
+                        @Override
+                        public void onRamdump(int cid, ParcelFileDescriptor ramdump) {
+                            executeCallback(
+                                    (cb) -> cb.onRamdump(VirtualMachine.this, ramdump));
+                        }
                     }
             );
             service.asBinder().linkToDeath(deathRecipient, 0);
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineCallback.java b/javalib/src/android/system/virtualmachine/VirtualMachineCallback.java
index 54d0701..a37c15b 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineCallback.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineCallback.java
@@ -142,4 +142,7 @@
 
     /** Called when the VM died. */
     void onDied(@NonNull VirtualMachine vm, @DeathReason int reason);
+
+    /** Called when kernel panic occurs and as a result ramdump is generated from the VM. */
+    void onRamdump(@NonNull VirtualMachine vm, @NonNull ParcelFileDescriptor ramdump);
 }
diff --git a/microdroid/kdump/crashdump.c b/microdroid/kdump/crashdump.c
index a606d43..47d359a 100644
--- a/microdroid/kdump/crashdump.c
+++ b/microdroid/kdump/crashdump.c
@@ -28,7 +28,7 @@
 #include <unistd.h>
 
 #define DUMP_SOURCE "/proc/vmcore"
-#define DUMP_TARGET "/dev/hvc3" // See virtualizationserice/crosvm.rs
+#define DUMP_TARGET "/dev/hvc1" // See virtualizationserice/crosvm.rs
 #define BUF_SIZE 4096
 
 #define FAIL(format, ...)                                                \
diff --git a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
index 5999af7..864d2d5 100644
--- a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
+++ b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
@@ -201,6 +201,9 @@
         public void onDied(VirtualMachine vm, @DeathReason int reason) {
             mExecutorService.shutdown();
         }
+
+        @Override
+        public void onRamdump(VirtualMachine vm, ParcelFileDescriptor ramdump) {}
     }
 
     private static class BootResult {
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 1f0e107..3a874c4 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -204,6 +204,9 @@
         public void onDied(VirtualMachine vm, @DeathReason int reason) {
             mExecutorService.shutdown();
         }
+
+        @Override
+        public void onRamdump(VirtualMachine vm, ParcelFileDescriptor ramdump) {}
     }
 
     private static final int MIN_MEM_ARM64 = 150;
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachineCallback.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachineCallback.aidl
index 12a056c..6c8eb4a 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachineCallback.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachineCallback.aidl
@@ -53,4 +53,9 @@
      * also use `link_to_death` to handle that.
      */
     void onDied(int cid, in DeathReason reason);
+
+    /**
+     * Called when kernel panic occurs and as a result ramdump is generated from the VM.
+     */
+    void onRamdump(int cid, in ParcelFileDescriptor ramdump);
 }
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index d8f0b2e..f925394 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -464,6 +464,19 @@
             })
             .collect::<Result<Vec<DiskFile>, _>>()?;
 
+        // Creating this ramdump file unconditionally is not harmful as ramdump will be created
+        // only when the VM is configured as such. `ramdump_write` is sent to crosvm and will
+        // be the backing store for the /dev/hvc1 where VM will emit ramdump to. `ramdump_read`
+        // will be sent back to the client (i.e. the VM owner) for readout.
+        let ramdump_path = temporary_directory.join("ramdump");
+        let ramdump = prepare_ramdump_file(&ramdump_path).map_err(|e| {
+            error!("Failed to prepare ramdump file: {}", e);
+            new_binder_exception(
+                ExceptionCode::SERVICE_SPECIFIC,
+                format!("Failed to prepare ramdump file: {}", e),
+            )
+        })?;
+
         // Actually start the VM.
         let crosvm_config = CrosvmConfig {
             cid,
@@ -479,6 +492,7 @@
             task_profiles: config.taskProfiles.clone(),
             console_fd,
             log_fd,
+            ramdump: Some(ramdump),
             indirect_files,
             platform_version: parse_platform_version_req(&config.platformVersion)?,
             detect_hangup: is_app_config,
@@ -558,6 +572,11 @@
     part.flush()
 }
 
+fn prepare_ramdump_file(ramdump_path: &Path) -> Result<File> {
+    File::create(&ramdump_path)
+        .context(format!("Failed to create ramdump file {:?}", &ramdump_path))
+}
+
 /// Given the configuration for a disk image, assembles the `DiskFile` to pass to crosvm.
 ///
 /// This may involve assembling a composite disk from a set of partition images.
@@ -883,6 +902,17 @@
         }
     }
 
+    /// Call all registered callbacks to say that there was a ramdump to download.
+    pub fn callback_on_ramdump(&self, cid: Cid, ramdump: File) {
+        let callbacks = &*self.0.lock().unwrap();
+        let pfd = ParcelFileDescriptor::new(ramdump);
+        for callback in callbacks {
+            if let Err(e) = callback.onRamdump(cid as i32, &pfd) {
+                error!("Error notifying ramdump of VM CID {}: {}", cid, e);
+            }
+        }
+    }
+
     /// Add a new callback to the set.
     fn add(&self, callback: Strong<dyn IVirtualMachineCallback>) {
         self.0.lock().unwrap().push(callback);
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index 23719a7..46ad6b3 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -16,7 +16,7 @@
 
 use crate::aidl::VirtualMachineCallbacks;
 use crate::Cid;
-use anyhow::{bail, Error};
+use anyhow::{bail, Context, Error};
 use command_fds::CommandFdExt;
 use lazy_static::lazy_static;
 use log::{debug, error, info};
@@ -81,6 +81,7 @@
     pub task_profiles: Vec<String>,
     pub console_fd: Option<File>,
     pub log_fd: Option<File>,
+    pub ramdump: Option<File>,
     pub indirect_files: Vec<File>,
     pub platform_version: VersionReq,
     pub detect_hangup: bool,
@@ -272,6 +273,7 @@
             Cow::from(s)
         };
 
+        self.handle_ramdump().unwrap_or_else(|e| error!("Error handling ramdump: {}", e));
         self.callbacks.callback_on_died(self.cid, death_reason(&result, &failure_string));
 
         // Delete temporary files.
@@ -313,6 +315,18 @@
             }
         }
     }
+
+    /// Checks if ramdump has been created. If so, send a notification to the user with the handle
+    /// to read the ramdump.
+    fn handle_ramdump(&self) -> Result<(), Error> {
+        let ramdump_path = self.temporary_directory.join("ramdump");
+        if std::fs::metadata(&ramdump_path)?.len() > 0 {
+            let ramdump = File::open(&ramdump_path)
+                .context(format!("Failed to open ramdump {:?} for reading", &ramdump_path))?;
+            self.callbacks.callback_on_ramdump(self.cid, ramdump);
+        }
+        Ok(())
+    }
 }
 
 fn death_reason(result: &Result<ExitStatus, io::Error>, failure_reason: &str) -> DeathReason {
@@ -404,7 +418,7 @@
     // 1. uart device: used as the output device by bootloaders and as early console by linux
     // 2. uart device: used to report the reason for the VM failing.
     // 3. virtio-console device: used as the console device where kmsg is redirected to
-    // 4. virtio-console device: used as the androidboot.console device (not used currently)
+    // 4. virtio-console device: used as the ramdump output
     // 5. virtio-console device: used as the logcat output
     //
     // When [console|log]_fd is not specified, the devices are attached to sink, which means what's
@@ -412,6 +426,7 @@
     let console_arg = format_serial_arg(&mut preserved_fds, &config.console_fd);
     let log_arg = format_serial_arg(&mut preserved_fds, &config.log_fd);
     let failure_serial_path = add_preserved_fd(&mut preserved_fds, &failure_pipe_write);
+    let ramdump_arg = format_serial_arg(&mut preserved_fds, &config.ramdump);
 
     // Warning: Adding more serial devices requires you to shift the PCI device ID of the boot
     // disks in bootconfig.x86_64. This is because x86 crosvm puts serial devices and the block
@@ -423,8 +438,8 @@
     command.arg(format!("--serial=type=file,path={},hardware=serial,num=2", &failure_serial_path));
     // /dev/hvc0
     command.arg(format!("--serial={},hardware=virtio-console,num=1", &console_arg));
-    // /dev/hvc1 (not used currently)
-    command.arg("--serial=type=sink,hardware=virtio-console,num=2");
+    // /dev/hvc1
+    command.arg(format!("--serial={},hardware=virtio-console,num=2", &ramdump_arg));
     // /dev/hvc2
     command.arg(format!("--serial={},hardware=virtio-console,num=3", &log_arg));
 
diff --git a/vm/src/main.rs b/vm/src/main.rs
index 8450b41..60786ac 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -67,6 +67,10 @@
         #[structopt(long)]
         log: Option<PathBuf>,
 
+        /// Path to file where ramdump is recorded on kernel panic
+        #[structopt(long)]
+        ramdump: Option<PathBuf>,
+
         /// Debug level of the VM. Supported values: "none" (default), "app_only", and "full".
         #[structopt(long, default_value = "none", parse(try_from_str=parse_debug_level))]
         debug: DebugLevel,
@@ -198,6 +202,7 @@
             daemonize,
             console,
             log,
+            ramdump,
             debug,
             protected,
             mem,
@@ -214,6 +219,7 @@
             daemonize,
             console.as_deref(),
             log.as_deref(),
+            ramdump.as_deref(),
             debug,
             protected,
             mem,
diff --git a/vm/src/run.rs b/vm/src/run.rs
index ca71665..44eb27a 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -49,6 +49,7 @@
     daemonize: bool,
     console_path: Option<&Path>,
     log_path: Option<&Path>,
+    ramdump_path: Option<&Path>,
     debug_level: DebugLevel,
     protected: bool,
     mem: Option<u32>,
@@ -115,6 +116,7 @@
         daemonize,
         console_path,
         log_path,
+        ramdump_path,
     )
 }
 
@@ -149,6 +151,7 @@
         daemonize,
         console_path,
         log_path,
+        /* ramdump_path */ None,
     )
 }
 
@@ -171,6 +174,7 @@
     daemonize: bool,
     console_path: Option<&Path>,
     log_path: Option<&Path>,
+    ramdump_path: Option<&Path>,
 ) -> Result<(), Error> {
     let console = if let Some(console_path) = console_path {
         Some(
@@ -214,12 +218,27 @@
         // Wait until the VM or VirtualizationService dies. If we just returned immediately then the
         // IVirtualMachine Binder object would be dropped and the VM would be killed.
         let death_reason = vm.wait_for_death();
+
+        if let Some(path) = ramdump_path {
+            save_ramdump_if_available(path, &vm)?;
+        }
         println!("{}", death_reason);
     }
 
     Ok(())
 }
 
+fn save_ramdump_if_available(path: &Path, vm: &VmInstance) -> Result<(), Error> {
+    if let Some(mut ramdump) = vm.get_ramdump() {
+        let mut file =
+            File::create(path).context(format!("Failed to create ramdump file {:?}", path))?;
+        let size = std::io::copy(&mut ramdump, &mut file)
+            .context(format!("Failed to save ramdump to file {:?}", path))?;
+        eprintln!("Ramdump ({} bytes) saved to {:?}", size, path);
+    }
+    Ok(())
+}
+
 fn parse_extra_apk_list(apk: &Path, config_path: &str) -> Result<Vec<String>, Error> {
     let mut archive = ZipArchive::new(File::open(apk)?)?;
     let config_file = archive.by_name(config_path)?;
@@ -268,6 +287,11 @@
         Ok(())
     }
 
+    fn onRamdump(&self, _cid: i32, _stream: &ParcelFileDescriptor) -> BinderResult<()> {
+        // Do nothing. We get ramdump from the vmclient library.
+        Ok(())
+    }
+
     fn onDied(&self, _cid: i32, _reason: DeathReason) -> BinderResult<()> {
         Ok(())
     }
diff --git a/vmclient/src/lib.rs b/vmclient/src/lib.rs
index d182b60..867c3a7 100644
--- a/vmclient/src/lib.rs
+++ b/vmclient/src/lib.rs
@@ -143,6 +143,11 @@
 
         FromIBinder::try_from(ibinder).map_err(GetServiceError::WrongServiceType)
     }
+
+    /// Get ramdump
+    pub fn get_ramdump(&self) -> Option<File> {
+        self.state.get_ramdump()
+    }
 }
 
 impl Debug for VmInstance {
@@ -170,6 +175,7 @@
 struct VmState {
     death_reason: Option<DeathReason>,
     reported_state: VirtualMachineState,
+    ramdump: Option<File>,
 }
 
 impl Monitor<VmState> {
@@ -186,6 +192,14 @@
         self.state.lock().unwrap().reported_state = state;
         self.cv.notify_all();
     }
+
+    fn set_ramdump(&self, ramdump: File) {
+        self.state.lock().unwrap().ramdump = Some(ramdump);
+    }
+
+    fn get_ramdump(&self) -> Option<File> {
+        self.state.lock().unwrap().ramdump.as_ref().and_then(|f| f.try_clone().ok())
+    }
 }
 
 #[derive(Debug)]
@@ -220,6 +234,12 @@
         Ok(())
     }
 
+    fn onRamdump(&self, _cid: i32, ramdump: &ParcelFileDescriptor) -> BinderResult<()> {
+        let ramdump: File = ramdump.as_ref().try_clone().unwrap();
+        self.state.set_ramdump(ramdump);
+        Ok(())
+    }
+
     fn onDied(&self, _cid: i32, reason: AidlDeathReason) -> BinderResult<()> {
         self.state.notify_death(reason.into());
         Ok(())