Merge "Support setting file mode"
diff --git a/apkdmverity/Android.bp b/apkdmverity/Android.bp
index 403e726..06d4500 100644
--- a/apkdmverity/Android.bp
+++ b/apkdmverity/Android.bp
@@ -18,7 +18,6 @@
         "liblibc",
         "libnix",
         "libnum_traits",
-        "librustutils",
         "libscopeguard",
         "libuuid",
     ],
diff --git a/apkdmverity/src/main.rs b/apkdmverity/src/main.rs
index a8a8f15..dbf3131 100644
--- a/apkdmverity/src/main.rs
+++ b/apkdmverity/src/main.rs
@@ -29,7 +29,6 @@
 use clap::{App, Arg};
 use idsig::{HashAlgorithm, V4Signature};
 use itertools::Itertools;
-use rustutils::system_properties;
 use std::fmt::Debug;
 use std::fs;
 use std::fs::File;
@@ -40,27 +39,26 @@
     let matches = App::new("apkdmverity")
         .about("Creates a dm-verity block device out of APK signed with APK signature scheme V4.")
         .arg(Arg::from_usage(
-            "--apk... <apk_path> <idsig_path> <name> \
-                            'Input APK file, idsig file, and the name of the block device. The APK \
-                            file must be signed using the APK signature scheme 4. The block device \
-                            is created at \"/dev/mapper/<name>\".'",
-        ))
+            "--apk... <apk_path> <idsig_path> <name> <root_hash> \
+                            'Input APK file, idsig file, name of the block device, and root hash. \
+                            The APK file must be signed using the APK signature scheme 4. The \
+                            block device is created at \"/dev/mapper/<name>\".' root_hash is \
+                            optional; idsig file's root hash will be used if specified as \"none\"."
+            ))
         .arg(Arg::with_name("verbose").short("v").long("verbose").help("Shows verbose output"))
         .get_matches();
 
     let apks = matches.values_of("apk").unwrap();
-    assert!(apks.len() % 3 == 0);
-
-    let roothash = if let Ok(val) = system_properties::read("microdroid_manager.apk_root_hash") {
-        Some(util::parse_hexstring(&val)?)
-    } else {
-        // This failure is not an error. We will use the roothash read from the idsig file.
-        None
-    };
+    assert!(apks.len() % 4 == 0);
 
     let verbose = matches.is_present("verbose");
 
-    for (apk, idsig, name) in apks.tuples() {
+    for (apk, idsig, name, roothash) in apks.tuples() {
+        let roothash = if roothash != "none" {
+            Some(util::parse_hexstring(roothash).expect("failed to parse roothash"))
+        } else {
+            None
+        };
         let ret = enable_verity(apk, idsig, name, roothash.as_deref())?;
         if verbose {
             println!(
diff --git a/compos/compos_key_cmd/Android.bp b/compos/compos_key_cmd/Android.bp
index 36c1b5c..75ea090 100644
--- a/compos/compos_key_cmd/Android.bp
+++ b/compos/compos_key_cmd/Android.bp
@@ -9,6 +9,7 @@
 
     static_libs: [
         "lib_compos_proto",
+        "lib_odsign_proto",
     ],
 
     shared_libs: [
diff --git a/compos/compos_key_cmd/compos_key_cmd.cpp b/compos/compos_key_cmd/compos_key_cmd.cpp
index 27c7275..159e9e9 100644
--- a/compos/compos_key_cmd/compos_key_cmd.cpp
+++ b/compos/compos_key_cmd/compos_key_cmd.cpp
@@ -39,12 +39,14 @@
 #include <condition_variable>
 #include <filesystem>
 #include <iostream>
+#include <map>
 #include <mutex>
 #include <string>
 #include <string_view>
 #include <thread>
 
 #include "compos_signature.pb.h"
+#include "odsign_info.pb.h"
 
 using namespace std::literals;
 
@@ -62,10 +64,12 @@
 using android::base::Fdopen;
 using android::base::Result;
 using android::base::unique_fd;
+using android::base::WriteFully;
 using compos::proto::Signature;
 using ndk::ScopedAStatus;
 using ndk::ScopedFileDescriptor;
 using ndk::SharedRefBase;
+using odsign::proto::OdsignInfo;
 
 constexpr unsigned int kRpcPort = 6432;
 
@@ -403,21 +407,12 @@
     return result;
 }
 
-static Result<void> signFile(ICompOsService* service, const std::string& file) {
+static Result<std::vector<uint8_t>> computeDigest(const std::string& file) {
     unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
     if (!fd.ok()) {
         return ErrnoError() << "Failed to open";
     }
 
-    std::filesystem::path signature_path{file};
-    signature_path += ".signature";
-    unique_fd out_fd(TEMP_FAILURE_RETRY(open(signature_path.c_str(),
-                                             O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
-                                             S_IRUSR | S_IWUSR | S_IRGRP)));
-    if (!out_fd.ok()) {
-        return ErrnoError() << "Unable to create signature file";
-    }
-
     struct stat filestat;
     if (fstat(fd, &filestat) != 0) {
         return ErrnoError() << "Failed to fstat";
@@ -443,12 +438,30 @@
     }
     std::unique_ptr<libfsverity_digest, decltype(&std::free)> digestOwner{digest, std::free};
 
-    std::vector<uint8_t> buffer(sizeof(fsverity_formatted_digest) + digest->digest_size);
+    return std::vector(&digest->digest[0], &digest->digest[digest->digest_size]);
+}
+
+static Result<void> signFile(ICompOsService* service, const std::string& file) {
+    std::filesystem::path signature_path{file};
+    signature_path += ".signature";
+    unique_fd out_fd(TEMP_FAILURE_RETRY(open(signature_path.c_str(),
+                                             O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
+                                             S_IRUSR | S_IWUSR | S_IRGRP)));
+    if (!out_fd.ok()) {
+        return ErrnoError() << "Unable to create signature file";
+    }
+
+    auto digest = computeDigest(file);
+    if (!digest.ok()) {
+        return digest.error();
+    }
+
+    std::vector<uint8_t> buffer(sizeof(fsverity_formatted_digest) + digest->size());
     auto to_be_signed = new (buffer.data()) fsverity_formatted_digest;
     memcpy(to_be_signed->magic, "FSVerity", sizeof(to_be_signed->magic));
-    to_be_signed->digest_algorithm = __cpu_to_le16(digest->digest_algorithm);
-    to_be_signed->digest_size = __cpu_to_le16(digest->digest_size);
-    memcpy(to_be_signed->digest, digest->digest, digest->digest_size);
+    to_be_signed->digest_algorithm = __cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
+    to_be_signed->digest_size = __cpu_to_le16(digest->size());
+    memcpy(to_be_signed->digest, digest->data(), digest->size());
 
     std::vector<uint8_t> signature;
     auto status = service->sign(buffer, &signature);
@@ -457,7 +470,7 @@
     }
 
     Signature compos_signature;
-    compos_signature.set_digest(digest->digest, digest->digest_size);
+    compos_signature.set_digest(digest->data(), digest->size());
     compos_signature.set_signature(signature.data(), signature.size());
     if (!compos_signature.SerializeToFileDescriptor(out_fd.get())) {
         return Error() << "Failed to write signature";
@@ -499,6 +512,87 @@
     return {};
 }
 
+static std::string toHex(const std::vector<uint8_t>& digest) {
+    std::stringstream ss;
+    for (auto it = digest.begin(); it != digest.end(); ++it) {
+        ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
+    }
+    return ss.str();
+}
+
+static Result<void> signInfo(TargetVm& vm, const std::string& blob_file,
+                             const std::string& info_file, const std::vector<std::string>& files) {
+    unique_fd info_fd(
+            TEMP_FAILURE_RETRY(open(info_file.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
+                                    S_IRUSR | S_IWUSR | S_IRGRP)));
+    if (!info_fd.ok()) {
+        return ErrnoError() << "Unable to create " << info_file;
+    }
+
+    std::string signature_file = info_file + ".signature";
+    unique_fd signature_fd(TEMP_FAILURE_RETRY(open(signature_file.c_str(),
+                                                   O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
+                                                   S_IRUSR | S_IWUSR | S_IRGRP)));
+    if (!signature_fd.ok()) {
+        return ErrnoError() << "Unable to create " << signature_file;
+    }
+
+    auto cid = vm.resolveCid();
+    if (!cid.ok()) {
+        return cid.error();
+    }
+    auto service = getService(*cid);
+    if (!service) {
+        return Error() << "No service";
+    }
+
+    auto blob = readBytesFromFile(blob_file);
+    if (!blob.ok()) {
+        return blob.error();
+    }
+
+    auto initialized = service->initializeSigningKey(blob.value());
+    if (!initialized.isOk()) {
+        return Error() << "Failed to initialize signing key: " << initialized.getDescription();
+    }
+
+    std::map<std::string, std::string> file_digests;
+
+    for (auto& file : files) {
+        auto digest = computeDigest(file);
+        if (!digest.ok()) {
+            return digest.error();
+        }
+        file_digests.emplace(file, toHex(*digest));
+    }
+
+    OdsignInfo info;
+    info.mutable_file_hashes()->insert(file_digests.begin(), file_digests.end());
+
+    std::vector<uint8_t> serialized(info.ByteSizeLong());
+    if (!info.SerializeToArray(serialized.data(), serialized.size())) {
+        return Error() << "Failed to serialize protobuf";
+    }
+
+    if (!WriteFully(info_fd, serialized.data(), serialized.size()) ||
+        close(info_fd.release()) != 0) {
+        return Error() << "Failed to write info file";
+    }
+
+    std::vector<uint8_t> signature;
+    auto status = service->sign(serialized, &signature);
+    if (!status.isOk()) {
+        return Error() << "Failed to sign: " << status.getDescription();
+    }
+
+    if (!WriteFully(signature_fd, signature.data(), signature.size()) ||
+        close(signature_fd.release()) != 0) {
+        return Error() << "Failed to write signature";
+    }
+
+    return {};
+}
+
 static Result<void> initializeKey(TargetVm& vm, const std::string& blob_file) {
     auto cid = vm.resolveCid();
     if (!cid.ok()) {
@@ -616,6 +710,17 @@
         } else {
             std::cerr << result.error() << '\n';
         }
+    } else if (argc >= 5 && argv[1] == "sign-info"sv) {
+        const std::string blob_file = argv[2];
+        const std::string info_file = argv[3];
+        const std::vector<std::string> files{&argv[4], &argv[argc]};
+        auto result = signInfo(vm, blob_file, info_file, files);
+        if (result.ok()) {
+            std::cerr << "Info file generated and signed.\n";
+            return 0;
+        } else {
+            std::cerr << result.error() << '\n';
+        }
     } else if (argc == 3 && argv[1] == "init-key"sv) {
         auto result = initializeKey(vm, argv[2]);
         if (result.ok()) {
@@ -631,16 +736,20 @@
             std::cerr << result.error() << '\n';
         }
     } else {
-        std::cerr << "Usage: compos_key_cmd [OPTIONS] generate|verify|sign|make-instance|init-key\n"
+        std::cerr << "Usage: compos_key_cmd [OPTIONS] COMMAND\n"
+                  << "Where COMMAND can be:\n"
+                  << "  make-instance <image file> Create an empty instance image file for a VM.\n"
                   << "  generate <blob file> <public key file> Generate new key pair and write\n"
                   << "    the private key blob and public key to the specified files.\n "
                   << "  verify <blob file> <public key file> Verify that the content of the\n"
                   << "    specified private key blob and public key files are valid.\n "
                   << "  init-key <blob file> Initialize the service key.\n"
                   << "  sign <blob file> <files to be signed> Generate signatures for one or\n"
-                  << "    more files using the supplied private key blob. Signature is stored in\n"
-                  << "    <filename>.signature\n"
-                  << "  make-instance <image file> Create an empty instance image file for a VM.\n"
+                  << "    more files using the supplied private key blob. Signature is stored \n"
+                  << "    in <filename>.signature\n"
+                  << "  sign-info <blob file> <info file> <files to be signed> Generate\n"
+                  << "    an info file listing the paths and root digests of each of the files to\n"
+                  << "    be signed, along with a signature of that file.\n"
                   << "\n"
                   << "OPTIONS: --log <log file> --debug --staged\n"
                   << "    (--cid <cid> | --start <image file>)\n"
diff --git a/compos/service/java/com/android/server/compos/IsolatedCompilationJobService.java b/compos/service/java/com/android/server/compos/IsolatedCompilationJobService.java
index 6cfd0ba..f801a8d 100644
--- a/compos/service/java/com/android/server/compos/IsolatedCompilationJobService.java
+++ b/compos/service/java/com/android/server/compos/IsolatedCompilationJobService.java
@@ -68,9 +68,9 @@
         int result = scheduler.schedule(new JobInfo.Builder(STAGED_APEX_JOB_ID, serviceName)
                 // Wait in case more APEXes are staged
                 .setMinimumLatency(TimeUnit.MINUTES.toMillis(60))
-                // We consume CPU, battery, and storage
+                // We consume CPU, power, and storage
                 .setRequiresDeviceIdle(true)
-                .setRequiresBatteryNotLow(true)
+                .setRequiresCharging(true)
                 .setRequiresStorageNotLow(true)
                 .build());
         if (result != JobScheduler.RESULT_SUCCESS) {
diff --git a/microdroid/init.rc b/microdroid/init.rc
index 664402f..86f6d0a 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -18,9 +18,13 @@
     start ueventd
 
     mkdir /mnt/apk 0755 system system
+    mkdir /mnt/extra-apk 0755 root root
     # Microdroid_manager starts apkdmverity/zipfuse/apexd
     start microdroid_manager
 
+    # restorecon so microdroid_manager can create subdirectories
+    restorecon /mnt/extra-apk
+
     # Wait for apexd to finish activating APEXes before starting more processes.
     wait_for_prop apexd.status activated
     perform_apex_config
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index c69d875..23a61d9 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -17,7 +17,9 @@
         "libbinder_rpc_unstable_bindgen",
         "libbinder_rs",
         "libbyteorder",
+        "libglob",
         "libidsig",
+        "libitertools",
         "libkernlog",
         "liblibc",
         "liblog_rust",
diff --git a/microdroid_manager/src/instance.rs b/microdroid_manager/src/instance.rs
index 8ba6f51..aadb71f 100644
--- a/microdroid_manager/src/instance.rs
+++ b/microdroid_manager/src/instance.rs
@@ -314,6 +314,7 @@
 #[derive(Debug, Serialize, Deserialize, PartialEq)]
 pub struct MicrodroidData {
     pub apk_data: ApkData,
+    pub extra_apks_data: Vec<ApkData>,
     pub apex_data: Vec<ApexData>,
     pub bootconfig: Box<[u8]>,
 }
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index efe6126..fccf031 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -23,7 +23,9 @@
 use apkverify::{get_public_key_der, verify};
 use binder::unstable_api::{new_spibinder, AIBinder};
 use binder::{FromIBinder, Strong};
+use glob::glob;
 use idsig::V4Signature;
+use itertools::sorted;
 use log::{error, info, warn};
 use microdroid_metadata::{write_metadata, Metadata};
 use microdroid_payload_config::{Task, TaskType, VmPayloadConfig};
@@ -31,7 +33,7 @@
 use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
 use rustutils::system_properties;
 use rustutils::system_properties::PropertyWatcher;
-use std::fs::{self, File, OpenOptions};
+use std::fs::{self, create_dir, File, OpenOptions};
 use std::os::unix::io::{FromRawFd, IntoRawFd};
 use std::path::Path;
 use std::process::{Child, Command, Stdio};
@@ -44,13 +46,11 @@
 };
 
 const WAIT_TIMEOUT: Duration = Duration::from_secs(10);
-const APK_DM_VERITY_ARGUMENT: ApkDmverityArgument = {
-    ApkDmverityArgument {
-        apk: "/dev/block/by-name/microdroid-apk",
-        idsig: "/dev/block/by-name/microdroid-apk-idsig",
-        name: "microdroid-apk",
-    }
-};
+const MAIN_APK_PATH: &str = "/dev/block/by-name/microdroid-apk";
+const MAIN_APK_IDSIG_PATH: &str = "/dev/block/by-name/microdroid-apk-idsig";
+const MAIN_APK_DEVICE_NAME: &str = "microdroid-apk";
+const EXTRA_APK_PATH_PATTERN: &str = "/dev/block/by-name/extra-apk-*";
+const EXTRA_IDSIG_PATH_PATTERN: &str = "/dev/block/by-name/extra-idsig-*";
 const DM_MOUNTED_APK_PATH: &str = "/dev/block/mapper/microdroid-apk";
 const APKDMVERITY_BIN: &str = "/system/bin/apkdmverity";
 const ZIPFUSE_BIN: &str = "/system/bin/zipfuse";
@@ -170,7 +170,16 @@
         !metadata.payload_config_path.is_empty(),
         MicrodroidError::InvalidConfig("No payload_config_path in metadata".to_string())
     );
+
     let config = load_config(Path::new(&metadata.payload_config_path))?;
+    if config.extra_apks.len() != verified_data.extra_apks_data.len() {
+        return Err(anyhow!(
+            "config expects {} extra apks, but found only {}",
+            config.extra_apks.len(),
+            verified_data.extra_apks_data.len()
+        ));
+    }
+    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) {
@@ -192,6 +201,7 @@
     apk: &'a str,
     idsig: &'a str,
     name: &'a str,
+    saved_root_hash: Option<&'a RootHash>,
 }
 
 fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
@@ -201,6 +211,11 @@
 
     for argument in args {
         cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
+        if let Some(root_hash) = argument.saved_root_hash {
+            cmd.arg(&to_hex_string(root_hash));
+        } else {
+            cmd.arg("none");
+        }
     }
 
     cmd.spawn().context("Spawn apkdmverity")
@@ -236,19 +251,86 @@
         );
     }
 
+    // Verify main APK
     let root_hash = saved_data.map(|d| &d.apk_data.root_hash);
-    let root_hash_from_idsig = get_apk_root_hash_from_idsig()?;
+    let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
     let root_hash_trustful = root_hash == Some(&root_hash_from_idsig);
 
     // If root_hash can be trusted, pass it to apkdmverity so that it uses the passed root_hash
     // instead of the value read from the idsig file.
-    if root_hash_trustful {
-        let root_hash = to_hex_string(root_hash.unwrap());
-        system_properties::write("microdroid_manager.apk_root_hash", &root_hash)?;
+    let main_apk_argument = {
+        ApkDmverityArgument {
+            apk: MAIN_APK_PATH,
+            idsig: MAIN_APK_IDSIG_PATH,
+            name: MAIN_APK_DEVICE_NAME,
+            saved_root_hash: if root_hash_trustful {
+                Some(root_hash_from_idsig.as_ref())
+            } else {
+                None
+            },
+        }
+    };
+    let mut apkdmverity_arguments = vec![main_apk_argument];
+
+    // Verify extra APKs
+    // For now, we can't read the payload config, so glob APKs and idsigs.
+    // Later, we'll see if it matches with the payload config.
+
+    // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
+    // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
+    let extra_apks =
+        sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
+    let extra_idsigs =
+        sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
+    if extra_apks.len() != extra_idsigs.len() {
+        return Err(anyhow!(
+            "Extra apks/idsigs mismatch: {} apks but {} idsigs",
+            extra_apks.len(),
+            extra_idsigs.len()
+        ));
+    }
+    let extra_apks_count = extra_apks.len();
+
+    let (extra_apk_names, extra_root_hashes_from_idsig): (Vec<_>, Vec<_>) = extra_idsigs
+        .iter()
+        .enumerate()
+        .map(|(i, extra_idsig)| {
+            (
+                format!("extra-apk-{}", i),
+                get_apk_root_hash_from_idsig(extra_idsig.to_str().unwrap())
+                    .expect("Can't find root hash from extra idsig"),
+            )
+        })
+        .unzip();
+
+    let saved_extra_root_hashes: Vec<_> = saved_data
+        .map(|d| d.extra_apks_data.iter().map(|apk_data| &apk_data.root_hash).collect())
+        .unwrap_or_else(Vec::new);
+    let extra_root_hashes_trustful: Vec<_> = extra_root_hashes_from_idsig
+        .iter()
+        .enumerate()
+        .map(|(i, root_hash_from_idsig)| {
+            saved_extra_root_hashes.get(i).copied() == Some(root_hash_from_idsig)
+        })
+        .collect();
+
+    for i in 0..extra_apks_count {
+        apkdmverity_arguments.push({
+            ApkDmverityArgument {
+                apk: extra_apks[i].to_str().unwrap(),
+                idsig: extra_idsigs[i].to_str().unwrap(),
+                name: &extra_apk_names[i],
+                saved_root_hash: if extra_root_hashes_trustful[i] {
+                    Some(&extra_root_hashes_from_idsig[i])
+                } else {
+                    None
+                },
+            }
+        });
     }
 
     // Start apkdmverity and wait for the dm-verify block
-    let mut apkdmverity_child = run_apkdmverity(&[APK_DM_VERITY_ARGUMENT])?;
+    let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
 
     // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
     // APEX payload.
@@ -280,26 +362,47 @@
     // taken only when the root_hash is un-trustful which can be either when this is the first boot
     // of the VM or APK was updated in the host.
     // TODO(jooyung): consider multithreading to make this faster
-    let apk_pubkey = if !root_hash_trustful {
-        verify(DM_MOUNTED_APK_PATH).context(MicrodroidError::PayloadVerificationFailed(format!(
-            "failed to verify {}",
-            DM_MOUNTED_APK_PATH
-        )))?
-    } else {
-        get_public_key_der(DM_MOUNTED_APK_PATH)?
-    };
+    let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
+    let extra_apks_data = extra_root_hashes_from_idsig
+        .into_iter()
+        .enumerate()
+        .map(|(i, extra_root_hash)| {
+            let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
+            let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
+            Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
+        })
+        .collect::<Result<Vec<_>>>()?;
 
     info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
 
     // 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 {
-        apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: apk_pubkey },
+        apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
+        extra_apks_data,
         apex_data: apex_data_from_payload,
         bootconfig: get_bootconfig()?.clone().into_boxed_slice(),
     })
 }
 
+fn mount_extra_apks(config: &VmPayloadConfig) -> Result<()> {
+    // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
+    for i in 0..config.extra_apks.len() {
+        let mount_dir = format!("/mnt/extra-apk/{}", i);
+        create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
+
+        // don't wait, just detach
+        run_zipfuse(
+            "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
+            Path::new(&format!("/dev/block/mapper/extra-apk-{}", i)),
+            Path::new(&mount_dir),
+        )
+        .context("Failed to zipfuse extra apks")?;
+    }
+
+    Ok(())
+}
+
 // Waits until linker config is generated
 fn wait_for_apex_config_done() -> Result<()> {
     let mut prop = PropertyWatcher::new(APEX_CONFIG_DONE_PROP)?;
@@ -313,17 +416,26 @@
     Ok(())
 }
 
-fn get_apk_root_hash_from_idsig() -> Result<Box<RootHash>> {
-    let mut idsig = File::open("/dev/block/by-name/microdroid-apk-idsig")?;
+fn get_apk_root_hash_from_idsig(path: &str) -> Result<Box<RootHash>> {
+    let mut idsig = File::open(path)?;
     let idsig = V4Signature::from(&mut idsig)?;
     Ok(idsig.hashing_info.raw_root_hash)
 }
 
+fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
+    if !root_hash_trustful {
+        verify(apk).context(MicrodroidError::PayloadVerificationFailed(format!(
+            "failed to verify {}",
+            apk
+        )))
+    } else {
+        get_public_key_der(apk)
+    }
+}
+
 fn get_bootconfig() -> Result<&'static Vec<u8>> {
     static VAL: OnceCell<Vec<u8>> = OnceCell::new();
-    VAL.get_or_try_init(|| {
-        fs::read("/proc/bootconfig").context("Failed to read bootconfig")
-    })
+    VAL.get_or_try_init(|| fs::read("/proc/bootconfig").context("Failed to read bootconfig"))
 }
 
 fn load_config(path: &Path) -> Result<VmPayloadConfig> {