Merge "Add test that makes fd_server use of fs-verity ioctl"
diff --git a/apkdmverity/Android.bp b/apkdmverity/Android.bp
index 97a6174..51ef4dd 100644
--- a/apkdmverity/Android.bp
+++ b/apkdmverity/Android.bp
@@ -43,4 +43,8 @@
rustlibs: [
"libtempfile",
],
+ data: [
+ "testdata/*.apk",
+ "testdata/*.idsig",
+ ],
}
diff --git a/apkdmverity/src/main.rs b/apkdmverity/src/main.rs
index de7f5bb..b99ed1c 100644
--- a/apkdmverity/src/main.rs
+++ b/apkdmverity/src/main.rs
@@ -31,7 +31,6 @@
use itertools::Itertools;
use std::fmt::Debug;
use std::fs;
-use std::fs::File;
use std::os::unix::fs::FileTypeExt;
use std::path::{Path, PathBuf};
@@ -104,9 +103,7 @@
// Parse the idsig file to locate the merkle tree in it, then attach the file to a loop device
// with the offset so that the start of the merkle tree becomes the beginning of the loop
// device.
- let sig = V4Signature::from(
- File::open(&idsig).context(format!("Failed to open idsig file {:?}", &idsig))?,
- )?;
+ let sig = V4Signature::from_idsig_path(&idsig)?;
let offset = sig.merkle_tree_offset;
let size = sig.merkle_tree_size as u64;
// Due to unknown reason(b/191344832), we can't enable "direct IO" for the IDSIG file (backing
@@ -143,8 +140,8 @@
#[cfg(test)]
mod tests {
use crate::*;
- use std::fs::OpenOptions;
- use std::io::{Cursor, Write};
+ use std::fs::{File, OpenOptions};
+ use std::io::Write;
use std::os::unix::fs::FileExt;
struct TestContext<'a> {
@@ -251,7 +248,9 @@
let idsig = include_bytes!("../testdata/test.apk.idsig");
// Make a single-byte change to the merkle tree
- let offset = V4Signature::from(Cursor::new(&idsig)).unwrap().merkle_tree_offset as usize;
+ let offset = V4Signature::from_idsig_path("testdata/test.apk.idsig")
+ .unwrap()
+ .merkle_tree_offset as usize;
let mut modified_idsig = Vec::new();
modified_idsig.extend_from_slice(idsig);
@@ -354,7 +353,10 @@
fn correct_custom_roothash() {
let apk = include_bytes!("../testdata/test.apk");
let idsig = include_bytes!("../testdata/test.apk.idsig");
- let roothash = V4Signature::from(Cursor::new(&idsig)).unwrap().hashing_info.raw_root_hash;
+ let roothash = V4Signature::from_idsig_path("testdata/test.apk.idsig")
+ .unwrap()
+ .hashing_info
+ .raw_root_hash;
run_test_with_hash(
apk.as_ref(),
idsig.as_ref(),
diff --git a/avmd/src/main.rs b/avmd/src/main.rs
index ca28f42..fc18225 100644
--- a/avmd/src/main.rs
+++ b/avmd/src/main.rs
@@ -16,7 +16,7 @@
use anyhow::{anyhow, bail, Result};
use apexutil::get_payload_vbmeta_image_hash;
-use apkverify::pick_v4_apk_digest;
+use apkverify::get_apk_digest;
use avmd::{ApkDescriptor, Avmd, Descriptor, ResourceIdentifier, VbMetaDescriptor};
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
use serde::ser::Serialize;
@@ -74,7 +74,7 @@
}
for (i, namespace, name, file) in NamespaceNameFileIterator::new(args, "apk") {
let file = File::open(file)?;
- let (signature_algorithm_id, apk_digest) = pick_v4_apk_digest(file)?;
+ let (signature_algorithm_id, apk_digest) = get_apk_digest(file, /*verify=*/ true)?;
descriptors.insert(
i,
Descriptor::Apk(ApkDescriptor {
diff --git a/compos/aidl/com/android/compos/ICompOsService.aidl b/compos/aidl/com/android/compos/ICompOsService.aidl
index 9b45e13..df8c91e 100644
--- a/compos/aidl/com/android/compos/ICompOsService.aidl
+++ b/compos/aidl/com/android/compos/ICompOsService.aidl
@@ -17,6 +17,7 @@
package com.android.compos;
/** {@hide} */
+@SuppressWarnings(value={"mixed-oneway"})
interface ICompOsService {
/**
* Initializes system properties. ART expects interesting properties that have to be passed from
@@ -41,28 +42,41 @@
TEST_COMPILE = 1,
}
+ /** Arguments to run odrefresh */
+ parcelable OdrefreshArgs {
+ /** The type of compilation to be performed */
+ CompilationMode compilationMode = CompilationMode.NORMAL_COMPILE;
+ /** An fd referring to /system */
+ int systemDirFd = -1;
+ /** An optional fd referring to /system_ext. Negative number means none. */
+ int systemExtDirFd = -1;
+ /** An fd referring to the output directory, ART_APEX_DATA */
+ int outputDirFd = -1;
+ /** An fd referring to the staging directory, e.g. ART_APEX_DATA/staging */
+ int stagingDirFd = -1;
+ /**
+ * The sub-directory of the output directory to which artifacts are to be written (e.g.
+ * dalvik-cache)
+ */
+ String targetDirName;
+ /** The zygote architecture (ro.zygote) */
+ String zygoteArch;
+ /** The compiler filter used to compile system server */
+ String systemServerCompilerFilter;
+ }
+
/**
* Run odrefresh in the VM context.
*
* The execution is based on the VM's APEX mounts, files on Android's /system and optionally
- * /system_ext (by accessing through systemDirFd and systemExtDirFd over AuthFS), and
- * *CLASSPATH derived in the VM, to generate the same odrefresh output artifacts to the output
- * directory (through outputDirFd).
+ * /system_ext (by accessing through OdrefreshArgs.systemDirFd and OdrefreshArgs.systemExtDirFd
+ * over AuthFS), and *CLASSPATH derived in the VM, to generate the same odrefresh output
+ * artifacts to the output directory (through OdrefreshArgs.outputDirFd).
*
- * @param compilationMode The type of compilation to be performed
- * @param systemDirFd An fd referring to /system
- * @param systemExtDirFd An optional fd referring to /system_ext. Negative number means none.
- * @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
- * @param targetDirName The sub-directory of the output directory to which artifacts are to be
- * written (e.g. dalvik-cache)
- * @param zygoteArch The zygote architecture (ro.zygote)
- * @param systemServerCompilerFilter The compiler filter used to compile system server
+ * @param args Arguments to configure the odrefresh context
* @return odrefresh exit code
*/
- byte odrefresh(CompilationMode compilation_mode, int systemDirFd, int systemExtDirFd,
- int outputDirFd, int stagingDirFd, String targetDirName, String zygoteArch,
- String systemServerCompilerFilter);
+ byte odrefresh(in OdrefreshArgs args);
/**
* Returns the current VM's signing key, as an Ed25519 public key
diff --git a/compos/apk/assets/vm_config.json b/compos/apk/assets/vm_config.json
index c4abfd9..1f5cdba 100644
--- a/compos/apk/assets/vm_config.json
+++ b/compos/apk/assets/vm_config.json
@@ -26,5 +26,6 @@
"name": "{CLASSPATH}"
}
],
- "export_tombstones": true
+ "export_tombstones": true,
+ "enable_authfs": true
}
diff --git a/compos/apk/assets/vm_config_staged.json b/compos/apk/assets/vm_config_staged.json
index 0be6e78..37b1d7a 100644
--- a/compos/apk/assets/vm_config_staged.json
+++ b/compos/apk/assets/vm_config_staged.json
@@ -27,5 +27,6 @@
"name": "{CLASSPATH}"
}
],
- "export_tombstones": true
+ "export_tombstones": true,
+ "enable_authfs": true
}
diff --git a/compos/apk/assets/vm_config_system_ext.json b/compos/apk/assets/vm_config_system_ext.json
index e60dee7..1ef43f0 100644
--- a/compos/apk/assets/vm_config_system_ext.json
+++ b/compos/apk/assets/vm_config_system_ext.json
@@ -29,5 +29,6 @@
"name": "{CLASSPATH}"
}
],
- "export_tombstones": true
+ "export_tombstones": true,
+ "enable_authfs": true
}
diff --git a/compos/apk/assets/vm_config_system_ext_staged.json b/compos/apk/assets/vm_config_system_ext_staged.json
index 99a4160..9103a9e 100644
--- a/compos/apk/assets/vm_config_system_ext_staged.json
+++ b/compos/apk/assets/vm_config_system_ext_staged.json
@@ -30,5 +30,6 @@
"name": "{CLASSPATH}"
}
],
- "export_tombstones": true
+ "export_tombstones": true,
+ "enable_authfs": true
}
diff --git a/compos/benchmark/src/java/com/android/compos/benchmark/ComposBenchmark.java b/compos/benchmark/src/java/com/android/compos/benchmark/ComposBenchmark.java
index 996d32a..c25de71 100644
--- a/compos/benchmark/src/java/com/android/compos/benchmark/ComposBenchmark.java
+++ b/compos/benchmark/src/java/com/android/compos/benchmark/ComposBenchmark.java
@@ -23,7 +23,6 @@
import android.app.Instrumentation;
import android.os.Bundle;
-import android.os.ParcelFileDescriptor;
import android.util.Log;
import com.android.microdroid.test.common.MetricsProcessor;
@@ -36,9 +35,7 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
@@ -58,7 +55,7 @@
private static final int BUFFER_SIZE = 1024;
private static final int ROUND_COUNT = 5;
private static final double NANOS_IN_SEC = 1_000_000_000.0;
- private static final String METRIC_PREFIX = "avf_perf/compos/";
+ private static final String METRIC_PREFIX = getMetricPrefix() + "compos/";
private final MetricsProcessor mMetricsProcessor = new MetricsProcessor(METRIC_PREFIX);
@@ -198,37 +195,8 @@
processMemory.forEach((k, v) -> reportMetric(prefix + k, unit, v));
}
- private byte[] executeCommandBlocking(String command) {
- try (InputStream is =
- new ParcelFileDescriptor.AutoCloseInputStream(
- mInstrumentation.getUiAutomation().executeShellCommand(command));
- ByteArrayOutputStream out = new ByteArrayOutputStream()) {
- byte[] buf = new byte[BUFFER_SIZE];
- int length;
- while ((length = is.read(buf)) >= 0) {
- out.write(buf, 0, length);
- }
- return out.toByteArray();
- } catch (IOException e) {
- Log.e(TAG, "Error executing: " + command, e);
- return null;
- }
- }
-
private String executeCommand(String command) {
- try {
- byte[] output = executeCommandBlocking(command);
-
- if (output == null) {
- throw new RuntimeException("Failed to run the command.");
- } else {
- String stdout = new String(output, "UTF-8");
- Log.i(TAG, "Get stdout : " + stdout);
- return stdout;
- }
- } catch (Exception e) {
- throw new RuntimeException("Error executing: " + command + " , Exception: " + e);
- }
+ return runInShell(TAG, mInstrumentation.getUiAutomation(), command);
}
private class GetMetricsRunnable implements Runnable {
diff --git a/compos/composd/src/odrefresh_task.rs b/compos/composd/src/odrefresh_task.rs
index 9276fb1..3a699ab 100644
--- a/compos/composd/src/odrefresh_task.rs
+++ b/compos/composd/src/odrefresh_task.rs
@@ -25,7 +25,7 @@
use anyhow::{Context, Result};
use binder::{Interface, Result as BinderResult, Strong};
use compos_aidl_interface::aidl::com::android::compos::ICompOsService::{
- CompilationMode::CompilationMode, ICompOsService,
+ CompilationMode::CompilationMode, ICompOsService, OdrefreshArgs::OdrefreshArgs,
};
use compos_common::odrefresh::{
is_system_property_interesting, ExitCode, ODREFRESH_OUTPUT_ROOT_DIR,
@@ -180,16 +180,18 @@
let zygote_arch = system_properties::read("ro.zygote")?.context("ro.zygote not set")?;
let system_server_compiler_filter =
system_properties::read("dalvik.vm.systemservercompilerfilter")?.unwrap_or_default();
- let exit_code = service.odrefresh(
- compilation_mode,
- system_dir_raw_fd,
- system_ext_dir_raw_fd,
- output_dir_raw_fd,
- staging_dir_raw_fd,
- target_dir_name,
- &zygote_arch,
- &system_server_compiler_filter,
- )?;
+
+ let args = OdrefreshArgs {
+ compilationMode: compilation_mode,
+ systemDirFd: system_dir_raw_fd,
+ systemExtDirFd: system_ext_dir_raw_fd,
+ outputDirFd: output_dir_raw_fd,
+ stagingDirFd: staging_dir_raw_fd,
+ targetDirName: target_dir_name.to_string(),
+ zygoteArch: zygote_arch,
+ systemServerCompilerFilter: system_server_compiler_filter,
+ };
+ let exit_code = service.odrefresh(&args)?;
drop(fd_server_raii);
ExitCode::from_i32(exit_code.into())
diff --git a/compos/src/compilation.rs b/compos/src/compilation.rs
index d165599..2872d95 100644
--- a/compos/src/compilation.rs
+++ b/compos/src/compilation.rs
@@ -33,95 +33,65 @@
IAuthFsService::IAuthFsService,
};
use binder::Strong;
-use compos_aidl_interface::aidl::com::android::compos::ICompOsService::CompilationMode::CompilationMode;
+use compos_aidl_interface::aidl::com::android::compos::ICompOsService::{
+ CompilationMode::CompilationMode, OdrefreshArgs::OdrefreshArgs,
+};
use compos_common::odrefresh::ExitCode;
const FD_SERVER_PORT: i32 = 3264; // TODO: support dynamic port
-pub struct OdrefreshContext<'a> {
- compilation_mode: CompilationMode,
- system_dir_fd: i32,
- system_ext_dir_fd: Option<i32>,
- output_dir_fd: i32,
- staging_dir_fd: i32,
- target_dir_name: &'a str,
- zygote_arch: &'a str,
- system_server_compiler_filter: &'a str,
-}
-
-impl<'a> OdrefreshContext<'a> {
- #[allow(clippy::too_many_arguments)]
- pub fn new(
- compilation_mode: CompilationMode,
- system_dir_fd: i32,
- system_ext_dir_fd: Option<i32>,
- output_dir_fd: i32,
- staging_dir_fd: i32,
- target_dir_name: &'a str,
- zygote_arch: &'a str,
- system_server_compiler_filter: &'a str,
- ) -> Result<Self> {
- if compilation_mode != CompilationMode::NORMAL_COMPILE {
- // Conservatively check debuggability.
- let debuggable =
- system_properties::read_bool("ro.boot.microdroid.app_debuggable", false)
- .unwrap_or(false);
- if !debuggable {
- bail!("Requested compilation mode only available in debuggable VMs");
- }
+fn validate_args(args: &OdrefreshArgs) -> Result<()> {
+ if args.compilationMode != CompilationMode::NORMAL_COMPILE {
+ // Conservatively check debuggability.
+ let debuggable = system_properties::read_bool("ro.boot.microdroid.app_debuggable", false)
+ .unwrap_or(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");
- }
- if !matches!(zygote_arch, "zygote64" | "zygote64_32") {
- bail!("Invalid zygote arch");
- }
- // Disallow any sort of path traversal
- if target_dir_name.contains(path::MAIN_SEPARATOR) {
- bail!("Invalid target directory {}", target_dir_name);
- }
-
- // We're not validating/allowlisting the compiler filter, and just assume the compiler will
- // reject an invalid string. We need to accept "verify" filter anyway, and potential
- // performance degration by the attacker is not currently in scope. This also allows ART to
- // specify new compiler filter and configure through system property without change to
- // CompOS.
-
- Ok(Self {
- compilation_mode,
- system_dir_fd,
- system_ext_dir_fd,
- output_dir_fd,
- staging_dir_fd,
- target_dir_name,
- zygote_arch,
- system_server_compiler_filter,
- })
}
+
+ if args.systemDirFd < 0 || args.outputDirFd < 0 || args.stagingDirFd < 0 {
+ bail!("The remote FDs are expected to be non-negative");
+ }
+ if !matches!(&args.zygoteArch[..], "zygote64" | "zygote64_32") {
+ bail!("Invalid zygote arch");
+ }
+ // Disallow any sort of path traversal
+ if args.targetDirName.contains(path::MAIN_SEPARATOR) {
+ bail!("Invalid target directory {}", args.targetDirName);
+ }
+
+ // We're not validating/allowlisting the compiler filter, and just assume the compiler will
+ // reject an invalid string. We need to accept "verify" filter anyway, and potential
+ // performance degration by the attacker is not currently in scope. This also allows ART to
+ // specify new compiler filter and configure through system property without change to
+ // CompOS.
+ Ok(())
}
pub fn odrefresh<F>(
odrefresh_path: &Path,
- context: OdrefreshContext,
+ args: &OdrefreshArgs,
authfs_service: Strong<dyn IAuthFsService>,
success_fn: F,
) -> Result<ExitCode>
where
F: FnOnce(PathBuf) -> Result<()>,
{
+ validate_args(args)?;
+
// Mount authfs (via authfs_service). The authfs instance unmounts once the `authfs` variable
// is out of scope.
let mut input_dir_fd_annotations = vec![InputDirFdAnnotation {
- fd: context.system_dir_fd,
+ fd: args.systemDirFd,
// Use the 0th APK of the extra_apks in compos/apk/assets/vm_config*.json
manifestPath: "/mnt/extra-apk/0/assets/build_manifest.pb".to_string(),
prefix: "system/".to_string(),
}];
- if let Some(fd) = context.system_ext_dir_fd {
+ if args.systemExtDirFd >= 0 {
input_dir_fd_annotations.push(InputDirFdAnnotation {
- fd,
+ fd: args.systemExtDirFd,
// Use the 1st APK of the extra_apks in compos/apk/assets/vm_config_system_ext_*.json
manifestPath: "/mnt/extra-apk/1/assets/build_manifest.pb".to_string(),
prefix: "system_ext/".to_string(),
@@ -132,8 +102,8 @@
port: FD_SERVER_PORT,
inputDirFdAnnotations: input_dir_fd_annotations,
outputDirFdAnnotations: vec![
- OutputDirFdAnnotation { fd: context.output_dir_fd },
- OutputDirFdAnnotation { fd: context.staging_dir_fd },
+ OutputDirFdAnnotation { fd: args.outputDirFd },
+ OutputDirFdAnnotation { fd: args.stagingDirFd },
],
..Default::default()
};
@@ -144,52 +114,50 @@
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(args.systemDirFd.to_string());
android_root.push("system");
odrefresh_vars.set("ANDROID_ROOT", path_to_str(&android_root)?);
debug!("ANDROID_ROOT={:?}", &android_root);
- if let Some(fd) = context.system_ext_dir_fd {
+ if args.systemExtDirFd >= 0 {
let mut system_ext_root = mountpoint.clone();
- system_ext_root.push(fd.to_string());
+ system_ext_root.push(args.systemExtDirFd.to_string());
system_ext_root.push("system_ext");
odrefresh_vars.set("SYSTEM_EXT_ROOT", path_to_str(&system_ext_root)?);
debug!("SYSTEM_EXT_ROOT={:?}", &system_ext_root);
}
- let art_apex_data = mountpoint.join(context.output_dir_fd.to_string());
+ let art_apex_data = mountpoint.join(args.outputDirFd.to_string());
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());
+ let staging_dir = mountpoint.join(args.stagingDirFd.to_string());
set_classpaths(&mut odrefresh_vars, &android_root)?;
- let mut args = vec![
+ let mut command_line_args = vec![
"odrefresh".to_string(),
"--compilation-os-mode".to_string(),
- format!("--zygote-arch={}", context.zygote_arch),
- format!("--dalvik-cache={}", context.target_dir_name),
+ format!("--zygote-arch={}", args.zygoteArch),
+ format!("--dalvik-cache={}", args.targetDirName),
format!("--staging-dir={}", staging_dir.display()),
"--no-refresh".to_string(),
];
- if !context.system_server_compiler_filter.is_empty() {
- args.push(format!(
- "--system-server-compiler-filter={}",
- context.system_server_compiler_filter
- ));
+ if !args.systemServerCompilerFilter.is_empty() {
+ command_line_args
+ .push(format!("--system-server-compiler-filter={}", args.systemServerCompilerFilter));
}
- let compile_flag = match context.compilation_mode {
+ let compile_flag = match args.compilationMode {
CompilationMode::NORMAL_COMPILE => "--compile",
CompilationMode::TEST_COMPILE => "--force-compile",
other => bail!("Unknown compilation mode {:?}", other),
};
- args.push(compile_flag.to_string());
+ command_line_args.push(compile_flag.to_string());
- debug!("Running odrefresh with args: {:?}", &args);
- let jail = spawn_jailed_task(odrefresh_path, &args, &odrefresh_vars.into_env())
+ debug!("Running odrefresh with args: {:?}", &command_line_args);
+ let jail = spawn_jailed_task(odrefresh_path, &command_line_args, &odrefresh_vars.into_env())
.context("Spawn odrefresh")?;
let exit_code = match jail.wait() {
Ok(_) => 0,
@@ -201,7 +169,7 @@
info!("odrefresh exited with {:?}", exit_code);
if exit_code == ExitCode::CompilationSuccess {
- let target_dir = art_apex_data.join(context.target_dir_name);
+ let target_dir = art_apex_data.join(&args.targetDirName);
success_fn(target_dir)?;
}
diff --git a/compos/src/compsvc.rs b/compos/src/compsvc.rs
index 7ce60cd..4330bbf 100644
--- a/compos/src/compsvc.rs
+++ b/compos/src/compsvc.rs
@@ -28,11 +28,11 @@
use std::sync::RwLock;
use crate::artifact_signer::ArtifactSigner;
-use crate::compilation::{odrefresh, OdrefreshContext};
+use crate::compilation::odrefresh;
use crate::compos_key;
use binder::{BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Status, Strong};
use compos_aidl_interface::aidl::com::android::compos::ICompOsService::{
- BnCompOsService, CompilationMode::CompilationMode, ICompOsService,
+ BnCompOsService, ICompOsService, OdrefreshArgs::OdrefreshArgs,
};
use compos_common::binder::to_binder_result;
use compos_common::odrefresh::{is_system_property_interesting, ODREFRESH_PATH};
@@ -98,17 +98,7 @@
Ok(())
}
- fn odrefresh(
- &self,
- compilation_mode: CompilationMode,
- system_dir_fd: i32,
- system_ext_dir_fd: i32,
- output_dir_fd: i32,
- staging_dir_fd: i32,
- target_dir_name: &str,
- zygote_arch: &str,
- system_server_compiler_filter: &str,
- ) -> BinderResult<i8> {
+ fn odrefresh(&self, args: &OdrefreshArgs) -> BinderResult<i8> {
let initialized = *self.initialized.read().unwrap();
if !initialized.unwrap_or(false) {
return Err(Status::new_exception_str(
@@ -117,30 +107,7 @@
));
}
- let context = to_binder_result(OdrefreshContext::new(
- compilation_mode,
- system_dir_fd,
- if system_ext_dir_fd >= 0 { Some(system_ext_dir_fd) } else { None },
- output_dir_fd,
- staging_dir_fd,
- target_dir_name,
- zygote_arch,
- system_server_compiler_filter,
- ))?;
-
- let authfs_service = binder::get_interface(AUTHFS_SERVICE_NAME)?;
- let exit_code = to_binder_result(
- odrefresh(&self.odrefresh_path, context, authfs_service, |output_dir| {
- // authfs only shows us the files we created, so it's ok to just sign everything
- // under the output directory.
- let mut artifact_signer = ArtifactSigner::new(&output_dir);
- add_artifacts(&output_dir, &mut artifact_signer)?;
-
- artifact_signer.write_info_and_signature(&output_dir.join("compos.info"))
- })
- .context("odrefresh failed"),
- )?;
- Ok(exit_code as i8)
+ to_binder_result(self.do_odrefresh(args))
}
fn getPublicKey(&self) -> BinderResult<Vec<u8>> {
@@ -158,6 +125,23 @@
}
}
+impl CompOsService {
+ fn do_odrefresh(&self, args: &OdrefreshArgs) -> Result<i8> {
+ let authfs_service = binder::get_interface(AUTHFS_SERVICE_NAME)
+ .context("Unable to connect to AuthFS service")?;
+ let exit_code = odrefresh(&self.odrefresh_path, args, authfs_service, |output_dir| {
+ // authfs only shows us the files we created, so it's ok to just sign everything
+ // under the output directory.
+ let mut artifact_signer = ArtifactSigner::new(&output_dir);
+ add_artifacts(&output_dir, &mut artifact_signer)?;
+
+ artifact_signer.write_info_and_signature(&output_dir.join("compos.info"))
+ })
+ .context("odrefresh failed")?;
+ Ok(exit_code as i8)
+ }
+}
+
fn add_artifacts(target_dir: &Path, artifact_signer: &mut ArtifactSigner) -> Result<()> {
for entry in
read_dir(&target_dir).with_context(|| format!("Traversing {}", target_dir.display()))?
diff --git a/libs/apexutil/src/lib.rs b/libs/apexutil/src/lib.rs
index 1183aea..999f16d 100644
--- a/libs/apexutil/src/lib.rs
+++ b/libs/apexutil/src/lib.rs
@@ -145,8 +145,9 @@
use super::*;
#[test]
- fn test_open_apex() {
+ fn apex_verification_returns_valid_result() {
let res = verify("tests/data/test.apex").unwrap();
+ // The expected hex is generated when we ran the method the first time.
assert_eq!(
to_hex_string(&res.root_digest),
"fe11ab17da0a3a738b54bdc3a13f6139cbdf91ec32f001f8d4bbbf8938e04e39"
@@ -154,7 +155,7 @@
}
#[test]
- fn test_payload_vbmeta_image_hash() {
+ fn payload_vbmeta_has_valid_image_hash() {
let result = get_payload_vbmeta_image_hash("tests/data/test.apex").unwrap();
assert_eq!(
to_hex_string(&result),
diff --git a/libs/apkverify/src/lib.rs b/libs/apkverify/src/lib.rs
index 92de9b0..1e0bd77 100644
--- a/libs/apkverify/src/lib.rs
+++ b/libs/apkverify/src/lib.rs
@@ -22,7 +22,9 @@
#[allow(dead_code)]
pub mod testing;
mod v3;
+mod v4;
mod ziputil;
pub use algorithms::SignatureAlgorithmID;
-pub use v3::{get_public_key_der, pick_v4_apk_digest, verify};
+pub use v3::{get_public_key_der, verify};
+pub use v4::get_apk_digest;
diff --git a/libs/apkverify/src/v3.rs b/libs/apkverify/src/v3.rs
index 5272834..fac0a7f 100644
--- a/libs/apkverify/src/v3.rs
+++ b/libs/apkverify/src/v3.rs
@@ -38,7 +38,7 @@
type Signers = LengthPrefixed<Vec<LengthPrefixed<Signer>>>;
-struct Signer {
+pub(crate) struct Signer {
signed_data: LengthPrefixed<Bytes>, // not verified yet
min_sdk: u32,
max_sdk: u32,
@@ -105,15 +105,9 @@
Ok(signer.public_key.public_key_to_der()?.into_boxed_slice())
}
-/// Gets the v4 [apk_digest].
-///
-/// [apk_digest]: https://source.android.com/docs/security/apksigning/v4#apk-digest
-pub fn pick_v4_apk_digest<R: Read + Seek>(apk: R) -> Result<(SignatureAlgorithmID, Box<[u8]>)> {
- let (signer, _) = extract_signer_and_apk_sections(apk)?;
- signer.pick_v4_apk_digest()
-}
-
-fn extract_signer_and_apk_sections<R: Read + Seek>(apk: R) -> Result<(Signer, ApkSections<R>)> {
+pub(crate) fn extract_signer_and_apk_sections<R: Read + Seek>(
+ apk: R,
+) -> Result<(Signer, ApkSections<R>)> {
let mut sections = ApkSections::new(apk)?;
let mut block = sections.find_signature(APK_SIGNATURE_SCHEME_V3_BLOCK_ID).context(
"Fallback to v2 when v3 block not found is not yet implemented. See b/197052981.",
@@ -144,7 +138,7 @@
.context("No supported signatures found")?)
}
- fn pick_v4_apk_digest(&self) -> Result<(SignatureAlgorithmID, Box<[u8]>)> {
+ pub(crate) fn pick_v4_apk_digest(&self) -> Result<(SignatureAlgorithmID, Box<[u8]>)> {
let strongest_algorithm_id = self
.strongest_signature()?
.signature_algorithm_id
diff --git a/libs/apkverify/src/v4.rs b/libs/apkverify/src/v4.rs
new file mode 100644
index 0000000..d0522a7
--- /dev/null
+++ b/libs/apkverify/src/v4.rs
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+//! API for APK Signature Scheme [v4].
+//!
+//! [v4]: https://source.android.com/security/apksigning/v4
+
+use anyhow::{ensure, Result};
+use std::io::{Read, Seek};
+
+use crate::algorithms::SignatureAlgorithmID;
+use crate::v3::extract_signer_and_apk_sections;
+
+/// Gets the v4 [apk_digest]. If `verify` is true, we verify that digest computed
+/// with the extracted algorithm is equal to the digest extracted directly from apk.
+/// Otherwise, the extracted digest will be returned directly.
+///
+/// [apk_digest]: https://source.android.com/docs/security/apksigning/v4#apk-digest
+pub fn get_apk_digest<R: Read + Seek>(
+ apk: R,
+ verify: bool,
+) -> Result<(SignatureAlgorithmID, Box<[u8]>)> {
+ let (signer, mut sections) = extract_signer_and_apk_sections(apk)?;
+ let (signature_algorithm_id, extracted_digest) = signer.pick_v4_apk_digest()?;
+ if verify {
+ let computed_digest = sections.compute_digest(signature_algorithm_id)?;
+ ensure!(
+ computed_digest == extracted_digest.as_ref(),
+ "Computed digest does not match the extracted digest."
+ );
+ }
+ Ok((signature_algorithm_id, extracted_digest))
+}
diff --git a/libs/apkverify/tests/apkverify_test.rs b/libs/apkverify/tests/apkverify_test.rs
index e17ba5c..047538c 100644
--- a/libs/apkverify/tests/apkverify_test.rs
+++ b/libs/apkverify/tests/apkverify_test.rs
@@ -15,7 +15,7 @@
*/
use apkverify::{
- get_public_key_der, pick_v4_apk_digest, testing::assert_contains, verify, SignatureAlgorithmID,
+ get_apk_digest, get_public_key_der, testing::assert_contains, verify, SignatureAlgorithmID,
};
use std::{fs, matches, path::Path};
@@ -221,16 +221,22 @@
/// Validates that the following apk_digest are equal:
/// * apk_digest directly extracted from apk without computation
+/// * computed apk_digest
/// * expected apk digest from the corresponding .apk_digest file
fn validate_apk_digest<P: AsRef<Path>>(apk_path: P, expected_algorithm_id: SignatureAlgorithmID) {
let apk = fs::File::open(&apk_path).expect("Unabled to open apk file");
- let (signature_algorithm_id, digest_from_apk) =
- pick_v4_apk_digest(apk).expect("Error when extracting apk digest.");
+ let (verified_algorithm_id, verified_digest) = get_apk_digest(&apk, /*verify=*/ true)
+ .expect("Error when extracting apk digest with verification.");
- assert_eq!(expected_algorithm_id, signature_algorithm_id);
+ assert_eq!(expected_algorithm_id, verified_algorithm_id);
let expected_digest_path = format!("{}.apk_digest", apk_path.as_ref().to_str().unwrap());
- assert_bytes_eq_to_data_in_file(&digest_from_apk, expected_digest_path);
+ assert_bytes_eq_to_data_in_file(&verified_digest, expected_digest_path);
+
+ let (unverified_algorithm_id, unverified_digest) = get_apk_digest(&apk, /*verify=*/ false)
+ .expect("Error when extracting apk digest without verification.");
+ assert_eq!(expected_algorithm_id, unverified_algorithm_id);
+ assert_eq!(verified_digest, unverified_digest);
}
fn assert_bytes_eq_to_data_in_file<P: AsRef<Path> + std::fmt::Display>(
diff --git a/libs/idsig/Android.bp b/libs/idsig/Android.bp
index 9f7d377..615d70e 100644
--- a/libs/idsig/Android.bp
+++ b/libs/idsig/Android.bp
@@ -31,5 +31,7 @@
compile_multilib: "first",
data: [
"testdata/input.*",
+ "testdata/*.apk",
+ "testdata/*.idsig",
],
}
diff --git a/libs/idsig/src/apksigv4.rs b/libs/idsig/src/apksigv4.rs
index 434a429..3f73df3 100644
--- a/libs/idsig/src/apksigv4.rs
+++ b/libs/idsig/src/apksigv4.rs
@@ -15,11 +15,13 @@
*/
use anyhow::{anyhow, bail, Context, Result};
-use apkverify::{pick_v4_apk_digest, SignatureAlgorithmID};
+use apkverify::{get_apk_digest, SignatureAlgorithmID};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::{FromPrimitive, ToPrimitive};
+use std::fs;
use std::io::{copy, Cursor, Read, Seek, SeekFrom, Write};
+use std::path::Path;
use crate::hashtree::*;
@@ -114,9 +116,17 @@
}
}
+impl V4Signature<fs::File> {
+ /// Creates a `V4Signature` struct from the given idsig path.
+ pub fn from_idsig_path<P: AsRef<Path>>(idsig_path: P) -> Result<Self> {
+ let idsig = fs::File::open(idsig_path).context("Cannot find idsig file")?;
+ Self::from_idsig(idsig)
+ }
+}
+
impl<R: Read + Seek> V4Signature<R> {
/// Consumes a stream for an idsig file into a `V4Signature` struct.
- pub fn from(mut r: R) -> Result<V4Signature<R>> {
+ pub fn from_idsig(mut r: R) -> Result<V4Signature<R>> {
Ok(V4Signature {
version: Version::from(r.read_u32::<LittleEndian>()?)?,
hashing_info: HashingInfo::from(&mut r)?,
@@ -158,7 +168,7 @@
ret.hashing_info.log2_blocksize = log2(block_size);
apk.seek(SeekFrom::Start(start))?;
- let (signature_algorithm_id, apk_digest) = pick_v4_apk_digest(apk)?;
+ let (signature_algorithm_id, apk_digest) = get_apk_digest(apk, /*verify=*/ false)?;
ret.signing_info.signature_algorithm_id = signature_algorithm_id;
ret.signing_info.apk_digest = apk_digest;
// TODO(jiyong): add a signature to the signing_info struct
@@ -293,14 +303,15 @@
use super::*;
use std::io::Cursor;
+ const TEST_APK_PATH: &str = "testdata/v4-digest-v3-Sha256withEC.apk";
+
fn hexstring_from(s: &[u8]) -> String {
s.iter().map(|byte| format!("{:02x}", byte)).reduce(|i, j| i + &j).unwrap_or_default()
}
#[test]
fn parse_idsig_file() {
- let idsig = Cursor::new(include_bytes!("../testdata/v4-digest-v3-Sha256withEC.apk.idsig"));
- let parsed = V4Signature::from(idsig).unwrap();
+ let parsed = V4Signature::from_idsig_path(format!("{}.idsig", TEST_APK_PATH)).unwrap();
assert_eq!(Version::V2, parsed.version);
@@ -334,13 +345,13 @@
/// the input file.
#[test]
fn parse_and_compose() {
- let input = Cursor::new(include_bytes!("../testdata/v4-digest-v3-Sha256withEC.apk.idsig"));
- let mut parsed = V4Signature::from(input.clone()).unwrap();
+ let idsig_path = format!("{}.idsig", TEST_APK_PATH);
+ let mut v4_signature = V4Signature::from_idsig_path(&idsig_path).unwrap();
let mut output = Cursor::new(Vec::new());
- parsed.write_into(&mut output).unwrap();
+ v4_signature.write_into(&mut output).unwrap();
- assert_eq!(input.get_ref().as_ref(), output.get_ref().as_slice());
+ assert_eq!(fs::read(&idsig_path).unwrap(), output.get_ref().as_slice());
}
/// Create V4Signature by hashing an APK. Merkle tree and the root hash should be the same
@@ -351,8 +362,7 @@
let mut created =
V4Signature::create(&mut input, 4096, &[], HashAlgorithm::SHA256).unwrap();
- let golden = Cursor::new(include_bytes!("../testdata/v4-digest-v3-Sha256withEC.apk.idsig"));
- let mut golden = V4Signature::from(golden).unwrap();
+ let mut golden = V4Signature::from_idsig_path(format!("{}.idsig", TEST_APK_PATH)).unwrap();
// Compare the root hash
assert_eq!(
diff --git a/microdroid/bootconfig.app_debuggable b/microdroid/bootconfig.app_debuggable
index 6e66371..529ed07 100644
--- a/microdroid/bootconfig.app_debuggable
+++ b/microdroid/bootconfig.app_debuggable
@@ -7,9 +7,8 @@
androidboot.microdroid.debuggable=0
# Console output is not redirect to the host-side.
-# TODO(b/219743539) This doesn't successfully disable the console
kernel.printk.devkmsg=off
-kernel.console=null
+kernel.console=ttynull
# ADB is supported but rooting is prohibited.
androidboot.adb.enabled=1
diff --git a/microdroid/bootconfig.normal b/microdroid/bootconfig.normal
index ec85f0d..5cc553c 100644
--- a/microdroid/bootconfig.normal
+++ b/microdroid/bootconfig.normal
@@ -5,10 +5,8 @@
androidboot.microdroid.debuggable=0
# Console output is not redirect to the host-side.
-# TODO(b/219743539) This doesn't successfully disable the console
kernel.printk.devkmsg=off
-# TODO(b/219743539) Setting this to null makes everything slow
-kernel.console=hvc0
+kernel.console=ttynull
# ADB is not enabled.
androidboot.adb.enabled=0
diff --git a/microdroid/init.rc b/microdroid/init.rc
index cd7332b..4e36726 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -149,7 +149,6 @@
restorecon /data/misc
mkdir /data/misc/authfs 0700 root root
- start authfs_service
on late-fs && property:ro.debuggable=1
# Ensure that tracefs has the correct permissions.
diff --git a/microdroid/payload/config/src/lib.rs b/microdroid/payload/config/src/lib.rs
index b82544f..54b745e 100644
--- a/microdroid/payload/config/src/lib.rs
+++ b/microdroid/payload/config/src/lib.rs
@@ -42,6 +42,11 @@
/// Whether to export the tomsbtones (VM crashes) out of VM to host
/// This does not have a default & the value is expected to be in json for deserialization
pub export_tombstones: bool,
+
+ /// Whether the authfs service should be started in the VM. This enables read or write of host
+ /// files with integrity checking, but not confidentiality.
+ #[serde(default)]
+ pub enable_authfs: bool,
}
/// OS config
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index e3ad495..c50bcbe 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -323,6 +323,11 @@
let config = load_config(Path::new(&metadata.payload_config_path))?;
+ let task = config
+ .task
+ .as_ref()
+ .ok_or_else(|| MicrodroidError::InvalidConfig("No task in VM config".to_string()))?;
+
if config.extra_apks.len() != verified_data.extra_apks_data.len() {
return Err(anyhow!(
"config expects {} extra apks, but found only {}",
@@ -338,18 +343,23 @@
// Start tombstone_transmit if enabled
if config.export_tombstones {
- system_properties::write("ctl.start", "tombstone_transmit")
- .context("Failed to start tombstone_transmit")?;
+ control_service("start", "tombstone_transmit")?;
} else {
- system_properties::write("ctl.stop", "tombstoned").context("Failed to stop tombstoned")?;
+ control_service("stop", "tombstoned")?;
}
- ensure!(
- config.task.is_some(),
- MicrodroidError::InvalidConfig("No task in VM config".to_string())
- );
+ // Start authfs if enabled
+ if config.enable_authfs {
+ control_service("start", "authfs_service")?;
+ }
+
system_properties::write("dev.bootcomplete", "1").context("set dev.bootcomplete")?;
- exec_task(&config.task.unwrap(), service)
+ exec_task(task, service)
+}
+
+fn control_service(action: &str, service: &str) -> Result<()> {
+ system_properties::write(&format!("ctl.{}", action), service)
+ .with_context(|| format!("Failed to {} {}", action, service))
}
struct ApkDmverityArgument<'a> {
@@ -471,7 +481,7 @@
.map(|(i, extra_idsig)| {
(
format!("extra-apk-{}", i),
- get_apk_root_hash_from_idsig(extra_idsig.to_str().unwrap())
+ get_apk_root_hash_from_idsig(extra_idsig)
.expect("Can't find root hash from extra idsig"),
)
})
@@ -591,10 +601,8 @@
Ok(())
}
-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_apk_root_hash_from_idsig<P: AsRef<Path>>(idsig_path: P) -> Result<Box<RootHash>> {
+ Ok(V4Signature::from_idsig_path(idsig_path)?.hashing_info.raw_root_hash)
}
fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
diff --git a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
index 7bf3c4e..d73eb9c 100644
--- a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
+++ b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
@@ -53,7 +53,7 @@
@RunWith(Parameterized.class)
public class MicrodroidBenchmarks extends MicrodroidDeviceTestBase {
private static final String TAG = "MicrodroidBenchmarks";
- private static final String METRIC_NAME_PREFIX = "avf_perf/microdroid/";
+ private static final String METRIC_NAME_PREFIX = getMetricPrefix() + "microdroid/";
private static final int IO_TEST_TRIAL_COUNT = 5;
@Rule public Timeout globalTimeout = Timeout.seconds(300);
diff --git a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
index e5eee27..efba60b 100644
--- a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
+++ b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
@@ -70,14 +70,14 @@
private static final int BOOT_COMPLETE_TIMEOUT_MS = 10 * 60 * 1000;
private static final double NANOS_IN_SEC = 1_000_000_000.0;
private static final int ROUND_COUNT = 5;
- private static final String METRIC_PREFIX = "avf_perf/hostside/";
- private final MetricsProcessor mMetricsProcessor = new MetricsProcessor(METRIC_PREFIX);
+ private MetricsProcessor mMetricsProcessor;
@Rule public TestMetrics mMetrics = new TestMetrics();
@Before
public void setUp() throws Exception {
testIfDeviceIsCapable(getDevice());
+ mMetricsProcessor = new MetricsProcessor(getMetricPrefix() + "hostside/");
}
@After
diff --git a/tests/helper/Android.bp b/tests/helper/Android.bp
index f77dae5..60d4be1 100644
--- a/tests/helper/Android.bp
+++ b/tests/helper/Android.bp
@@ -20,6 +20,7 @@
static_libs: [
"androidx.test.runner",
"androidx.test.ext.junit",
+ "MicrodroidTestHelper",
"VirtualizationTestHelper",
"truth-prebuilt",
],
diff --git a/tests/helper/src/java/com/android/microdroid/test/common/MetricsProcessor.java b/tests/helper/src/java/com/android/microdroid/test/common/MetricsProcessor.java
index 41534f1..b6bc479 100644
--- a/tests/helper/src/java/com/android/microdroid/test/common/MetricsProcessor.java
+++ b/tests/helper/src/java/com/android/microdroid/test/common/MetricsProcessor.java
@@ -24,6 +24,12 @@
public final class MetricsProcessor {
private final String mPrefix;
+ public static String getMetricPrefix(String debugTag) {
+ return "avf_perf"
+ + ((debugTag != null && !debugTag.isEmpty()) ? "[" + debugTag + "]" : "")
+ + "/";
+ }
+
public MetricsProcessor(String prefix) {
mPrefix = prefix;
}
diff --git a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
index a07731e..5dcd77b 100644
--- a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
+++ b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
@@ -19,6 +19,7 @@
import static org.junit.Assume.assumeNoException;
+import android.app.UiAutomation;
import android.content.Context;
import android.os.ParcelFileDescriptor;
import android.os.SystemProperties;
@@ -32,9 +33,12 @@
import androidx.annotation.CallSuper;
import androidx.test.core.app.ApplicationProvider;
+import com.android.microdroid.test.common.MetricsProcessor;
import com.android.virt.VirtualizationTestHelper;
import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.OptionalLong;
@@ -48,6 +52,11 @@
return VirtualizationTestHelper.isCuttlefish(SystemProperties.get("ro.product.name"));
}
+ public static String getMetricPrefix() {
+ return MetricsProcessor.getMetricPrefix(
+ SystemProperties.get("debug.hypervisor.metrics_tag"));
+ }
+
// TODO(b/220920264): remove Inner class; this is a hack to hide virt APEX types
protected static class Inner {
private final boolean mProtectedVm;
@@ -334,4 +343,20 @@
listener.getInitStartedNanoTime(),
listener.getPayloadStartedNanoTime());
}
+
+ /** Execute a command. Returns stdout. */
+ protected String runInShell(String tag, UiAutomation uiAutomation, String command) {
+ try (InputStream is =
+ new ParcelFileDescriptor.AutoCloseInputStream(
+ uiAutomation.executeShellCommand(command));
+ ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ is.transferTo(out);
+ String stdout = out.toString("UTF-8");
+ Log.i(tag, "Got stdout : " + stdout);
+ return stdout;
+ } catch (IOException e) {
+ Log.e(tag, "Error executing: " + command, e);
+ throw new RuntimeException("Failed to run the command.");
+ }
+ }
}
diff --git a/tests/hostside/helper/Android.bp b/tests/hostside/helper/Android.bp
index af88bb6..b2333ab 100644
--- a/tests/hostside/helper/Android.bp
+++ b/tests/hostside/helper/Android.bp
@@ -11,6 +11,7 @@
"truth-prebuilt",
],
static_libs: [
+ "MicrodroidTestHelper",
"VirtualizationTestHelper",
],
}
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
index 875d89f..0417123 100644
--- a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
+++ b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
@@ -27,6 +27,7 @@
import static org.junit.Assume.assumeTrue;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.microdroid.test.common.MetricsProcessor;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
@@ -97,6 +98,11 @@
return VirtualizationTestHelper.isCuttlefish(getDevice().getProperty("ro.product.name"));
}
+ protected String getMetricPrefix() throws Exception {
+ return MetricsProcessor.getMetricPrefix(
+ getDevice().getProperty("debug.hypervisor.metrics_tag"));
+ }
+
public static void testIfDeviceIsCapable(ITestDevice androidDevice) throws Exception {
assumeTrue("Need an actual TestDevice", androidDevice instanceof TestDevice);
TestDevice testDevice = (TestDevice) androidDevice;
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
index 69218a8..48dd1e0 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
@@ -93,6 +93,8 @@
@Rule public TestName mTestName = new TestName();
@Rule public TestMetrics mMetrics = new TestMetrics();
+ private String mMetricPrefix;
+
private int minMemorySize() throws DeviceNotAvailableException {
CommandRunner android = new CommandRunner(getDevice());
String abi = android.run("getprop", "ro.product.cpu.abi");
@@ -792,7 +794,7 @@
for (Map.Entry<String, Long> stat : getProcMemInfo().entrySet()) {
mMetrics.addTestMetric(
- "avf_perf/microdroid/meminfo/" + stat.getKey().toLowerCase(),
+ mMetricPrefix + "meminfo/" + stat.getKey().toLowerCase(),
stat.getValue().toString());
}
@@ -800,7 +802,7 @@
for (Map.Entry<String, Long> stat : getProcSmapsRollup(proc.mPid).entrySet()) {
String name = stat.getKey().toLowerCase();
mMetrics.addTestMetric(
- "avf_perf/microdroid/smaps/" + name + "/" + proc.mName,
+ mMetricPrefix + "smaps/" + name + "/" + proc.mName,
stat.getValue().toString());
}
}
@@ -845,6 +847,7 @@
@Before
public void setUp() throws Exception {
testIfDeviceIsCapable(getDevice());
+ mMetricPrefix = getMetricPrefix() + "microdroid/";
prepareVirtualizationTestSetup(getDevice());