virtualizationservice: pass *CLASSPATH apexes to VM
Apps can tell virtualizationservice to pass APEXes contributing
*CLASSPATH environment variables to VM instance.
In VM's config(json), app can add "apexes" entries like following.
"apexes": [
{ "name": "{BOOTCLASSPATH}" },
..
]
For now BOOTCLASSPATH, DEX2OATBOOTCLASSPATH, SYSTEMSERVERCLASSPATH
variables are supported.
Bug: 201788989
Test: atest virtualizationservice_device_test
Test: atest MicrodroidHostTestCases
Change-Id: I9295ffa0b66444fefc2839255e953f92bf755217
diff --git a/virtualizationservice/src/payload.rs b/virtualizationservice/src/payload.rs
index 59ad81c..3520d9f 100644
--- a/virtualizationservice/src/payload.rs
+++ b/virtualizationservice/src/payload.rs
@@ -21,13 +21,14 @@
use android_system_virtualizationservice::binder::ParcelFileDescriptor;
use anyhow::{anyhow, Context, Result};
use binder::{wait_for_interface, Strong};
-use log::info;
+use log::{error, info};
use microdroid_metadata::{ApexPayload, ApkPayload, Metadata};
use microdroid_payload_config::{ApexConfig, VmPayloadConfig};
use once_cell::sync::OnceCell;
use packagemanager_aidl::aidl::android::content::pm::IPackageManagerNative::IPackageManagerNative;
use serde::Deserialize;
use serde_xml_rs::from_reader;
+use std::env;
use std::fs::{File, OpenOptions};
use std::path::{Path, PathBuf};
use vmconfig::open_parcel_file;
@@ -107,17 +108,17 @@
fn make_metadata_file(
config_path: &str,
- apexes: &[ApexConfig],
+ apex_names: &[String],
temporary_directory: &Path,
) -> Result<ParcelFileDescriptor> {
let metadata_path = temporary_directory.join("metadata");
let metadata = Metadata {
version: 1,
- apexes: apexes
+ apexes: apex_names
.iter()
.enumerate()
- .map(|(i, apex)| ApexPayload {
- name: apex.name.clone(),
+ .map(|(i, apex_name)| ApexPayload {
+ name: apex_name.clone(),
partition_name: format!("microdroid-apex-{}", i),
..Default::default()
})
@@ -157,7 +158,7 @@
apk_file: File,
idsig_file: File,
config_path: &str,
- apexes: &[ApexConfig],
+ apexes: &[String],
prefer_staged: bool,
temporary_directory: &Path,
) -> Result<DiskImage> {
@@ -171,7 +172,7 @@
let pm = PackageManager::new()?;
for (i, apex) in apexes.iter().enumerate() {
- let apex_path = pm.get_apex_path(&apex.name, prefer_staged)?;
+ let apex_path = pm.get_apex_path(apex, prefer_staged)?;
let apex_file = open_parcel_file(&apex_path, false)?;
partitions.push(Partition {
label: format!("microdroid-apex-{}", i),
@@ -193,6 +194,47 @@
Ok(DiskImage { image: None, partitions, writable: false })
}
+fn find_apex_names_in_classpath_env(classpath_env_var: &str) -> Vec<String> {
+ let val = env::var(classpath_env_var).unwrap_or_else(|e| {
+ error!("Reading {} failed: {}", classpath_env_var, e);
+ String::from("")
+ });
+ val.split(':')
+ .filter_map(|path| {
+ Path::new(path)
+ .strip_prefix("/apex/")
+ .map(|stripped| {
+ let first = stripped.iter().next().unwrap();
+ first.to_str().unwrap().to_string()
+ })
+ .ok()
+ })
+ .collect()
+}
+
+// Collect APEX names from config
+fn collect_apex_names(apexes: &[ApexConfig]) -> Vec<String> {
+ // Process pseudo names like "{BOOTCLASSPATH}".
+ // For now we have following pseudo APEX names:
+ // - {BOOTCLASSPATH}: represents APEXes contributing "BOOTCLASSPATH" environment variable
+ // - {DEX2OATBOOTCLASSPATH}: represents APEXes contributing "DEX2OATBOOTCLASSPATH" environment variable
+ // - {SYSTEMSERVERCLASSPATH}: represents APEXes contributing "SYSTEMSERVERCLASSPATH" environment variable
+ let mut apex_names: Vec<String> = apexes
+ .iter()
+ .flat_map(|apex| match apex.name.as_str() {
+ "{BOOTCLASSPATH}" => find_apex_names_in_classpath_env("BOOTCLASSPATH"),
+ "{DEX2OATBOOTCLASSPATH}" => find_apex_names_in_classpath_env("DEX2OATBOOTCLASSPATH"),
+ "{SYSTEMSERVERCLASSPATH}" => find_apex_names_in_classpath_env("SYSTEMSERVERCLASSPATH"),
+ _ => vec![apex.name.clone()],
+ })
+ .collect();
+ // Add required APEXes
+ apex_names.extend(MICRODROID_REQUIRED_APEXES.iter().map(|name| name.to_string()));
+ apex_names.sort();
+ apex_names.dedup();
+ apex_names
+}
+
pub fn add_microdroid_images(
config: &VirtualMachineAppConfig,
temporary_directory: &Path,
@@ -202,12 +244,9 @@
vm_payload_config: &VmPayloadConfig,
vm_config: &mut VirtualMachineRawConfig,
) -> Result<()> {
- let mut apexes = vm_payload_config.apexes.clone();
- apexes.extend(
- MICRODROID_REQUIRED_APEXES.iter().map(|name| ApexConfig { name: name.to_string() }),
- );
- apexes.dedup_by(|a, b| a.name == b.name);
-
+ // collect APEX names from config
+ let apexes = collect_apex_names(&vm_payload_config.apexes);
+ info!("Microdroid payload APEXes: {:?}", apexes);
vm_config.disks.push(make_payload_disk(
apk_file,
idsig_file,
@@ -237,3 +276,18 @@
Ok(())
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ #[test]
+ fn test_find_apex_names_in_classpath_env() {
+ let key = "TEST_BOOTCLASSPATH";
+ let classpath = "/apex/com.android.foo/javalib/foo.jar:/system/framework/framework.jar:/apex/com.android.bar/javalib/bar.jar";
+ env::set_var(key, classpath);
+ assert_eq!(
+ find_apex_names_in_classpath_env(key),
+ vec!["com.android.foo".to_owned(), "com.android.bar".to_owned()]
+ );
+ }
+}