Merge "Add test for atoms VmCpuStatusReported and VmMemStatusReported"
diff --git a/javalib/Android.bp b/javalib/Android.bp
index a6c3b80..51dd381 100644
--- a/javalib/Android.bp
+++ b/javalib/Android.bp
@@ -7,7 +7,7 @@
     name: "android.system.virtualmachine.res",
     installable: true,
     apex_available: ["com.android.virt"],
-    sdk_version: "current",
+    platform_apis: true,
 }
 
 java_sdk_library {
diff --git a/javalib/AndroidManifest.xml b/javalib/AndroidManifest.xml
index e68b5a4..95b9cfa 100644
--- a/javalib/AndroidManifest.xml
+++ b/javalib/AndroidManifest.xml
@@ -17,12 +17,26 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.android.virtualmachine.res">
 
+  <!-- @SystemApi Allows an application to create and run a Virtual Machine
+       using the Virtualization Framework APIs
+       (android.system.virtualmachine.*).
+       <p>Protection level: signature|privileged|development
+       @hide
+  -->
   <permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE"
-      android:protectionLevel="signature|development" />
+      android:protectionLevel="signature|privileged|development" />
 
+  <!-- @hide Allows an application to run a Virtual Machine with a custom
+       kernel or a Microdroid configuration file.
+       <p>Not for use by third-party applications.
+  -->
   <permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE"
       android:protectionLevel="signature|development" />
 
+  <!-- @hide Allows an application to access various Virtual Machine debug
+       facilities, e.g. list all running VMs.
+       <p>Not for use by third-party applications.
+  -->
   <permission android:name="android.permission.DEBUG_VIRTUAL_MACHINE"
       android:protectionLevel="signature" />
 
diff --git a/microdroid/vm_payload/Android.bp b/microdroid/vm_payload/Android.bp
index 925928e..dc314ce 100644
--- a/microdroid/vm_payload/Android.bp
+++ b/microdroid/vm_payload/Android.bp
@@ -14,6 +14,7 @@
         "libanyhow",
         "libbinder_rs",
         "liblog_rust",
+        "librpcbinder_rs",
     ],
     apex_available: [
         "com.android.compos",
diff --git a/microdroid/vm_payload/include/vm_payload.h b/microdroid/vm_payload/include/vm_payload.h
index 6e065a5..dc01662 100644
--- a/microdroid/vm_payload/include/vm_payload.h
+++ b/microdroid/vm_payload/include/vm_payload.h
@@ -25,6 +25,9 @@
 extern "C" {
 #endif
 
+struct AIBinder;
+typedef struct AIBinder AIBinder;
+
 /**
  * Notifies the host that the payload is ready.
  *
@@ -33,6 +36,27 @@
 bool AVmPayload_notifyPayloadReady(void);
 
 /**
+ * Runs a binder RPC server, serving the supplied binder service implementation on the given vsock
+ * port.
+ *
+ * If and when the server is ready for connections (it is listening on the port), `on_ready` is
+ * called to allow appropriate action to be taken - e.g. to notify clients that they may now
+ * attempt to connect with `AVmPayload_notifyPayloadReady`.
+ *
+ * The current thread is joined to the binder thread pool to handle incoming messages.
+ *
+ * \param service the service to bind to the given port.
+ * \param port vsock port.
+ * \param on_ready the callback to execute once the server is ready for connections. The callback
+ *                 will be called at most once.
+ * \param param param for the `on_ready` callback.
+ *
+ * \return true if the server has shutdown normally, false if it failed in some way.
+ */
+bool AVmPayload_runVsockRpcServer(AIBinder *service, unsigned int port,
+                                  void (*on_ready)(void *param), void *param);
+
+/**
  * Get a secret that is uniquely bound to this VM instance. The secrets are 32-byte values and the
  * value associated with an identifier will not change over the lifetime of the VM instance.
  *
diff --git a/microdroid/vm_payload/src/vm_payload_service.rs b/microdroid/vm_payload/src/vm_payload_service.rs
index 44013c9..bec4fde 100644
--- a/microdroid/vm_payload/src/vm_payload_service.rs
+++ b/microdroid/vm_payload/src/vm_payload_service.rs
@@ -17,8 +17,10 @@
 use android_system_virtualization_payload::aidl::android::system::virtualization::payload::IVmPayloadService::{
     IVmPayloadService, VM_PAYLOAD_SERVICE_NAME};
 use anyhow::{Context, Result};
-use binder::{wait_for_interface, Strong};
+use binder::{wait_for_interface, Strong, unstable_api::{AIBinder, new_spibinder}};
 use log::{error, info, Level};
+use rpcbinder::run_vsock_rpc_server;
+use std::os::raw::c_void;
 
 /// Notifies the host that the payload is ready.
 /// Returns true if the notification succeeds else false.
@@ -42,6 +44,44 @@
     get_vm_payload_service()?.notifyPayloadReady().context("Cannot notify payload ready")
 }
 
+/// Runs a binder RPC server, serving the supplied binder service implementation on the given vsock
+/// port.
+///
+/// If and when the server is ready for connections (it is listening on the port), `on_ready` is
+/// called to allow appropriate action to be taken - e.g. to notify clients that they may now
+/// attempt to connect.
+///
+/// The current thread is joined to the binder thread pool to handle incoming messages.
+///
+/// Returns true if the server has shutdown normally, false if it failed in some way.
+///
+/// # Safety
+///
+/// The `on_ready` callback is only called inside `run_vsock_rpc_server`, within the lifetime of
+/// `ReadyNotifier` (the last parameter of `run_vsock_rpc_server`). If `on_ready` is called with
+/// wrong param, the callback execution could go wrong.
+#[no_mangle]
+pub unsafe extern "C" fn AVmPayload_runVsockRpcServer(
+    service: *mut AIBinder,
+    port: u32,
+    on_ready: Option<unsafe extern "C" fn(param: *mut c_void)>,
+    param: *mut c_void,
+) -> bool {
+    // SAFETY: AIBinder returned has correct reference count, and the ownership can
+    // safely be taken by new_spibinder.
+    let service = new_spibinder(service);
+    if let Some(service) = service {
+        run_vsock_rpc_server(service, port, || {
+            if let Some(on_ready) = on_ready {
+                on_ready(param);
+            }
+        })
+    } else {
+        error!("Failed to convert the given service from AIBinder to SpIBinder.");
+        false
+    }
+}
+
 /// Get a secret that is uniquely bound to this VM instance.
 ///
 /// # Safety
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 00c3dce..e42f159 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -39,6 +39,7 @@
 use diced_utils::cbor::{encode_header, encode_number};
 use glob::glob;
 use itertools::sorted;
+use libc::VMADDR_CID_HOST;
 use log::{error, info};
 use microdroid_metadata::{write_metadata, Metadata, PayloadMetadata};
 use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
@@ -62,6 +63,7 @@
 use vsock::VsockStream;
 
 const WAIT_TIMEOUT: Duration = Duration::from_secs(10);
+const SENDING_VM_STATUS_CYCLE_PERIOD: Duration = Duration::from_secs(60);
 const MAIN_APK_PATH: &str = "/dev/block/by-name/microdroid-apk";
 const MAIN_APK_IDSIG_PATH: &str = "/dev/block/by-name/microdroid-apk-idsig";
 const MAIN_APK_DEVICE_NAME: &str = "microdroid-apk";
@@ -75,9 +77,6 @@
 const DEBUG_MICRODROID_NO_VERIFIED_BOOT: &str =
     "/sys/firmware/devicetree/base/virtualization/guest/debug-microdroid,no-verified-boot";
 
-/// The CID representing the host VM
-const VMADDR_CID_HOST: u32 = 2;
-
 const APEX_CONFIG_DONE_PROP: &str = "apex_config.done";
 const APP_DEBUGGABLE_PROP: &str = "ro.boot.microdroid.app_debuggable";
 const APK_MOUNT_DONE_PROP: &str = "microdroid_manager.apk.mounted";
@@ -97,39 +96,39 @@
     InvalidConfig(String),
 }
 
-fn send_vm_status() -> Result<()> {
+fn send_vm_status(service: &Strong<dyn IVirtualMachineService>) -> Result<()> {
+    // Collect VM CPU time information and creating VmCpuStatus atom for metrics.
+    let cpu_time = get_cpu_time()?;
+    let vm_cpu_status = VirtualMachineCpuStatus {
+        cpu_time_user: cpu_time.user,
+        cpu_time_nice: cpu_time.nice,
+        cpu_time_sys: cpu_time.sys,
+        cpu_time_idle: cpu_time.idle,
+    };
+    service.notifyCpuStatus(&vm_cpu_status).expect("Can't send information about VM CPU status");
+
+    // Collect VM memory information and creating VmMemStatus atom for metrics.
+    let mem_info = get_mem_info()?;
+    let vm_mem_status = VirtualMachineMemStatus {
+        mem_total: mem_info.total,
+        mem_free: mem_info.free,
+        mem_available: mem_info.available,
+        mem_buffer: mem_info.buffer,
+        mem_cached: mem_info.cached,
+    };
+    service.notifyMemStatus(&vm_mem_status).expect("Can't send information about VM memory status");
+
+    Ok(())
+}
+
+fn send_vm_status_periodically() -> Result<()> {
     let service = get_vms_rpc_binder()
         .context("cannot connect to VirtualMachineService")
         .map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
 
-    let one_second = Duration::from_millis(1000);
     loop {
-        // Collect VM CPU time information and creating VmCpuStatus atom for metrics.
-        let cpu_time = get_cpu_time()?;
-        let vm_cpu_status = VirtualMachineCpuStatus {
-            cpu_time_user: cpu_time.user,
-            cpu_time_nice: cpu_time.nice,
-            cpu_time_sys: cpu_time.sys,
-            cpu_time_idle: cpu_time.idle,
-        };
-        service
-            .notifyCpuStatus(&vm_cpu_status)
-            .expect("Can't send information about VM CPU status");
-
-        // Collect VM memory information and creating VmMemStatus atom for metrics.
-        let mem_info = get_mem_info()?;
-        let vm_mem_status = VirtualMachineMemStatus {
-            mem_total: mem_info.total,
-            mem_free: mem_info.free,
-            mem_available: mem_info.available,
-            mem_buffer: mem_info.buffer,
-            mem_cached: mem_info.cached,
-        };
-        service
-            .notifyMemStatus(&vm_mem_status)
-            .expect("Can't send information about VM memory status");
-
-        thread::sleep(one_second);
+        send_vm_status(&service)?;
+        thread::sleep(SENDING_VM_STATUS_CYCLE_PERIOD);
     }
 }
 
@@ -226,7 +225,7 @@
         .map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
 
     thread::spawn(move || {
-        if let Err(e) = send_vm_status() {
+        if let Err(e) = send_vm_status_periodically() {
             error!("failed to get virtual machine status: {:?}", e);
         }
     });
@@ -450,6 +449,7 @@
     ProcessState::start_thread_pool();
 
     system_properties::write("dev.bootcomplete", "1").context("set dev.bootcomplete")?;
+    send_vm_status(service)?;
 
     exec_task(task, service).context("Failed to run payload")
 }
@@ -782,12 +782,11 @@
     service.notifyPayloadStarted()?;
 
     let exit_status = command.spawn()?.wait()?;
+    send_vm_status(service)?;
     exit_status.code().ok_or_else(|| anyhow!("Failed to get exit_code from the paylaod."))
 }
 
 fn build_command(task: &Task) -> Result<Command> {
-    const VMADDR_CID_HOST: u32 = 2;
-
     let mut command = match task.type_ {
         TaskType::Executable => Command::new(&task.command),
         TaskType::MicrodroidLauncher => {
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index a606462..99c67fb 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -18,7 +18,7 @@
 use crate::mmio_guard;
 use core::arch::asm;
 use core::slice;
-use log::{debug, LevelFilter};
+use log::{debug, error, LevelFilter};
 use vmbase::{console, logger, main, power::reboot};
 
 #[derive(Debug, Clone)]
@@ -77,6 +77,11 @@
     // This wrapper allows main() to be blissfully ignorant of platform details.
     crate::main(fdt, payload);
 
+    mmio_guard::unmap(console::BASE_ADDRESS).map_err(|e| {
+        error!("Failed to unshare the UART: {e}");
+        RebootReason::InternalError
+    })?;
+
     Ok(())
 }
 
diff --git a/pvmfw/src/mmio_guard.rs b/pvmfw/src/mmio_guard.rs
index 421f2c4..eb6c1fa 100644
--- a/pvmfw/src/mmio_guard.rs
+++ b/pvmfw/src/mmio_guard.rs
@@ -17,6 +17,7 @@
 use crate::helpers;
 use crate::smccc;
 use core::{fmt, result};
+use log::info;
 
 #[derive(Debug, Clone)]
 pub enum Error {
@@ -26,6 +27,8 @@
     InfoFailed(smccc::Error),
     /// Failed to MMIO_GUARD_MAP a page.
     MapFailed(smccc::Error),
+    /// Failed to MMIO_GUARD_UNMAP a page.
+    UnmapFailed(smccc::Error),
     /// The MMIO_GUARD granule used by the hypervisor is not supported.
     UnsupportedGranule(usize),
 }
@@ -38,6 +41,7 @@
             Self::EnrollFailed(e) => write!(f, "Failed to enroll into MMIO_GUARD: {e}"),
             Self::InfoFailed(e) => write!(f, "Failed to get the MMIO_GUARD granule: {e}"),
             Self::MapFailed(e) => write!(f, "Failed to MMIO_GUARD map: {e}"),
+            Self::UnmapFailed(e) => write!(f, "Failed to MMIO_GUARD unmap: {e}"),
             Self::UnsupportedGranule(g) => write!(f, "Unsupported MMIO_GUARD granule: {g}"),
         }
     }
@@ -56,6 +60,10 @@
     mmio_guard_map(helpers::page_4kb_of(addr) as u64).map_err(Error::MapFailed)
 }
 
+pub fn unmap(addr: usize) -> Result<()> {
+    mmio_guard_unmap(helpers::page_4kb_of(addr) as u64).map_err(Error::UnmapFailed)
+}
+
 fn mmio_guard_info() -> smccc::Result<u64> {
     const VENDOR_HYP_KVM_MMIO_GUARD_INFO_FUNC_ID: u32 = 0xc6000005;
     let args = [0u64; 17];
@@ -75,5 +83,31 @@
     let mut args = [0u64; 17];
     args[0] = ipa;
 
-    smccc::checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID, args)
+    // TODO(b/253586500): pKVM currently returns a i32 instead of a i64.
+    let is_i32_error_code = |n| u32::try_from(n).ok().filter(|v| (*v as i32) < 0).is_some();
+    match smccc::checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID, args) {
+        Err(smccc::Error::Unexpected(e)) if is_i32_error_code(e) => {
+            info!("Handled a pKVM bug by interpreting the MMIO_GUARD_MAP return value as i32");
+            match e as u32 as i32 {
+                -1 => Err(smccc::Error::NotSupported),
+                -2 => Err(smccc::Error::NotRequired),
+                -3 => Err(smccc::Error::InvalidParameter),
+                ret => Err(smccc::Error::Unknown(ret as i64)),
+            }
+        }
+        res => res,
+    }
+}
+
+fn mmio_guard_unmap(ipa: u64) -> smccc::Result<()> {
+    const VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID: u32 = 0xc6000008;
+    let mut args = [0u64; 17];
+    args[0] = ipa;
+
+    // TODO(b/251426790): pKVM currently returns NOT_SUPPORTED for SUCCESS.
+    info!("Expecting a bug making MMIO_GUARD_UNMAP return NOT_SUPPORTED on success");
+    match smccc::checked_hvc64_expect_zero(VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID, args) {
+        Err(smccc::Error::NotSupported) | Ok(_) => Ok(()),
+        x => x,
+    }
 }
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index bb17058..da2c626 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -38,7 +38,6 @@
         "com.android.microdroid.testservice-ndk",
         "libbase",
         "libbinder_ndk",
-        "libbinder_rpc_unstable",
         "MicrodroidTestNativeLibSub",
         "libvm_payload",
     ],
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index 5d6ca8b..d57d224 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -30,7 +30,6 @@
 #include <vm_main.h>
 #include <vm_payload.h>
 
-#include <binder_rpc_unstable.hpp>
 #include <string>
 
 using android::base::ErrnoError;
@@ -122,8 +121,8 @@
             abort();
         }
     };
-    if (!RunVsockRpcServerCallback(testService->asBinder().get(), testService->SERVICE_PORT,
-                                   callback, nullptr)) {
+    if (!AVmPayload_runVsockRpcServer(testService->asBinder().get(), testService->SERVICE_PORT,
+                                      callback, nullptr)) {
         return Error() << "RPC Server failed to run";
     }
 
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 0551229..26d41c9 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -32,6 +32,7 @@
         "libcommand_fds",
         "libdisk",
         "liblazy_static",
+        "liblibc",
         "liblog_rust",
         "libmicrodroid_metadata",
         "libmicrodroid_payload_config",
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 827dcb0..b4ce9d2 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -22,7 +22,6 @@
 use crate::crosvm::{CrosvmConfig, DiskFile, PayloadState, VmInstance, VmState};
 use crate::payload::{add_microdroid_payload_images, add_microdroid_system_images};
 use crate::selinux::{getfilecon, SeContext};
-use crate::{Cid, FIRST_GUEST_CID, SYSPROP_LAST_CID};
 use android_os_permissions_aidl::aidl::android::os::IPermissionController;
 use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::ErrorCode::ErrorCode;
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
@@ -55,6 +54,7 @@
     SpIBinder, Status, StatusCode, Strong, ThreadState,
 };
 use disk::QcowFile;
+use libc::VMADDR_CID_HOST;
 use log::{debug, error, info, warn};
 use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
 use rpcbinder::run_vsock_rpc_server_with_factory;
@@ -73,13 +73,19 @@
 use vsock::{VsockListener, VsockStream};
 use zip::ZipArchive;
 
+/// The unique ID of a VM used (together with a port number) for vsock communication.
+pub type Cid = u32;
+
 pub const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtualizationservice";
 
 /// Directory in which to write disk image files used while running VMs.
 pub const TEMPORARY_DIRECTORY: &str = "/data/misc/virtualizationservice";
 
-/// The CID representing the host VM
-const VMADDR_CID_HOST: u32 = 2;
+/// The first CID to assign to a guest VM managed by the VirtualizationService. CIDs lower than this
+/// are reserved for the host or other usage.
+const FIRST_GUEST_CID: Cid = 10;
+
+const SYSPROP_LAST_CID: &str = "virtualizationservice.state.last_cid";
 
 /// The size of zero.img.
 /// Gaps in composite disk images are filled with a shared zero.img.
@@ -358,7 +364,7 @@
         let log_fd = log_fd.map(clone_file).transpose()?;
         let requester_uid = ThreadState::get_calling_uid();
         let requester_debug_pid = ThreadState::get_calling_pid();
-        let cid = next_cid().or(Err(ExceptionCode::ILLEGAL_STATE))?;
+        let cid = state.next_cid().or(Err(ExceptionCode::ILLEGAL_STATE))?;
 
         // Counter to generate unique IDs for temporary image files.
         let mut next_temporary_image_id = 0;
@@ -969,27 +975,27 @@
         let vm = self.debug_held_vms.swap_remove(pos);
         Some(vm)
     }
-}
 
-/// Get the next available CID, or an error if we have run out. The last CID used is stored in
-/// a system property so that restart of virtualizationservice doesn't reuse CID while the host
-/// Android is up.
-fn next_cid() -> Result<Cid> {
-    let next = if let Some(val) = system_properties::read(SYSPROP_LAST_CID)? {
-        if let Ok(num) = val.parse::<u32>() {
-            num.checked_add(1).ok_or_else(|| anyhow!("run out of CID"))?
+    /// Get the next available CID, or an error if we have run out. The last CID used is stored in
+    /// a system property so that restart of virtualizationservice doesn't reuse CID while the host
+    /// Android is up.
+    fn next_cid(&mut self) -> Result<Cid> {
+        let next = if let Some(val) = system_properties::read(SYSPROP_LAST_CID)? {
+            if let Ok(num) = val.parse::<u32>() {
+                num.checked_add(1).ok_or_else(|| anyhow!("run out of CID"))?
+            } else {
+                error!("Invalid last CID {}. Using {}", &val, FIRST_GUEST_CID);
+                FIRST_GUEST_CID
+            }
         } else {
-            error!("Invalid last CID {}. Using {}", &val, FIRST_GUEST_CID);
+            // First VM since the boot
             FIRST_GUEST_CID
-        }
-    } else {
-        // First VM since the boot
-        FIRST_GUEST_CID
-    };
-    // Persist the last value for next use
-    let str_val = format!("{}", next);
-    system_properties::write(SYSPROP_LAST_CID, &str_val)?;
-    Ok(next)
+        };
+        // Persist the last value for next use
+        let str_val = format!("{}", next);
+        system_properties::write(SYSPROP_LAST_CID, &str_val)?;
+        Ok(next)
+    }
 }
 
 /// Gets the `VirtualMachineState` of the given `VmInstance`.
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index f3abee0..6f646b7 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -14,15 +14,15 @@
 
 //! Functions for running instances of `crosvm`.
 
-use crate::aidl::VirtualMachineCallbacks;
+use crate::aidl::{Cid, VirtualMachineCallbacks};
 use crate::atom::write_vm_exited_stats;
-use crate::Cid;
 use anyhow::{anyhow, bail, Context, 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 regex::{Captures, Regex};
 use shared_child::SharedChild;
 use std::borrow::Cow;
 use std::fs::{remove_dir_all, File};
@@ -546,7 +546,8 @@
     debug!("Preserving FDs {:?}", preserved_fds);
     command.preserved_fds(preserved_fds);
 
-    info!("Running {:?}", command);
+    print_crosvm_args(&command);
+
     let result = SharedChild::spawn(&mut command)?;
     debug!("Spawned crosvm({}).", result.id());
     Ok(result)
@@ -573,6 +574,31 @@
     Ok(())
 }
 
+/// Print arguments of the crosvm command. In doing so, /proc/self/fd/XX is annotated with the
+/// actual file path if the FD is backed by a regular file. If not, the /proc path is printed
+/// unmodified.
+fn print_crosvm_args(command: &Command) {
+    let re = Regex::new(r"/proc/self/fd/[\d]+").unwrap();
+    info!(
+        "Running crosvm with args: {:?}",
+        command
+            .get_args()
+            .map(|s| s.to_string_lossy())
+            .map(|s| {
+                re.replace_all(&s, |caps: &Captures| {
+                    let path = &caps[0];
+                    if let Ok(realpath) = std::fs::canonicalize(path) {
+                        format!("{} ({})", path, realpath.to_string_lossy())
+                    } else {
+                        path.to_owned()
+                    }
+                })
+                .into_owned()
+            })
+            .collect::<Vec<_>>()
+    );
+}
+
 /// Adds the file descriptor for `file` to `preserved_fds`, and returns a string of the form
 /// "/proc/self/fd/N" where N is the file descriptor.
 fn add_preserved_fd(preserved_fds: &mut Vec<RawFd>, file: &dyn AsRawFd) -> String {
diff --git a/virtualizationservice/src/main.rs b/virtualizationservice/src/main.rs
index 828d3a2..cea2747 100644
--- a/virtualizationservice/src/main.rs
+++ b/virtualizationservice/src/main.rs
@@ -29,17 +29,8 @@
 use log::{info, Level};
 use std::fs::{remove_dir_all, remove_file, read_dir};
 
-/// The first CID to assign to a guest VM managed by the VirtualizationService. CIDs lower than this
-/// are reserved for the host or other usage.
-const FIRST_GUEST_CID: Cid = 10;
-
-const SYSPROP_LAST_CID: &str = "virtualizationservice.state.last_cid";
-
 const LOG_TAG: &str = "VirtualizationService";
 
-/// The unique ID of a VM used (together with a port number) for vsock communication.
-type Cid = u32;
-
 fn main() {
     android_logger::init_once(
         Config::default()
diff --git a/vm/src/run.rs b/vm/src/run.rs
index 967314b..de8f1c0 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -55,6 +55,8 @@
     task_profiles: Vec<String>,
     extra_idsigs: &[PathBuf],
 ) -> Result<(), Error> {
+    let apk_file = File::open(apk).context("Failed to open APK file")?;
+
     let extra_apks = parse_extra_apk_list(apk, config_path)?;
     if extra_apks.len() != extra_idsigs.len() {
         bail!(
@@ -70,7 +72,6 @@
         service.createOrUpdateIdsigFile(&extra_apk_fd, &extra_idsig_fd)?;
     }
 
-    let apk_file = File::open(apk).context("Failed to open APK file")?;
     let idsig_file = File::create(idsig).context("Failed to create idsig file")?;
 
     let apk_fd = ParcelFileDescriptor::new(apk_file);