Merge "Start to add a function to compile staged APEXes"
diff --git a/compos/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl b/compos/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
index 6f4476c..5e72cd2 100644
--- a/compos/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
+++ b/compos/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
@@ -20,6 +20,17 @@
 
 interface IIsolatedCompilationService {
     /**
+     * Compile BCP extensions and system server, using any staged APEXes that are present in
+     * preference to active APEXes, writing the results to the pending artifacts directory to be
+     * verified by odsing on next boot.
+     *
+     * Compilation continues in the background, and success/failure is reported via the supplied
+     * callback, unless the returned ICompilationTask is cancelled. The caller should maintain
+     * a reference to the ICompilationTask until compilation completes or is cancelled.
+     */
+    ICompilationTask startStagedApexCompile(ICompilationTaskCallback callback);
+
+    /**
      * Run "odrefresh --dalvik-cache=pending-test --force-compile" in a test instance of CompOS.
      *
      * This compiles BCP extensions and system server, even if the system artifacts are up to date,
diff --git a/compos/composd/src/compilation_task.rs b/compos/composd/src/compilation_task.rs
index c4eed52..18f5aac 100644
--- a/compos/composd/src/compilation_task.rs
+++ b/compos/composd/src/compilation_task.rs
@@ -52,6 +52,23 @@
         self.running_task.lock().unwrap().take()
     }
 
+    pub fn start_staged_apex_compile(
+        comp_os: Arc<CompOsInstance>,
+        callback: &Strong<dyn ICompilationTaskCallback>,
+    ) -> Result<CompilationTask> {
+        // TODO: Write to pending
+        // TODO: Delete any existing artifacts
+        let odrefresh = Odrefresh::spawn_compile("test-artifacts")?;
+        let odrefresh = Arc::new(odrefresh);
+        let task =
+            RunningTask { odrefresh: odrefresh.clone(), comp_os, callback: callback.clone() };
+        let task = CompilationTask { running_task: Arc::new(Mutex::new(Some(task))) };
+
+        task.clone().start_waiting_thread(odrefresh);
+
+        Ok(task)
+    }
+
     pub fn start_test_compile(
         comp_os: Arc<CompOsInstance>,
         callback: &Strong<dyn ICompilationTaskCallback>,
diff --git a/compos/composd/src/instance_manager.rs b/compos/composd/src/instance_manager.rs
index 6291d59..767e9f7 100644
--- a/compos/composd/src/instance_manager.rs
+++ b/compos/composd/src/instance_manager.rs
@@ -23,7 +23,7 @@
 use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
 use compos_aidl_interface::binder::Strong;
 use compos_common::compos_client::VmParameters;
-use compos_common::{CURRENT_INSTANCE_DIR, TEST_INSTANCE_DIR};
+use compos_common::{PENDING_INSTANCE_DIR, TEST_INSTANCE_DIR};
 use std::sync::{Arc, Mutex, Weak};
 use virtualizationservice::IVirtualizationService::IVirtualizationService;
 
@@ -43,9 +43,8 @@
         Ok(instance.get_service())
     }
 
-    #[allow(dead_code)] // TODO: Make use of this
-    pub fn start_current_instance(&self) -> Result<Arc<CompOsInstance>> {
-        self.start_instance(CURRENT_INSTANCE_DIR, VmParameters::default())
+    pub fn start_pending_instance(&self) -> Result<Arc<CompOsInstance>> {
+        self.start_instance(PENDING_INSTANCE_DIR, VmParameters::default())
     }
 
     pub fn start_test_instance(&self) -> Result<Arc<CompOsInstance>> {
diff --git a/compos/composd/src/instance_starter.rs b/compos/composd/src/instance_starter.rs
index 3e18c3e..8189fe0 100644
--- a/compos/composd/src/instance_starter.rs
+++ b/compos/composd/src/instance_starter.rs
@@ -199,7 +199,7 @@
 
     fn check_files_exist(&self) -> Result<()> {
         if !self.instance_root.is_dir() {
-            bail!("Directory {} not found", self.instance_root.display())
+            bail!("Directory {:?} not found", self.instance_root)
         };
         Self::check_file_exists(&self.instance_image)?;
         Self::check_file_exists(&self.key_blob)?;
@@ -209,7 +209,7 @@
 
     fn check_file_exists(file: &Path) -> Result<()> {
         if !file.is_file() {
-            bail!("File {} not found", file.display())
+            bail!("File {:?} not found", file)
         };
         Ok(())
     }
diff --git a/compos/composd/src/odrefresh.rs b/compos/composd/src/odrefresh.rs
index 16dcb0f..9debf00 100644
--- a/compos/composd/src/odrefresh.rs
+++ b/compos/composd/src/odrefresh.rs
@@ -44,7 +44,15 @@
 }
 
 impl Odrefresh {
+    pub fn spawn_compile(target_dir: &str) -> Result<Self> {
+        Self::spawn_odrefresh(target_dir, "--compile")
+    }
+
     pub fn spawn_forced_compile(target_dir: &str) -> Result<Self> {
+        Self::spawn_odrefresh(target_dir, "--force-compile")
+    }
+
+    fn spawn_odrefresh(target_dir: &str, compile_arg: &str) -> Result<Self> {
         // We don`t need to capture stdout/stderr - odrefresh writes to the log
         let mut cmdline = Command::new(ODREFRESH_BIN);
         if need_extra_time()? {
@@ -61,7 +69,7 @@
         cmdline
             .arg(format!("--use-compilation-os={}", VMADDR_CID_ANY as i32))
             .arg(format!("--dalvik-cache={}", target_dir))
-            .arg("--force-compile");
+            .arg(compile_arg);
         let child = SharedChild::spawn(&mut cmdline).context("Running odrefresh")?;
         Ok(Odrefresh { child })
     }
diff --git a/compos/composd/src/service.rs b/compos/composd/src/service.rs
index 3738e18..aa96ddf 100644
--- a/compos/composd/src/service.rs
+++ b/compos/composd/src/service.rs
@@ -53,21 +53,41 @@
 impl Interface for IsolatedCompilationService {}
 
 impl IIsolatedCompilationService for IsolatedCompilationService {
+    fn startStagedApexCompile(
+        &self,
+        callback: &Strong<dyn ICompilationTaskCallback>,
+    ) -> binder::Result<Strong<dyn ICompilationTask>> {
+        check_permissions()?;
+        to_binder_result(self.do_start_staged_apex_compile(callback))
+    }
+
     fn startTestCompile(
         &self,
         callback: &Strong<dyn ICompilationTaskCallback>,
     ) -> binder::Result<Strong<dyn ICompilationTask>> {
-        check_test_permissions()?;
+        check_permissions()?;
         to_binder_result(self.do_start_test_compile(callback))
     }
 
     fn startTestOdrefresh(&self) -> binder::Result<i8> {
-        check_test_permissions()?;
+        check_permissions()?;
         to_binder_result(self.do_odrefresh_for_test())
     }
 }
 
 impl IsolatedCompilationService {
+    fn do_start_staged_apex_compile(
+        &self,
+        callback: &Strong<dyn ICompilationTaskCallback>,
+    ) -> Result<Strong<dyn ICompilationTask>> {
+        // TODO: Try to start the current instance with staged APEXes to see if it works?
+        let comp_os = self.instance_manager.start_pending_instance().context("Starting CompOS")?;
+
+        let task = CompilationTask::start_staged_apex_compile(comp_os, callback)?;
+
+        Ok(BnCompilationTask::new_binder(task, BinderFeatures::default()))
+    }
+
     fn do_start_test_compile(
         &self,
         callback: &Strong<dyn ICompilationTaskCallback>,
@@ -114,7 +134,7 @@
     }
 }
 
-fn check_test_permissions() -> binder::Result<()> {
+fn check_permissions() -> binder::Result<()> {
     let calling_uid = ThreadState::get_calling_uid();
     // This should only be called by system server, or root while testing
     if calling_uid != AID_SYSTEM && calling_uid != AID_ROOT {
@@ -135,5 +155,5 @@
         // make the library happy. The value does not appear to matter elsewhere in the library.
         .read(true)
         .open(path)
-        .with_context(|| format!("Failed to open {} directory as path fd", path.display()))
+        .with_context(|| format!("Failed to open {:?} directory as path fd", path))
 }
diff --git a/compos/composd_cmd/composd_cmd.rs b/compos/composd_cmd/composd_cmd.rs
index e591794..f22dc13 100644
--- a/compos/composd_cmd/composd_cmd.rs
+++ b/compos/composd_cmd/composd_cmd.rs
@@ -18,12 +18,13 @@
 
 use android_system_composd::{
     aidl::android::system::composd::{
+        ICompilationTask::ICompilationTask,
         ICompilationTaskCallback::{BnCompilationTaskCallback, ICompilationTaskCallback},
         IIsolatedCompilationService::IIsolatedCompilationService,
     },
     binder::{
         wait_for_interface, BinderFeatures, DeathRecipient, IBinder, Interface, ProcessState,
-        Result as BinderResult,
+        Result as BinderResult, Strong,
     },
 };
 use anyhow::{bail, Context, Result};
@@ -37,7 +38,7 @@
             .index(1)
             .takes_value(true)
             .required(true)
-            .possible_values(&["forced-compile-test", "forced-odrefresh"]),
+            .possible_values(&["staged-apex-compile", "forced-compile-test", "forced-odrefresh"]),
     );
     let args = app.get_matches();
     let command = args.value_of("command").unwrap();
@@ -45,6 +46,7 @@
     ProcessState::start_thread_pool();
 
     match command {
+        "staged-apex-compile" => run_staged_apex_compile()?,
         "forced-compile-test" => run_forced_compile_for_test()?,
         "forced-odrefresh" => run_forced_odrefresh_for_test()?,
         _ => panic!("Unexpected command {}", command),
@@ -103,14 +105,28 @@
     }
 }
 
+fn run_staged_apex_compile() -> Result<()> {
+    run_async_compilation(|service, callback| service.startStagedApexCompile(callback))
+}
+
 fn run_forced_compile_for_test() -> Result<()> {
+    run_async_compilation(|service, callback| service.startTestCompile(callback))
+}
+
+fn run_async_compilation<F>(start_compile_fn: F) -> Result<()>
+where
+    F: FnOnce(
+        &dyn IIsolatedCompilationService,
+        &Strong<dyn ICompilationTaskCallback>,
+    ) -> BinderResult<Strong<dyn ICompilationTask>>,
+{
     let service = wait_for_interface::<dyn IIsolatedCompilationService>("android.system.composd")
         .context("Failed to connect to composd service")?;
 
     let state = Arc::new(State::default());
     let callback = Callback(state.clone());
     let callback = BnCompilationTaskCallback::new_binder(callback, BinderFeatures::default());
-    let task = service.startTestCompile(&callback).context("Compilation failed")?;
+    let task = start_compile_fn(&*service, &callback).context("Compilation failed")?;
 
     // Make sure composd keeps going even if we don't hold a reference to its service.
     drop(service);
diff --git a/compos/service/java/com/android/server/compos/IsolatedCompilationJobService.java b/compos/service/java/com/android/server/compos/IsolatedCompilationJobService.java
index da399cc..685d60c 100644
--- a/compos/service/java/com/android/server/compos/IsolatedCompilationJobService.java
+++ b/compos/service/java/com/android/server/compos/IsolatedCompilationJobService.java
@@ -138,6 +138,7 @@
             }
 
             try {
+                // TODO(b/205296305) Call startStagedApexCompile instead
                 ICompilationTask composTask = composd.startTestCompile(this);
                 mTask.set(composTask);
                 composTask.asBinder().linkToDeath(this, 0);