Merge "When testing CompOS, write somewhere harmless"
diff --git a/compos/common/lib.rs b/compos/common/lib.rs
index 104b8e5..0b84a28 100644
--- a/compos/common/lib.rs
+++ b/compos/common/lib.rs
@@ -33,11 +33,15 @@
 
 /// The sub-directory where we store information relating to the pending instance
 /// of CompOS (based on staged APEXes).
-pub const PENDING_DIR: &str = "pending";
+pub const PENDING_INSTANCE_DIR: &str = "pending";
 
 /// The sub-directory where we store information relating to the current instance
 /// of CompOS (based on active APEXes).
-pub const CURRENT_DIR: &str = "current";
+pub const CURRENT_INSTANCE_DIR: &str = "current";
+
+/// The sub-directory where we store information relating to the instance of CompOS used for
+/// tests.
+pub const TEST_INSTANCE_DIR: &str = "test";
 
 /// The file that holds the encrypted private key for a CompOS instance.
 pub const PRIVATE_KEY_BLOB_FILE: &str = "key.blob";
diff --git a/compos/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl b/compos/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
index a1bb92c..3d0ad31 100644
--- a/compos/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
+++ b/compos/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
@@ -19,8 +19,13 @@
 import com.android.compos.FdAnnotation;
 
 interface IIsolatedCompilationService {
-    /** Run "odrefresh --force-compile" in CompOS. */
-    void runForcedCompile();
+    /**
+     * 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,
+     * and writes the results to a test directory to avoid disrupting any real artifacts in
+     * existence.
+     */
+    void runForcedCompileForTest();
 
     /**
      * Run dex2oat in the currently running instance of the CompOS VM. This is a simple proxy
diff --git a/compos/composd/src/instance_manager.rs b/compos/composd/src/instance_manager.rs
index 6b36ed8..e31296d 100644
--- a/compos/composd/src/instance_manager.rs
+++ b/compos/composd/src/instance_manager.rs
@@ -22,7 +22,7 @@
 use anyhow::{bail, Context, Result};
 use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
 use compos_aidl_interface::binder::Strong;
-use compos_common::CURRENT_DIR;
+use compos_common::{CURRENT_INSTANCE_DIR, TEST_INSTANCE_DIR};
 use std::sync::{Arc, Mutex, Weak};
 use virtualizationservice::IVirtualizationService::IVirtualizationService;
 
@@ -42,13 +42,22 @@
         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)
+    }
+
+    pub fn start_test_instance(&self) -> Result<Arc<CompOsInstance>> {
+        self.start_instance(TEST_INSTANCE_DIR)
+    }
+
+    fn start_instance(&self, instance_name: &str) -> Result<Arc<CompOsInstance>> {
         let mut state = self.state.lock().unwrap();
         state.mark_starting()?;
         // Don't hold the lock while we start the instance to avoid blocking other callers.
         drop(state);
 
-        let instance = self.try_start_current_instance();
+        let instance = self.try_start_instance(instance_name);
 
         let mut state = self.state.lock().unwrap();
         if let Ok(ref instance) = instance {
@@ -59,8 +68,8 @@
         instance
     }
 
-    fn try_start_current_instance(&self) -> Result<Arc<CompOsInstance>> {
-        let instance_starter = InstanceStarter::new(CURRENT_DIR);
+    fn try_start_instance(&self, instance_name: &str) -> Result<Arc<CompOsInstance>> {
+        let instance_starter = InstanceStarter::new(instance_name);
         let compos_instance = instance_starter.create_or_start_instance(&*self.service)?;
 
         Ok(Arc::new(compos_instance))
diff --git a/compos/composd/src/odrefresh.rs b/compos/composd/src/odrefresh.rs
index 2d880e2..2dfc3a1 100644
--- a/compos/composd/src/odrefresh.rs
+++ b/compos/composd/src/odrefresh.rs
@@ -37,10 +37,11 @@
     CleanupFailed = EX_MAX + 4,
 }
 
-pub fn run_forced_compile() -> Result<ExitCode> {
+pub fn run_forced_compile(target_dir: &str) -> Result<ExitCode> {
     // We don`t need to capture stdout/stderr - odrefresh writes to the log
     let mut odrefresh = Command::new(ODREFRESH_BIN)
         .arg(format!("--use-compilation-os={}", VMADDR_CID_ANY as i32))
+        .arg(format!("--dalvik-cache={}", target_dir))
         .arg("--force-compile")
         .spawn()
         .context("Running odrefresh")?;
diff --git a/compos/composd/src/service.rs b/compos/composd/src/service.rs
index be9c30c..d3b73a1 100644
--- a/compos/composd/src/service.rs
+++ b/compos/composd/src/service.rs
@@ -42,9 +42,9 @@
 impl Interface for IsolatedCompilationService {}
 
 impl IIsolatedCompilationService for IsolatedCompilationService {
-    fn runForcedCompile(&self) -> binder::Result<()> {
+    fn runForcedCompileForTest(&self) -> binder::Result<()> {
         // TODO - check caller is system or shell/root?
-        to_binder_result(self.do_run_forced_compile())
+        to_binder_result(self.do_run_forced_compile_for_test())
     }
 
     fn compile_cmd(
@@ -70,12 +70,12 @@
 }
 
 impl IsolatedCompilationService {
-    fn do_run_forced_compile(&self) -> Result<()> {
-        info!("runForcedCompile");
+    fn do_run_forced_compile_for_test(&self) -> Result<()> {
+        info!("runForcedCompileForTest");
 
-        let comp_os = self.instance_manager.start_current_instance().context("Starting CompOS")?;
+        let comp_os = self.instance_manager.start_test_instance().context("Starting CompOS")?;
 
-        let exit_code = odrefresh::run_forced_compile()?;
+        let exit_code = odrefresh::run_forced_compile("test-artifacts")?;
 
         if exit_code != odrefresh::ExitCode::CompilationSuccess {
             bail!("Unexpected odrefresh result: {:?}", exit_code);
diff --git a/compos/composd_cmd/Android.bp b/compos/composd_cmd/Android.bp
index 5fef86f..0081a0d 100644
--- a/compos/composd_cmd/Android.bp
+++ b/compos/composd_cmd/Android.bp
@@ -10,6 +10,7 @@
         "android.system.composd-rust",
         "libanyhow",
         "libbinder_rs",
+        "libclap",
     ],
     prefer_rlib: true,
     apex_available: [
diff --git a/compos/composd_cmd/composd_cmd.rs b/compos/composd_cmd/composd_cmd.rs
index e4884e3..04398c0 100644
--- a/compos/composd_cmd/composd_cmd.rs
+++ b/compos/composd_cmd/composd_cmd.rs
@@ -23,12 +23,25 @@
 use anyhow::{Context, Result};
 
 fn main() -> Result<()> {
+    let app = clap::App::new("composd_cmd").arg(
+        clap::Arg::with_name("command")
+            .index(1)
+            .takes_value(true)
+            .required(true)
+            .possible_values(&["forced-compile-test"]),
+    );
+    let args = app.get_matches();
+    let command = args.value_of("command").unwrap();
+
     ProcessState::start_thread_pool();
 
     let service = wait_for_interface::<dyn IIsolatedCompilationService>("android.system.composd")
         .context("Failed to connect to composd service")?;
 
-    service.runForcedCompile().context("Compilation failed")?;
+    match command {
+        "forced-compile-test" => service.runForcedCompileForTest().context("Compilation failed")?,
+        _ => panic!("Unexpected command {}", command),
+    }
 
     println!("All Ok!");
 
diff --git a/compos/tests/java/android/compos/test/ComposTestCase.java b/compos/tests/java/android/compos/test/ComposTestCase.java
index 81e78ab..ba8d970 100644
--- a/compos/tests/java/android/compos/test/ComposTestCase.java
+++ b/compos/tests/java/android/compos/test/ComposTestCase.java
@@ -37,12 +37,13 @@
 
     // Binaries used in test. (These paths are valid both in host and Microdroid.)
     private static final String ODREFRESH_BIN = "/apex/com.android.art/bin/odrefresh";
-    private static final String COMPOS_KEY_CMD_BIN = "/apex/com.android.compos/bin/compos_key_cmd";
     private static final String COMPOSD_CMD_BIN = "/apex/com.android.compos/bin/composd_cmd";
 
     /** Output directory of odrefresh */
+    private static final String TEST_ARTIFACTS_DIR = "test-artifacts";
+
     private static final String ODREFRESH_OUTPUT_DIR =
-            "/data/misc/apexdata/com.android.art/dalvik-cache";
+            "/data/misc/apexdata/com.android.art/" + TEST_ARTIFACTS_DIR;
 
     /** Timeout of odrefresh to finish */
     private static final int ODREFRESH_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
@@ -51,12 +52,8 @@
     private static final int OKAY = 0;
     private static final int COMPILATION_SUCCESS = 80;
 
-    // Files that define the "current" instance of CompOS
-    private static final String COMPOS_CURRENT_ROOT =
-            "/data/misc/apexdata/com.android.compos/current/";
-    private static final String INSTANCE_IMAGE = COMPOS_CURRENT_ROOT + "instance.img";
-    private static final String PUBLIC_KEY = COMPOS_CURRENT_ROOT + "key.pubkey";
-    private static final String PRIVATE_KEY_BLOB = COMPOS_CURRENT_ROOT + "key.blob";
+    // Files that define the "test" instance of CompOS
+    private static final String COMPOS_TEST_ROOT = "/data/misc/apexdata/com.android.compos/test/";
 
     @Before
     public void setUp() throws Exception {
@@ -70,7 +67,7 @@
         CommandRunner android = new CommandRunner(getDevice());
 
         // Clear up any CompOS instance files we created
-        android.tryRun("rm", "-rf", COMPOS_CURRENT_ROOT);
+        android.tryRun("rm", "-rf", COMPOS_TEST_ROOT);
 
         // And any artifacts generated by odrefresh
         android.tryRun("rm", "-rf", ODREFRESH_OUTPUT_DIR);
@@ -83,9 +80,7 @@
         // Prepare the groundtruth. The compilation on Android should finish successfully.
         {
             long start = System.currentTimeMillis();
-            CommandResult result =
-                    android.runForResultWithTimeout(
-                            ODREFRESH_TIMEOUT_MS, ODREFRESH_BIN, "--force-compile");
+            CommandResult result = runOdrefresh(android, "--force-compile");
             long elapsed = System.currentTimeMillis() - start;
             assertThat(result.getExitCode()).isEqualTo(COMPILATION_SUCCESS);
             CLog.i("Local compilation took " + elapsed + "ms");
@@ -95,17 +90,18 @@
         String expectedChecksumSnapshot = checksumDirectoryContent(android, ODREFRESH_OUTPUT_DIR);
 
         // Let --check clean up the output.
-        CommandResult result =
-                android.runForResultWithTimeout(ODREFRESH_TIMEOUT_MS, ODREFRESH_BIN, "--check");
+        CommandResult result = runOdrefresh(android, "--check");
         assertThat(result.getExitCode()).isEqualTo(OKAY);
 
         // Make sure we generate a fresh instance
-        android.tryRun("rm", "-rf", COMPOS_CURRENT_ROOT);
+        android.tryRun("rm", "-rf", COMPOS_TEST_ROOT);
 
         // Expect the compilation in Compilation OS to finish successfully.
         {
             long start = System.currentTimeMillis();
-            result = android.runForResultWithTimeout(ODREFRESH_TIMEOUT_MS, COMPOSD_CMD_BIN);
+            result =
+                    android.runForResultWithTimeout(
+                            ODREFRESH_TIMEOUT_MS, COMPOSD_CMD_BIN, "forced-compile-test");
             long elapsed = System.currentTimeMillis() - start;
             assertThat(result.getExitCode()).isEqualTo(0);
             CLog.i("Comp OS compilation took " + elapsed + "ms");
@@ -116,16 +112,24 @@
         String actualChecksumSnapshot = checksumDirectoryContent(android, ODREFRESH_OUTPUT_DIR);
 
         // Expect the output to be valid.
-        result = android.runForResultWithTimeout(ODREFRESH_TIMEOUT_MS, ODREFRESH_BIN, "--verify");
+        result = runOdrefresh(android, "--verify");
         assertThat(result.getExitCode()).isEqualTo(OKAY);
         // --check can delete the output, so run later.
-        result = android.runForResultWithTimeout(ODREFRESH_TIMEOUT_MS, ODREFRESH_BIN, "--check");
+        result = runOdrefresh(android, "--check");
         assertThat(result.getExitCode()).isEqualTo(OKAY);
 
         // Expect the output of Comp OS to be the same as compiled on Android.
         assertThat(actualChecksumSnapshot).isEqualTo(expectedChecksumSnapshot);
     }
 
+    private CommandResult runOdrefresh(CommandRunner android, String command) throws Exception {
+        return android.runForResultWithTimeout(
+                ODREFRESH_TIMEOUT_MS,
+                ODREFRESH_BIN,
+                "--dalvik-cache=" + TEST_ARTIFACTS_DIR,
+                command);
+    }
+
     private void killVmAndReconnectAdb() throws Exception {
         CommandRunner android = new CommandRunner(getDevice());
 
diff --git a/compos/verify_key/verify_key.rs b/compos/verify_key/verify_key.rs
index 0cc6473..a5b0b6b 100644
--- a/compos/verify_key/verify_key.rs
+++ b/compos/verify_key/verify_key.rs
@@ -21,8 +21,8 @@
 use compos_aidl_interface::binder::ProcessState;
 use compos_common::compos_client::VmInstance;
 use compos_common::{
-    COMPOS_DATA_ROOT, CURRENT_DIR, INSTANCE_IMAGE_FILE, PENDING_DIR, PRIVATE_KEY_BLOB_FILE,
-    PUBLIC_KEY_FILE,
+    COMPOS_DATA_ROOT, CURRENT_INSTANCE_DIR, INSTANCE_IMAGE_FILE, PENDING_INSTANCE_DIR,
+    PRIVATE_KEY_BLOB_FILE, PUBLIC_KEY_FILE,
 };
 use std::fs::{self, File};
 use std::io::Read;
@@ -49,7 +49,9 @@
     let do_pending = matches.value_of("instance").unwrap() == "pending";
 
     let instance_dir: PathBuf =
-        [COMPOS_DATA_ROOT, if do_pending { PENDING_DIR } else { CURRENT_DIR }].iter().collect();
+        [COMPOS_DATA_ROOT, if do_pending { PENDING_INSTANCE_DIR } else { CURRENT_INSTANCE_DIR }]
+            .iter()
+            .collect();
 
     if !instance_dir.is_dir() {
         bail!("{} is not a directory", instance_dir.display());
@@ -112,7 +114,7 @@
 }
 
 fn promote_to_current(instance_dir: &Path) -> Result<()> {
-    let current_dir: PathBuf = [COMPOS_DATA_ROOT, CURRENT_DIR].iter().collect();
+    let current_dir: PathBuf = [COMPOS_DATA_ROOT, CURRENT_INSTANCE_DIR].iter().collect();
 
     // This may fail if the directory doesn't exist - which is fine, we only care about the rename
     // succeeding.
diff --git a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
index 9872fbe..b3a76ce 100644
--- a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
+++ b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
@@ -259,7 +259,7 @@
         }
     }
 
-    public static void rootMicrodroid() throws DeviceNotAvailableException {
+    public static void rootMicrodroid() {
         runOnHost("adb", "-s", MICRODROID_SERIAL, "root");
 
         // TODO(192660959): Figure out the root cause and remove the sleep. For unknown reason,
@@ -280,8 +280,7 @@
 
     // Establish an adb connection to microdroid by letting Android forward the connection to
     // microdroid. Wait until the connection is established and microdroid is booted.
-    public static void adbConnectToMicrodroid(ITestDevice androidDevice, String cid)
-            throws DeviceNotAvailableException {
+    public static void adbConnectToMicrodroid(ITestDevice androidDevice, String cid) {
         long start = System.currentTimeMillis();
         long timeoutMillis = MICRODROID_ADB_CONNECT_TIMEOUT_MINUTES * 60 * 1000;
         long elapsed = 0;