Snap for 8140651 from 64246ce4327d266631524e5f649ad4663fa92fa4 to sdk-release

Change-Id: I50e1eadd7c9fad58aa74a7846d3f9d73ee85402e
diff --git a/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb b/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb
new file mode 100644
index 0000000..4199b2b
--- /dev/null
+++ b/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb
@@ -0,0 +1,12 @@
+drops {
+  android_build_drop {
+    build_id: "8134799"
+    target: "u-boot_pvmfw"
+    source_file: "pvmfw.img"
+  }
+  dest_file: "pvmfw/pvmfw.img"
+  version: ""
+  version_group: ""
+  git_project: "platform/packages/modules/Virtualization"
+  git_branch: "master"
+}
diff --git a/apex/sign_virt_apex.py b/apex/sign_virt_apex.py
index 9a0fe1a..8fe3403 100644
--- a/apex/sign_virt_apex.py
+++ b/apex/sign_virt_apex.py
@@ -294,6 +294,8 @@
         input_dir, 'etc', 'microdroid_bootconfig.app_debuggable')
     bootconfig_full_debuggable = os.path.join(
         input_dir, 'etc', 'microdroid_bootconfig.full_debuggable')
+    uboot_env_img = os.path.join(
+        input_dir, 'etc', 'uboot_env.img')
 
     # Key(pubkey) for bootloader should match with the one used to make VBmeta below
     # while it's okay to use different keys for other image files.
@@ -330,17 +332,21 @@
         MakeVbmetaImage(args, key, vbmeta_img, images=[
                         boot_img, vendor_boot_img, init_boot_img, system_a_img, vendor_a_img])
 
-    # Re-sign bootconfigs with the same key
+    # Re-sign bootconfigs and the uboot_env with the same key
     bootconfig_sign_key = key
     AddHashFooter(args, bootconfig_sign_key, bootconfig_normal)
     AddHashFooter(args, bootconfig_sign_key, bootconfig_app_debuggable)
     AddHashFooter(args, bootconfig_sign_key, bootconfig_full_debuggable)
+    AddHashFooter(args, bootconfig_sign_key, uboot_env_img)
 
-    # Re-sign vbmeta_bootconfig with a chained_partition to "bootconfig"
-    # Note that, for now, `key` and `bootconfig_sign_key` are the same, but technically they
-    # can be different. Vbmeta records pubkeys which signed chained partitions.
+    # Re-sign vbmeta_bootconfig with chained_partitions to "bootconfig" and
+    # "uboot_env". Note that, for now, `key` and `bootconfig_sign_key` are the
+    # same, but technically they can be different. Vbmeta records pubkeys which
+    # signed chained partitions.
     MakeVbmetaImage(args, key, vbmeta_bootconfig_img, chained_partitions={
-                    'bootconfig': bootconfig_sign_key})
+                    'bootconfig': bootconfig_sign_key,
+                    'uboot_env': bootconfig_sign_key,
+    })
 
 
 def VerifyVirtApex(args):
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/common/compos_client.rs b/compos/common/compos_client.rs
index f4b3440..6a35fb0 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -122,6 +122,7 @@
             configPath: config_path.to_owned(),
             debugLevel: debug_level,
             extraIdsigs: vec![idsig_manifest_apk_fd],
+            protectedVm: false,
             memoryMib: VM_MEMORY_MIB,
             numCpus: parameters.cpus.map_or(1, NonZeroU32::get) as i32,
             cpuAffinity: parameters.cpu_set.clone(),
diff --git a/compos/composd/src/instance_manager.rs b/compos/composd/src/instance_manager.rs
index e8c1d9a..2f15cb5 100644
--- a/compos/composd/src/instance_manager.rs
+++ b/compos/composd/src/instance_manager.rs
@@ -43,22 +43,14 @@
     }
 
     pub fn start_pending_instance(&self) -> Result<Arc<CompOsInstance>> {
-        let config_path = Some(PREFER_STAGED_VM_CONFIG_PATH.to_owned());
-        let mut vm_parameters = VmParameters { config_path, ..Default::default() };
-        vm_parameters.cpus = match system_properties::read(DEX2OAT_THREADS_PROP_NAME) {
-            Ok(s) => Some(NonZeroU32::from_str(&s)?),
-            Err(_) => {
-                // dex2oat uses all CPUs by default. To match the behavior, give the VM all CPUs by
-                // default.
-                NonZeroU32::new(num_cpus::get() as u32)
-            }
-        };
-        vm_parameters.cpu_set = system_properties::read(DEX2OAT_CPU_SET_PROP_NAME).ok();
+        let mut vm_parameters = new_vm_parameters()?;
+        vm_parameters.config_path = Some(PREFER_STAGED_VM_CONFIG_PATH.to_owned());
         self.start_instance(PENDING_INSTANCE_DIR, vm_parameters)
     }
 
     pub fn start_test_instance(&self) -> Result<Arc<CompOsInstance>> {
-        let vm_parameters = VmParameters { debug_mode: true, ..Default::default() };
+        let mut vm_parameters = new_vm_parameters()?;
+        vm_parameters.debug_mode = true;
         self.start_instance(TEST_INSTANCE_DIR, vm_parameters)
     }
 
@@ -90,6 +82,19 @@
     }
 }
 
+fn new_vm_parameters() -> Result<VmParameters> {
+    let cpus = match system_properties::read(DEX2OAT_THREADS_PROP_NAME) {
+        Ok(s) => Some(NonZeroU32::from_str(&s)?),
+        Err(_) => {
+            // dex2oat uses all CPUs by default. To match the behavior, give the VM all CPUs by
+            // default.
+            NonZeroU32::new(num_cpus::get() as u32)
+        }
+    };
+    let cpu_set = system_properties::read(DEX2OAT_CPU_SET_PROP_NAME).ok();
+    Ok(VmParameters { cpus, cpu_set, ..Default::default() })
+}
+
 // Ensures we only run one instance at a time.
 // Valid states:
 // Starting: is_starting is true, running_instance is None.
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/native/compos_native.cpp b/compos/native/compos_native.cpp
index f529421..81280fd 100644
--- a/compos/native/compos_native.cpp
+++ b/compos/native/compos_native.cpp
@@ -42,7 +42,7 @@
     bssl::UniquePtr<RSA> key_pair(RSA_new());
 
     // This function specifies that the public exponent is always 65537, which is good because
-    // that's  what odsign is expecting.
+    // that's what odsign is expecting.
     if (!RSA_generate_key_fips(key_pair.get(), KEY_BITS, /*callback=*/nullptr)) {
         return make_key_error("Failed to generate key pair");
     }
diff --git a/compos/src/compilation.rs b/compos/src/compilation.rs
index 850a0a8..7e3834a 100644
--- a/compos/src/compilation.rs
+++ b/compos/src/compilation.rs
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
-use anyhow::{bail, Context, Result};
+use anyhow::{anyhow, bail, Context, Result};
 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;
 use std::fs::read_dir;
-use std::os::unix::io::AsRawFd;
 use std::path::{self, Path, PathBuf};
 use std::process::Command;
 
@@ -34,16 +35,14 @@
     },
     IAuthFsService::IAuthFsService,
 };
-use authfs_aidl_interface::binder::{ParcelFileDescriptor, Strong};
+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
 
-/// The number that represents the file descriptor number expecting by the task. The number may be
-/// meaningless in the current process.
-pub type PseudoRawFd = i32;
-
 pub struct OdrefreshContext<'a> {
+    compilation_mode: CompilationMode,
     system_dir_fd: i32,
     output_dir_fd: i32,
     staging_dir_fd: i32,
@@ -54,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,
@@ -61,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");
         }
@@ -79,6 +86,7 @@
         // CompOS.
 
         Ok(Self {
+            compilation_mode,
             system_dir_fd,
             output_dir_fd,
             staging_dir_fd,
@@ -114,19 +122,22 @@
     let authfs = authfs_service.mount(&authfs_config)?;
     let mountpoint = PathBuf::from(authfs.getMountPoint()?);
 
+    // Make a copy of our environment as the basis of the one we will give odrefresh
+    let mut odrefresh_vars = EnvMap::from_current_env();
+
     let mut android_root = mountpoint.clone();
     android_root.push(context.system_dir_fd.to_string());
     android_root.push("system");
-    env::set_var("ANDROID_ROOT", &android_root);
+    odrefresh_vars.set("ANDROID_ROOT", path_to_str(&android_root)?);
     debug!("ANDROID_ROOT={:?}", &android_root);
 
     let art_apex_data = mountpoint.join(context.output_dir_fd.to_string());
-    env::set_var("ART_APEX_DATA", &art_apex_data);
+    odrefresh_vars.set("ART_APEX_DATA", path_to_str(&art_apex_data)?);
     debug!("ART_APEX_DATA={:?}", &art_apex_data);
 
     let staging_dir = mountpoint.join(context.staging_dir_fd.to_string());
 
-    set_classpaths(&android_root)?;
+    set_classpaths(&mut odrefresh_vars, &android_root)?;
 
     let mut args = vec![
         "odrefresh".to_string(),
@@ -144,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, Vec::new() /* fd_mapping */)
+    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);
@@ -173,9 +187,13 @@
     Ok(exit_code)
 }
 
-fn set_classpaths(android_root: &Path) -> Result<()> {
+fn path_to_str(path: &Path) -> Result<&str> {
+    path.to_str().ok_or_else(|| anyhow!("Bad path {:?}", path))
+}
+
+fn set_classpaths(odrefresh_vars: &mut EnvMap, android_root: &Path) -> Result<()> {
     let export_lines = run_derive_classpath(android_root)?;
-    load_classpath_vars(&export_lines)
+    load_classpath_vars(odrefresh_vars, &export_lines)
 }
 
 fn run_derive_classpath(android_root: &Path) -> Result<String> {
@@ -203,15 +221,14 @@
     String::from_utf8(result.stdout).context("Converting derive_classpath output")
 }
 
-fn load_classpath_vars(export_lines: &str) -> Result<()> {
+fn load_classpath_vars(odrefresh_vars: &mut EnvMap, export_lines: &str) -> Result<()> {
     // Each line should be in the format "export <var name> <value>"
     let pattern = Regex::new(r"^export ([^ ]+) ([^ ]+)$").context("Failed to construct Regex")?;
     for line in export_lines.lines() {
         if let Some(captures) = pattern.captures(line) {
             let name = &captures[1];
             let value = &captures[2];
-            // TODO(b/213416778) Don't modify our env, construct a fresh one for odrefresh
-            env::set_var(name, value);
+            odrefresh_vars.set(name, value);
         } else {
             warn!("Malformed line from derive_classpath: {}", line);
         }
@@ -238,14 +255,28 @@
     Ok(())
 }
 
-fn spawn_jailed_task(
-    executable: &Path,
-    args: &[String],
-    fd_mapping: Vec<(ParcelFileDescriptor, PseudoRawFd)>,
-) -> Result<Minijail> {
+fn spawn_jailed_task(executable: &Path, args: &[String], env_vars: &[String]) -> Result<Minijail> {
     // TODO(b/185175567): Run in a more restricted sandbox.
     let jail = Minijail::new()?;
-    let preserve_fds: Vec<_> = fd_mapping.iter().map(|(f, id)| (f.as_raw_fd(), *id)).collect();
-    let _pid = jail.run_remap(executable, preserve_fds.as_slice(), args)?;
+    let keep_fds = [];
+    let command = minijail::Command::new_for_path(executable, &keep_fds, args, Some(env_vars))?;
+    let _pid = jail.run_command(command)?;
     Ok(jail)
 }
+
+struct EnvMap(HashMap<String, String>);
+
+impl EnvMap {
+    fn from_current_env() -> Self {
+        Self(env::vars().collect())
+    }
+
+    fn set(&mut self, key: &str, value: &str) {
+        self.0.insert(key.to_owned(), value.to_owned());
+    }
+
+    fn into_env(self) -> Vec<String> {
+        // execve() expects an array of "k=v" strings, rather than a list of (k, v) pairs.
+        self.0.into_iter().map(|(k, v)| k + "=" + &v).collect()
+    }
+}
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,
diff --git a/compos/tests/java/android/compos/test/ComposKeyTestCase.java b/compos/tests/java/android/compos/test/ComposKeyTestCase.java
index d59d3d9..49235fe 100644
--- a/compos/tests/java/android/compos/test/ComposKeyTestCase.java
+++ b/compos/tests/java/android/compos/test/ComposKeyTestCase.java
@@ -38,13 +38,25 @@
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class ComposKeyTestCase extends VirtualizationTestCaseBase {
 
-    /** Wait time for service to be ready on boot */
+    /**
+     * Wait time for service to be ready on boot
+     */
     private static final int READY_LATENCY_MS = 10 * 1000; // 10 seconds
 
-    /** Path to compos_key_cmd tool */
+    /**
+     * Path to compos_key_cmd tool
+     */
     private static final String COMPOS_KEY_CMD_BIN = "/apex/com.android.compos/bin/compos_key_cmd";
 
-    /** Config of the test VM. This is a path inside the APK. */
+    /**
+     * Path to the com.android.compos.payload APK
+     */
+    private static final String COMPOS_PAYLOAD_APK_PATH =
+            "/apex/com.android.compos/app/CompOSPayloadApp/CompOSPayloadApp.apk";
+
+    /**
+     * Config of the test VM. This is a path inside the APK.
+     */
     private static final String VM_TEST_CONFIG_PATH = "assets/vm_test_config.json";
 
     private String mCid;
@@ -134,7 +146,9 @@
                         getDevice(),
                         getBuild(),
                         /* apkName, no need to install */ null,
-                        packageName,
+                        COMPOS_PAYLOAD_APK_PATH,
+                        /* packageName - not needed, we know the path */ null,
+                        /* extraIdSigPaths */ null,
                         VM_TEST_CONFIG_PATH,
                         /* debug */ true,
                         /* use default memoryMib */ 0,
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index 7d1f9b0..04a90e0 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -51,6 +51,7 @@
     private static final String KEY_APKPATH = "apkPath";
     private static final String KEY_PAYLOADCONFIGPATH = "payloadConfigPath";
     private static final String KEY_DEBUGLEVEL = "debugLevel";
+    private static final String KEY_PROTECTED_VM = "protectedVm";
     private static final String KEY_MEMORY_MIB = "memoryMib";
     private static final String KEY_NUM_CPUS = "numCpus";
     private static final String KEY_CPU_AFFINITY = "cpuAffinity";
@@ -83,6 +84,11 @@
     private final DebugLevel mDebugLevel;
 
     /**
+     * Whether to run the VM in protected mode, so the host can't access its memory.
+     */
+    private final boolean mProtectedVm;
+
+    /**
      * The amount of RAM to give the VM, in MiB. If this is 0 or negative the default will be used.
      */
     private final int mMemoryMib;
@@ -111,6 +117,7 @@
             @NonNull Signature[] certs,
             @NonNull String payloadConfigPath,
             DebugLevel debugLevel,
+            boolean protectedVm,
             int memoryMib,
             int numCpus,
             String cpuAffinity) {
@@ -118,6 +125,7 @@
         mCerts = certs;
         mPayloadConfigPath = payloadConfigPath;
         mDebugLevel = debugLevel;
+        mProtectedVm = protectedVm;
         mMemoryMib = memoryMib;
         mNumCpus = numCpus;
         mCpuAffinity = cpuAffinity;
@@ -149,11 +157,12 @@
             throw new VirtualMachineException("No payloadConfigPath");
         }
         final DebugLevel debugLevel = DebugLevel.values()[b.getInt(KEY_DEBUGLEVEL)];
+        final boolean protectedVm = b.getBoolean(KEY_PROTECTED_VM);
         final int memoryMib = b.getInt(KEY_MEMORY_MIB);
         final int numCpus = b.getInt(KEY_NUM_CPUS);
         final String cpuAffinity = b.getString(KEY_CPU_AFFINITY);
-        return new VirtualMachineConfig(apkPath, certs, payloadConfigPath, debugLevel, memoryMib,
-                numCpus, cpuAffinity);
+        return new VirtualMachineConfig(apkPath, certs, payloadConfigPath, debugLevel, protectedVm,
+                memoryMib, numCpus, cpuAffinity);
     }
 
     /** Persists this config to a stream, for example a file. */
@@ -169,6 +178,8 @@
         b.putStringArray(KEY_CERTS, certs);
         b.putString(KEY_PAYLOADCONFIGPATH, mPayloadConfigPath);
         b.putInt(KEY_DEBUGLEVEL, mDebugLevel.ordinal());
+        b.putBoolean(KEY_PROTECTED_VM, mProtectedVm);
+        b.putInt(KEY_NUM_CPUS, mNumCpus);
         if (mMemoryMib > 0) {
             b.putInt(KEY_MEMORY_MIB, mMemoryMib);
         }
@@ -219,6 +230,7 @@
                 parcel.debugLevel = VirtualMachineAppConfig.DebugLevel.FULL;
                 break;
         }
+        parcel.protectedVm = mProtectedVm;
         parcel.memoryMib = mMemoryMib;
         parcel.numCpus = mNumCpus;
         parcel.cpuAffinity = mCpuAffinity;
@@ -230,16 +242,17 @@
         private Context mContext;
         private String mPayloadConfigPath;
         private DebugLevel mDebugLevel;
+        private boolean mProtectedVm;
         private int mMemoryMib;
         private int mNumCpus;
         private String mCpuAffinity;
-        // TODO(jiyong): add more items like # of cpu, size of ram, debuggability, etc.
 
         /** Creates a builder for the given context (APK), and the payload config file in APK. */
         public Builder(@NonNull Context context, @NonNull String payloadConfigPath) {
             mContext = context;
             mPayloadConfigPath = payloadConfigPath;
             mDebugLevel = DebugLevel.NONE;
+            mProtectedVm = false;
             mNumCpus = 1;
             mCpuAffinity = null;
         }
@@ -250,6 +263,12 @@
             return this;
         }
 
+        /** Sets whether to protect the VM memory from the host. Defaults to false. */
+        public Builder protectedVm(boolean protectedVm) {
+            mProtectedVm = protectedVm;
+            return this;
+        }
+
         /**
          * Sets the amount of RAM to give the VM. If this is zero or negative then the default will
          * be used.
@@ -309,8 +328,8 @@
             }
 
             return new VirtualMachineConfig(
-                    apkPath, certs, mPayloadConfigPath, mDebugLevel, mMemoryMib, mNumCpus,
-                    mCpuAffinity);
+                    apkPath, certs, mPayloadConfigPath, mDebugLevel, mProtectedVm, mMemoryMib,
+                    mNumCpus, mCpuAffinity);
         }
     }
 }
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index e078108..3566bd2 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -73,7 +73,6 @@
         "apexd",
         "debuggerd",
         "diced.microdroid",
-        "keystore2_microdroid",
         "linker",
         "linkerconfig",
         "servicemanager.microdroid",
@@ -81,15 +80,10 @@
         "cgroups.json",
         "public.libraries.android.txt",
 
-        // TODO(b/185767624): remove hidl after full keymint support
-        "hwservicemanager",
-
         "microdroid_plat_sepolicy_and_mapping.sha256",
         "microdroid_file_contexts",
-        "microdroid_hwservice_contexts",
         "microdroid_property_contexts",
         "microdroid_service_contexts",
-        "microdroid_keystore2_key_contexts",
         "microdroid_compatibility_matrix",
         "microdroid_manifest",
 
@@ -179,7 +173,6 @@
     use_avb: true,
     deps: [
         "android.hardware.security.dice-service.microdroid",
-        "android.hardware.security.keymint-service.microdroid",
         "microdroid_fstab",
         "microdroid_precompiled_sepolicy.plat_sepolicy_and_mapping.sha256",
         "microdroid_vendor_manifest",
@@ -355,6 +348,10 @@
             name: "bootconfig",
             private_key: ":microdroid_sign_key",
         },
+        {
+            name: "uboot_env",
+            private_key: ":microdroid_sign_key",
+        },
     ],
 }
 
@@ -531,18 +528,42 @@
 
 genrule {
     name: "microdroid_uboot_env_gen",
-    tools: ["mkenvimage_host"],
-    srcs: ["uboot-env.txt"],
+    tools: [
+        "mkenvimage_host",
+        "avbtool",
+    ],
+    srcs: [
+        "uboot-env.txt",
+        ":microdroid_sign_key",
+    ],
     out: ["output.img"],
-    cmd: "$(location mkenvimage_host) -s 4096 -o $(out) $(in)",
+    cmd: "$(location mkenvimage_host) -s 4096 -o $(out) $(location uboot-env.txt) && " +
+        "$(location avbtool) add_hash_footer " +
+        "--algorithm SHA256_RSA4096 " +
+        "--partition_name uboot_env " +
+        "--key $(location :microdroid_sign_key) " +
+        "--partition_size $$(( " + avb_hash_footer_kb + " * 1024 + ( $$(stat --format=%s $(out)) + 4096 - 1 ) / 4096 * 4096 )) " +
+        "--image $(out)",
 }
 
 genrule {
     name: "microdroid_uboot_env_gen_x86_64",
-    tools: ["mkenvimage_host"],
-    srcs: ["uboot-env-x86_64.txt"],
+    tools: [
+        "mkenvimage_host",
+        "avbtool",
+    ],
+    srcs: [
+        "uboot-env-x86_64.txt",
+        ":microdroid_sign_key",
+    ],
     out: ["output.img"],
-    cmd: "$(location mkenvimage_host) -s 4096 -o $(out) $(in)",
+    cmd: "$(location mkenvimage_host) -s 4096 -o $(out) $(location uboot-env-x86_64.txt) && " +
+        "$(location avbtool) add_hash_footer " +
+        "--algorithm SHA256_RSA4096 " +
+        "--partition_name uboot_env " +
+        "--key $(location :microdroid_sign_key) " +
+        "--partition_size $$(( " + avb_hash_footer_kb + " * 1024 + ( $$(stat --format=%s $(out)) + 4096 - 1 ) / 4096 * 4096 )) " +
+        "--image $(out)",
 }
 
 // Note that keys can be different for filesystem images even though we're using the same key
diff --git a/microdroid/init.rc b/microdroid/init.rc
index e76260e..ebe2464 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -83,9 +83,6 @@
 
     setprop ro.debuggable ${ro.boot.microdroid.debuggable:-0}
 
-    # TODO(b/185767624): remove hidl after full keymint support
-    start hwservicemanager
-
 on init && property:ro.boot.logd.enabled=1
     # Start logd before any other services run to ensure we capture all of their logs.
     start logd
@@ -138,21 +135,15 @@
     # We restorecon /data in case the userdata partition has been reset.
     restorecon /data
 
-    # set up keystore directory structure first so that we can end early boot
+    # set up misc directory structure first so that we can end early boot
     # and start apexd
     mkdir /data/misc 01771 system misc
-    mkdir /data/misc/keystore 0700 keystore keystore
     # work around b/183668221
-    restorecon /data/misc /data/misc/keystore
-
-    start keystore2
+    restorecon /data/misc
 
     mkdir /data/misc/authfs 0700 root root
     start authfs_service
 
-on late-fs
-    start vendor.keymint-microdroid
-
 on post-fs-data
     mark_post_data
 
@@ -169,12 +160,6 @@
 
     start tombstoned
 
-    # Boot level 30
-    # odsign signing keys have MAX_BOOT_LEVEL=30
-    # This is currently the earliest boot level, but we start at 30
-    # to leave room for earlier levels.
-    setprop keystore.boot_level 30
-
     # For security reasons, /data/local/tmp should always be empty.
     # Do not place files or directories in /data/local/tmp
     mkdir /data/local 0751 root root
diff --git a/microdroid/keymint/Android.bp b/microdroid/keymint/Android.bp
deleted file mode 100644
index 7915ada..0000000
--- a/microdroid/keymint/Android.bp
+++ /dev/null
@@ -1,41 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_binary {
-    name: "android.hardware.security.keymint-service.microdroid",
-    relative_install_path: "hw",
-    init_rc: ["android.hardware.security.keymint-service.microdroid.rc"],
-    vintf_fragments: [
-        "android.hardware.security.keymint-service.microdroid.xml",
-    ],
-    vendor: true,
-    cflags: [
-        "-Wall",
-        "-Wextra",
-    ],
-    defaults: [
-        "keymint_use_latest_hal_aidl_ndk_shared",
-    ],
-    shared_libs: [
-        "lib_android_keymaster_keymint_utils",
-        "libbase",
-        "libbinder_ndk",
-        "libcppbor_external",
-        "libcrypto",
-        "libkeymaster_portable",
-        "libkeymint",
-        "liblog",
-        "libpuresoftkeymasterdevice",
-        "libsoft_attestation_cert",
-        "libutils",
-    ],
-    local_include_dirs: [
-        "include",
-    ],
-    srcs: [
-        "MicrodroidKeyMintDevice.cpp",
-        "MicrodroidKeymasterContext.cpp",
-        "service.cpp",
-    ],
-}
diff --git a/microdroid/keymint/MicrodroidKeyMintDevice.cpp b/microdroid/keymint/MicrodroidKeyMintDevice.cpp
deleted file mode 100644
index c2f01f2..0000000
--- a/microdroid/keymint/MicrodroidKeyMintDevice.cpp
+++ /dev/null
@@ -1,431 +0,0 @@
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "android.hardware.security.keymint-impl"
-#include "MicrodroidKeyMintDevice.h"
-
-#include <AndroidKeyMintOperation.h>
-#include <KeyMintUtils.h>
-#include <aidl/android/hardware/security/keymint/ErrorCode.h>
-#include <android-base/logging.h>
-#include <keymaster/android_keymaster.h>
-#include <keymaster/contexts/pure_soft_keymaster_context.h>
-#include <keymaster/keymaster_configuration.h>
-
-#include "MicrodroidKeyMintDevice.h"
-#include "MicrodroidKeymasterContext.h"
-
-namespace aidl::android::hardware::security::keymint {
-
-using namespace keymaster; // NOLINT(google-build-using-namespace)
-
-using km_utils::authToken2AidlVec;
-using km_utils::kmBlob2vector;
-using km_utils::kmError2ScopedAStatus;
-using km_utils::kmParam2Aidl;
-using km_utils::KmParamSet;
-using km_utils::kmParamSet2Aidl;
-using km_utils::legacy_enum_conversion;
-using secureclock::TimeStampToken;
-
-namespace {
-
-vector<KeyCharacteristics> convertKeyCharacteristics(const AuthorizationSet& requestParams,
-                                                     const AuthorizationSet& sw_enforced,
-                                                     const AuthorizationSet& hw_enforced,
-                                                     bool include_keystore_enforced = true) {
-    KeyCharacteristics keyMintEnforced{SecurityLevel::SOFTWARE, {}};
-    KeyCharacteristics keystoreEnforced{SecurityLevel::KEYSTORE, {}};
-    CHECK(hw_enforced.empty()) << "Hardware-enforced list is non-empty for pure SW KeyMint";
-
-    // This is a pure software implementation, so all tags are in sw_enforced.
-    // We need to walk through the SW-enforced list and figure out which tags to
-    // return in the software list and which in the keystore list.
-
-    for (auto& entry : sw_enforced) {
-        switch (entry.tag) {
-            /* Invalid and unused */
-            case KM_TAG_ECIES_SINGLE_HASH_MODE:
-            case KM_TAG_INVALID:
-            case KM_TAG_KDF:
-            case KM_TAG_ROLLBACK_RESISTANCE:
-                CHECK(false) << "We shouldn't see tag " << entry.tag;
-                break;
-
-            /* Unimplemented */
-            case KM_TAG_ALLOW_WHILE_ON_BODY:
-            case KM_TAG_BOOTLOADER_ONLY:
-            case KM_TAG_EARLY_BOOT_ONLY:
-            case KM_TAG_ROLLBACK_RESISTANT:
-            case KM_TAG_STORAGE_KEY:
-            case KM_TAG_TRUSTED_CONFIRMATION_REQUIRED:
-            case KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED:
-                break;
-
-            /* Keystore-enforced if not locally generated. */
-            case KM_TAG_CREATION_DATETIME:
-                // A KeyMaster implementation is required to add this tag to generated/imported
-                // keys. A KeyMint implementation is not required to create this tag, only to echo
-                // it back if it was included in the key generation/import request.
-                if (requestParams.Contains(KM_TAG_CREATION_DATETIME)) {
-                    keystoreEnforced.authorizations.push_back(kmParam2Aidl(entry));
-                }
-                break;
-
-            /* Disallowed in KeyCharacteristics */
-            case KM_TAG_APPLICATION_DATA:
-            case KM_TAG_ATTESTATION_APPLICATION_ID:
-                break;
-
-            /* Not key characteristics */
-            case KM_TAG_ASSOCIATED_DATA:
-            case KM_TAG_ATTESTATION_CHALLENGE:
-            case KM_TAG_ATTESTATION_ID_BRAND:
-            case KM_TAG_ATTESTATION_ID_DEVICE:
-            case KM_TAG_ATTESTATION_ID_IMEI:
-            case KM_TAG_ATTESTATION_ID_MANUFACTURER:
-            case KM_TAG_ATTESTATION_ID_MEID:
-            case KM_TAG_ATTESTATION_ID_MODEL:
-            case KM_TAG_ATTESTATION_ID_PRODUCT:
-            case KM_TAG_ATTESTATION_ID_SERIAL:
-            case KM_TAG_AUTH_TOKEN:
-            case KM_TAG_CERTIFICATE_SERIAL:
-            case KM_TAG_CERTIFICATE_SUBJECT:
-            case KM_TAG_CERTIFICATE_NOT_AFTER:
-            case KM_TAG_CERTIFICATE_NOT_BEFORE:
-            case KM_TAG_CONFIRMATION_TOKEN:
-            case KM_TAG_DEVICE_UNIQUE_ATTESTATION:
-            case KM_TAG_IDENTITY_CREDENTIAL_KEY:
-            case KM_TAG_MAC_LENGTH:
-            case KM_TAG_NONCE:
-            case KM_TAG_RESET_SINCE_ID_ROTATION:
-            case KM_TAG_ROOT_OF_TRUST:
-            case KM_TAG_UNIQUE_ID:
-                break;
-
-            /* KeyMint-enforced */
-            case KM_TAG_ALGORITHM:
-            case KM_TAG_APPLICATION_ID:
-            case KM_TAG_AUTH_TIMEOUT:
-            case KM_TAG_BLOB_USAGE_REQUIREMENTS:
-            case KM_TAG_BLOCK_MODE:
-            case KM_TAG_BOOT_PATCHLEVEL:
-            case KM_TAG_CALLER_NONCE:
-            case KM_TAG_DIGEST:
-            case KM_TAG_EC_CURVE:
-            case KM_TAG_EXPORTABLE:
-            case KM_TAG_INCLUDE_UNIQUE_ID:
-            case KM_TAG_KEY_SIZE:
-            case KM_TAG_MAX_USES_PER_BOOT:
-            case KM_TAG_MIN_MAC_LENGTH:
-            case KM_TAG_MIN_SECONDS_BETWEEN_OPS:
-            case KM_TAG_NO_AUTH_REQUIRED:
-            case KM_TAG_ORIGIN:
-            case KM_TAG_OS_PATCHLEVEL:
-            case KM_TAG_OS_VERSION:
-            case KM_TAG_PADDING:
-            case KM_TAG_PURPOSE:
-            case KM_TAG_RSA_OAEP_MGF_DIGEST:
-            case KM_TAG_RSA_PUBLIC_EXPONENT:
-            case KM_TAG_UNLOCKED_DEVICE_REQUIRED:
-            case KM_TAG_USER_AUTH_TYPE:
-            case KM_TAG_USER_SECURE_ID:
-            case KM_TAG_VENDOR_PATCHLEVEL:
-                keyMintEnforced.authorizations.push_back(kmParam2Aidl(entry));
-                break;
-
-            /* Keystore-enforced */
-            case KM_TAG_ACTIVE_DATETIME:
-            case KM_TAG_ALL_APPLICATIONS:
-            case KM_TAG_ALL_USERS:
-            case KM_TAG_MAX_BOOT_LEVEL:
-            case KM_TAG_ORIGINATION_EXPIRE_DATETIME:
-            case KM_TAG_USAGE_EXPIRE_DATETIME:
-            case KM_TAG_USER_ID:
-            case KM_TAG_USAGE_COUNT_LIMIT:
-                keystoreEnforced.authorizations.push_back(kmParam2Aidl(entry));
-                break;
-        }
-    }
-
-    vector<KeyCharacteristics> retval;
-    retval.reserve(2);
-    if (!keyMintEnforced.authorizations.empty()) retval.push_back(std::move(keyMintEnforced));
-    if (include_keystore_enforced && !keystoreEnforced.authorizations.empty()) {
-        retval.push_back(std::move(keystoreEnforced));
-    }
-
-    return retval;
-}
-
-Certificate convertCertificate(const keymaster_blob_t& cert) {
-    return {std::vector<uint8_t>(cert.data, cert.data + cert.data_length)};
-}
-
-vector<Certificate> convertCertificateChain(const CertificateChain& chain) {
-    vector<Certificate> retval;
-    retval.reserve(chain.entry_count);
-    std::transform(chain.begin(), chain.end(), std::back_inserter(retval), convertCertificate);
-    return retval;
-}
-
-void addClientAndAppData(const std::vector<uint8_t>& appId, const std::vector<uint8_t>& appData,
-                         ::keymaster::AuthorizationSet* params) {
-    params->Clear();
-    if (appId.size()) {
-        params->push_back(::keymaster::TAG_APPLICATION_ID, appId.data(), appId.size());
-    }
-    if (appData.size()) {
-        params->push_back(::keymaster::TAG_APPLICATION_DATA, appData.data(), appData.size());
-    }
-}
-
-} // namespace
-
-constexpr size_t kOperationTableSize = 16;
-
-MicrodroidKeyMintDevice::MicrodroidKeyMintDevice(::keymaster::KeymasterKeyBlob& rootKey)
-      : impl_(new ::keymaster::AndroidKeymaster(
-                [&]() -> auto {
-                    auto context = new MicrodroidKeymasterContext(KmVersion::KEYMINT_1, rootKey);
-                    context->SetSystemVersion(::keymaster::GetOsVersion(),
-                                              ::keymaster::GetOsPatchlevel());
-                    return context;
-                }(),
-                kOperationTableSize)) {}
-
-MicrodroidKeyMintDevice::~MicrodroidKeyMintDevice() {}
-
-ScopedAStatus MicrodroidKeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* info) {
-    info->versionNumber = 1;
-    info->securityLevel = SecurityLevel::SOFTWARE;
-    info->keyMintName = "MicrodroidKeyMintDevice";
-    info->keyMintAuthorName = "Google";
-    info->timestampTokenRequired = false;
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::addRngEntropy(const vector<uint8_t>& data) {
-    if (data.size() == 0) {
-        return ScopedAStatus::ok();
-    }
-
-    AddEntropyRequest request(impl_->message_version());
-    request.random_data.Reinitialize(data.data(), data.size());
-
-    AddEntropyResponse response(impl_->message_version());
-    impl_->AddRngEntropy(request, &response);
-
-    return kmError2ScopedAStatus(response.error);
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::generateKey(const vector<KeyParameter>& keyParams,
-                                                   const optional<AttestationKey>& attestationKey,
-                                                   KeyCreationResult* creationResult) {
-    GenerateKeyRequest request(impl_->message_version());
-    request.key_description.Reinitialize(KmParamSet(keyParams));
-    if (attestationKey) {
-        request.attestation_signing_key_blob =
-                KeymasterKeyBlob(attestationKey->keyBlob.data(), attestationKey->keyBlob.size());
-        request.attest_key_params.Reinitialize(KmParamSet(attestationKey->attestKeyParams));
-        request.issuer_subject = KeymasterBlob(attestationKey->issuerSubjectName.data(),
-                                               attestationKey->issuerSubjectName.size());
-    }
-
-    GenerateKeyResponse response(impl_->message_version());
-    impl_->GenerateKey(request, &response);
-
-    if (response.error != KM_ERROR_OK) {
-        // Note a key difference between this current aidl and previous hal, is
-        // that hal returns void where as aidl returns the error status.  If
-        // aidl returns error, then aidl will not return any change you may make
-        // to the out parameters.  This is quite different from hal where all
-        // output variable can be modified due to hal returning void.
-        //
-        // So the caller need to be aware not to expect aidl functions to clear
-        // the output variables for you in case of error.  If you left some
-        // wrong data set in the out parameters, they will stay there.
-        return kmError2ScopedAStatus(response.error);
-    }
-
-    creationResult->keyBlob = kmBlob2vector(response.key_blob);
-    creationResult->keyCharacteristics =
-            convertKeyCharacteristics(request.key_description, response.unenforced,
-                                      response.enforced);
-    creationResult->certificateChain = convertCertificateChain(response.certificate_chain);
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::importKey(const vector<KeyParameter>& keyParams,
-                                                 KeyFormat keyFormat,
-                                                 const vector<uint8_t>& keyData,
-                                                 const optional<AttestationKey>& attestationKey,
-                                                 KeyCreationResult* creationResult) {
-    ImportKeyRequest request(impl_->message_version());
-    request.key_description.Reinitialize(KmParamSet(keyParams));
-    request.key_format = legacy_enum_conversion(keyFormat);
-    request.key_data = KeymasterKeyBlob(keyData.data(), keyData.size());
-    if (attestationKey) {
-        request.attestation_signing_key_blob =
-                KeymasterKeyBlob(attestationKey->keyBlob.data(), attestationKey->keyBlob.size());
-        request.attest_key_params.Reinitialize(KmParamSet(attestationKey->attestKeyParams));
-        request.issuer_subject = KeymasterBlob(attestationKey->issuerSubjectName.data(),
-                                               attestationKey->issuerSubjectName.size());
-    }
-
-    ImportKeyResponse response(impl_->message_version());
-    impl_->ImportKey(request, &response);
-
-    if (response.error != KM_ERROR_OK) {
-        return kmError2ScopedAStatus(response.error);
-    }
-
-    creationResult->keyBlob = kmBlob2vector(response.key_blob);
-    creationResult->keyCharacteristics =
-            convertKeyCharacteristics(request.key_description, response.unenforced,
-                                      response.enforced);
-    creationResult->certificateChain = convertCertificateChain(response.certificate_chain);
-
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::importWrappedKey(
-        const vector<uint8_t>& wrappedKeyData, const vector<uint8_t>& wrappingKeyBlob,
-        const vector<uint8_t>& maskingKey, const vector<KeyParameter>& unwrappingParams,
-        int64_t passwordSid, int64_t biometricSid, KeyCreationResult* creationResult) {
-    ImportWrappedKeyRequest request(impl_->message_version());
-    request.SetWrappedMaterial(wrappedKeyData.data(), wrappedKeyData.size());
-    request.SetWrappingMaterial(wrappingKeyBlob.data(), wrappingKeyBlob.size());
-    request.SetMaskingKeyMaterial(maskingKey.data(), maskingKey.size());
-    request.additional_params.Reinitialize(KmParamSet(unwrappingParams));
-    request.password_sid = static_cast<uint64_t>(passwordSid);
-    request.biometric_sid = static_cast<uint64_t>(biometricSid);
-
-    ImportWrappedKeyResponse response(impl_->message_version());
-    impl_->ImportWrappedKey(request, &response);
-
-    if (response.error != KM_ERROR_OK) {
-        return kmError2ScopedAStatus(response.error);
-    }
-
-    creationResult->keyBlob = kmBlob2vector(response.key_blob);
-    creationResult->keyCharacteristics =
-            convertKeyCharacteristics(request.additional_params, response.unenforced,
-                                      response.enforced);
-    creationResult->certificateChain = convertCertificateChain(response.certificate_chain);
-
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::upgradeKey(const vector<uint8_t>& keyBlobToUpgrade,
-                                                  const vector<KeyParameter>& upgradeParams,
-                                                  vector<uint8_t>* keyBlob) {
-    UpgradeKeyRequest request(impl_->message_version());
-    request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
-    request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
-
-    UpgradeKeyResponse response(impl_->message_version());
-    impl_->UpgradeKey(request, &response);
-
-    if (response.error != KM_ERROR_OK) {
-        return kmError2ScopedAStatus(response.error);
-    }
-
-    *keyBlob = kmBlob2vector(response.upgraded_key);
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::deleteKey(const vector<uint8_t>&) {
-    // There's nothing to be done to delete software key blobs.
-    return kmError2ScopedAStatus(KM_ERROR_OK);
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::deleteAllKeys() {
-    // There's nothing to be done to delete software key blobs.
-    return kmError2ScopedAStatus(KM_ERROR_OK);
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::destroyAttestationIds() {
-    return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::begin(KeyPurpose purpose, const vector<uint8_t>& keyBlob,
-                                             const vector<KeyParameter>& params,
-                                             const optional<HardwareAuthToken>& authToken,
-                                             BeginResult* result) {
-    BeginOperationRequest request(impl_->message_version());
-    request.purpose = legacy_enum_conversion(purpose);
-    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
-    request.additional_params.Reinitialize(KmParamSet(params));
-
-    vector<uint8_t> vector_token = authToken2AidlVec(authToken);
-    request.additional_params.push_back(TAG_AUTH_TOKEN,
-                                        reinterpret_cast<uint8_t*>(vector_token.data()),
-                                        vector_token.size());
-
-    BeginOperationResponse response(impl_->message_version());
-    impl_->BeginOperation(request, &response);
-
-    if (response.error != KM_ERROR_OK) {
-        return kmError2ScopedAStatus(response.error);
-    }
-
-    result->params = kmParamSet2Aidl(response.output_params);
-    result->challenge = response.op_handle;
-    result->operation =
-            ndk::SharedRefBase::make<AndroidKeyMintOperation>(impl_, response.op_handle);
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::deviceLocked(
-        bool, const std::optional<secureclock::TimeStampToken>&) {
-    // Microdroid doesn't yet have a concept of a locked device.
-    return kmError2ScopedAStatus(KM_ERROR_OK);
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::earlyBootEnded() {
-    return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::convertStorageKeyToEphemeral(
-        const std::vector<uint8_t>& /* storageKeyBlob */,
-        std::vector<uint8_t>* /* ephemeralKeyBlob */) {
-    return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
-}
-
-ScopedAStatus MicrodroidKeyMintDevice::getKeyCharacteristics(
-        const std::vector<uint8_t>& keyBlob, const std::vector<uint8_t>& appId,
-        const std::vector<uint8_t>& appData, std::vector<KeyCharacteristics>* keyCharacteristics) {
-    GetKeyCharacteristicsRequest request(impl_->message_version());
-    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
-    addClientAndAppData(appId, appData, &request.additional_params);
-
-    GetKeyCharacteristicsResponse response(impl_->message_version());
-    impl_->GetKeyCharacteristics(request, &response);
-
-    if (response.error != KM_ERROR_OK) {
-        return kmError2ScopedAStatus(response.error);
-    }
-
-    AuthorizationSet emptySet;
-    *keyCharacteristics =
-            convertKeyCharacteristics(emptySet, response.unenforced, response.enforced,
-                                      /* include_keystore_enforced = */ false);
-
-    return ScopedAStatus::ok();
-}
-
-} // namespace aidl::android::hardware::security::keymint
diff --git a/microdroid/keymint/MicrodroidKeymasterContext.cpp b/microdroid/keymint/MicrodroidKeymasterContext.cpp
deleted file mode 100644
index 1d1346b..0000000
--- a/microdroid/keymint/MicrodroidKeymasterContext.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "MicrodroidKeymasterContext.h"
-
-#include <android-base/logging.h>
-#include <keymaster/key.h>
-#include <keymaster/key_blob_utils/auth_encrypted_key_blob.h>
-#include <keymaster/key_blob_utils/software_keyblobs.h>
-
-using namespace ::keymaster;
-
-// This value is used for the ROOT_OF_TRUST tag which is only used in
-// attestation records which aren't supported in this implementation so a
-// constant doesn't cause any hard. MicroDroid SoftWare root-of-trust.
-static uint8_t SWROT[] = {'M', 'D', 'S', 'W'};
-static const KeymasterBlob microdroidSoftwareRootOfTrust(SWROT);
-
-keymaster_error_t MicrodroidKeymasterContext::CreateKeyBlob(const AuthorizationSet& key_description,
-                                                            keymaster_key_origin_t origin,
-                                                            const KeymasterKeyBlob& key_material,
-                                                            KeymasterKeyBlob* blob,
-                                                            AuthorizationSet* hw_enforced,
-                                                            AuthorizationSet* sw_enforced) const {
-    keymaster_error_t error;
-
-    if (key_description.GetTagValue(TAG_ROLLBACK_RESISTANCE)) {
-        return KM_ERROR_ROLLBACK_RESISTANCE_UNAVAILABLE;
-    }
-
-    error = SetKeyBlobAuthorizations(key_description, origin, os_version_, os_patchlevel_,
-                                     hw_enforced, sw_enforced);
-    if (error != KM_ERROR_OK) return error;
-
-    AuthorizationSet hidden;
-    error = BuildHiddenAuthorizations(key_description, &hidden, microdroidSoftwareRootOfTrust);
-    if (error != KM_ERROR_OK) return error;
-
-    CHECK(hw_enforced->empty());
-
-    // Note that the authorizations included in the blob are not encrypted. This
-    // doesn't pose a problem for the current applications but may be a
-    // candidate for hardening.
-    auto encrypted_key = EncryptKey(key_material, AES_GCM_WITH_SW_ENFORCED, *hw_enforced,
-                                    *sw_enforced, hidden, SecureDeletionData{}, root_key_, random_);
-    if (!encrypted_key) return encrypted_key.error();
-
-    auto serialized = SerializeAuthEncryptedBlob(*encrypted_key, *hw_enforced, *sw_enforced,
-                                                 0 /* key_slot */);
-    if (!serialized) return serialized.error();
-    *blob = *serialized;
-    return KM_ERROR_OK;
-}
-
-keymaster_error_t MicrodroidKeymasterContext::ParseKeyBlob(
-        const KeymasterKeyBlob& blob, const AuthorizationSet& additional_params,
-        UniquePtr<Key>* key) const {
-    keymaster_error_t error;
-
-    AuthorizationSet hidden;
-    error = BuildHiddenAuthorizations(additional_params, &hidden, microdroidSoftwareRootOfTrust);
-    if (error != KM_ERROR_OK) return error;
-
-    auto deserialized_key = DeserializeAuthEncryptedBlob(blob);
-    if (!deserialized_key) return deserialized_key.error();
-
-    keymaster_algorithm_t algorithm;
-    if (!deserialized_key->sw_enforced.GetTagValue(TAG_ALGORITHM, &algorithm)) {
-        return KM_ERROR_INVALID_ARGUMENT;
-    }
-
-    auto key_material = DecryptKey(*deserialized_key, hidden, SecureDeletionData{}, root_key_);
-    if (!key_material) return key_material.error();
-
-    auto factory = GetKeyFactory(algorithm);
-    return factory->LoadKey(move(*key_material), additional_params,
-                            move(deserialized_key->hw_enforced),
-                            move(deserialized_key->sw_enforced), key);
-}
-
-static bool UpgradeIntegerTag(keymaster_tag_t tag, uint32_t value, AuthorizationSet* set) {
-    int index = set->find(tag);
-    if (index == -1) {
-        keymaster_key_param_t param;
-        param.tag = tag;
-        param.integer = value;
-        set->push_back(param);
-        return true;
-    }
-
-    if (set->params[index].integer > value) return false;
-
-    if (set->params[index].integer != value) {
-        set->params[index].integer = value;
-    }
-    return true;
-}
-
-keymaster_error_t MicrodroidKeymasterContext::UpgradeKeyBlob(const KeymasterKeyBlob& key_to_upgrade,
-                                                             const AuthorizationSet& upgrade_params,
-                                                             KeymasterKeyBlob* upgraded_key) const {
-    UniquePtr<Key> key;
-    keymaster_error_t error = ParseKeyBlob(key_to_upgrade, upgrade_params, &key);
-    if (error != KM_ERROR_OK) return error;
-
-    if (os_version_ == 0) {
-        // We need to allow "upgrading" OS version to zero, to support upgrading from proper
-        // numbered releases to unnumbered development and preview releases.
-
-        int key_os_version_pos = key->sw_enforced().find(TAG_OS_VERSION);
-        if (key_os_version_pos != -1) {
-            uint32_t key_os_version = key->sw_enforced()[key_os_version_pos].integer;
-            if (key_os_version != 0) {
-                key->sw_enforced()[key_os_version_pos].integer = os_version_;
-            }
-        }
-    }
-
-    if (!UpgradeIntegerTag(TAG_OS_VERSION, os_version_, &key->sw_enforced()) ||
-        !UpgradeIntegerTag(TAG_OS_PATCHLEVEL, os_patchlevel_, &key->sw_enforced()))
-        // One of the version fields would have been a downgrade. Not allowed.
-        return KM_ERROR_INVALID_ARGUMENT;
-
-    AuthorizationSet hidden;
-    error = BuildHiddenAuthorizations(upgrade_params, &hidden, microdroidSoftwareRootOfTrust);
-    if (error != KM_ERROR_OK) return error;
-
-    auto encrypted_key =
-            EncryptKey(key->key_material(), AES_GCM_WITH_SW_ENFORCED, key->hw_enforced(),
-                       key->sw_enforced(), hidden, SecureDeletionData{}, root_key_, random_);
-    if (!encrypted_key) return encrypted_key.error();
-
-    auto serialized = SerializeAuthEncryptedBlob(*encrypted_key, key->hw_enforced(),
-                                                 key->sw_enforced(), 0 /* key_slot */);
-    if (!serialized) return serialized.error();
-
-    *upgraded_key = std::move(*serialized);
-    return error;
-}
diff --git a/microdroid/keymint/android.hardware.security.keymint-service.microdroid.rc b/microdroid/keymint/android.hardware.security.keymint-service.microdroid.rc
deleted file mode 100644
index d6851bd..0000000
--- a/microdroid/keymint/android.hardware.security.keymint-service.microdroid.rc
+++ /dev/null
@@ -1,3 +0,0 @@
-service vendor.keymint-microdroid /vendor/bin/hw/android.hardware.security.keymint-service.microdroid
-    class early_hal
-    user nobody
diff --git a/microdroid/keymint/android.hardware.security.keymint-service.microdroid.xml b/microdroid/keymint/android.hardware.security.keymint-service.microdroid.xml
deleted file mode 100644
index 73d15a8..0000000
--- a/microdroid/keymint/android.hardware.security.keymint-service.microdroid.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<manifest version="1.0" type="device">
-    <hal format="aidl">
-        <name>android.hardware.security.keymint</name>
-        <fqname>IKeyMintDevice/default</fqname>
-    </hal>
-</manifest>
diff --git a/microdroid/keymint/include/MicrodroidKeyMintDevice.h b/microdroid/keymint/include/MicrodroidKeyMintDevice.h
deleted file mode 100644
index dec7baa..0000000
--- a/microdroid/keymint/include/MicrodroidKeyMintDevice.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <aidl/android/hardware/security/keymint/BnKeyMintDevice.h>
-#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
-#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
-#include <keymaster/android_keymaster_utils.h>
-
-namespace keymaster {
-class AndroidKeymaster;
-}
-
-namespace aidl::android::hardware::security::keymint {
-using ::ndk::ScopedAStatus;
-using std::optional;
-using std::shared_ptr;
-using std::vector;
-
-using secureclock::TimeStampToken;
-
-class MicrodroidKeyMintDevice : public BnKeyMintDevice {
-public:
-    explicit MicrodroidKeyMintDevice(::keymaster::KeymasterKeyBlob& rootKey);
-    virtual ~MicrodroidKeyMintDevice();
-
-    ScopedAStatus getHardwareInfo(KeyMintHardwareInfo* info) override;
-
-    ScopedAStatus addRngEntropy(const vector<uint8_t>& data) override;
-
-    ScopedAStatus generateKey(const vector<KeyParameter>& keyParams,
-                              const optional<AttestationKey>& attestationKey,
-                              KeyCreationResult* creationResult) override;
-
-    ScopedAStatus importKey(const vector<KeyParameter>& keyParams, KeyFormat keyFormat,
-                            const vector<uint8_t>& keyData,
-                            const optional<AttestationKey>& attestationKey,
-                            KeyCreationResult* creationResult) override;
-
-    ScopedAStatus importWrappedKey(const vector<uint8_t>& wrappedKeyData,
-                                   const vector<uint8_t>& wrappingKeyBlob,
-                                   const vector<uint8_t>& maskingKey,
-                                   const vector<KeyParameter>& unwrappingParams,
-                                   int64_t passwordSid, int64_t biometricSid,
-                                   KeyCreationResult* creationResult) override;
-
-    ScopedAStatus upgradeKey(const vector<uint8_t>& keyBlobToUpgrade,
-                             const vector<KeyParameter>& upgradeParams,
-                             vector<uint8_t>* keyBlob) override;
-
-    ScopedAStatus deleteKey(const vector<uint8_t>& keyBlob) override;
-    ScopedAStatus deleteAllKeys() override;
-    ScopedAStatus destroyAttestationIds() override;
-
-    ScopedAStatus begin(KeyPurpose purpose, const vector<uint8_t>& keyBlob,
-                        const vector<KeyParameter>& params,
-                        const optional<HardwareAuthToken>& authToken, BeginResult* result) override;
-
-    ScopedAStatus deviceLocked(bool passwordOnly,
-                               const optional<TimeStampToken>& timestampToken) override;
-    ScopedAStatus earlyBootEnded() override;
-
-    ScopedAStatus convertStorageKeyToEphemeral(const std::vector<uint8_t>& storageKeyBlob,
-                                               std::vector<uint8_t>* ephemeralKeyBlob) override;
-
-    ScopedAStatus getKeyCharacteristics(
-            const std::vector<uint8_t>& keyBlob, const std::vector<uint8_t>& appId,
-            const std::vector<uint8_t>& appData,
-            std::vector<KeyCharacteristics>* keyCharacteristics) override;
-
-    shared_ptr<::keymaster::AndroidKeymaster>& getKeymasterImpl() { return impl_; }
-
-protected:
-    std::shared_ptr<::keymaster::AndroidKeymaster> impl_;
-};
-
-} // namespace aidl::android::hardware::security::keymint
diff --git a/microdroid/keymint/include/MicrodroidKeymasterContext.h b/microdroid/keymint/include/MicrodroidKeymasterContext.h
deleted file mode 100644
index 636d240..0000000
--- a/microdroid/keymint/include/MicrodroidKeymasterContext.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <keymaster/contexts/pure_soft_keymaster_context.h>
-#include <keymaster/km_openssl/software_random_source.h>
-
-class MicrodroidKeymasterContext : public ::keymaster::PureSoftKeymasterContext {
-public:
-    explicit MicrodroidKeymasterContext(::keymaster::KmVersion version,
-                                        ::keymaster::KeymasterKeyBlob& root_key)
-          : PureSoftKeymasterContext(version, KM_SECURITY_LEVEL_SOFTWARE), root_key_(root_key) {}
-
-    keymaster_error_t CreateKeyBlob(const ::keymaster::AuthorizationSet& auths,
-                                    keymaster_key_origin_t origin,
-                                    const ::keymaster::KeymasterKeyBlob& key_material,
-                                    ::keymaster::KeymasterKeyBlob* blob,
-                                    ::keymaster::AuthorizationSet* hw_enforced,
-                                    ::keymaster::AuthorizationSet* sw_enforced) const override;
-
-    keymaster_error_t ParseKeyBlob(const ::keymaster::KeymasterKeyBlob& blob,
-                                   const ::keymaster::AuthorizationSet& additional_params,
-                                   ::keymaster::UniquePtr<::keymaster::Key>* key) const override;
-
-    keymaster_error_t UpgradeKeyBlob(const ::keymaster::KeymasterKeyBlob& key_to_upgrade,
-                                     const ::keymaster::AuthorizationSet& upgrade_params,
-                                     ::keymaster::KeymasterKeyBlob* upgraded_key) const override;
-
-private:
-    ::keymaster::SoftwareRandomSource random_;
-    ::keymaster::KeymasterKeyBlob root_key_;
-};
diff --git a/microdroid/keymint/service.cpp b/microdroid/keymint/service.cpp
deleted file mode 100644
index 5fc0bd2..0000000
--- a/microdroid/keymint/service.cpp
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "android.hardware.security.keymint-service"
-
-#include <AndroidKeyMintDevice.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/result.h>
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
-#include <keymaster/android_keymaster_utils.h>
-#include <keymaster/mem.h>
-#include <keymaster/soft_keymaster_logger.h>
-#include <openssl/digest.h>
-#include <openssl/hkdf.h>
-#include <openssl/is_boringssl.h>
-#include <openssl/sha.h>
-
-#include "MicrodroidKeyMintDevice.h"
-
-using aidl::android::hardware::security::keymint::MicrodroidKeyMintDevice;
-using aidl::android::hardware::security::keymint::SecurityLevel;
-
-using android::base::Error;
-using android::base::GetProperty;
-using android::base::Result;
-
-using keymaster::KeymasterBlob;
-using keymaster::KeymasterKeyBlob;
-using keymaster::memset_s;
-
-namespace {
-
-template <typename T, class... Args>
-std::shared_ptr<T> addService(Args&&... args) {
-    std::shared_ptr<T> ser = ndk::SharedRefBase::make<T>(std::forward<Args>(args)...);
-    auto instanceName = std::string(T::descriptor) + "/default";
-    LOG(INFO) << "adding keymint service instance: " << instanceName;
-    binder_status_t status =
-            AServiceManager_addService(ser->asBinder().get(), instanceName.c_str());
-    CHECK(status == STATUS_OK);
-    return ser;
-}
-
-Result<KeymasterKeyBlob> getRootKey() {
-    const std::string prop = "ro.vmsecret.keymint";
-    const std::chrono::seconds timeout(15);
-    while (!android::base::WaitForPropertyCreation(prop, timeout)) {
-        LOG(WARNING) << "waited " << timeout.count() << "seconds for " << prop
-                     << ", still waiting...";
-    }
-
-    // In a small effort to avoid spreading the secret around too widely in
-    // memory, move the secert into a buffer that will wipe itself and clear
-    // the original string.
-    std::string secretProp = GetProperty(prop, "");
-    KeymasterBlob secret(reinterpret_cast<const uint8_t*>(secretProp.data()), secretProp.size());
-    memset_s(secretProp.data(), 0, secretProp.size());
-    if (secret.size() < 64u) return Error() << "secret is too small";
-
-    // Derive the root key from the secret to avoid getting locked into using
-    // the secret directly.
-    KeymasterKeyBlob rootKey(SHA512_DIGEST_LENGTH);
-    const uint8_t kRootKeyIkm[] = "keymint_root_key";
-    const uint8_t* kNoSalt = nullptr;
-    const size_t kNoSaltLen = 0;
-    if (!HKDF(rootKey.writable_data(), rootKey.size(), EVP_sha512(), (uint8_t*)secret.begin(),
-              secret.size(), kNoSalt, kNoSaltLen, kRootKeyIkm, sizeof(kRootKeyIkm))) {
-        return Error() << "Failed to derive a key";
-    }
-    if (rootKey.size() < 64u) return Error() << "root key is too small";
-
-    LOG(INFO) << "root key obtained";
-    return rootKey;
-}
-
-} // namespace
-
-int main() {
-    auto rootKey = getRootKey();
-    if (!rootKey.ok()) {
-        LOG(FATAL) << "Failed to get root key: " << rootKey.error();
-    }
-
-    // Zero threads seems like a useless pool, but below we'll join this thread
-    // to it, increasing the pool size to 1.
-    ABinderProcess_setThreadPoolMaxThreadCount(0);
-
-    // Add Keymint Service
-    std::shared_ptr<MicrodroidKeyMintDevice> keyMint =
-            ndk::SharedRefBase::make<MicrodroidKeyMintDevice>(*rootKey);
-    auto instanceName = std::string(MicrodroidKeyMintDevice::descriptor) + "/default";
-    LOG(INFO) << "adding keymint service instance: " << instanceName;
-    binder_status_t status =
-            AServiceManager_addService(keyMint->asBinder().get(), instanceName.c_str());
-    CHECK(status == STATUS_OK);
-
-    ABinderProcess_joinThreadPool();
-    return EXIT_FAILURE; // should not reach
-}
diff --git a/microdroid/microdroid.json b/microdroid/microdroid.json
index c6c743d..cb27a24 100644
--- a/microdroid/microdroid.json
+++ b/microdroid/microdroid.json
@@ -37,6 +37,5 @@
       "writable": true
     }
   ],
-  "memory_mib": 2048,
-  "protected": false
+  "memory_mib": 256
 }
diff --git a/microdroid/microdroid_compatibility_matrix.xml b/microdroid/microdroid_compatibility_matrix.xml
index dbc12a8..a345e30 100644
--- a/microdroid/microdroid_compatibility_matrix.xml
+++ b/microdroid/microdroid_compatibility_matrix.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <compatibility-matrix version="1.0" type="framework">
     <hal format="aidl" optional="true">
-        <name>android.hardware.security.keymint</name>
+        <name>android.hardware.security.dice</name>
         <version>1</version>
         <interface>
-            <name>IKeyMintDevice</name>
+            <name>IDiceDevice</name>
             <instance>default</instance>
         </interface>
     </hal>
diff --git a/microdroid/microdroid_manifest.xml b/microdroid/microdroid_manifest.xml
index 28a374f..b84ba8f 100644
--- a/microdroid/microdroid_manifest.xml
+++ b/microdroid/microdroid_manifest.xml
@@ -1,24 +1,4 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <manifest version="1.0" type="framework">
-    <!--TODO(b/185767624): remove hidl after full keymint support-->
-    <hal format="hidl">
-        <name>android.hidl.manager</name>
-        <transport>hwbinder</transport>
-        <version>1.2</version>
-        <interface>
-            <name>IServiceManager</name>
-            <instance>default</instance>
-        </interface>
-        <fqname>@1.2::IServiceManager/default</fqname>
-    </hal>
-    <hal format="hidl">
-        <name>android.hidl.token</name>
-        <transport>hwbinder</transport>
-        <version>1.0</version>
-        <interface>
-            <name>ITokenManager</name>
-            <instance>default</instance>
-        </interface>
-        <fqname>@1.0::ITokenManager/default</fqname>
-    </hal>
+    <!-- empty -->
 </manifest>
diff --git a/microdroid/microdroid_vendor_compatibility_matrix.xml b/microdroid/microdroid_vendor_compatibility_matrix.xml
index efa1c98..44735d8 100644
--- a/microdroid/microdroid_vendor_compatibility_matrix.xml
+++ b/microdroid/microdroid_vendor_compatibility_matrix.xml
@@ -1,27 +1,4 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <compatibility-matrix version="1.0" type="device">
-    <hal format="aidl">
-        <name>android.system.keystore2</name>
-        <interface>
-            <name>IKeystoreService</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <!--TODO(b/185767624): remove hidl after full keymint support-->
-    <hal format="hidl" optional="true">
-        <name>android.hidl.manager</name>
-        <version>1.0</version>
-        <interface>
-            <name>IServiceManager</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hidl.token</name>
-        <version>1.0</version>
-        <interface>
-            <name>ITokenManager</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
+    <!-- empty -->
 </compatibility-matrix>
diff --git a/microdroid/payload/metadata.proto b/microdroid/payload/metadata.proto
index 9e60b38..2e92f55 100644
--- a/microdroid/payload/metadata.proto
+++ b/microdroid/payload/metadata.proto
@@ -43,6 +43,10 @@
   // The timestamp in seconds when the APEX was last updated. This should match the value in
   // apex-info-list.xml.
   uint64 last_update_seconds = 5;
+
+  // Required.
+  // Whether the APEX is a factory version or not.
+  bool is_factory = 6;
 }
 
 message ApkPayload {
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index f888b80..1878c87 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -9,6 +9,7 @@
     edition: "2018",
     prefer_rlib: true,
     rustlibs: [
+        "android.hardware.security.dice-V1-rust",
         "android.security.dice-rust",
         "android.system.virtualizationservice-rust",
         "android.system.virtualmachineservice-rust",
@@ -18,6 +19,7 @@
         "libbinder_rpc_unstable_bindgen",
         "libbinder_rs",
         "libbyteorder",
+        "libdiced_utils",
         "libglob",
         "libidsig",
         "libitertools",
diff --git a/microdroid_manager/src/instance.rs b/microdroid_manager/src/instance.rs
index 1068792..f3bbf16 100644
--- a/microdroid_manager/src/instance.rs
+++ b/microdroid_manager/src/instance.rs
@@ -316,6 +316,7 @@
 
 #[derive(Debug, Serialize, Deserialize, PartialEq)]
 pub struct MicrodroidData {
+    pub salt: Vec<u8>, // Should be [u8; 64] but that isn't serializable.
     pub apk_data: ApkData,
     pub extra_apks_data: Vec<ApkData>,
     pub apex_data: Vec<ApexData>,
@@ -336,4 +337,5 @@
     pub public_key: Vec<u8>,
     pub root_digest: Vec<u8>,
     pub last_update_seconds: u64,
+    pub is_factory: bool,
 }
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 4420a49..827f9ff 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -19,20 +19,28 @@
 mod payload;
 
 use crate::instance::{ApkData, InstanceDisk, MicrodroidData, RootHash};
+use android_hardware_security_dice::aidl::android::hardware::security::dice::{
+    Config::Config, InputValues::InputValues, Mode::Mode,
+};
+use android_security_dice::aidl::android::security::dice::IDiceMaintenance::IDiceMaintenance;
 use anyhow::{anyhow, bail, ensure, Context, Error, Result};
 use apkverify::{get_public_key_der, verify};
 use binder::unstable_api::{new_spibinder, AIBinder};
-use binder::{FromIBinder, Strong};
+use binder::{wait_for_interface, FromIBinder, Strong};
+use diced_utils::cbor::encode_header;
 use glob::glob;
 use idsig::V4Signature;
 use itertools::sorted;
-use log::{error, info, warn};
+use log::{error, info};
 use microdroid_metadata::{write_metadata, Metadata};
 use microdroid_payload_config::{Task, TaskType, VmPayloadConfig};
 use once_cell::sync::OnceCell;
 use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
+use rand::Fill;
+use ring::digest;
 use rustutils::system_properties;
 use rustutils::system_properties::PropertyWatcher;
+use std::convert::TryInto;
 use std::fs::{self, create_dir, File, OpenOptions};
 use std::io::BufRead;
 use std::os::unix::io::{FromRawFd, IntoRawFd};
@@ -61,6 +69,8 @@
 
 const APEX_CONFIG_DONE_PROP: &str = "apex_config.done";
 const LOGD_ENABLED_PROP: &str = "ro.boot.logd.enabled";
+const ADBD_ENABLED_PROP: &str = "ro.boot.adb.enabled";
+const DEBUGGABLE_PROP: &str = "ro.boot.microdroid.debuggable";
 
 #[derive(thiserror::Error, Debug)]
 enum MicrodroidError {
@@ -137,6 +147,61 @@
     }
 }
 
+fn is_debuggable() -> Result<bool> {
+    // Read all the properties so the behaviour is most similar between debug and non-debug boots.
+    // Defensively default to debug enabled for unrecognised values.
+    let adb = system_properties::read_bool(ADBD_ENABLED_PROP, true)?;
+    let logd = system_properties::read_bool(LOGD_ENABLED_PROP, true)?;
+    let debuggable = system_properties::read_bool(DEBUGGABLE_PROP, true)?;
+    Ok(adb || logd || debuggable)
+}
+
+fn dice_derivation(verified_data: MicrodroidData, payload_config_path: &str) -> Result<()> {
+    // Calculate compound digests of code and authorities
+    let mut code_hash_ctx = digest::Context::new(&digest::SHA512);
+    let mut authority_hash_ctx = digest::Context::new(&digest::SHA512);
+    code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
+    authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
+    for extra_apk in verified_data.extra_apks_data {
+        code_hash_ctx.update(extra_apk.root_hash.as_ref());
+        authority_hash_ctx.update(extra_apk.pubkey.as_ref());
+    }
+    for apex in verified_data.apex_data {
+        code_hash_ctx.update(apex.root_digest.as_ref());
+        authority_hash_ctx.update(apex.public_key.as_ref());
+    }
+    let code_hash = code_hash_ctx.finish().as_ref().try_into().unwrap();
+    let authority_hash = authority_hash_ctx.finish().as_ref().try_into().unwrap();
+
+    // {
+    //   -70002: "Microdroid payload",
+    //   -71000: payload_config_path
+    // }
+    let mut config_desc = vec![
+        0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72, 0x6f,
+        0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01, 0x15, 0x57,
+    ];
+    let config_path_bytes = payload_config_path.as_bytes();
+    encode_header(3, config_path_bytes.len().try_into().unwrap(), &mut config_desc)?;
+    config_desc.extend_from_slice(config_path_bytes);
+
+    // Send the details to diced
+    let diced =
+        wait_for_interface::<dyn IDiceMaintenance>("android.security.dice.IDiceMaintenance")
+            .context("IDiceMaintenance service not found")?;
+    diced
+        .demoteSelf(&[InputValues {
+            codeHash: code_hash,
+            config: Config { desc: config_desc },
+            authorityHash: authority_hash,
+            authorityDescriptor: None,
+            mode: if is_debuggable()? { Mode::DEBUG } else { Mode::NORMAL },
+            hidden: verified_data.salt.try_into().unwrap(),
+        }])
+        .context("IDiceMaintenance::demoteSelf failed")?;
+    Ok(())
+}
+
 fn try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
     let metadata = load_metadata().context("Failed to load payload metadata")?;
 
@@ -182,10 +247,8 @@
     }
     mount_extra_apks(&config)?;
 
-    let fake_secret = "This is a placeholder for a value that is derived from the images that are loaded in the VM.";
-    if let Err(err) = rustutils::system_properties::write("ro.vmsecret.keymint", fake_secret) {
-        warn!("failed to set ro.vmsecret.keymint: {}", err);
-    }
+    info!("DICE derivation for payload");
+    dice_derivation(verified_data, &metadata.payload_config_path)?;
 
     // Wait until apex config is done. (e.g. linker configuration for apexes)
     // TODO(jooyung): wait until sys.boot_completed?
@@ -376,9 +439,19 @@
 
     info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
 
+    // Use the salt from a verified instance, or generate a salt for a new instance.
+    let salt = if let Some(saved_data) = saved_data {
+        saved_data.salt.clone()
+    } else {
+        let mut salt = vec![0u8; 64];
+        salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
+        salt
+    };
+
     // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
     // fully verifying the APK or by comparing it with the saved root_hash.
     Ok(MicrodroidData {
+        salt,
         apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
         extra_apks_data,
         apex_data: apex_data_from_payload,
@@ -409,8 +482,7 @@
     let mut prop = PropertyWatcher::new(APEX_CONFIG_DONE_PROP)?;
     loop {
         prop.wait()?;
-        let val = system_properties::read(APEX_CONFIG_DONE_PROP)?;
-        if val == "true" {
+        if system_properties::read_bool(APEX_CONFIG_DONE_PROP, false)? {
             break;
         }
     }
@@ -480,7 +552,7 @@
 
     // Start logging if enabled
     // TODO(b/200914564) set filterspec if debug_level is app_only
-    if system_properties::read(LOGD_ENABLED_PROP)? == "1" {
+    if system_properties::read_bool(LOGD_ENABLED_PROP, false)? {
         system_properties::write("ctl.start", "seriallogging")?;
     }
 
diff --git a/microdroid_manager/src/payload.rs b/microdroid_manager/src/payload.rs
index 661af5f..48535f3 100644
--- a/microdroid_manager/src/payload.rs
+++ b/microdroid_manager/src/payload.rs
@@ -48,6 +48,7 @@
                 public_key: result.public_key,
                 root_digest: result.root_digest,
                 last_update_seconds: apex.last_update_seconds,
+                is_factory: apex.is_factory,
             })
         })
         .collect()
@@ -63,6 +64,7 @@
                 public_key: data.public_key.clone(),
                 root_digest: data.root_digest.clone(),
                 last_update_seconds: data.last_update_seconds,
+                is_factory: data.is_factory,
                 ..Default::default()
             })
             .collect(),
diff --git a/pvmfw/pvmfw.img b/pvmfw/pvmfw.img
index 510b2c4..a2541a3 100644
--- a/pvmfw/pvmfw.img
+++ b/pvmfw/pvmfw.img
Binary files differ
diff --git a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
index 208d61f..f15036c 100644
--- a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
+++ b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
@@ -24,4 +24,7 @@
 
     /* read a system property. */
     String readProperty(String prop);
+
+    /* get the VM's stable secret. */
+    byte[] insecurelyExposeSecret();
 }
diff --git a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
index 678fe84..e15f1ae 100644
--- a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
+++ b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
@@ -204,6 +204,24 @@
             Optional<Integer> numCpus,
             Optional<String> cpuAffinity)
             throws DeviceNotAvailableException {
+        return startMicrodroid(androidDevice, buildInfo, apkName, null, packageName,
+                extraIdsigPaths, configPath, debug,
+                memoryMib, numCpus, cpuAffinity);
+    }
+
+    public static String startMicrodroid(
+            ITestDevice androidDevice,
+            IBuildInfo buildInfo,
+            String apkName,
+            String apkPath,
+            String packageName,
+            String[] extraIdsigPaths,
+            String configPath,
+            boolean debug,
+            int memoryMib,
+            Optional<Integer> numCpus,
+            Optional<String> cpuAffinity)
+            throws DeviceNotAvailableException {
         CommandRunner android = new CommandRunner(androidDevice);
 
         // Install APK if necessary
@@ -215,9 +233,11 @@
         // Get the path to the installed apk. Note that
         // getDevice().getAppPackageInfo(...).getCodePath() doesn't work due to the incorrect
         // parsing of the "=" character. (b/190975227). So we use the `pm path` command directly.
-        String apkPath = android.run("pm", "path", packageName);
-        assertTrue(apkPath.startsWith("package:"));
-        apkPath = apkPath.substring("package:".length());
+        if (apkPath == null) {
+            apkPath = android.run("pm", "path", packageName);
+            assertTrue(apkPath.startsWith("package:"));
+            apkPath = apkPath.substring("package:".length());
+        }
 
         android.run("mkdir", "-p", TEST_ROOT);
 
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 6cd16c2..40d72fe 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -10,6 +10,7 @@
         "androidx.test.runner",
         "androidx.test.ext.junit",
         "com.android.microdroid.testservice-java",
+        "truth-prebuilt",
     ],
     libs: ["android.system.virtualmachine"],
     jni_libs: ["MicrodroidTestNativeLib"],
@@ -22,7 +23,7 @@
     name: "MicrodroidTestNativeLib",
     srcs: ["src/native/testbinary.cpp"],
     shared_libs: [
-        "android.system.keystore2-V1-ndk",
+        "android.security.dice-ndk",
         "android.system.virtualmachineservice-ndk",
         "com.android.microdroid.testservice-ndk",
         "libbase",
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 032ecfd..803bdc6 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -15,14 +15,14 @@
  */
 package com.android.microdroid.test;
 
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsNot.not;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeNoException;
-import static org.junit.Assume.assumeThat;
 
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 
@@ -52,6 +52,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
@@ -176,9 +177,6 @@
                                     testService.readProperty("debug.microdroid.app.sublib.run"),
                                     "true");
                             assertEquals(
-                                    testService.readProperty("debug.microdroid.test.keystore"),
-                                    "PASS");
-                            assertEquals(
                                     testService.readProperty("debug.microdroid.test.extra_apk"),
                                     "PASS");
                         } catch (Exception e) {
@@ -215,8 +213,10 @@
     @Test
     public void changingDebugLevelInvalidatesVmIdentity()
             throws VirtualMachineException, InterruptedException, IOException {
-        assumeThat("Skip on Cuttlefish. b/195765441",
-                android.os.Build.DEVICE, is(not("vsoc_x86_64")));
+        assume()
+            .withMessage("Skip on Cuttlefish. b/195765441")
+            .that(android.os.Build.DEVICE)
+            .isNotEqualTo("vsoc_x86_64");
 
         VirtualMachineConfig.Builder builder =
                 new VirtualMachineConfig.Builder(mInner.mContext, "assets/vm_config.json");
@@ -272,4 +272,64 @@
                 };
         listener.runToFinish(mInner.mVm);
     }
+
+    private byte[] launchVmAndGetSecret(String instanceName)
+            throws VirtualMachineException, InterruptedException {
+        VirtualMachineConfig.Builder builder =
+                new VirtualMachineConfig.Builder(mInner.mContext, "assets/vm_config.json");
+        VirtualMachineConfig normalConfig = builder.debugLevel(DebugLevel.NONE).build();
+        mInner.mVm = mInner.mVmm.getOrCreate(instanceName, normalConfig);
+        final CompletableFuture<byte[]> secret = new CompletableFuture<>();
+        VmEventListener listener =
+                new VmEventListener() {
+                    @Override
+                    public void onPayloadReady(VirtualMachine vm) {
+                        try {
+                            ITestService testService = ITestService.Stub.asInterface(
+                                    vm.connectToVsockServer(ITestService.SERVICE_PORT).get());
+                            secret.complete(testService.insecurelyExposeSecret());
+                        } catch (Exception e) {
+                            fail("Exception while connecting to service: " + e.toString());
+                        }
+                        // TODO(b/208639280): remove this sleep. For now, we need to wait for a few
+                        // seconds so that crosvm can actually persist instance.img.
+                        try {
+                            Thread.sleep(30 * 1000);
+                        } catch (InterruptedException e) { }
+                        forceStop(vm);
+                    }
+                };
+        listener.runToFinish(mInner.mVm);
+        return secret.getNow(null);
+    }
+
+    @Test
+    public void instancesOfSameVmHaveDifferentSecrets()
+            throws VirtualMachineException, InterruptedException {
+        assume()
+            .withMessage("Skip on Cuttlefish. b/195765441")
+            .that(android.os.Build.DEVICE)
+            .isNotEqualTo("vsoc_x86_64");
+
+        byte[] vm_a_secret = launchVmAndGetSecret("test_vm_a");
+        byte[] vm_b_secret = launchVmAndGetSecret("test_vm_b");
+        assertThat(vm_a_secret).isNotNull();
+        assertThat(vm_b_secret).isNotNull();
+        assertThat(vm_a_secret).isNotEqualTo(vm_b_secret);
+    }
+
+    @Test
+    public void sameInstanceKeepsSameSecrets()
+            throws VirtualMachineException, InterruptedException {
+        assume()
+            .withMessage("Skip on Cuttlefish. b/195765441")
+            .that(android.os.Build.DEVICE)
+            .isNotEqualTo("vsoc_x86_64");
+
+        byte[] vm_secret_first_boot = launchVmAndGetSecret("test_vm");
+        byte[] vm_secret_second_boot = launchVmAndGetSecret("test_vm");
+        assertThat(vm_secret_first_boot).isNotNull();
+        assertThat(vm_secret_second_boot).isNotNull();
+        assertThat(vm_secret_first_boot).isEqualTo(vm_secret_second_boot);
+    }
 }
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index c748b2a..417ff4a 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -13,13 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include <aidl/android/system/keystore2/IKeystoreService.h>
+#include <aidl/android/security/dice/IDiceNode.h>
 #include <aidl/android/system/virtualmachineservice/IVirtualMachineService.h>
 #include <aidl/com/android/microdroid/testservice/BnTestService.h>
 #include <android-base/file.h>
 #include <android-base/properties.h>
 #include <android-base/result.h>
-#include <android-base/unique_fd.h>
 #include <android/binder_auto_utils.h>
 #include <android/binder_manager.h>
 #include <fcntl.h>
@@ -34,159 +33,19 @@
 #include <binder_rpc_unstable.hpp>
 #include <string>
 
-using aidl::android::hardware::security::keymint::Algorithm;
-using aidl::android::hardware::security::keymint::Digest;
-using aidl::android::hardware::security::keymint::KeyParameter;
-using aidl::android::hardware::security::keymint::KeyParameterValue;
-using aidl::android::hardware::security::keymint::KeyPurpose;
-using aidl::android::hardware::security::keymint::SecurityLevel;
-using aidl::android::hardware::security::keymint::Tag;
-
-using aidl::android::system::keystore2::CreateOperationResponse;
-using aidl::android::system::keystore2::Domain;
-using aidl::android::system::keystore2::IKeystoreSecurityLevel;
-using aidl::android::system::keystore2::IKeystoreService;
-using aidl::android::system::keystore2::KeyDescriptor;
-using aidl::android::system::keystore2::KeyMetadata;
+using aidl::android::hardware::security::dice::BccHandover;
+using aidl::android::security::dice::IDiceNode;
 
 using aidl::android::system::virtualmachineservice::IVirtualMachineService;
 
 using android::base::ErrnoError;
 using android::base::Error;
 using android::base::Result;
-using android::base::unique_fd;
 
 extern void testlib_sub();
 
 namespace {
 
-Result<void> test_keystore() {
-    // Connect to Keystore.
-    ndk::SpAIBinder binder(
-            AServiceManager_waitForService("android.system.keystore2.IKeystoreService/default"));
-    auto service = IKeystoreService::fromBinder(binder);
-    if (service == nullptr) {
-        return Error() << "Failed to find Keystore";
-    }
-    std::shared_ptr<IKeystoreSecurityLevel> securityLevel;
-    auto status = service->getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT, &securityLevel);
-    if (!status.isOk()) {
-        return Error() << "Failed to get security level";
-    }
-
-    // Create a signing key.
-    std::vector<KeyParameter> params;
-
-    KeyParameter algo;
-    algo.tag = Tag::ALGORITHM;
-    algo.value = KeyParameterValue::make<KeyParameterValue::algorithm>(Algorithm::HMAC);
-    params.push_back(algo);
-
-    KeyParameter key_size;
-    key_size.tag = Tag::KEY_SIZE;
-    key_size.value = KeyParameterValue::make<KeyParameterValue::integer>(256);
-    params.push_back(key_size);
-
-    KeyParameter min_mac_length;
-    min_mac_length.tag = Tag::MIN_MAC_LENGTH;
-    min_mac_length.value = KeyParameterValue::make<KeyParameterValue::integer>(256);
-    params.push_back(min_mac_length);
-
-    KeyParameter digest;
-    digest.tag = Tag::DIGEST;
-    digest.value = KeyParameterValue::make<KeyParameterValue::digest>(Digest::SHA_2_256);
-    params.push_back(digest);
-
-    KeyParameter purposeSign;
-    purposeSign.tag = Tag::PURPOSE;
-    purposeSign.value = KeyParameterValue::make<KeyParameterValue::keyPurpose>(KeyPurpose::SIGN);
-    params.push_back(purposeSign);
-
-    KeyParameter purposeVerify;
-    purposeVerify.tag = Tag::PURPOSE;
-    purposeVerify.value =
-            KeyParameterValue::make<KeyParameterValue::keyPurpose>(KeyPurpose::VERIFY);
-    params.push_back(purposeVerify);
-
-    KeyParameter auth;
-    auth.tag = Tag::NO_AUTH_REQUIRED;
-    auth.value = KeyParameterValue::make<KeyParameterValue::boolValue>(true);
-    params.push_back(auth);
-
-    KeyDescriptor descriptor;
-    descriptor.domain = Domain::SELINUX;
-    descriptor.alias = "payload-test-key";
-    descriptor.nspace = 140; // vm_payload_key
-
-    KeyMetadata metadata;
-    status = securityLevel->generateKey(descriptor, {}, params, 0, {}, &metadata);
-    if (!status.isOk()) {
-        return Error() << "Failed to create new HMAC key";
-    }
-
-    // Sign something.
-    params.clear();
-    params.push_back(algo);
-    params.push_back(digest);
-    params.push_back(purposeSign);
-
-    KeyParameter mac_length;
-    mac_length.tag = Tag::MAC_LENGTH;
-    mac_length.value = KeyParameterValue::make<KeyParameterValue::integer>(256);
-    params.push_back(mac_length);
-
-    CreateOperationResponse opResponse;
-    status = securityLevel->createOperation(descriptor, params, false, &opResponse);
-    if (!status.isOk()) {
-        return Error() << "Failed to create keystore signing operation: "
-                       << status.getServiceSpecificError();
-    }
-    auto operation = opResponse.iOperation;
-
-    std::string message = "This is the message to sign";
-    std::optional<std::vector<uint8_t>> out;
-    status = operation->update({message.begin(), message.end()}, &out);
-    if (!status.isOk()) {
-        return Error() << "Failed to call keystore update operation.";
-    }
-
-    std::optional<std::vector<uint8_t>> signature;
-    status = operation->finish({}, {}, &signature);
-    if (!status.isOk()) {
-        return Error() << "Failed to call keystore finish operation.";
-    }
-
-    if (!signature.has_value()) {
-        return Error() << "Didn't receive a signature from keystore finish operation.";
-    }
-
-    // Verify the signature.
-    params.clear();
-    params.push_back(algo);
-    params.push_back(digest);
-    params.push_back(purposeVerify);
-
-    status = securityLevel->createOperation(descriptor, params, false, &opResponse);
-    if (!status.isOk()) {
-        return Error() << "Failed to create keystore verification operation: "
-                       << status.getServiceSpecificError();
-    }
-    operation = opResponse.iOperation;
-
-    status = operation->update({message.begin(), message.end()}, &out);
-    if (!status.isOk()) {
-        return Error() << "Failed to call keystore update operation.";
-    }
-
-    std::optional<std::vector<uint8_t>> out_signature;
-    status = operation->finish({}, signature.value(), &out_signature);
-    if (!status.isOk()) {
-        return Error() << "Failed to call keystore finish operation.";
-    }
-
-    return {};
-}
-
 template <typename T>
 Result<T> report_test(std::string name, Result<T> result) {
     auto property = "debug.microdroid.test." + name;
@@ -219,6 +78,23 @@
 
             return ndk::ScopedAStatus::ok();
         }
+
+        ndk::ScopedAStatus insecurelyExposeSecret(std::vector<uint8_t>* out) override {
+            ndk::SpAIBinder binder(AServiceManager_getService("android.security.dice.IDiceNode"));
+            auto service = IDiceNode::fromBinder(binder);
+            if (service == nullptr) {
+                return ndk::ScopedAStatus::
+                        fromServiceSpecificErrorWithMessage(0, "Failed to find diced");
+            }
+            BccHandover handover;
+            auto deriveStatus = service->derive({}, &handover);
+            if (!deriveStatus.isOk()) {
+                return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(0,
+                                                                               "Failed call diced");
+            }
+            *out = {handover.cdiSeal.begin(), handover.cdiSeal.end()};
+            return ndk::ScopedAStatus::ok();
+        }
     };
     auto testService = ndk::SharedRefBase::make<TestService>();
 
@@ -283,7 +159,6 @@
     report_test("extra_apk", verify_apk());
 
     __system_property_set("debug.microdroid.app.run", "true");
-    if (!report_test("keystore", test_keystore()).ok()) return 1;
 
     if (auto res = start_test_service(); res.ok()) {
         return 0;
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 653524e..b82064b 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -31,7 +31,6 @@
         "libcommand_fds",
         "libdisk",
         "libidsig",
-        "libkvm",
         "liblog_rust",
         "libmicrodroid_metadata",
         "libmicrodroid_payload_config",
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl
index 6562159..6f3d4f0 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualMachine.aidl
@@ -28,9 +28,6 @@
     /**
      * Register a Binder object to get callbacks when the state of the VM changes, such as if it
      * dies.
-     *
-     * TODO(jiyong): this should be registered when IVirtualizationService.run is called. Otherwise,
-     * we might miss some events that happen before the registration is done.
      */
     void registerCallback(IVirtualMachineCallback callback);
 
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
index 8265f96..c36e561 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
@@ -47,6 +47,9 @@
     /** Debug level of the VM */
     DebugLevel debugLevel;
 
+    /** Whether the VM should be a protected VM. */
+    boolean protectedVm;
+
     /**
      * The amount of RAM to give the VM, in MiB. If this is 0 or negative then it will default to
      * the value in microdroid.json, if any, or the crosvm default.
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 42eb1e6..5b0c9b7 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -50,7 +50,6 @@
 use binder_common::{lazy_service::LazyServiceGuard, new_binder_exception};
 use disk::QcowFile;
 use idsig::{HashAlgorithm, V4Signature};
-use kvm::{Kvm, Cap};
 use log::{debug, error, info, warn};
 use microdroid_payload_config::VmPayloadConfig;
 use rustutils::system_properties;
@@ -190,9 +189,7 @@
             VirtualMachineConfig::AppConfig(config) => BorrowedOrOwned::Owned(
                 load_app_config(config, &temporary_directory).map_err(|e| {
                     error!("Failed to load app config from {}: {}", &config.configPath, e);
-                    // At this point, we do not know the protected status of Vm
-                    // setting it to false, though this may not be correct.
-                    write_vm_creation_stats(false, false);
+                    write_vm_creation_stats(config.protectedVm, false);
                     new_binder_exception(
                         ExceptionCode::SERVICE_SPECIFIC,
                         format!("Failed to load app config from {}: {}", &config.configPath, e),
@@ -202,7 +199,15 @@
             VirtualMachineConfig::RawConfig(config) => BorrowedOrOwned::Borrowed(config),
         };
         let config = config.as_ref();
-        let protected_vm = config.protectedVm;
+        let protected = config.protectedVm;
+
+        // Debug level FULL is only supported for non-protected VMs.
+        if is_debug_level_full && protected {
+            return Err(new_binder_exception(
+                ExceptionCode::SERVICE_SPECIFIC,
+                "FULL debug level not supported for protected VMs.",
+            ));
+        };
 
         // Check if partition images are labeled incorrectly. This is to prevent random images
         // which are not protected by the Android Verified Boot (e.g. bits downloaded by apps) from
@@ -226,7 +231,7 @@
         let zero_filler_path = temporary_directory.join("zero.img");
         write_zero_filler(&zero_filler_path).map_err(|e| {
             error!("Failed to make composite image: {}", e);
-            write_vm_creation_stats(protected_vm, false);
+            write_vm_creation_stats(protected, false);
             new_binder_exception(
                 ExceptionCode::SERVICE_SPECIFIC,
                 format!("Failed to make composite image: {}", e),
@@ -248,24 +253,6 @@
             })
             .collect::<Result<Vec<DiskFile>, _>>()?;
 
-        let protected_vm_supported = Kvm::new()
-            .map_err(|e| new_binder_exception(ExceptionCode::SERVICE_SPECIFIC, e.to_string()))?
-            .check_extension(Cap::ArmProtectedVm);
-        let protected = config.protectedVm && protected_vm_supported;
-        if config.protectedVm && !protected_vm_supported {
-            warn!("Protected VM was requested, but it isn't supported on this machine. Ignored.");
-        }
-
-        // And force run in non-protected mode when debug level is FULL
-        let protected = if is_debug_level_full {
-            if protected {
-                warn!("VM will run in FULL debug level. Running in non-protected mode");
-            }
-            false
-        } else {
-            protected
-        };
-
         // Actually start the VM.
         let crosvm_config = CrosvmConfig {
             cid,
@@ -292,7 +279,7 @@
             )
             .map_err(|e| {
                 error!("Failed to create VM with config {:?}: {}", config, e);
-                write_vm_creation_stats(protected_vm, false);
+                write_vm_creation_stats(protected, false);
                 new_binder_exception(
                     ExceptionCode::SERVICE_SPECIFIC,
                     format!("Failed to create VM: {}", e),
@@ -300,7 +287,7 @@
             })?,
         );
         state.add_vm(Arc::downgrade(&instance));
-        write_vm_creation_stats(protected_vm, true);
+        write_vm_creation_stats(protected, true);
         Ok(VirtualMachine::create(instance))
     }
 
@@ -587,6 +574,7 @@
         vm_config.memoryMib = config.memoryMib;
     }
 
+    vm_config.protectedVm = config.protectedVm;
     vm_config.numCpus = config.numCpus;
     vm_config.cpuAffinity = config.cpuAffinity.clone();
 
diff --git a/virtualizationservice/src/payload.rs b/virtualizationservice/src/payload.rs
index 65adc25..7b8cb7f 100644
--- a/virtualizationservice/src/payload.rs
+++ b/virtualizationservice/src/payload.rs
@@ -53,7 +53,7 @@
     list: Vec<ApexInfo>,
 }
 
-#[derive(Clone, Debug, Deserialize)]
+#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
 struct ApexInfo {
     #[serde(rename = "moduleName")]
     name: String,
@@ -66,6 +66,15 @@
     // The field claims to be milliseconds but is actually seconds.
     #[serde(rename = "lastUpdateMillis")]
     last_update_seconds: u64,
+
+    #[serde(rename = "isFactory")]
+    is_factory: bool,
+
+    #[serde(rename = "isActive")]
+    is_active: bool,
+
+    #[serde(rename = "provideSharedApexLibs")]
+    provide_shared_apex_libs: bool,
 }
 
 impl ApexInfoList {
@@ -91,26 +100,23 @@
             Ok(apex_info_list)
         })
     }
+}
 
-    /// Returns the list of apex names matching with the predicate
-    fn get_matching(&self, predicate: fn(&ApexInfo) -> bool) -> Vec<String> {
-        self.list.iter().filter(|info| predicate(info)).map(|info| info.name.clone()).collect()
-    }
-
-    fn get(&self, apex_name: &str) -> Result<&ApexInfo> {
-        self.list
-            .iter()
-            .find(|apex| apex.name == apex_name)
-            .ok_or_else(|| anyhow!("{} not found.", apex_name))
-    }
-
-    fn get_path_for(&self, apex_name: &str) -> Result<PathBuf> {
-        Ok(self.get(apex_name)?.path.clone())
+impl ApexInfo {
+    fn matches(&self, apex_config: &ApexConfig) -> bool {
+        // Match with pseudo name "{CLASSPATH}" which represents APEXes contributing
+        // to any derive_classpath environment variable
+        if apex_config.name == "{CLASSPATH}" && self.has_classpath_jar {
+            return true;
+        }
+        if apex_config.name == self.name {
+            return true;
+        }
+        false
     }
 }
 
 struct PackageManager {
-    // TODO(b/199146189) use IPackageManagerNative
     apex_info_list: &'static ApexInfoList,
 }
 
@@ -137,6 +143,8 @@
                         let metadata = metadata(&apex_info.path)?;
                         apex_info.last_update_seconds =
                             metadata.modified()?.duration_since(SystemTime::UNIX_EPOCH)?.as_secs();
+                        // by definition, staged apex can't be a factory apex.
+                        apex_info.is_factory = false;
                     }
                 }
             }
@@ -147,21 +155,21 @@
 
 fn make_metadata_file(
     config_path: &str,
-    apex_names: &[String],
+    apex_infos: &[&ApexInfo],
     temporary_directory: &Path,
-    apex_list: &ApexInfoList,
 ) -> Result<ParcelFileDescriptor> {
     let metadata_path = temporary_directory.join("metadata");
     let metadata = Metadata {
         version: 1,
-        apexes: apex_names
+        apexes: apex_infos
             .iter()
             .enumerate()
-            .map(|(i, apex_name)| {
+            .map(|(i, apex_info)| {
                 Ok(ApexPayload {
-                    name: apex_name.clone(),
+                    name: apex_info.name.clone(),
                     partition_name: format!("microdroid-apex-{}", i),
-                    last_update_seconds: apex_list.get(apex_name)?.last_update_seconds,
+                    last_update_seconds: apex_info.last_update_seconds,
+                    is_factory: apex_info.is_factory,
                     ..Default::default()
                 })
             })
@@ -220,12 +228,13 @@
     let pm = PackageManager::new()?;
     let apex_list = pm.get_apex_list(vm_payload_config.prefer_staged)?;
 
-    // collect APEX names from config
-    let apexes = collect_apex_names(&apex_list, &vm_payload_config.apexes, app_config.debugLevel);
-    info!("Microdroid payload APEXes: {:?}", apexes);
+    // collect APEXes from config
+    let apex_infos =
+        collect_apex_infos(&apex_list, &vm_payload_config.apexes, app_config.debugLevel);
+    info!("Microdroid payload APEXes: {:?}", apex_infos.iter().map(|ai| &ai.name));
 
     let metadata_file =
-        make_metadata_file(&app_config.configPath, &apexes, temporary_directory, &apex_list)?;
+        make_metadata_file(&app_config.configPath, &apex_infos, temporary_directory)?;
     // put metadata at the first partition
     let mut partitions = vec![Partition {
         label: "payload-metadata".to_owned(),
@@ -233,9 +242,8 @@
         writable: false,
     }];
 
-    for (i, apex) in apexes.iter().enumerate() {
-        let apex_path = apex_list.get_path_for(apex)?;
-        let apex_file = open_parcel_file(&apex_path, false)?;
+    for (i, apex_info) in apex_infos.iter().enumerate() {
+        let apex_file = open_parcel_file(&apex_info.path, false)?;
         partitions.push(Partition {
             label: format!("microdroid-apex-{}", i),
             image: Some(apex_file),
@@ -310,30 +318,26 @@
     Ok(apexes)
 }
 
-// Collect APEX names from config
-fn collect_apex_names(
-    apex_list: &ApexInfoList,
-    apexes: &[ApexConfig],
+// Collect ApexInfos from VM config
+fn collect_apex_infos<'a>(
+    apex_list: &'a ApexInfoList,
+    apex_configs: &[ApexConfig],
     debug_level: DebugLevel,
-) -> Vec<String> {
-    // Process pseudo names like "{CLASSPATH}".
-    // For now we have following pseudo APEX names:
-    // - {CLASSPATH}: represents APEXes contributing to any derive_classpath environment variable
-    let mut apex_names: Vec<String> = apexes
-        .iter()
-        .flat_map(|apex| match apex.name.as_str() {
-            "{CLASSPATH}" => apex_list.get_matching(|apex| apex.has_classpath_jar),
-            _ => vec![apex.name.clone()],
-        })
-        .collect();
-    // Add required APEXes
-    apex_names.extend(MICRODROID_REQUIRED_APEXES.iter().map(|name| name.to_string()));
+) -> Vec<&'a ApexInfo> {
+    let mut additional_apexes: Vec<&str> = MICRODROID_REQUIRED_APEXES.to_vec();
     if debug_level != DebugLevel::NONE {
-        apex_names.extend(MICRODROID_REQUIRED_APEXES_DEBUG.iter().map(|name| name.to_string()));
+        additional_apexes.extend(MICRODROID_REQUIRED_APEXES_DEBUG.to_vec());
     }
-    apex_names.sort();
-    apex_names.dedup();
-    apex_names
+
+    apex_list
+        .list
+        .iter()
+        .filter(|ai| {
+            apex_configs.iter().any(|cfg| ai.matches(cfg) && ai.is_active)
+                || additional_apexes.iter().any(|name| name == &ai.name && ai.is_active)
+                || ai.provide_shared_apex_libs
+        })
+        .collect()
 }
 
 pub fn add_microdroid_images(
@@ -403,34 +407,125 @@
     }
 
     #[test]
-    fn test_collect_apex_names() {
-        let apex_list = ApexInfoList {
+    fn test_collect_apexes() {
+        let apex_info_list = ApexInfoList {
             list: vec![
                 ApexInfo {
-                    name: "hasnt_classpath".to_string(),
-                    path: PathBuf::from("path0"),
+                    // 0
+                    name: "com.android.adbd".to_string(),
+                    path: PathBuf::from("adbd"),
                     has_classpath_jar: false,
                     last_update_seconds: 12345678,
+                    is_factory: true,
+                    is_active: true,
+                    ..Default::default()
                 },
                 ApexInfo {
+                    // 1
+                    name: "com.android.os.statsd".to_string(),
+                    path: PathBuf::from("statsd"),
+                    has_classpath_jar: false,
+                    last_update_seconds: 12345678,
+                    is_factory: true,
+                    is_active: false,
+                    ..Default::default()
+                },
+                ApexInfo {
+                    // 2
+                    name: "com.android.os.statsd".to_string(),
+                    path: PathBuf::from("statsd/updated"),
+                    has_classpath_jar: false,
+                    last_update_seconds: 12345678 + 1,
+                    is_factory: false,
+                    is_active: true,
+                    ..Default::default()
+                },
+                ApexInfo {
+                    // 3
+                    name: "no_classpath".to_string(),
+                    path: PathBuf::from("no_classpath"),
+                    has_classpath_jar: false,
+                    last_update_seconds: 12345678,
+                    is_factory: true,
+                    is_active: true,
+                    ..Default::default()
+                },
+                ApexInfo {
+                    // 4
                     name: "has_classpath".to_string(),
-                    path: PathBuf::from("path1"),
+                    path: PathBuf::from("has_classpath"),
                     has_classpath_jar: true,
                     last_update_seconds: 87654321,
+                    is_factory: true,
+                    is_active: false,
+                    ..Default::default()
+                },
+                ApexInfo {
+                    // 5
+                    name: "has_classpath".to_string(),
+                    path: PathBuf::from("has_classpath/updated"),
+                    has_classpath_jar: true,
+                    last_update_seconds: 87654321 + 1,
+                    is_factory: false,
+                    is_active: true,
+                    ..Default::default()
+                },
+                ApexInfo {
+                    // 6
+                    name: "apex-foo".to_string(),
+                    path: PathBuf::from("apex-foo"),
+                    has_classpath_jar: false,
+                    last_update_seconds: 87654321,
+                    is_factory: true,
+                    is_active: false,
+                    ..Default::default()
+                },
+                ApexInfo {
+                    // 7
+                    name: "apex-foo".to_string(),
+                    path: PathBuf::from("apex-foo/updated"),
+                    has_classpath_jar: false,
+                    last_update_seconds: 87654321 + 1,
+                    is_factory: false,
+                    is_active: true,
+                    ..Default::default()
+                },
+                ApexInfo {
+                    // 8
+                    name: "sharedlibs".to_string(),
+                    path: PathBuf::from("apex-foo"),
+                    last_update_seconds: 87654321,
+                    is_factory: true,
+                    provide_shared_apex_libs: true,
+                    ..Default::default()
+                },
+                ApexInfo {
+                    // 9
+                    name: "sharedlibs".to_string(),
+                    path: PathBuf::from("apex-foo/updated"),
+                    last_update_seconds: 87654321 + 1,
+                    is_active: true,
+                    provide_shared_apex_libs: true,
+                    ..Default::default()
                 },
             ],
         };
-        let apexes = vec![
-            ApexConfig { name: "config_name".to_string() },
+        let apex_configs = vec![
+            ApexConfig { name: "apex-foo".to_string() },
             ApexConfig { name: "{CLASSPATH}".to_string() },
         ];
         assert_eq!(
-            collect_apex_names(&apex_list, &apexes, DebugLevel::FULL),
+            collect_apex_infos(&apex_info_list, &apex_configs, DebugLevel::FULL),
             vec![
-                "com.android.adbd".to_string(),
-                "com.android.os.statsd".to_string(),
-                "config_name".to_string(),
-                "has_classpath".to_string(),
+                // Pass active/required APEXes
+                &apex_info_list.list[0],
+                &apex_info_list.list[2],
+                // Pass active APEXes specified in the config
+                &apex_info_list.list[5],
+                &apex_info_list.list[7],
+                // Pass both preinstalled(inactive) and updated(active) for "sharedlibs" APEXes
+                &apex_info_list.list[8],
+                &apex_info_list.list[9],
             ]
         );
     }
diff --git a/vm/src/main.rs b/vm/src/main.rs
index ad8c201..25f9bfb 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -72,6 +72,10 @@
         #[structopt(long, default_value = "none", parse(try_from_str=parse_debug_level))]
         debug: DebugLevel,
 
+        /// Run VM in protected mode.
+        #[structopt(short, long)]
+        protected: bool,
+
         /// Memory size (in MiB) of the VM. If unspecified, defaults to the value of `memory_mib`
         /// in the VM config file.
         #[structopt(short, long)]
@@ -174,6 +178,7 @@
             console,
             log,
             debug,
+            protected,
             mem,
             cpus,
             cpu_affinity,
@@ -188,6 +193,7 @@
             console.as_deref(),
             log.as_deref(),
             debug,
+            protected,
             mem,
             cpus,
             cpu_affinity,
diff --git a/vm/src/run.rs b/vm/src/run.rs
index 8583fe2..d558add 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -50,6 +50,7 @@
     console_path: Option<&Path>,
     log_path: Option<&Path>,
     debug_level: DebugLevel,
+    protected: bool,
     mem: Option<u32>,
     cpus: Option<u32>,
     cpu_affinity: Option<String>,
@@ -100,6 +101,7 @@
         instanceImage: open_parcel_file(instance, true /* writable */)?.into(),
         configPath: config_path.to_owned(),
         debugLevel: debug_level,
+        protectedVm: protected,
         memoryMib: mem.unwrap_or(0) as i32, // 0 means use the VM default
         numCpus: cpus.unwrap_or(1) as i32,
         cpuAffinity: cpu_affinity,