Merge "Migrate to reading properties as bools"
diff --git a/authfs/tests/Android.bp b/authfs/tests/Android.bp
index 6b3a474..ca403e8 100644
--- a/authfs/tests/Android.bp
+++ b/authfs/tests/Android.bp
@@ -14,7 +14,7 @@
         "VirtualizationTestHelper",
     ],
     test_suites: ["general-tests"],
-    target_required: ["open_then_run_module"],
+    data_device_bins: ["open_then_run"],
     data: [
         ":authfs_test_files",
         ":MicrodroidTestApp.signed",
@@ -22,16 +22,7 @@
 }
 
 rust_test {
-    // PushFilePreparer can sometimes push the directory (if named "open_then_run", which contains
-    // the actual executable in a per-architecture sub-directory) instead of the executable. This
-    // makes it harder to use because the host Java test have to detect the executable path
-    // dynamically, e.g. if it's a directory, append the device's architecture to build the actual
-    // executable path. By simply renaming the module (thus the host directory), this forces
-    // PushFilePreparer to always push the executable to the destination, so that the Java test can
-    // easily locate the executable with a constant path.
-    name: "open_then_run_module",
-    stem: "open_then_run",
-
+    name: "open_then_run",
     crate_name: "open_then_run",
     srcs: ["open_then_run.rs"],
     edition: "2018",
diff --git a/compos/Android.bp b/compos/Android.bp
index c54348a..0bcbcdd 100644
--- a/compos/Android.bp
+++ b/compos/Android.bp
@@ -28,6 +28,7 @@
         "libprotobuf",
         "libregex",
         "libring",
+        "librustutils",
         "libscopeguard",
     ],
     prefer_rlib: true,
diff --git a/compos/aidl/com/android/compos/ICompOsService.aidl b/compos/aidl/com/android/compos/ICompOsService.aidl
index cead5d0..18e163e 100644
--- a/compos/aidl/com/android/compos/ICompOsService.aidl
+++ b/compos/aidl/com/android/compos/ICompOsService.aidl
@@ -21,6 +21,17 @@
 /** {@hide} */
 interface ICompOsService {
     /**
+     * What type of compilation to perform.
+     */
+    @Backing(type="int")
+    enum CompilationMode {
+        /** Compile artifacts required by the current set of APEXes for use on reboot. */
+        NORMAL_COMPILE = 0,
+        /** Compile a full set of artifacts for test purposes. */
+        TEST_COMPILE = 1,
+    }
+
+    /**
      * Initializes the service with the supplied encrypted private key blob. The key cannot be
      * changed once initialized, so once initiailzed, a repeated call will fail with
      * EX_ILLEGAL_STATE.
@@ -37,6 +48,7 @@
      * through systemDirFd over AuthFS), and *CLASSPATH derived in the VM, to generate the same
      * odrefresh output artifacts to the output directory (through outputDirFd).
      *
+     * @param compilationMode The type of compilation to be performed
      * @param systemDirFd An fd referring to /system
      * @param outputDirFd An fd referring to the output directory, ART_APEX_DATA
      * @param stagingDirFd An fd referring to the staging directory, e.g. ART_APEX_DATA/staging
@@ -46,8 +58,9 @@
      * @param systemServerCompilerFilter The compiler filter used to compile system server
      * @return odrefresh exit code
      */
-    byte odrefresh(int systemDirFd, int outputDirFd, int stagingDirFd, String targetDirName,
-            String zygoteArch, String systemServerCompilerFilter);
+    byte odrefresh(CompilationMode compilation_mode, int systemDirFd, int outputDirFd,
+            int stagingDirFd, String targetDirName, String zygoteArch,
+            String systemServerCompilerFilter);
 
     /**
      * Generate a new public/private key pair suitable for signing CompOs output files.
diff --git a/compos/composd/src/odrefresh_task.rs b/compos/composd/src/odrefresh_task.rs
index 330f0ab..47ff590 100644
--- a/compos/composd/src/odrefresh_task.rs
+++ b/compos/composd/src/odrefresh_task.rs
@@ -23,7 +23,9 @@
 };
 use android_system_composd::binder::{Interface, Result as BinderResult, Strong};
 use anyhow::{Context, Result};
-use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
+use compos_aidl_interface::aidl::com::android::compos::ICompOsService::{
+    CompilationMode::CompilationMode, ICompOsService,
+};
 use compos_common::odrefresh::ExitCode;
 use log::{error, warn};
 use rustutils::system_properties;
@@ -68,6 +70,7 @@
 
     pub fn start(
         comp_os: Arc<CompOsInstance>,
+        compilation_mode: CompilationMode,
         target_dir_name: String,
         callback: &Strong<dyn ICompilationTaskCallback>,
     ) -> Result<OdrefreshTask> {
@@ -75,14 +78,19 @@
         let task = RunningTask { comp_os, callback: callback.clone() };
         let task = OdrefreshTask { running_task: Arc::new(Mutex::new(Some(task))) };
 
-        task.clone().start_thread(service, target_dir_name);
+        task.clone().start_thread(service, compilation_mode, target_dir_name);
 
         Ok(task)
     }
 
-    fn start_thread(self, service: Strong<dyn ICompOsService>, target_dir_name: String) {
+    fn start_thread(
+        self,
+        service: Strong<dyn ICompOsService>,
+        compilation_mode: CompilationMode,
+        target_dir_name: String,
+    ) {
         thread::spawn(move || {
-            let exit_code = run_in_vm(service, &target_dir_name);
+            let exit_code = run_in_vm(service, compilation_mode, &target_dir_name);
 
             let task = self.take();
             // We don't do the callback if cancel has already happened.
@@ -106,7 +114,11 @@
     }
 }
 
-fn run_in_vm(service: Strong<dyn ICompOsService>, target_dir_name: &str) -> Result<ExitCode> {
+fn run_in_vm(
+    service: Strong<dyn ICompOsService>,
+    compilation_mode: CompilationMode,
+    target_dir_name: &str,
+) -> Result<ExitCode> {
     let output_root = Path::new(ART_APEX_DATA);
 
     // We need to remove the target directory because odrefresh running in compos will create it
@@ -134,6 +146,7 @@
     let system_server_compiler_filter =
         system_properties::read("dalvik.vm.systemservercompilerfilter").unwrap_or_default();
     let exit_code = service.odrefresh(
+        compilation_mode,
         system_dir.as_raw_fd(),
         output_dir.as_raw_fd(),
         staging_dir.as_raw_fd(),
diff --git a/compos/composd/src/service.rs b/compos/composd/src/service.rs
index 6cdcd85..f4121e7 100644
--- a/compos/composd/src/service.rs
+++ b/compos/composd/src/service.rs
@@ -28,6 +28,7 @@
     self, BinderFeatures, ExceptionCode, Interface, Status, Strong, ThreadState,
 };
 use anyhow::{Context, Result};
+use compos_aidl_interface::aidl::com::android::compos::ICompOsService::CompilationMode::CompilationMode;
 use compos_common::binder::to_binder_result;
 use rustutils::{users::AID_ROOT, users::AID_SYSTEM};
 use std::sync::Arc;
@@ -72,7 +73,12 @@
         let comp_os = self.instance_manager.start_pending_instance().context("Starting CompOS")?;
 
         let target_dir_name = "compos-pending".to_owned();
-        let task = OdrefreshTask::start(comp_os, target_dir_name, callback)?;
+        let task = OdrefreshTask::start(
+            comp_os,
+            CompilationMode::NORMAL_COMPILE,
+            target_dir_name,
+            callback,
+        )?;
 
         Ok(BnCompilationTask::new_binder(task, BinderFeatures::default()))
     }
@@ -84,7 +90,12 @@
         let comp_os = self.instance_manager.start_test_instance().context("Starting CompOS")?;
 
         let target_dir_name = "test-artifacts".to_owned();
-        let task = OdrefreshTask::start(comp_os, target_dir_name, callback)?;
+        let task = OdrefreshTask::start(
+            comp_os,
+            CompilationMode::TEST_COMPILE,
+            target_dir_name,
+            callback,
+        )?;
 
         Ok(BnCompilationTask::new_binder(task, BinderFeatures::default()))
     }
diff --git a/compos/src/compilation.rs b/compos/src/compilation.rs
index e8f55f8..7e3834a 100644
--- a/compos/src/compilation.rs
+++ b/compos/src/compilation.rs
@@ -18,6 +18,7 @@
 use log::{debug, info, warn};
 use minijail::{self, Minijail};
 use regex::Regex;
+use rustutils::system_properties;
 use std::collections::HashMap;
 use std::env;
 use std::ffi::OsString;
@@ -35,11 +36,13 @@
     IAuthFsService::IAuthFsService,
 };
 use authfs_aidl_interface::binder::Strong;
+use compos_aidl_interface::aidl::com::android::compos::ICompOsService::CompilationMode::CompilationMode;
 use compos_common::odrefresh::ExitCode;
 
 const FD_SERVER_PORT: i32 = 3264; // TODO: support dynamic port
 
 pub struct OdrefreshContext<'a> {
+    compilation_mode: CompilationMode,
     system_dir_fd: i32,
     output_dir_fd: i32,
     staging_dir_fd: i32,
@@ -50,6 +53,7 @@
 
 impl<'a> OdrefreshContext<'a> {
     pub fn new(
+        compilation_mode: CompilationMode,
         system_dir_fd: i32,
         output_dir_fd: i32,
         staging_dir_fd: i32,
@@ -57,6 +61,13 @@
         zygote_arch: &'a str,
         system_server_compiler_filter: &'a str,
     ) -> Result<Self> {
+        if compilation_mode != CompilationMode::NORMAL_COMPILE {
+            let debuggable = system_properties::read_bool("ro.boot.microdroid.debuggable", false)?;
+            if !debuggable {
+                bail!("Requested compilation mode only available in debuggable VMs");
+            }
+        }
+
         if system_dir_fd < 0 || output_dir_fd < 0 || staging_dir_fd < 0 {
             bail!("The remote FDs are expected to be non-negative");
         }
@@ -75,6 +86,7 @@
         // CompOS.
 
         Ok(Self {
+            compilation_mode,
             system_dir_fd,
             output_dir_fd,
             staging_dir_fd,
@@ -143,18 +155,21 @@
         ));
     }
 
-    args.push("--compile".to_string());
+    let compile_flag = match context.compilation_mode {
+        CompilationMode::NORMAL_COMPILE => "--compile",
+        CompilationMode::TEST_COMPILE => "--force-compile",
+        other => bail!("Unknown compilation mode {:?}", other),
+    };
+    args.push(compile_flag.to_string());
 
     debug!("Running odrefresh with args: {:?}", &args);
     let jail = spawn_jailed_task(odrefresh_path, &args, &odrefresh_vars.into_env())
         .context("Spawn odrefresh")?;
     let exit_code = match jail.wait() {
-        Ok(_) => Result::<u8>::Ok(0),
-        Err(minijail::Error::ReturnCode(exit_code)) => Ok(exit_code),
-        Err(e) => {
-            bail!("Unexpected minijail error: {}", e)
-        }
-    }?;
+        Ok(_) => 0,
+        Err(minijail::Error::ReturnCode(exit_code)) => exit_code,
+        Err(e) => bail!("Unexpected minijail error: {}", e),
+    };
 
     let exit_code = ExitCode::from_i32(exit_code.into())?;
     info!("odrefresh exited with {:?}", exit_code);
diff --git a/compos/src/compsvc.rs b/compos/src/compsvc.rs
index 422f271..9d754a7 100644
--- a/compos/src/compsvc.rs
+++ b/compos/src/compsvc.rs
@@ -31,7 +31,7 @@
 use authfs_aidl_interface::aidl::com::android::virt::fs::IAuthFsService::IAuthFsService;
 use compos_aidl_interface::aidl::com::android::compos::{
     CompOsKeyData::CompOsKeyData,
-    ICompOsService::{BnCompOsService, ICompOsService},
+    ICompOsService::{BnCompOsService, CompilationMode::CompilationMode, ICompOsService},
 };
 use compos_aidl_interface::binder::{
     BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Strong,
@@ -82,6 +82,7 @@
 
     fn odrefresh(
         &self,
+        compilation_mode: CompilationMode,
         system_dir_fd: i32,
         output_dir_fd: i32,
         staging_dir_fd: i32,
@@ -90,6 +91,7 @@
         system_server_compiler_filter: &str,
     ) -> BinderResult<i8> {
         let context = to_binder_result(OdrefreshContext::new(
+            compilation_mode,
             system_dir_fd,
             output_dir_fd,
             staging_dir_fd,