Merge changes from topic "build_libforwarder" into main
* changes:
Make libforwarder buildable
Introduce libforwarder
diff --git a/OWNERS b/OWNERS
index 40c709f..717a4db 100644
--- a/OWNERS
+++ b/OWNERS
@@ -28,3 +28,10 @@
tabba@google.com
vdonnefort@google.com
victorhsieh@google.com
+
+# Ferrochrome
+per-file android/FerrochromeApp/**=jiyong@google.com,jeongik@google.com
+per-file android/LinuxInstaller/**=jiyong@google.com,jeongik@google.com
+per-file android/TerminalApp/**=jiyong@google.com,jeongik@google.com
+per-file android/VmLauncherApp/**=jiyong@google.com,jeongik@google.com
+per-file libs/vm_launcher_lib/**=jiyong@google.com,jeongik@google.com
diff --git a/android/LinuxInstaller/linux_image_builder/ttyd.service b/android/LinuxInstaller/linux_image_builder/ttyd.service
index 3a8f181..f71557d 100644
--- a/android/LinuxInstaller/linux_image_builder/ttyd.service
+++ b/android/LinuxInstaller/linux_image_builder/ttyd.service
@@ -3,7 +3,7 @@
After=syslog.target
After=network.target
[Service]
-ExecStart=/usr/local/bin/ttyd -W login
+ExecStart=/usr/local/bin/ttyd -W screen -aAxR -S main login
Type=simple
Restart=always
User=root
diff --git a/android/TerminalApp/Android.bp b/android/TerminalApp/Android.bp
index 3ae014e..1a7c581 100644
--- a/android/TerminalApp/Android.bp
+++ b/android/TerminalApp/Android.bp
@@ -9,8 +9,10 @@
static_libs: [
"vm_launcher_lib",
],
- sdk_version: "system_current",
+ platform_apis: true,
+ privileged: true,
optimize: {
+ proguard_flags_files: ["proguard.flags"],
shrink_resources: true,
},
apex_available: [
diff --git a/android/TerminalApp/AndroidManifest.xml b/android/TerminalApp/AndroidManifest.xml
index c92da67..e338c49 100644
--- a/android/TerminalApp/AndroidManifest.xml
+++ b/android/TerminalApp/AndroidManifest.xml
@@ -2,9 +2,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.virtualization.terminal" >
+ <uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
+ <uses-permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE" />
<uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="com.android.virtualization.vmlauncher.permission.USE_VM_LAUNCHER"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
+ <uses-feature android:name="android.software.virtualization_framework" android:required="true" />
<application
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
@@ -27,6 +31,20 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
+
+ <service
+ android:name="com.android.virtualization.vmlauncher.VmLauncherService"
+ android:enabled="true"
+ android:exported="false"
+ android:foregroundServiceType="specialUse">
+ <property
+ android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
+ android:value="Run VM instances" />
+ <intent-filter>
+ <action android:name="android.virtualization.START_VM_LAUNCHER_SERVICE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </service>
</application>
</manifest>
diff --git a/android/TerminalApp/proguard.flags b/android/TerminalApp/proguard.flags
new file mode 100644
index 0000000..13ec24e
--- /dev/null
+++ b/android/TerminalApp/proguard.flags
@@ -0,0 +1,7 @@
+# Keep the no-args constructor of the deserialized class
+-keepclassmembers class com.android.virtualization.vmlauncher.ConfigJson {
+ <init>();
+}
+-keepclassmembers class com.android.virtualization.vmlauncher.ConfigJson$* {
+ <init>();
+}
diff --git a/android/VmLauncherApp/Android.bp b/android/VmLauncherApp/Android.bp
index 7dd2473..2e8cc93 100644
--- a/android/VmLauncherApp/Android.bp
+++ b/android/VmLauncherApp/Android.bp
@@ -11,7 +11,7 @@
"android.system.virtualizationservice_internal-java",
// TODO(b/331708504): will be removed when AVF framework handles surface
"libcrosvm_android_display_service-java",
- "gson",
+ "vm_launcher_lib",
],
libs: [
"framework-virtualization.impl",
diff --git a/android/VmLauncherApp/AndroidManifest.xml b/android/VmLauncherApp/AndroidManifest.xml
index 583fce7..4fb4b5c 100644
--- a/android/VmLauncherApp/AndroidManifest.xml
+++ b/android/VmLauncherApp/AndroidManifest.xml
@@ -6,8 +6,6 @@
<uses-permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
<uses-feature android:name="android.software.virtualization_framework" android:required="true" />
<permission android:name="com.android.virtualization.vmlauncher.permission.USE_VM_LAUNCHER"
@@ -28,20 +26,6 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
- <service
- android:name=".VmLauncherService"
- android:enabled="true"
- android:exported="true"
- android:permission="com.android.virtualization.vmlauncher.permission.USE_VM_LAUNCHER"
- android:foregroundServiceType="specialUse">
- <property
- android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
- android:value="Run VM instances" />
- <intent-filter>
- <action android:name="android.virtualization.START_VM_LAUNCHER_SERVICE" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </service>
</application>
diff --git a/android/fd_server/Android.bp b/android/fd_server/Android.bp
index b02c104..748c660 100644
--- a/android/fd_server/Android.bp
+++ b/android/fd_server/Android.bp
@@ -18,6 +18,7 @@
"liblog_rust",
"libnix",
"librpcbinder_rs",
+ "librustutils",
],
prefer_rlib: true,
apex_available: ["com.android.virt"],
@@ -39,6 +40,7 @@
"liblog_rust",
"libnix",
"librpcbinder_rs",
+ "librustutils",
],
prefer_rlib: true,
test_suites: ["general-tests"],
diff --git a/android/fd_server/src/main.rs b/android/fd_server/src/main.rs
index 26315bf..07f0896 100644
--- a/android/fd_server/src/main.rs
+++ b/android/fd_server/src/main.rs
@@ -29,9 +29,10 @@
use log::debug;
use nix::sys::stat::{umask, Mode};
use rpcbinder::RpcServer;
+use rustutils::inherited_fd::take_fd_ownership;
use std::collections::BTreeMap;
use std::fs::File;
-use std::os::unix::io::{FromRawFd, OwnedFd};
+use std::os::unix::io::OwnedFd;
use aidl::{FdConfig, FdService};
use authfs_fsverity_metadata::parse_fsverity_metadata;
@@ -39,20 +40,6 @@
// TODO(b/259920193): support dynamic port for multiple fd_server instances
const RPC_SERVICE_PORT: u32 = 3264;
-fn is_fd_valid(fd: i32) -> bool {
- // SAFETY: a query-only syscall
- let retval = unsafe { libc::fcntl(fd, libc::F_GETFD) };
- retval >= 0
-}
-
-fn fd_to_owned<T: FromRawFd>(fd: i32) -> Result<T> {
- if !is_fd_valid(fd) {
- bail!("Bad FD: {}", fd);
- }
- // SAFETY: The caller is supposed to provide valid FDs to this process.
- Ok(unsafe { T::from_raw_fd(fd) })
-}
-
fn parse_arg_ro_fds(arg: &str) -> Result<(i32, FdConfig)> {
let result: Result<Vec<i32>, _> = arg.split(':').map(|x| x.parse::<i32>()).collect();
let fds = result?;
@@ -62,13 +49,13 @@
Ok((
fds[0],
FdConfig::Readonly {
- file: fd_to_owned(fds[0])?,
+ file: take_fd_ownership(fds[0])?.into(),
// Alternative metadata source, if provided
alt_metadata: fds
.get(1)
- .map(|fd| fd_to_owned(*fd))
+ .map(|fd| take_fd_ownership(*fd))
.transpose()?
- .and_then(|f| parse_fsverity_metadata(f).ok()),
+ .and_then(|f| parse_fsverity_metadata(f.into()).ok()),
},
))
}
@@ -105,23 +92,26 @@
fd_pool.insert(fd, config);
}
for fd in args.rw_fds {
- let file = fd_to_owned::<File>(fd)?;
+ let file: File = take_fd_ownership(fd)?.into();
if file.metadata()?.len() > 0 {
bail!("File is expected to be empty");
}
fd_pool.insert(fd, FdConfig::ReadWrite(file));
}
for fd in args.ro_dirs {
- fd_pool.insert(fd, FdConfig::InputDir(fd_to_owned(fd)?));
+ fd_pool.insert(fd, FdConfig::InputDir(take_fd_ownership(fd)?));
}
for fd in args.rw_dirs {
- fd_pool.insert(fd, FdConfig::OutputDir(fd_to_owned(fd)?));
+ fd_pool.insert(fd, FdConfig::OutputDir(take_fd_ownership(fd)?));
}
- let ready_fd = args.ready_fd.map(fd_to_owned).transpose()?;
+ let ready_fd = args.ready_fd.map(take_fd_ownership).transpose()?;
Ok((fd_pool, ready_fd))
}
fn main() -> Result<()> {
+ // SAFETY: nobody has taken ownership of the inherited FDs yet.
+ unsafe { rustutils::inherited_fd::init_once()? };
+
android_logger::init_once(
android_logger::Config::default()
.with_tag("fd_server")
diff --git a/android/virtmgr/Android.bp b/android/virtmgr/Android.bp
index 0148ff6..d0d7915 100644
--- a/android/virtmgr/Android.bp
+++ b/android/virtmgr/Android.bp
@@ -34,7 +34,6 @@
"libapkverify",
"libavf_features",
"libavflog",
- "libbase_rust",
"libbinder_rs",
"libcfg_if",
"libclap",
diff --git a/android/virtmgr/src/crosvm.rs b/android/virtmgr/src/crosvm.rs
index 0f41932..b2be736 100644
--- a/android/virtmgr/src/crosvm.rs
+++ b/android/virtmgr/src/crosvm.rs
@@ -57,7 +57,6 @@
use rpcbinder::RpcServer;
/// external/crosvm
-use base::UnixSeqpacketListener;
use vm_control::{BalloonControlCommand, VmRequest, VmResponse};
const CROSVM_PATH: &str = "/apex/com.android.virt/bin/crosvm";
@@ -1057,8 +1056,8 @@
command.arg(add_preserved_fd(&mut preserved_fds, kernel));
}
- let control_sock = UnixSeqpacketListener::bind(crosvm_control_socket_path)
- .context("failed to create control server")?;
+ let control_sock = create_crosvm_control_listener(crosvm_control_socket_path)
+ .context("failed to create control listener")?;
command.arg("--socket").arg(add_preserved_fd(&mut preserved_fds, control_sock));
if let Some(dt_overlay) = config.device_tree_overlay {
@@ -1272,3 +1271,22 @@
let (read_fd, write_fd) = pipe2(OFlag::O_CLOEXEC)?;
Ok((read_fd.into(), write_fd.into()))
}
+
+/// Creates and binds a unix seqpacket listening socket to be passed as crosvm's `--socket`
+/// argument. See `UnixSeqpacketListener::bind` in crosvm's code for reference.
+fn create_crosvm_control_listener(crosvm_control_socket_path: &Path) -> Result<OwnedFd> {
+ use nix::sys::socket;
+ let fd = socket::socket(
+ socket::AddressFamily::Unix,
+ socket::SockType::SeqPacket,
+ socket::SockFlag::empty(),
+ None,
+ )
+ .context("socket failed")?;
+ socket::bind(fd.as_raw_fd(), &socket::UnixAddr::new(crosvm_control_socket_path)?)
+ .context("bind failed")?;
+ // The exact backlog size isn't imporant. crosvm uses 128 internally. We use 127 here
+ // because of a `nix` bug.
+ socket::listen(&fd, socket::Backlog::new(127).unwrap()).context("listen failed")?;
+ Ok(fd)
+}
diff --git a/android/virtmgr/src/main.rs b/android/virtmgr/src/main.rs
index 67e7282..1625009 100644
--- a/android/virtmgr/src/main.rs
+++ b/android/virtmgr/src/main.rs
@@ -25,15 +25,15 @@
use crate::aidl::{GLOBAL_SERVICE, VirtualizationService};
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualizationService::BnVirtualizationService;
-use anyhow::{bail, Context, Result};
+use anyhow::{bail, Result};
use binder::{BinderFeatures, ProcessState};
use log::{info, LevelFilter};
use rpcbinder::{FileDescriptorTransportMode, RpcServer};
-use std::os::unix::io::{AsFd, FromRawFd, OwnedFd, RawFd};
+use std::os::unix::io::{AsFd, RawFd};
use std::sync::LazyLock;
use clap::Parser;
-use nix::fcntl::{fcntl, F_GETFD, F_SETFD, FdFlag};
use nix::unistd::{write, Pid, Uid};
+use rustutils::inherited_fd::take_fd_ownership;
use std::os::unix::raw::{pid_t, uid_t};
const LOG_TAG: &str = "virtmgr";
@@ -71,32 +71,6 @@
ready_fd: RawFd,
}
-fn take_fd_ownership(raw_fd: RawFd, owned_fds: &mut Vec<RawFd>) -> Result<OwnedFd, anyhow::Error> {
- // Basic check that the integer value does correspond to a file descriptor.
- fcntl(raw_fd, F_GETFD).with_context(|| format!("Invalid file descriptor {raw_fd}"))?;
-
- // The file descriptor had CLOEXEC disabled to be inherited from the parent.
- // Re-enable it to make sure it is not accidentally inherited further.
- fcntl(raw_fd, F_SETFD(FdFlag::FD_CLOEXEC))
- .with_context(|| format!("Could not set CLOEXEC on file descriptor {raw_fd}"))?;
-
- // Creating OwnedFd for stdio FDs is not safe.
- if [libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO].contains(&raw_fd) {
- bail!("File descriptor {raw_fd} is standard I/O descriptor");
- }
-
- // Reject RawFds that already have a corresponding OwnedFd.
- if owned_fds.contains(&raw_fd) {
- bail!("File descriptor {raw_fd} already owned");
- }
- owned_fds.push(raw_fd);
-
- // SAFETY: Initializing OwnedFd for a RawFd provided in cmdline arguments.
- // We checked that the integer value corresponds to a valid FD and that this
- // is the first argument to claim its ownership.
- Ok(unsafe { OwnedFd::from_raw_fd(raw_fd) })
-}
-
fn check_vm_support() -> Result<()> {
if hypervisor_props::is_any_vm_supported()? {
Ok(())
@@ -109,6 +83,10 @@
}
fn main() {
+ // SAFETY: nobody has taken ownership of the inherited FDs yet.
+ unsafe { rustutils::inherited_fd::init_once() }
+ .expect("Failed to take ownership of inherited FDs");
+
android_logger::init_once(
android_logger::Config::default()
.with_tag(LOG_TAG)
@@ -120,11 +98,9 @@
let args = Args::parse();
- let mut owned_fds = vec![];
- let rpc_server_fd = take_fd_ownership(args.rpc_server_fd, &mut owned_fds)
- .expect("Failed to take ownership of rpc_server_fd");
- let ready_fd = take_fd_ownership(args.ready_fd, &mut owned_fds)
- .expect("Failed to take ownership of ready_fd");
+ let rpc_server_fd =
+ take_fd_ownership(args.rpc_server_fd).expect("Failed to take ownership of rpc_server_fd");
+ let ready_fd = take_fd_ownership(args.ready_fd).expect("Failed to take ownership of ready_fd");
// Start thread pool for kernel Binder connection to VirtualizationServiceInternal.
ProcessState::start_thread_pool();
diff --git a/android/virtualizationservice/aidl/Android.bp b/android/virtualizationservice/aidl/Android.bp
index c1bff5e..79a9d40 100644
--- a/android/virtualizationservice/aidl/Android.bp
+++ b/android/virtualizationservice/aidl/Android.bp
@@ -29,6 +29,7 @@
rust: {
enabled: true,
apex_available: [
+ "//apex_available:platform",
"com.android.virt",
"com.android.compos",
"com.android.microfuchsia",
@@ -149,6 +150,7 @@
rust: {
enabled: true,
apex_available: [
+ "//apex_available:platform",
"com.android.virt",
"com.android.compos",
"com.android.microfuchsia",
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index f493202..4916df7 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -107,6 +107,7 @@
filesystems: microdroid_filesystem_images,
prebuilts: [
"rialto_bin",
+ "android_bootloader_crosvm_aarch64",
],
},
x86_64: {
@@ -125,6 +126,9 @@
default: [],
}),
filesystems: microdroid_filesystem_images,
+ prebuilts: [
+ "android_bootloader_crosvm_x86_64",
+ ],
},
},
binaries: [
@@ -132,13 +136,11 @@
"vm",
],
prebuilts: [
- "features_com.android.virt.xml",
"microdroid_initrd_debuggable",
"microdroid_initrd_normal",
"microdroid.json",
"microdroid_kernel",
"com.android.virt.init.rc",
- "android_bootloader_crosvm_aarch64",
] + select(soong_config_variable("ANDROID", "avf_microdroid_guest_gki_version"), {
"android15_66": [
"microdroid_gki-android15-6.6_initrd_debuggable",
diff --git a/build/apex/permissions/Android.bp b/build/apex/permissions/Android.bp
index 0c925ce..678a4f2 100644
--- a/build/apex/permissions/Android.bp
+++ b/build/apex/permissions/Android.bp
@@ -21,4 +21,5 @@
name: "features_com.android.virt.xml",
sub_dir: "permissions",
src: "features_com.android.virt.xml",
+ soc_specific: true,
}
diff --git a/build/apex/product_packages.mk b/build/apex/product_packages.mk
index a024192..b2a4ca2 100644
--- a/build/apex/product_packages.mk
+++ b/build/apex/product_packages.mk
@@ -24,6 +24,7 @@
PRODUCT_PACKAGES += \
com.android.compos \
+ features_com.android.virt.xml
# TODO(b/207336449): Figure out how to get these off /system
PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST := \
diff --git a/build/debian/build.sh b/build/debian/build.sh
new file mode 100755
index 0000000..3d3820a
--- /dev/null
+++ b/build/debian/build.sh
@@ -0,0 +1,116 @@
+#!/bin/bash
+
+# This is a script to build a Debian image that can run in a VM created via AVF.
+# TODOs:
+# - Support x86_64 architecture
+# - Add Android-specific packages via a new class
+# - Use a stable release from debian-cloud-images
+
+show_help() {
+ echo Usage: $0 [OPTION]... [FILE]
+ echo Builds a debian image and save it to FILE.
+ echo Options:
+ echo -h Pring usage and this help message and exit.
+}
+
+check_sudo() {
+ if [ "$EUID" -ne 0 ]; then
+ echo "Please run as root."
+ exit
+ fi
+}
+
+parse_options() {
+ while getopts ":h" option; do
+ case ${option} in
+ h)
+ show_help
+ exit;;
+ esac
+ done
+ if [ -n "$1" ]; then
+ built_image=$1
+ fi
+}
+
+install_prerequisites() {
+ apt update
+ DEBIAN_FRONTEND=noninteractive \
+ apt install --no-install-recommends --assume-yes \
+ ca-certificates \
+ debsums \
+ dosfstools \
+ fai-server \
+ fai-setup-storage \
+ fdisk \
+ make \
+ python3 \
+ python3-libcloud \
+ python3-marshmallow \
+ python3-pytest \
+ python3-yaml \
+ qemu-utils \
+ udev \
+ qemu-system-arm \
+ qemu-user-static
+
+ sed -i s/losetup\ -f/losetup\ -P\ -f/g /usr/sbin/fai-diskimage
+ sed -i 's/wget \$/wget -t 0 \$/g' /usr/share/debootstrap/functions
+
+ apt install --no-install-recommends --assume-yes curl
+ # just for testing
+ echo libseccomp: $(curl -is https://deb.debian.org/debian/pool/main/libs/libseccomp/libseccomp2_2.5.4-1+deb12u1_arm64.deb | head -n 1)
+ echo libsemanage-common: $(curl -is https://deb.debian.org/debian/pool/main/libs/libsemanage/libsemanage-common_3.4-1_all.deb | head -n 1)
+ echo libpcre2: $(curl -is https://deb.debian.org/debian/pool/main/p/pcre2/libpcre2-8-0_10.42-1_arm64.deb | head -n 1)
+}
+
+download_debian_cloud_image() {
+ local ver=master
+ local prj=debian-cloud-images
+ local url=https://salsa.debian.org/cloud-team/${prj}/-/archive/${ver}/${prj}-${ver}.tar.gz
+ local outdir=${debian_cloud_image}
+
+ mkdir -p ${outdir}
+ wget -O - ${url} | tar xz -C ${outdir} --strip-components=1
+}
+
+copy_android_config() {
+ local src=$(dirname $0)/fai_config
+ local dst=${config_space}
+
+ cp -R ${src}/* ${dst}
+ cp $(dirname $0)/image.yaml ${resources_dir}
+
+ local ttyd_version=1.7.7
+ local url=https://github.com/tsl0922/ttyd/releases/download/${ttyd_version}/ttyd.aarch64
+ mkdir -p ${dst}/files/usr/local/bin/ttyd
+ wget ${url} -O ${dst}/files/usr/local/bin/ttyd/AVF
+ chmod 777 ${dst}/files/usr/local/bin/ttyd/AVF
+}
+
+run_fai() {
+ local out=${built_image}
+ make -C ${debian_cloud_image} image_bookworm_nocloud_arm64
+ mv ${debian_cloud_image}/image_bookworm_nocloud_arm64.raw ${out}
+}
+
+clean_up() {
+ rm -rf ${workdir}
+}
+
+set -e
+trap clean_up EXIT
+
+built_image=image.raw
+workdir=$(mktemp -d)
+debian_cloud_image=${workdir}/debian_cloud_image
+debian_version=bookworm
+config_space=${debian_cloud_image}/config_space/${debian_version}
+resources_dir=${debian_cloud_image}/src/debian_cloud_images/resources
+check_sudo
+parse_options $@
+install_prerequisites
+download_debian_cloud_image
+copy_android_config
+run_fai
+fdisk -l image.raw
diff --git a/build/debian/fai_config/class/AVF.var b/build/debian/fai_config/class/AVF.var
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build/debian/fai_config/class/AVF.var
diff --git a/build/debian/fai_config/files/etc/systemd/system/ttyd.service/AVF b/build/debian/fai_config/files/etc/systemd/system/ttyd.service/AVF
new file mode 100644
index 0000000..f71557d
--- /dev/null
+++ b/build/debian/fai_config/files/etc/systemd/system/ttyd.service/AVF
@@ -0,0 +1,12 @@
+[Unit]
+Description=TTYD
+After=syslog.target
+After=network.target
+[Service]
+ExecStart=/usr/local/bin/ttyd -W screen -aAxR -S main login
+Type=simple
+Restart=always
+User=root
+Group=root
+[Install]
+WantedBy=multi-user.target
diff --git a/build/debian/fai_config/files/etc/systemd/system/vsockip.service/AVF b/build/debian/fai_config/files/etc/systemd/system/vsockip.service/AVF
new file mode 100644
index 0000000..a29020b
--- /dev/null
+++ b/build/debian/fai_config/files/etc/systemd/system/vsockip.service/AVF
@@ -0,0 +1,12 @@
+[Unit]
+Description=vsock ip service
+After=syslog.target
+After=network.target
+[Service]
+ExecStart=/usr/bin/python3 /usr/local/bin/vsock.py
+Type=simple
+Restart=always
+User=root
+Group=root
+[Install]
+WantedBy=multi-user.target
diff --git a/build/debian/fai_config/files/usr/local/bin/vsock.py/AVF b/build/debian/fai_config/files/usr/local/bin/vsock.py/AVF
new file mode 100755
index 0000000..292d953
--- /dev/null
+++ b/build/debian/fai_config/files/usr/local/bin/vsock.py/AVF
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+
+import socket
+
+# Constants for vsock (from linux/vm_sockets.h)
+AF_VSOCK = 40
+SOCK_STREAM = 1
+VMADDR_CID_ANY = -1
+
+def get_local_ip():
+ """Retrieves the first IPv4 address found on the system.
+
+ Returns:
+ str: The local IPv4 address, or '127.0.0.1' if no IPv4 address is found.
+ """
+
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ try:
+ s.connect(('8.8.8.8', 80))
+ ip = s.getsockname()[0]
+ except Exception:
+ ip = '127.0.0.1'
+ finally:
+ s.close()
+ return ip
+
+def main():
+ PORT = 1024
+
+ # Create a vsock socket
+ server_socket = socket.socket(AF_VSOCK, SOCK_STREAM)
+
+ # Bind the socket to the server address
+ server_address = (VMADDR_CID_ANY, PORT)
+ server_socket.bind(server_address)
+
+ # Listen for incoming connections
+ server_socket.listen(1)
+ print(f"VSOCK server listening on port {PORT}...")
+
+ while True:
+ # Accept a connection
+ connection, client_address = server_socket.accept()
+ print(f"Connection from: {client_address}")
+
+ try:
+ # Get the local IP address
+ local_ip = get_local_ip()
+
+ # Send the IP address to the client
+ connection.sendall(local_ip.encode())
+ finally:
+ # Close the connection
+ connection.close()
+
+if __name__ == "__main__":
+ main()
diff --git a/build/debian/fai_config/hooks/extrbase.BASE b/build/debian/fai_config/hooks/extrbase.BASE
new file mode 100755
index 0000000..05d1e96
--- /dev/null
+++ b/build/debian/fai_config/hooks/extrbase.BASE
@@ -0,0 +1,6 @@
+#!/bin/sh
+set -euE
+
+touch "${LOGDIR}/skip.extrbase"
+
+debootstrap --verbose --variant minbase --arch "$DEBOOTSTRAP_ARCH" "$SUITE" "$FAI_ROOT" "$DEBOOTSTRAP_MIRROR"
diff --git a/build/debian/fai_config/hooks/partition.ARM64 b/build/debian/fai_config/hooks/partition.ARM64
new file mode 100755
index 0000000..b3b603b
--- /dev/null
+++ b/build/debian/fai_config/hooks/partition.ARM64
@@ -0,0 +1,53 @@
+#!/bin/sh
+set -eu
+touch $LOGDIR/skip.partition
+
+set -- $disklist
+device=/dev/$1
+
+wait_for_device() {
+ for s in $(seq 10); do
+ if [ -e "$1" ]; then
+ break
+ fi
+ sleep 1
+ done
+}
+
+sfdisk "$device" << EOF
+label: gpt
+unit: sectors
+
+# EFI system
+p15 : start=2048, size=260096, type="EFI System", uuid=${PARTUUID_ESP}
+# Linux
+p1 : start=262144, type="Linux root (ARM-64)", uuid=${PARTUUID_ROOT}
+EOF
+
+file=$(losetup -O BACK-FILE ${device} | tail -1)
+
+root_offset=$(parted -m ${device} unit B print | awk -F '[B:]' '/1:/{ print $2 }')
+root_size=$( parted -m ${device} unit B print | awk -F '[B:]' '/1:/{ print $6 }')
+efi_offset=$( parted -m ${device} unit B print | awk -F '[B:]' '/15:/{ print $2 }')
+efi_size=$( parted -m ${device} unit B print | awk -F '[B:]' '/15:/{ print $6 }')
+device_root=$(losetup -o ${root_offset} --sizelimit ${root_size} --show -f ${file})
+device_efi=$(losetup -o ${efi_offset} --sizelimit ${efi_size} --show -f ${file})
+rm -f ${device}p1
+rm -f ${device}p15
+ln -sf ${device_root} ${device}p1
+ln -sf ${device_efi} ${device}p15
+
+ls -al /dev/loop*
+losetup -a -l
+parted ${device} unit B print
+
+partprobe "$device"
+
+wait_for_device "$device_root"
+mkfs.ext4 -U "$FSUUID_ROOT" "$device_root"
+tune2fs -c 0 -i 0 "$device_root"
+
+wait_for_device "$device_efi"
+mkfs.vfat "$device_efi"
+
+parted ${device} unit B print
diff --git a/build/debian/fai_config/package_config/AVF b/build/debian/fai_config/package_config/AVF
new file mode 100644
index 0000000..7d86d41
--- /dev/null
+++ b/build/debian/fai_config/package_config/AVF
@@ -0,0 +1,4 @@
+PACKAGES install
+
+# Just for testing
+tree
diff --git a/build/debian/fai_config/scripts/AVF/10-systemd b/build/debian/fai_config/scripts/AVF/10-systemd
new file mode 100755
index 0000000..e04a562
--- /dev/null
+++ b/build/debian/fai_config/scripts/AVF/10-systemd
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+chmod +x $target/usr/local/bin/ttyd
+chmod +x $target/usr/local/bin/vsock.py
+ln -s /etc/systemd/system/ttyd.service $target/etc/systemd/system/multi-user.target.wants/ttyd.service
+ln -s /etc/systemd/system/vsockip.service $target/etc/systemd/system/multi-user.target.wants/vsockip.service
\ No newline at end of file
diff --git a/build/debian/image.yaml b/build/debian/image.yaml
new file mode 100644
index 0000000..eb42a07
--- /dev/null
+++ b/build/debian/image.yaml
@@ -0,0 +1,60 @@
+# After modifications, please call:
+# "python3 -m debian_cloud_images.cli.generate_ci .gitlab/ci/generated.yml"
+---
+apiVersion: cloud.debian.org/v1alpha1
+kind: ImageConfig
+
+archs:
+- name: amd64
+ azureName: X64
+ ociArch: amd64
+ faiClasses: [AMD64]
+- name: arm64
+ azureName: Arm64
+ ociArch: arm64
+ faiClasses: [ARM64]
+- name: ppc64el
+ faiClasses: [PPC64EL]
+ ociArch: ppc64le
+- name: riscv64
+ faiClasses: [RISCV64]
+ ociArch: riscv64
+
+releases:
+- name: bookworm
+ basename: bookworm
+ id: '12'
+ baseid: '12'
+ faiClasses: [BOOKWORM, LINUX_VERSION_BASE, EXTRAS]
+ matches:
+ - matchArches: [amd64, arm64, ppc64el]
+- name: bookworm-backports
+ basename: bookworm-backports
+ id: 12-backports
+ baseid: '12'
+ faiClasses: [BOOKWORM, LINUX_VERSION_BACKPORTS, EXTRAS]
+ matches:
+ - matchArches: [amd64, arm64, ppc64el]
+- name: trixie
+ basename: trixie
+ id: '13'
+ baseid: '13'
+ faiClasses: [TRIXIE, LINUX_VERSION_BASE, EXTRAS]
+ matches:
+ - matchArches: [amd64, arm64, ppc64el]
+
+vendors:
+- name: nocloud
+ faiClasses: [SYSTEM_BOOT, NOCLOUD, LINUX_VARIANT_BASE, TIME_SYSTEMD, AVF]
+ size: 2
+
+types:
+- name: dev
+ faiClasses: [TYPE_DEV]
+ outputName: 'debian-{release}-{vendor}-{arch}-{build_type}-{build_id}-{version}'
+ outputVersion: '{version}'
+ outputVersionAzure: '0.0.{version!s}'
+- name: official
+ outputName: 'debian-{release}-{vendor}-{arch}-{build_type}-{version}'
+ outputVersion: '{date}-{version}'
+ outputVersionAzure: '0.{date!s}.{version!s}'
diff --git a/build/debian/kokoro/gcp_ubuntu_docker/build.sh b/build/debian/kokoro/gcp_ubuntu_docker/build.sh
new file mode 100644
index 0000000..fb2a1a3
--- /dev/null
+++ b/build/debian/kokoro/gcp_ubuntu_docker/build.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -e
+
+cd "${KOKORO_ARTIFACTS_DIR}/git/avf/build/debian/"
+sudo losetup -D
+sudo ./build.sh
diff --git a/build/debian/kokoro/gcp_ubuntu_docker/continuous.cfg b/build/debian/kokoro/gcp_ubuntu_docker/continuous.cfg
new file mode 100644
index 0000000..d92031e
--- /dev/null
+++ b/build/debian/kokoro/gcp_ubuntu_docker/continuous.cfg
@@ -0,0 +1,7 @@
+# -*- protobuffer -*-
+# proto-file: google3/devtools/kokoro/config/proto/build.proto
+# proto-message: BuildConfig
+
+# Location of the bash script. Should have value <git_on_borg_scm.name>/<path_from_repository_root>.
+# git_on_borg_scm.name is specified in the job configuration (next section).
+build_file: "avf/build/debian/kokoro/gcp_ubuntu_docker/build.sh"
diff --git a/build/debian/kokoro/gcp_ubuntu_docker/presubmit.cfg b/build/debian/kokoro/gcp_ubuntu_docker/presubmit.cfg
new file mode 100644
index 0000000..d92031e
--- /dev/null
+++ b/build/debian/kokoro/gcp_ubuntu_docker/presubmit.cfg
@@ -0,0 +1,7 @@
+# -*- protobuffer -*-
+# proto-file: google3/devtools/kokoro/config/proto/build.proto
+# proto-message: BuildConfig
+
+# Location of the bash script. Should have value <git_on_borg_scm.name>/<path_from_repository_root>.
+# git_on_borg_scm.name is specified in the job configuration (next section).
+build_file: "avf/build/debian/kokoro/gcp_ubuntu_docker/build.sh"
diff --git a/docs/device_assignment.md b/docs/device_assignment.md
index 4b2296c..6011d8f 100644
--- a/docs/device_assignment.md
+++ b/docs/device_assignment.md
@@ -205,6 +205,18 @@
* `<sysfs_path>`: Sysfs path of the device in host, used to bind to the VFIO
driver. Must be non-empty and unique in the XML.
+### List support assignable devices
+
+In order to query list of the devices that can be assigned to a pVM, run the
+following command:
+
+```bash
+adb shell /apex/com.android.virt/bin/vm info
+```
+
+All supported assignable devices will be located under the "Assignable devices:"
+section of the output.
+
## Boot with VM DTBO
Bootloader should provide VM DTBO to both Android and pvmfw.
diff --git a/docs/pvm_dice_chain.md b/docs/pvm_dice_chain.md
index 11cdb6f..67d1f28 100644
--- a/docs/pvm_dice_chain.md
+++ b/docs/pvm_dice_chain.md
@@ -6,11 +6,13 @@
![][pvm-dice-chain-img]
-The full RKP VM DICE chain, starting from `UDS_Pub` rooted in ROM, is
-sent to the RKP server during [pVM remote attestation][vm-attestation].
+The full [RKP VM DICE chain][rkpvm-dice-chain], starting from `UDS_Pub`
+rooted in ROM, is sent to the RKP server during
+[pVM remote attestation][vm-attestation].
[vm-attestation]: vm_remote_attestation.md
[pvm-dice-chain-img]: img/pvm-dice.png
+[rkpvm-dice-chain]: vm_remote_attestation.md#rkp-vm-marker
## Key derivation
diff --git a/guest/authfs_service/src/main.rs b/guest/authfs_service/src/main.rs
index 97e684d..be0f1b2 100644
--- a/guest/authfs_service/src/main.rs
+++ b/guest/authfs_service/src/main.rs
@@ -28,7 +28,6 @@
use rustutils::sockets::android_get_control_socket;
use std::ffi::OsString;
use std::fs::{create_dir, read_dir, remove_dir_all, remove_file};
-use std::os::unix::io::{FromRawFd, OwnedFd};
use std::sync::atomic::{AtomicUsize, Ordering};
use authfs_aidl_interface::aidl::com::android::virt::fs::AuthFsConfig::AuthFsConfig;
@@ -108,27 +107,11 @@
Ok(())
}
-/// Prepares a socket file descriptor for the authfs service.
-///
-/// # Safety requirement
-///
-/// The caller must ensure that this function is the only place that claims ownership
-/// of the file descriptor and it is called only once.
-unsafe fn prepare_authfs_service_socket() -> Result<OwnedFd> {
- let raw_fd = android_get_control_socket(AUTHFS_SERVICE_SOCKET_NAME)?;
-
- // Creating OwnedFd for stdio FDs is not safe.
- if [libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO].contains(&raw_fd) {
- bail!("File descriptor {raw_fd} is standard I/O descriptor");
- }
- // SAFETY: Initializing OwnedFd for a RawFd created by the init.
- // We checked that the integer value corresponds to a valid FD and that the caller
- // ensures that this is the only place to claim its ownership.
- Ok(unsafe { OwnedFd::from_raw_fd(raw_fd) })
-}
-
#[allow(clippy::eq_op)]
fn try_main() -> Result<()> {
+ // SAFETY: nobody has taken ownership of the inherited FDs yet.
+ unsafe { rustutils::inherited_fd::init_once()? };
+
let debuggable = env!("TARGET_BUILD_VARIANT") != "user";
let log_level = if debuggable { log::LevelFilter::Trace } else { log::LevelFilter::Info };
android_logger::init_once(
@@ -137,8 +120,7 @@
clean_up_working_directory()?;
- // SAFETY: This is the only place we take the ownership of the fd of the authfs service.
- let socket_fd = unsafe { prepare_authfs_service_socket()? };
+ let socket_fd = android_get_control_socket(AUTHFS_SERVICE_SOCKET_NAME)?;
let service = AuthFsService::new_binder(debuggable).as_binder();
debug!("{} is starting as a rpc service.", AUTHFS_SERVICE_SOCKET_NAME);
let server = RpcServer::new_bound_socket(service, socket_fd)?;
diff --git a/guest/microdroid_manager/src/main.rs b/guest/microdroid_manager/src/main.rs
index 8186e9d..fa089fa 100644
--- a/guest/microdroid_manager/src/main.rs
+++ b/guest/microdroid_manager/src/main.rs
@@ -56,7 +56,7 @@
use std::ffi::CString;
use std::fs::{self, create_dir, File, OpenOptions};
use std::io::{Read, Write};
-use std::os::unix::io::{FromRawFd, OwnedFd};
+use std::os::unix::io::OwnedFd;
use std::os::unix::process::CommandExt;
use std::os::unix::process::ExitStatusExt;
use std::path::Path;
@@ -170,6 +170,9 @@
}
fn main() -> Result<()> {
+ // SAFETY: nobody has taken ownership of the inherited FDs yet.
+ unsafe { rustutils::inherited_fd::init_once()? };
+
// If debuggable, print full backtrace to console log with stdio_to_kmsg
if is_debuggable()? {
env::set_var("RUST_BACKTRACE", "full");
@@ -199,13 +202,7 @@
);
info!("started.");
- // SAFETY: This is the only place we take the ownership of the fd of the vm payload service.
- //
- // To ensure that the CLOEXEC flag is set on the file descriptor as early as possible,
- // it is necessary to fetch the socket corresponding to vm_payload_service at the
- // very beginning, as android_get_control_socket() sets the CLOEXEC flag on the file
- // descriptor.
- let vm_payload_service_fd = unsafe { prepare_vm_payload_service_socket()? };
+ let vm_payload_service_fd = android_get_control_socket(VM_PAYLOAD_SERVICE_SOCKET_NAME)?;
load_crashkernel_if_supported().context("Failed to load crashkernel")?;
@@ -486,25 +483,6 @@
.context("Could not connect to IVirtualMachineService")
}
-/// Prepares a socket file descriptor for the vm payload service.
-///
-/// # Safety
-///
-/// The caller must ensure that this function is the only place that claims ownership
-/// of the file descriptor and it is called only once.
-unsafe fn prepare_vm_payload_service_socket() -> Result<OwnedFd> {
- let raw_fd = android_get_control_socket(VM_PAYLOAD_SERVICE_SOCKET_NAME)?;
-
- // Creating OwnedFd for stdio FDs is not safe.
- if [libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO].contains(&raw_fd) {
- bail!("File descriptor {raw_fd} is standard I/O descriptor");
- }
- // SAFETY: Initializing OwnedFd for a RawFd created by the init.
- // We checked that the integer value corresponds to a valid FD and that the caller
- // ensures that this is the only place to claim its ownership.
- Ok(unsafe { OwnedFd::from_raw_fd(raw_fd) })
-}
-
fn is_strict_boot() -> bool {
Path::new(AVF_STRICT_BOOT).exists()
}
diff --git a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
index cb21ccf..de1b081 100644
--- a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -78,7 +78,8 @@
private static final String TAG = "VirtualMachineConfig";
private static String[] EMPTY_STRING_ARRAY = {};
- private static final String U_BOOT_PREBUILT_PATH = "/apex/com.android.virt/etc/u-boot.bin";
+ private static final String U_BOOT_PREBUILT_PATH_ARM = "/apex/com.android.virt/etc/u-boot.bin";
+ private static final String U_BOOT_PREBUILT_PATH_X86 = "/apex/com.android.virt/etc/u-boot.rom";
// These define the schema of the config file persisted on disk.
// Please bump up the version number when adding a new key.
@@ -668,7 +669,11 @@
.orElse(null);
if (config.kernel == null && config.bootloader == null) {
- config.bootloader = openOrNull(new File(U_BOOT_PREBUILT_PATH), MODE_READ_ONLY);
+ if (Arrays.stream(Build.SUPPORTED_ABIS).anyMatch("x86_64"::equals)) {
+ config.bootloader = openOrNull(new File(U_BOOT_PREBUILT_PATH_X86), MODE_READ_ONLY);
+ } else {
+ config.bootloader = openOrNull(new File(U_BOOT_PREBUILT_PATH_ARM), MODE_READ_ONLY);
+ }
}
config.params =
diff --git a/libs/libvmclient/Android.bp b/libs/libvmclient/Android.bp
index 5bd59da..d318d0e 100644
--- a/libs/libvmclient/Android.bp
+++ b/libs/libvmclient/Android.bp
@@ -23,6 +23,7 @@
"com.android.compos",
"com.android.microfuchsia",
"com.android.virt",
+ "//apex_available:platform",
],
}
diff --git a/libs/libvmclient/src/lib.rs b/libs/libvmclient/src/lib.rs
index bc9d683..ce7d5a5 100644
--- a/libs/libvmclient/src/lib.rs
+++ b/libs/libvmclient/src/lib.rs
@@ -55,6 +55,7 @@
time::Duration,
};
+const EARLY_VIRTMGR_PATH: &str = "/apex/com.android.virt/bin/early_virtmgr";
const VIRTMGR_PATH: &str = "/apex/com.android.virt/bin/virtmgr";
const VIRTMGR_THREADS: usize = 2;
@@ -122,10 +123,20 @@
/// Spawns a new instance of virtmgr, a child process that will host
/// the VirtualizationService AIDL service.
pub fn new() -> Result<VirtualizationService, io::Error> {
+ Self::new_with_path(VIRTMGR_PATH)
+ }
+
+ /// Spawns a new instance of early_virtmgr, a child process that will host
+ /// the VirtualizationService AIDL service for early VMs.
+ pub fn new_early() -> Result<VirtualizationService, io::Error> {
+ Self::new_with_path(EARLY_VIRTMGR_PATH)
+ }
+
+ fn new_with_path(virtmgr_path: &str) -> Result<VirtualizationService, io::Error> {
let (wait_fd, ready_fd) = posix_pipe()?;
let (client_fd, server_fd) = posix_socketpair()?;
- let mut command = Command::new(VIRTMGR_PATH);
+ let mut command = Command::new(virtmgr_path);
// Can't use BorrowedFd as it doesn't implement Display
command.arg("--rpc-server-fd").arg(format!("{}", server_fd.as_raw_fd()));
command.arg("--ready-fd").arg(format!("{}", ready_fd.as_raw_fd()));
diff --git a/libs/vm_launcher_lib/Android.bp b/libs/vm_launcher_lib/Android.bp
index 8591c8d..cb6fc9e 100644
--- a/libs/vm_launcher_lib/Android.bp
+++ b/libs/vm_launcher_lib/Android.bp
@@ -9,5 +9,12 @@
"//apex_available:platform",
"com.android.virt",
],
- sdk_version: "system_current",
+ platform_apis: true,
+ static_libs: [
+ "gson",
+ ],
+ libs: [
+ "framework-virtualization.impl",
+ "framework-annotations-lib",
+ ],
}
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/ConfigJson.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java
similarity index 100%
rename from android/VmLauncherApp/java/com/android/virtualization/vmlauncher/ConfigJson.java
rename to libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/Logger.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/Logger.java
similarity index 100%
rename from android/VmLauncherApp/java/com/android/virtualization/vmlauncher/Logger.java
rename to libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/Logger.java
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/Runner.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/Runner.java
similarity index 98%
rename from android/VmLauncherApp/java/com/android/virtualization/vmlauncher/Runner.java
rename to libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/Runner.java
index a5f58fe..9b97fee 100644
--- a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/Runner.java
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/Runner.java
@@ -30,7 +30,7 @@
/** Utility class for creating a VM and waiting for it to finish. */
class Runner {
- private static final String TAG = MainActivity.TAG;
+ private static final String TAG = Runner.class.getSimpleName();
private final VirtualMachine mVirtualMachine;
private final Callback mCallback;
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/VmLauncherService.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java
similarity index 100%
rename from android/VmLauncherApp/java/com/android/virtualization/vmlauncher/VmLauncherService.java
rename to libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index 2eca2fa..d0838a6 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -16,7 +16,6 @@
static_libs: [
"MicrodroidHostTestHelper",
"compatibility-host-util",
- "cts-host-utils",
"cts-statsd-atom-host-test-utils",
"microdroid_payload_metadata",
],
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 0f7be20..2d55d66 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -32,12 +32,11 @@
import static java.util.stream.Collectors.toList;
-import android.cts.host.utils.DeviceJUnit4ClassRunnerWithParameters;
-import android.cts.host.utils.DeviceJUnit4Parameterized;
import android.cts.statsdatom.lib.ConfigUtils;
import android.cts.statsdatom.lib.ReportUtils;
import com.android.compatibility.common.util.CddTest;
+import com.android.compatibility.common.util.VsrTest;
import com.android.microdroid.test.common.ProcessUtil;
import com.android.microdroid.test.host.CommandRunner;
import com.android.microdroid.test.host.MicrodroidHostTestCaseBase;
@@ -48,6 +47,7 @@
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.device.TestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
+import com.android.tradefed.testtype.junit4.DeviceParameterizedRunner;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.CommandStatus;
import com.android.tradefed.util.FileUtil;
@@ -55,6 +55,9 @@
import com.android.tradefed.util.xml.AbstractXmlParser;
import com.android.virt.PayloadMetadata;
+import junitparams.Parameters;
+import junitparams.naming.TestCaseName;
+
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.After;
@@ -64,8 +67,6 @@
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
@@ -75,8 +76,8 @@
import java.io.PipedOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -87,8 +88,7 @@
import java.util.regex.Pattern;
import java.util.stream.Collectors;
-@RunWith(DeviceJUnit4Parameterized.class)
-@UseParametersRunnerFactory(DeviceJUnit4ClassRunnerWithParameters.RunnerFactory.class)
+@RunWith(DeviceParameterizedRunner.class)
public class MicrodroidHostTests extends MicrodroidHostTestCaseBase {
private static final String APK_NAME = "MicrodroidTestApp.apk";
private static final String APK_UPDATED_NAME = "MicrodroidTestAppUpdated.apk";
@@ -111,26 +111,38 @@
}
}
- @Parameterized.Parameters(name = "protectedVm={0},gki={1}")
- public static Collection<Object[]> params() {
- List<Object[]> ret = new ArrayList<>();
- ret.add(new Object[] {true /* protectedVm */, null /* use microdroid kernel */});
- ret.add(new Object[] {false /* protectedVm */, null /* use microdroid kernel */});
+ // This map is needed because the parameterizer `DeviceParameterizedRunner` doesn't support "-"
+ // in test names. The key is the test name, while the value is the actual kernel version.
+ private static HashMap<String, String> sGkiVersions = new HashMap<>();
+
+ private static void initGkiVersions() {
+ if (!sGkiVersions.isEmpty()) {
+ return;
+ }
+ sGkiVersions.put("null", null); /* use microdroid kernel */
// TODO(b/302465542): run only the latest GKI on presubmit to reduce running time
for (String gki : SUPPORTED_GKI_VERSIONS) {
- ret.add(new Object[] {true /* protectedVm */, gki});
- ret.add(new Object[] {false /* protectedVm */, gki});
+ String key = gki.split("-")[0];
+ assertThat(sGkiVersions.containsKey(key)).isFalse();
+ sGkiVersions.put(key, gki);
+ }
+ }
+
+ public static List<Object[]> params() {
+ List<Object[]> ret = new ArrayList<>();
+ for (Object[] gki : gkiVersions()) {
+ ret.add(new Object[] {true /* protectedVm */, gki[0]});
+ ret.add(new Object[] {false /* protectedVm */, gki[0]});
}
return ret;
}
- @Parameterized.Parameter(0)
- public boolean mProtectedVm;
-
- @Parameterized.Parameter(1)
- public String mGki;
-
- private String mOs;
+ public static List<Object[]> gkiVersions() {
+ initGkiVersions();
+ return sGkiVersions.keySet().stream()
+ .map(gki -> new Object[] {gki})
+ .collect(Collectors.toList());
+ }
@Rule public TestLogData mTestLogs = new TestLogData();
@Rule public TestName mTestName = new TestName();
@@ -290,9 +302,11 @@
File key,
Map<String, File> keyOverrides,
boolean isProtected,
- boolean updateBootconfigs)
+ boolean updateBootconfigs,
+ String gki)
throws Exception {
CommandRunner android = new CommandRunner(getDevice());
+ gki = sGkiVersions.get(gki);
File virtApexDir = FileUtil.createTempDir("virt_apex");
@@ -344,7 +358,8 @@
// - its idsig
// Load etc/microdroid.json
- File microdroidConfigFile = new File(virtApexEtcDir, mOs + ".json");
+ final String os = (gki == null) ? "microdroid" : "microdroid_gki-" + gki;
+ File microdroidConfigFile = new File(virtApexEtcDir, os + ".json");
JSONObject config = new JSONObject(FileUtil.readStringFromFile(microdroidConfigFile));
// Replace paths so that the config uses re-signed images from TEST_ROOT
@@ -360,7 +375,7 @@
}
// Add partitions to the second disk
- final String initrdPath = TEST_ROOT + "etc/" + mOs + "_initrd_debuggable.img";
+ final String initrdPath = TEST_ROOT + "etc/" + os + "_initrd_debuggable.img";
config.put("initrd", initrdPath);
// Add instance image as a partition in disks[1]
disks.put(
@@ -408,6 +423,9 @@
"--console " + CONSOLE_PATH,
"--log " + LOG_PATH,
configPath);
+ if (gki != null) {
+ args.add("--gki " + gki);
+ }
PipedInputStream pis = new PipedInputStream();
Process process = createRunUtil().runCmdInBackground(args, new PipedOutputStream(pis));
@@ -416,28 +434,34 @@
@Test
@CddTest
+ @VsrTest(requirements = {"VSR-7.1-001.008"})
public void UpgradedPackageIsAcceptedWithSecretkeeper() throws Exception {
+ // Preconditions
+ assumeVmTypeSupported(true);
assumeUpdatableVmSupported();
+
getDevice().uninstallPackage(PACKAGE_NAME);
getDevice().installPackage(findTestFile(APK_NAME), /* reinstall= */ true);
- ensureMicrodroidBootsSuccessfully(INSTANCE_ID_FILE, INSTANCE_IMG);
+ ensureProtectedMicrodroidBootsSuccessfully(INSTANCE_ID_FILE, INSTANCE_IMG);
getDevice().uninstallPackage(PACKAGE_NAME);
cleanUpVirtualizationTestSetup(getDevice());
// Install the updated version of app (versionCode 6)
getDevice().installPackage(findTestFile(APK_UPDATED_NAME), /* reinstall= */ true);
- ensureMicrodroidBootsSuccessfully(INSTANCE_ID_FILE, INSTANCE_IMG);
+ ensureProtectedMicrodroidBootsSuccessfully(INSTANCE_ID_FILE, INSTANCE_IMG);
}
@Test
@CddTest
+ @VsrTest(requirements = {"VSR-7.1-001.008"})
public void DowngradedPackageIsRejectedProtectedVm() throws Exception {
- assumeProtectedVm(); // Rollback protection is provided only for protected VM.
+ // Preconditions: Rollback protection is provided only for protected VM.
+ assumeVmTypeSupported(true);
// Install the upgraded version (v6)
getDevice().uninstallPackage(PACKAGE_NAME);
getDevice().installPackage(findTestFile(APK_UPDATED_NAME), /* reinstall= */ true);
- ensureMicrodroidBootsSuccessfully(INSTANCE_ID_FILE, INSTANCE_IMG);
+ ensureProtectedMicrodroidBootsSuccessfully(INSTANCE_ID_FILE, INSTANCE_IMG);
getDevice().uninstallPackage(PACKAGE_NAME);
cleanUpVirtualizationTestSetup(getDevice());
@@ -447,11 +471,11 @@
assertThrows(
"pVM must fail to boot with downgraded payload apk",
DeviceRuntimeException.class,
- () -> ensureMicrodroidBootsSuccessfully(INSTANCE_ID_FILE, INSTANCE_IMG));
+ () -> ensureProtectedMicrodroidBootsSuccessfully(INSTANCE_ID_FILE, INSTANCE_IMG));
}
- private void ensureMicrodroidBootsSuccessfully(String instanceIdPath, String instanceImgPath)
- throws DeviceNotAvailableException {
+ private void ensureProtectedMicrodroidBootsSuccessfully(
+ String instanceIdPath, String instanceImgPath) throws DeviceNotAvailableException {
final String configPath = "assets/vm_config.json";
ITestDevice microdroid = null;
int timeout = 30000; // 30 seconds
@@ -461,7 +485,7 @@
.debugLevel("full")
.memoryMib(minMemorySize())
.cpuTopology("match_host")
- .protectedVm(mProtectedVm)
+ .protectedVm(true)
.instanceIdFile(instanceIdPath)
.instanceImgFile(instanceImgPath)
.setAdbConnectTimeoutMs(timeout)
@@ -476,10 +500,13 @@
}
@Test
+ @Parameters(method = "gkiVersions")
+ @TestCaseName("{method}_gki_{0}")
@CddTest(requirements = {"9.17/C-2-1", "9.17/C-2-2", "9.17/C-2-6"})
- public void protectedVmRunsPvmfw() throws Exception {
+ public void protectedVmRunsPvmfw(String gki) throws Exception {
// Arrange
- assumeProtectedVm();
+ assumeKernelSupported(gki);
+ assumeVmTypeSupported(true);
final String configPath = "assets/vm_config_apex.json";
// Act
@@ -489,7 +516,7 @@
.memoryMib(minMemorySize())
.cpuTopology("match_host")
.protectedVm(true)
- .gki(mGki)
+ .gki(sGkiVersions.get(gki))
.name("protected_vm_runs_pvmfw")
.build(getAndroidDevice());
@@ -506,10 +533,13 @@
}
@Test
- @CddTest(requirements = {"9.17/C-2-1", "9.17/C-2-2", "9.17/C-2-6"})
- public void protectedVmWithImageSignedWithDifferentKeyFailsToVerifyPayload() throws Exception {
- // Arrange
- assumeProtectedVm();
+ @Parameters(method = "gkiVersions")
+ @TestCaseName("{method}_gki_{0}")
+ @CddTest(requirements = {"9.17/C-2-1", "9.17/C-2-2", "9.17/C-2-5", "9.17/C-2-6"})
+ public void protectedVmWithImageSignedWithDifferentKeyFailsToVerifyPayload(String gki)
+ throws Exception {
+ assumeKernelSupported(gki);
+ assumeVmTypeSupported(true);
File key = findTestFile("test.com.android.virt.pem");
// Act
@@ -518,7 +548,8 @@
key,
/* keyOverrides= */ Map.of(),
/* isProtected= */ true,
- /* updateBootconfigs= */ true);
+ /* updateBootconfigs= */ true,
+ gki);
// Assert
vmInfo.mProcess.waitFor(5L, TimeUnit.SECONDS);
@@ -531,15 +562,23 @@
}
@Test
+ @Parameters(method = "gkiVersions")
+ @TestCaseName("{method}_gki_{0}")
@CddTest(requirements = {"9.17/C-2-2", "9.17/C-2-6"})
- public void testBootSucceedsWhenNonProtectedVmStartsWithImagesSignedWithDifferentKey()
+ public void testBootSucceedsWhenNonProtectedVmStartsWithImagesSignedWithDifferentKey(String gki)
throws Exception {
- assumeNonProtectedVm();
+ // Preconditions
+ assumeKernelSupported(gki);
+
File key = findTestFile("test.com.android.virt.pem");
Map<String, File> keyOverrides = Map.of();
VmInfo vmInfo =
runMicrodroidWithResignedImages(
- key, keyOverrides, /* isProtected= */ false, /* updateBootconfigs= */ true);
+ key,
+ keyOverrides,
+ /* isProtected= */ false,
+ /* updateBootconfigs= */ true,
+ gki);
assertThatEventually(
100000,
() ->
@@ -551,16 +590,23 @@
}
@Test
- @CddTest(requirements = {"9.17/C-2-2", "9.17/C-2-6"})
- public void testBootFailsWhenVbMetaDigestDoesNotMatchBootconfig() throws Exception {
+ @Parameters(method = "gkiVersions")
+ @TestCaseName("{method}_gki_{0}")
+ @CddTest(requirements = {"9.17/C-2-2", "9.17/C-2-5", "9.17/C-2-6"})
+ public void testBootFailsWhenVbMetaDigestDoesNotMatchBootconfig(String gki) throws Exception {
// protectedVmWithImageSignedWithDifferentKeyRunsPvmfw() is the protected case.
- assumeNonProtectedVm();
+ assumeKernelSupported(gki);
+
// Sign everything with key1 except vbmeta
File key = findTestFile("test.com.android.virt.pem");
// To be able to stop it, it should be a daemon.
VmInfo vmInfo =
runMicrodroidWithResignedImages(
- key, Map.of(), /* isProtected= */ false, /* updateBootconfigs= */ false);
+ key,
+ Map.of(),
+ /* isProtected= */ false,
+ /* updateBootconfigs= */ false,
+ gki);
// Wait so that init can print errors to console (time in cuttlefish >> in real device)
assertThatEventually(
100000,
@@ -608,7 +654,8 @@
}
private boolean isTombstoneGeneratedWithCmd(
- boolean protectedVm, String configPath, String... crashCommand) throws Exception {
+ boolean protectedVm, String gki, String configPath, String... crashCommand)
+ throws Exception {
CommandRunner android = new CommandRunner(getDevice());
String testStartTime = android.runWithTimeout(1000, "date", "'+%Y-%m-%d %H:%M:%S.%N'");
@@ -618,7 +665,7 @@
.memoryMib(minMemorySize())
.cpuTopology("match_host")
.protectedVm(protectedVm)
- .gki(mGki)
+ .gki(sGkiVersions.get(gki))
.build(getAndroidDevice());
mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
mMicrodroidDevice.enableAdbRoot();
@@ -634,12 +681,19 @@
}
@Test
- public void testTombstonesAreGeneratedUponUserspaceCrash() throws Exception {
+ @Parameters(method = "params")
+ @TestCaseName("{method}_protectedVm_{0}_gki_{1}")
+ public void testTombstonesAreGeneratedUponUserspaceCrash(boolean protectedVm, String gki)
+ throws Exception {
+ // Preconditions
+ assumeKernelSupported(gki);
+ assumeVmTypeSupported(protectedVm);
// TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
assertThat(
isTombstoneGeneratedWithCmd(
- mProtectedVm,
+ protectedVm,
+ gki,
"assets/vm_config.json",
"kill",
"-SIGSEGV",
@@ -648,12 +702,19 @@
}
@Test
- public void testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrash() throws Exception {
+ @Parameters(method = "params")
+ @TestCaseName("{method}_protectedVm_{0}_gki_{1}")
+ public void testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrash(
+ boolean protectedVm, String gki) throws Exception {
+ // Preconditions
+ assumeKernelSupported(gki);
+ assumeVmTypeSupported(protectedVm);
// TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
assertThat(
isTombstoneGeneratedWithCmd(
- mProtectedVm,
+ protectedVm,
+ gki,
"assets/vm_config_no_tombstone.json",
"kill",
"-SIGSEGV",
@@ -662,13 +723,22 @@
}
@Test
+ @Parameters(method = "params")
+ @TestCaseName("{method}_protectedVm_{0}_gki_{1}")
@Ignore("b/341087884") // TODO(b/341087884): fix & re-enable
- public void testTombstonesAreGeneratedUponKernelCrash() throws Exception {
+ public void testTombstonesAreGeneratedUponKernelCrash(boolean protectedVm, String gki)
+ throws Exception {
+ // Preconditions
+ assumeKernelSupported(gki);
+ assumeVmTypeSupported(protectedVm);
assumeFalse("Cuttlefish is not supported", isCuttlefish());
assumeFalse("Skipping test because ramdump is disabled on user build", isUserBuild());
+
+ // Act
assertThat(
isTombstoneGeneratedWithCmd(
- mProtectedVm,
+ protectedVm,
+ gki,
"assets/vm_config.json",
"echo",
"c",
@@ -678,10 +748,12 @@
}
private boolean isTombstoneGeneratedWithVmRunApp(
- boolean protectedVm, boolean debuggable, String... additionalArgs) throws Exception {
+ boolean protectedVm, String gki, boolean debuggable, String... additionalArgs)
+ throws Exception {
// we can't use microdroid builder as it wants ADB connection (debuggable)
CommandRunner android = new CommandRunner(getDevice());
String testStartTime = android.runWithTimeout(1000, "date", "'+%Y-%m-%d %H:%M:%S.%N'");
+ gki = sGkiVersions.get(gki);
android.run("rm", "-rf", TEST_ROOT + "*");
android.run("mkdir", "-p", TEST_ROOT + "*");
@@ -708,9 +780,9 @@
if (protectedVm) {
cmd.add("--protected");
}
- if (mGki != null) {
+ if (gki != null) {
cmd.add("--gki");
- cmd.add(mGki);
+ cmd.add(gki);
}
Collections.addAll(cmd, additionalArgs);
@@ -718,52 +790,89 @@
return isTombstoneReceivedFromHostLogcat(testStartTime);
}
- private boolean isTombstoneGeneratedWithCrashPayload(boolean protectedVm, boolean debuggable)
- throws Exception {
+ private boolean isTombstoneGeneratedWithCrashPayload(
+ boolean protectedVm, String gki, boolean debuggable) throws Exception {
return isTombstoneGeneratedWithVmRunApp(
- protectedVm, debuggable, "--payload-binary-name", "MicrodroidCrashNativeLib.so");
+ protectedVm,
+ gki,
+ debuggable,
+ "--payload-binary-name",
+ "MicrodroidCrashNativeLib.so");
}
@Test
- public void testTombstonesAreGeneratedWithCrashPayload() throws Exception {
+ @Parameters(method = "params")
+ @TestCaseName("{method}_protectedVm_{0}_gki_{1}")
+ public void testTombstonesAreGeneratedWithCrashPayload(boolean protectedVm, String gki)
+ throws Exception {
+ // Preconditions
// TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
- assertThat(isTombstoneGeneratedWithCrashPayload(mProtectedVm, /* debuggable= */ true))
+ assumeKernelSupported(gki);
+ assumeVmTypeSupported(protectedVm);
+
+ // Act
+ assertThat(isTombstoneGeneratedWithCrashPayload(protectedVm, gki, /* debuggable= */ true))
.isTrue();
}
@Test
- public void testTombstonesAreNotGeneratedWithCrashPayloadWhenNonDebuggable() throws Exception {
+ @Parameters(method = "params")
+ @TestCaseName("{method}_protectedVm_{0}_gki_{1}")
+ public void testTombstonesAreNotGeneratedWithCrashPayloadWhenNonDebuggable(
+ boolean protectedVm, String gki) throws Exception {
+ // Preconditions
// TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
- assertThat(isTombstoneGeneratedWithCrashPayload(mProtectedVm, /* debuggable= */ false))
+ assumeKernelSupported(gki);
+ assumeVmTypeSupported(protectedVm);
+
+ // Act
+ assertThat(isTombstoneGeneratedWithCrashPayload(protectedVm, gki, /* debuggable= */ false))
.isFalse();
}
- private boolean isTombstoneGeneratedWithCrashConfig(boolean protectedVm, boolean debuggable)
- throws Exception {
+ private boolean isTombstoneGeneratedWithCrashConfig(
+ boolean protectedVm, String gki, boolean debuggable) throws Exception {
return isTombstoneGeneratedWithVmRunApp(
- protectedVm, debuggable, "--config-path", "assets/vm_config_crash.json");
+ protectedVm, gki, debuggable, "--config-path", "assets/vm_config_crash.json");
}
@Test
- public void testTombstonesAreGeneratedWithCrashConfig() throws Exception {
+ @Parameters(method = "params")
+ @TestCaseName("{method}_protectedVm_{0}_gki_{1}")
+ public void testTombstonesAreGeneratedWithCrashConfig(boolean protectedVm, String gki)
+ throws Exception {
+ // Preconditions
// TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
- assertThat(isTombstoneGeneratedWithCrashConfig(mProtectedVm, /* debuggable= */ true))
+ assumeKernelSupported(gki);
+ assumeVmTypeSupported(protectedVm);
+
+ // Act
+ assertThat(isTombstoneGeneratedWithCrashConfig(protectedVm, gki, /* debuggable= */ true))
.isTrue();
}
@Test
- public void testTombstonesAreNotGeneratedWithCrashConfigWhenNonDebuggable() throws Exception {
+ @Parameters(method = "params")
+ @TestCaseName("{method}_protectedVm_{0}_gki_{1}")
+ public void testTombstonesAreNotGeneratedWithCrashConfigWhenNonDebuggable(
+ boolean protectedVm, String gki) throws Exception {
// TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
- assertThat(isTombstoneGeneratedWithCrashConfig(mProtectedVm, /* debuggable= */ false))
+ assumeKernelSupported(gki);
+ assumeVmTypeSupported(protectedVm);
+ assertThat(isTombstoneGeneratedWithCrashConfig(protectedVm, gki, /* debuggable= */ false))
.isFalse();
}
@Test
- public void testTelemetryPushedAtoms() throws Exception {
+ @Parameters(method = "params")
+ @TestCaseName("{method}_protectedVm_{0}_gki_{1}")
+ public void testTelemetryPushedAtoms(boolean protectedVm, String gki) throws Exception {
+ assumeKernelSupported(gki);
+ assumeVmTypeSupported(protectedVm);
// Reset statsd config and report before the test
ConfigUtils.removeConfig(getDevice());
ReportUtils.clearReports(getDevice());
@@ -784,8 +893,8 @@
.debugLevel("full")
.memoryMib(minMemorySize())
.cpuTopology("match_host")
- .protectedVm(mProtectedVm)
- .gki(mGki)
+ .protectedVm(protectedVm)
+ .gki(sGkiVersions.get(gki))
.name("test_telemetry_pushed_atoms")
.build(device);
microdroid.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
@@ -815,7 +924,7 @@
assertThat(atomVmCreationRequested.getHypervisor())
.isEqualTo(AtomsProto.VmCreationRequested.Hypervisor.PKVM);
}
- assertThat(atomVmCreationRequested.getIsProtected()).isEqualTo(mProtectedVm);
+ assertThat(atomVmCreationRequested.getIsProtected()).isEqualTo(protectedVm);
assertThat(atomVmCreationRequested.getCreationSucceeded()).isTrue();
assertThat(atomVmCreationRequested.getBinderExceptionCode()).isEqualTo(0);
assertThat(atomVmCreationRequested.getVmIdentifier())
@@ -921,29 +1030,41 @@
}
@Test
+ @Parameters(method = "params")
+ @TestCaseName("{method}_protectedVm_{0}_gki_{1}")
@CddTest(requirements = {"9.17/C-1-1", "9.17/C-1-2", "9.17/C/1-3"})
- public void testMicrodroidBoots() throws Exception {
+ public void testMicrodroidBoots(boolean protectedVm, String gki) throws Exception {
+ // Preconditions
+ assumeKernelSupported(gki);
+ assumeVmTypeSupported(protectedVm);
+
final String configPath = "assets/vm_config.json"; // path inside the APK
testMicrodroidBootsWithBuilder(
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
.debugLevel("full")
.memoryMib(minMemorySize())
.cpuTopology("match_host")
- .protectedVm(mProtectedVm)
+ .protectedVm(protectedVm)
.name("test_microdroid_boots")
- .gki(mGki));
+ .gki(sGkiVersions.get(gki)));
}
@Test
- public void testMicrodroidRamUsage() throws Exception {
+ @Parameters(method = "params")
+ @TestCaseName("{method}_protectedVm_{0}_gki_{1}")
+ public void testMicrodroidRamUsage(boolean protectedVm, String gki) throws Exception {
+ // Preconditions
+ assumeKernelSupported(gki);
+ assumeVmTypeSupported(protectedVm);
+
final String configPath = "assets/vm_config.json";
mMicrodroidDevice =
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
.debugLevel("full")
.memoryMib(minMemorySize())
.cpuTopology("match_host")
- .protectedVm(mProtectedVm)
- .gki(mGki)
+ .protectedVm(protectedVm)
+ .gki(sGkiVersions.get(gki))
.name("test_microdroid_ram_usage")
.build(getAndroidDevice());
mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
@@ -1125,8 +1246,12 @@
}
@Test
- public void testDeviceAssignment() throws Exception {
- // Check for preconditions
+ @Parameters(method = "params")
+ @TestCaseName("{method}_protectedVm_{0}_gki_{1}")
+ public void testDeviceAssignment(boolean protectedVm, String gki) throws Exception {
+ // Preconditions
+ assumeKernelSupported(gki);
+ assumeVmTypeSupported(protectedVm);
assumeVfioPlatformSupported();
List<AssignableDevice> devices = getAssignableDevices();
@@ -1136,7 +1261,7 @@
// Try assign devices one by one
for (AssignableDevice device : devices) {
- launchWithDeviceAssignment(device.node);
+ launchWithDeviceAssignment(device.node, protectedVm, gki);
String dtPath =
new CommandRunner(mMicrodroidDevice)
@@ -1158,7 +1283,8 @@
}
}
- private void launchWithDeviceAssignment(String device) throws Exception {
+ private void launchWithDeviceAssignment(String device, boolean protectedVm, String gki)
+ throws Exception {
Objects.requireNonNull(device);
final String configPath = "assets/vm_config.json";
@@ -1167,8 +1293,8 @@
.debugLevel("full")
.memoryMib(minMemorySize())
.cpuTopology("match_host")
- .protectedVm(mProtectedVm)
- .gki(mGki)
+ .protectedVm(protectedVm)
+ .gki(sGkiVersions.get(gki))
.addAssignableDevice(device)
.build(getAndroidDevice());
@@ -1186,7 +1312,13 @@
}
@Test
- public void testHugePages() throws Exception {
+ @Parameters(method = "params")
+ @TestCaseName("{method}_protectedVm_{0}_gki_{1}")
+ public void testHugePages(boolean protectedVm, String gki) throws Exception {
+ // Preconditions
+ assumeKernelSupported(gki);
+ assumeVmTypeSupported(protectedVm);
+
ITestDevice device = getDevice();
boolean disableRoot = !device.isAdbRoot();
CommandRunner android = new CommandRunner(device);
@@ -1207,8 +1339,8 @@
.debugLevel("full")
.memoryMib(minMemorySize())
.cpuTopology("match_host")
- .protectedVm(mProtectedVm)
- .gki(mGki)
+ .protectedVm(protectedVm)
+ .gki(sGkiVersions.get(gki))
.hugePages(true)
.name("test_huge_pages")
.build(getAndroidDevice());
@@ -1230,19 +1362,6 @@
getDevice().installPackage(findTestFile(APK_NAME), /* reinstall= */ false);
- // Skip test if given device doesn't support protected or non-protected VM.
- assumeTrue(
- "Microdroid is not supported for specific VM protection type",
- getAndroidDevice().supportsMicrodroid(mProtectedVm));
-
- if (mGki != null) {
- assumeTrue(
- "GKI version \"" + mGki + "\" is not supported on this device",
- getSupportedGKIVersions().contains(mGki));
- }
-
- mOs = (mGki != null) ? "microdroid_gki-" + mGki : "microdroid";
-
new CommandRunner(getDevice())
.tryRun(
"pm",
@@ -1265,14 +1384,6 @@
getDevice().uninstallPackage(PACKAGE_NAME);
}
- private void assumeProtectedVm() {
- assumeTrue("This test is only for protected VM.", mProtectedVm);
- }
-
- private void assumeNonProtectedVm() {
- assumeFalse("This test is only for non-protected VM.", mProtectedVm);
- }
-
private void assumeVfioPlatformSupported() throws Exception {
TestDevice device = getAndroidDevice();
assumeTrue(
@@ -1302,4 +1413,19 @@
runUtil.unsetEnvVariable("LD_LIBRARY_PATH");
return runUtil;
}
+
+ private void assumeKernelSupported(String gki) throws Exception {
+ String gkiVersion = sGkiVersions.get(gki);
+ if (gkiVersion != null) {
+ assumeTrue(
+ "Skipping test as the GKI is not supported: " + gkiVersion,
+ getSupportedGKIVersions().contains(gkiVersion));
+ }
+ }
+
+ private void assumeVmTypeSupported(boolean protectedVm) throws Exception {
+ assumeTrue(
+ "Microdroid is not supported for specific VM protection type",
+ getAndroidDevice().supportsMicrodroid(protectedVm));
+ }
}
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 d38af45..c09f033 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -1212,7 +1212,7 @@
}
@Test
- @CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-7"})
+ @CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-7", "9.17/C-3-4"})
public void instancesOfSameVmHaveDifferentCdis() throws Exception {
assumeSupportedDevice();
// TODO(b/325094712): VMs on CF with same payload have the same secret. This is because
@@ -1239,7 +1239,7 @@
}
@Test
- @CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-7"})
+ @CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-7", "9.17/C-3-4"})
public void sameInstanceKeepsSameCdis() throws Exception {
assumeSupportedDevice();
assume().withMessage("Skip on CF. Too Slow. b/257270529").that(isCuttlefish()).isFalse();