compsvc: run odrefresh in the VM
On request, compsvc is responsible to prepare an authfs mountpoint and
specify expected environment variables and command line flags to
odrefresh.
Bug: 205750213
Test: atest ComposHostTestCases # no regression
Change-Id: Ib3c5e5f85e7c76616f1a0937bd43466078598701
diff --git a/authfs/aidl/com/android/virt/fs/IAuthFs.aidl b/authfs/aidl/com/android/virt/fs/IAuthFs.aidl
index 064b6f3..f7b2c8d 100644
--- a/authfs/aidl/com/android/virt/fs/IAuthFs.aidl
+++ b/authfs/aidl/com/android/virt/fs/IAuthFs.aidl
@@ -22,4 +22,7 @@
interface IAuthFs {
/** Returns a file descriptor given the name of a remote file descriptor. */
ParcelFileDescriptor openFile(int remoteFdName, boolean writable);
+
+ /** Returns the mount path of the current IAuthFs instance. */
+ String getMountPoint();
}
diff --git a/authfs/service/src/authfs.rs b/authfs/service/src/authfs.rs
index 2d4f707..e1d820a 100644
--- a/authfs/service/src/authfs.rs
+++ b/authfs/service/src/authfs.rs
@@ -67,6 +67,14 @@
})?;
Ok(ParcelFileDescriptor::new(file))
}
+
+ fn getMountPoint(&self) -> binder::Result<String> {
+ if let Some(s) = self.mountpoint.to_str() {
+ Ok(s.to_string())
+ } else {
+ Err(new_binder_exception(ExceptionCode::SERVICE_SPECIFIC, "Bad string encoding"))
+ }
+ }
}
impl AuthFs {
diff --git a/compos/aidl/com/android/compos/ICompOsService.aidl b/compos/aidl/com/android/compos/ICompOsService.aidl
index db746fd..7af2ada 100644
--- a/compos/aidl/com/android/compos/ICompOsService.aidl
+++ b/compos/aidl/com/android/compos/ICompOsService.aidl
@@ -42,6 +42,19 @@
String bootClasspath, String dex2oatBootClasspath, String systemServerClassPath);
/**
+ * Run odrefresh in the VM context.
+ *
+ * The execution is based on the VM's APEX mounts, files on Android's /system (by accessing
+ * through systemDirFd over AuthFS), and *CLASSPATH derived in the VM, to generate the same
+ * odrefresh output aritfacts to the output directory (through outputDirFd).
+ *
+ * The caller/Android is allowed to specify the zygote arch (ro.zygote).
+ *
+ * @return a CompilationResult
+ */
+ CompilationResult odrefresh(int systemDirFd, int outputDirFd, String zygoteArch);
+
+ /**
* Run dex2oat command with provided args, in a context that may be specified in FdAnnotation,
* e.g. with file descriptors pre-opened. The service is responsible to decide what executables
* it may run.
diff --git a/compos/src/compilation.rs b/compos/src/compilation.rs
index 72dca14..b726a1e 100644
--- a/compos/src/compilation.rs
+++ b/compos/src/compilation.rs
@@ -17,14 +17,17 @@
use anyhow::{anyhow, bail, Context, Result};
use log::error;
use minijail::{self, Minijail};
-use std::fs::File;
+use std::env;
+use std::fs::{create_dir, File};
use std::os::unix::io::{AsRawFd, RawFd};
-use std::path::Path;
+use std::path::{Path, PathBuf};
use crate::fsverity;
use authfs_aidl_interface::aidl::com::android::virt::fs::{
AuthFsConfig::{
- AuthFsConfig, InputFdAnnotation::InputFdAnnotation, OutputFdAnnotation::OutputFdAnnotation,
+ AuthFsConfig, InputDirFdAnnotation::InputDirFdAnnotation,
+ InputFdAnnotation::InputFdAnnotation, OutputDirFdAnnotation::OutputDirFdAnnotation,
+ OutputFdAnnotation::OutputFdAnnotation,
},
IAuthFs::IAuthFs,
IAuthFsService::IAuthFsService,
@@ -32,6 +35,8 @@
use authfs_aidl_interface::binder::{ParcelFileDescriptor, Strong};
use compos_aidl_interface::aidl::com::android::compos::FdAnnotation::FdAnnotation;
+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;
@@ -53,6 +58,60 @@
image: ParcelFileDescriptor,
}
+pub fn odrefresh(
+ odrefresh_path: &Path,
+ system_dir_fd: i32,
+ output_dir_fd: i32,
+ zygote_arch: &str,
+ authfs_service: Strong<dyn IAuthFsService>,
+) -> Result<CompilerOutput> {
+ // Mount authfs (via authfs_service). The authfs instance unmounts once the `authfs` variable
+ // is out of scope.
+ let authfs_config = AuthFsConfig {
+ port: FD_SERVER_PORT,
+ inputDirFdAnnotations: vec![InputDirFdAnnotation {
+ fd: system_dir_fd,
+ // TODO(206869687): Replace /dev/null with the real path when possible.
+ manifestPath: "/dev/null".to_string(),
+ prefix: "/system".to_string(),
+ }],
+ outputDirFdAnnotations: vec![OutputDirFdAnnotation { fd: output_dir_fd }],
+ ..Default::default()
+ };
+ let authfs = authfs_service.mount(&authfs_config)?;
+ let mountpoint = PathBuf::from(authfs.getMountPoint()?);
+
+ let mut android_root = mountpoint.clone();
+ android_root.push(system_dir_fd.to_string());
+ android_root.push("system");
+ env::set_var("ANDROID_ROOT", &android_root);
+
+ let mut staging_dir = mountpoint;
+ staging_dir.push(output_dir_fd.to_string());
+ staging_dir.push("staging");
+ create_dir(&staging_dir).context("Create staging directory")?;
+
+ let args = vec![
+ "odrefresh".to_string(),
+ format!("--zygote-arch={}", zygote_arch),
+ format!("--staging-dir={}", staging_dir.display()),
+ "--force-compile".to_string(),
+ ];
+ let jail = spawn_jailed_task(odrefresh_path, &args, Vec::new() /* fd_mapping */)
+ .context("Spawn odrefresh")?;
+ match jail.wait() {
+ // TODO(161471326): On success, sign all files in the output directory.
+ Ok(()) => Ok(CompilerOutput::ExitCode(0)),
+ Err(minijail::Error::ReturnCode(exit_code)) => {
+ error!("dex2oat failed with exit code {}", exit_code);
+ Ok(CompilerOutput::ExitCode(exit_code as i8))
+ }
+ Err(e) => {
+ bail!("Unexpected minijail error: {}", e)
+ }
+ }
+}
+
/// Runs the compiler with given flags with file descriptors described in `fd_annotation` retrieved
/// via `authfs_service`. Returns exit code of the compiler process.
pub fn compile_cmd(
@@ -140,7 +199,7 @@
fn build_authfs_config(fd_annotation: &FdAnnotation) -> AuthFsConfig {
AuthFsConfig {
- port: 3264, // TODO: support dynamic port
+ port: FD_SERVER_PORT,
inputFdAnnotations: fd_annotation
.input_fds
.iter()
@@ -151,8 +210,7 @@
.iter()
.map(|fd| OutputFdAnnotation { fd: *fd })
.collect(),
- inputDirFdAnnotations: vec![],
- outputDirFdAnnotations: vec![],
+ ..Default::default()
}
}
diff --git a/compos/src/compsvc.rs b/compos/src/compsvc.rs
index 9d2f1dc..0a15876 100644
--- a/compos/src/compsvc.rs
+++ b/compos/src/compsvc.rs
@@ -26,7 +26,7 @@
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
-use crate::compilation::{compile_cmd, CompilerOutput};
+use crate::compilation::{compile_cmd, odrefresh, CompilerOutput};
use crate::compos_key_service::CompOsKeyService;
use crate::fsverity;
use authfs_aidl_interface::aidl::com::android::virt::fs::IAuthFsService::IAuthFsService;
@@ -42,11 +42,13 @@
const AUTHFS_SERVICE_NAME: &str = "authfs_service";
const DEX2OAT_PATH: &str = "/apex/com.android.art/bin/dex2oat64";
+const ODREFRESH_PATH: &str = "/apex/com.android.art/bin/odrefresh";
/// Constructs a binder object that implements ICompOsService.
pub fn new_binder() -> Result<Strong<dyn ICompOsService>> {
let service = CompOsService {
dex2oat_path: PathBuf::from(DEX2OAT_PATH),
+ odrefresh_path: PathBuf::from(ODREFRESH_PATH),
key_service: CompOsKeyService::new()?,
key_blob: Arc::new(RwLock::new(Vec::new())),
};
@@ -55,6 +57,7 @@
struct CompOsService {
dex2oat_path: PathBuf,
+ odrefresh_path: PathBuf,
key_service: CompOsKeyService,
key_blob: Arc<RwLock<Vec<u8>>>,
}
@@ -99,6 +102,48 @@
Ok(())
}
+ fn odrefresh(
+ &self,
+ system_dir_fd: i32,
+ output_dir_fd: i32,
+ zygote_arch: &str,
+ ) -> BinderResult<CompilationResult> {
+ if system_dir_fd < 0 || output_dir_fd < 0 {
+ return Err(new_binder_exception(
+ ExceptionCode::ILLEGAL_ARGUMENT,
+ "The remote FDs are expected to be non-negative",
+ ));
+ }
+ if zygote_arch != "zygote64" && zygote_arch != "zygote64_32" {
+ return Err(new_binder_exception(
+ ExceptionCode::ILLEGAL_ARGUMENT,
+ "Invalid zygote arch",
+ ));
+ }
+
+ let authfs_service = get_authfs_service()?;
+ let output = odrefresh(
+ &self.odrefresh_path,
+ system_dir_fd,
+ output_dir_fd,
+ zygote_arch,
+ authfs_service,
+ )
+ .map_err(|e| {
+ warn!("odrefresh failed: {}", e);
+ new_binder_exception(
+ ExceptionCode::SERVICE_SPECIFIC,
+ format!("odrefresh failed: {}", e),
+ )
+ })?;
+ match output {
+ CompilerOutput::ExitCode(exit_code) => {
+ Ok(CompilationResult { exitCode: exit_code, ..Default::default() })
+ }
+ _ => Err(new_binder_exception(ExceptionCode::SERVICE_SPECIFIC, "odrefresh failed")),
+ }
+ }
+
fn compile_cmd(
&self,
args: &[String],