Merge "Move the pci start address from 70000000 to 2c000000" into main
diff --git a/android/TerminalApp/AndroidManifest.xml b/android/TerminalApp/AndroidManifest.xml
index c11b1a0..8ed2f79 100644
--- a/android/TerminalApp/AndroidManifest.xml
+++ b/android/TerminalApp/AndroidManifest.xml
@@ -48,6 +48,7 @@
</intent-filter>
</activity>
<activity android:name=".DisplayActivity"
+ android:taskAffinity="com.android.virtualization.terminal.display"
android:screenOrientation="landscape"
android:resizeableActivity="false"
android:theme="@style/FullscreenTheme"
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
index 1daeadb..0f18261 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
@@ -368,6 +368,8 @@
return true
} else if (id == R.id.menu_item_display) {
val intent = Intent(this, DisplayActivity::class.java)
+ intent.flags =
+ intent.flags or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
this.startActivity(intent)
return true
}
diff --git a/android/TerminalApp/res/values-az/strings.xml b/android/TerminalApp/res/values-az/strings.xml
index 94edf81..b3f45fc 100644
--- a/android/TerminalApp/res/values-az/strings.xml
+++ b/android/TerminalApp/res/values-az/strings.xml
@@ -33,7 +33,7 @@
<string name="installer_error_no_wifi" msgid="1180164894845030969">"Wi-Fi əlçatan olmadığı üçün quraşdırmaq alınmadı"</string>
<string name="installer_error_unknown" msgid="5657920711470180224">"Quraşdırmaq alınmadı. Yenidən cəhd edin"</string>
<string name="action_settings" msgid="5729342767795123227">"Ayarlar"</string>
- <string name="action_display" msgid="8487008779926038139">"Displey"</string>
+ <string name="action_display" msgid="8487008779926038139">"Göstərin"</string>
<string name="vm_creation_message" msgid="6594953532721367502">"Terminal hazırlanır"</string>
<string name="vm_stop_message" msgid="3978349856095529255">"Terminal dayandırılır"</string>
<string name="vm_error_message" msgid="5231867246177661525">"Terminal çökdü"</string>
diff --git a/android/TerminalApp/res/values-in/strings.xml b/android/TerminalApp/res/values-in/strings.xml
index a1fe894..b114246 100644
--- a/android/TerminalApp/res/values-in/strings.xml
+++ b/android/TerminalApp/res/values-in/strings.xml
@@ -33,7 +33,7 @@
<string name="installer_error_no_wifi" msgid="1180164894845030969">"Gagal menginstal karena Wi-Fi tidak tersedia"</string>
<string name="installer_error_unknown" msgid="5657920711470180224">"Gagal menginstal. Coba lagi"</string>
<string name="action_settings" msgid="5729342767795123227">"Setelan"</string>
- <string name="action_display" msgid="8487008779926038139">"Tampilan"</string>
+ <string name="action_display" msgid="8487008779926038139">"Layar"</string>
<string name="vm_creation_message" msgid="6594953532721367502">"Menyiapkan terminal"</string>
<string name="vm_stop_message" msgid="3978349856095529255">"Menghentikan terminal"</string>
<string name="vm_error_message" msgid="5231867246177661525">"Terminal error"</string>
diff --git a/android/android.system.virtualmachine.res/Android.bp b/android/android.system.virtualmachine.res/Android.bp
index 1c55f78..c5d2dbb 100644
--- a/android/android.system.virtualmachine.res/Android.bp
+++ b/android/android.system.virtualmachine.res/Android.bp
@@ -2,23 +2,11 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-soong_config_module_type {
- name: "avf_flag_aware_android_app",
- module_type: "android_app",
- config_namespace: "ANDROID",
- bool_variables: ["release_avf_allow_preinstalled_apps"],
- properties: ["manifest"],
-}
-
// Defines our permissions
-avf_flag_aware_android_app {
+android_app {
name: "android.system.virtualmachine.res",
installable: true,
apex_available: ["com.android.virt"],
platform_apis: true,
- soong_config_variables: {
- release_avf_allow_preinstalled_apps: {
- manifest: "AndroidManifestNext.xml",
- },
- },
+ manifest: "AndroidManifest.xml",
}
diff --git a/android/android.system.virtualmachine.res/AndroidManifest.xml b/android/android.system.virtualmachine.res/AndroidManifest.xml
index 95b9cfa..c38d2b1 100644
--- a/android/android.system.virtualmachine.res/AndroidManifest.xml
+++ b/android/android.system.virtualmachine.res/AndroidManifest.xml
@@ -20,11 +20,11 @@
<!-- @SystemApi Allows an application to create and run a Virtual Machine
using the Virtualization Framework APIs
(android.system.virtualmachine.*).
- <p>Protection level: signature|privileged|development
+ <p>Protection level: signature|preinstalled|development
@hide
-->
<permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|preinstalled|development" />
<!-- @hide Allows an application to run a Virtual Machine with a custom
kernel or a Microdroid configuration file.
@@ -40,5 +40,14 @@
<permission android:name="android.permission.DEBUG_VIRTUAL_MACHINE"
android:protectionLevel="signature" />
+ <!-- @hide Makes Microdroid pVM use a more relaxed rollback protection scheme.
+ Should only be used by payloads delivered inside Mainline modules.
+ See packages/modules/Virtualization/docs/mainline_module_payload.md.
+ <p>Protection level: signature|development|privileged
+ -->
+ <permission android:name="android.permission.USE_RELAXED_MICRODROID_ROLLBACK_PROTECTION"
+ android:protectionLevel="signature|development|privileged" />
+
+
<application android:hasCode="false" />
</manifest>
diff --git a/android/android.system.virtualmachine.res/AndroidManifestNext.xml b/android/android.system.virtualmachine.res/AndroidManifestNext.xml
deleted file mode 100644
index ebcb8ba..0000000
--- a/android/android.system.virtualmachine.res/AndroidManifestNext.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2021 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.
- -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.virtualmachine.res">
-
- <!-- @SystemApi Allows an application to create and run a Virtual Machine
- using the Virtualization Framework APIs
- (android.system.virtualmachine.*).
- <p>Protection level: signature|preinstalled|development
- @hide
- -->
- <permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE"
- android:protectionLevel="signature|preinstalled|development" />
-
- <!-- @hide Allows an application to run a Virtual Machine with a custom
- kernel or a Microdroid configuration file.
- <p>Not for use by third-party applications.
- -->
- <permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE"
- android:protectionLevel="signature|development" />
-
- <!-- @hide Allows an application to access various Virtual Machine debug
- facilities, e.g. list all running VMs.
- <p>Not for use by third-party applications.
- -->
- <permission android:name="android.permission.DEBUG_VIRTUAL_MACHINE"
- android:protectionLevel="signature" />
-
- <application android:hasCode="false" />
-</manifest>
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index 33f3be1..2d31b87 100644
--- a/android/virtmgr/src/aidl.rs
+++ b/android/virtmgr/src/aidl.rs
@@ -71,7 +71,7 @@
use glob::glob;
use libc::{AF_VSOCK, sa_family_t, sockaddr_vm};
use log::{debug, error, info, warn};
-use microdroid_payload_config::{ApkConfig, Task, TaskType, VmPayloadConfig};
+use microdroid_payload_config::{ApexConfig, ApkConfig, Task, TaskType, VmPayloadConfig};
use nix::unistd::pipe;
use rpcbinder::RpcServer;
use rustutils::system_properties;
@@ -1409,7 +1409,19 @@
let extra_apks =
(0..extra_apk_count).map(|i| ApkConfig { path: format!("extra-apk-{i}") }).collect();
- Ok(VmPayloadConfig { task: Some(task), extra_apks, ..Default::default() })
+ if check_use_relaxed_microdroid_rollback_protection().is_ok() {
+ // The only payload delivered via Mainline module in this release requires
+ // com.android.i18n apex. However, we are past all the reasonable deadlines to add new
+ // APIs, so we use the fact that the payload is granted
+ // USE_RELAXED_MICRODROID_ROLLBACK_PROTECTION permission as a signal that we should include
+ // com.android.i18n APEX.
+ // TODO: remove this after we provide a stable @SystemApi to load additional APEXes to
+ // Microdroid pVMs.
+ let apexes = vec![ApexConfig { name: String::from("com.android.i18n") }];
+ Ok(VmPayloadConfig { task: Some(task), apexes, extra_apks, ..Default::default() })
+ } else {
+ Ok(VmPayloadConfig { task: Some(task), extra_apks, ..Default::default() })
+ }
}
/// Generates a unique filename to use for a composite disk image.
@@ -1469,6 +1481,12 @@
check_permission("android.permission.USE_CUSTOM_VIRTUAL_MACHINE")
}
+/// Check whether the caller of the current binder method is allowed to use relaxed microdroid
+/// rollback protection schema.
+fn check_use_relaxed_microdroid_rollback_protection() -> binder::Result<()> {
+ check_permission("android.permission.USE_RELAXED_MICRODROID_ROLLBACK_PROTECTION")
+}
+
/// Return whether a partition is exempt from selinux label checks, because we know that it does
/// not contain code and is likely to be generated in an app-writable directory.
fn is_safe_app_partition(label: &str) -> bool {
diff --git a/android/virtmgr/src/crosvm.rs b/android/virtmgr/src/crosvm.rs
index 561c201..00858cb 100644
--- a/android/virtmgr/src/crosvm.rs
+++ b/android/virtmgr/src/crosvm.rs
@@ -1110,6 +1110,9 @@
command.arg("--mem").arg(memory_mib.to_string());
if let Some(cpus) = config.cpus {
+ #[cfg(target_arch = "aarch64")]
+ command.arg("--cpus").arg(cpus.to_string() + ",sve=[auto=true]");
+ #[cfg(not(target_arch = "aarch64"))]
command.arg("--cpus").arg(cpus.to_string());
}
@@ -1121,7 +1124,12 @@
command.arg("--virt-cpufreq");
}
}
+ #[cfg(target_arch = "aarch64")]
+ command.arg("--cpus").arg("sve=[auto=true]");
} else if let Some(cpus) = get_num_cpus() {
+ #[cfg(target_arch = "aarch64")]
+ command.arg("--cpus").arg(cpus.to_string() + ",sve=[auto=true]");
+ #[cfg(not(target_arch = "aarch64"))]
command.arg("--cpus").arg(cpus.to_string());
} else {
bail!("Could not determine the number of CPUs in the system");
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index 946bc8c..20f44fe 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -149,6 +149,12 @@
"microdroid_gki-android15-6.6_kernel",
"microdroid_gki-android15-6.6.json",
],
+ "android16_612": [
+ "microdroid_gki-android16-6.12_initrd_debuggable",
+ "microdroid_gki-android16-6.12_initrd_normal",
+ "microdroid_gki-android16-6.12_kernel",
+ "microdroid_gki-android16-6.12.json",
+ ],
default: [],
}) + select(release_flag("RELEASE_AVF_ENABLE_DEVICE_ASSIGNMENT"), {
true: ["com.android.virt.vfio_handler.rc"],
diff --git a/build/apex/sign_virt_apex.py b/build/apex/sign_virt_apex.py
index e042f8d..57961a4 100644
--- a/build/apex/sign_virt_apex.py
+++ b/build/apex/sign_virt_apex.py
@@ -498,7 +498,7 @@
RunCommand(args, cmd)
-gki_versions = ['android15-6.6']
+gki_versions = ['android15-6.6', 'android16-6.12']
# dict of (key, file) for re-sign/verification. keys are un-versioned for readability.
virt_apex_non_gki_files = {
diff --git a/build/debian/fai_config/files/etc/systemd/system/avahi_ttyd.service/AVF b/build/debian/fai_config/files/etc/systemd/system/avahi_ttyd.service/AVF
new file mode 100644
index 0000000..09d4ee6
--- /dev/null
+++ b/build/debian/fai_config/files/etc/systemd/system/avahi_ttyd.service/AVF
@@ -0,0 +1,13 @@
+[Unit]
+Description=avahi_TTYD
+After=ttyd.service
+
+[Service]
+ExecStart=/usr/bin/avahi-publish-service ttyd _http._tcp 7681
+Type=simple
+Restart=always
+User=root
+Group=root
+
+[Install]
+WantedBy=multi-user.target
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
index d86bab0..cf06fb3 100644
--- a/build/debian/fai_config/files/etc/systemd/system/ttyd.service/AVF
+++ b/build/debian/fai_config/files/etc/systemd/system/ttyd.service/AVF
@@ -6,7 +6,6 @@
[Service]
ExecStart=/usr/local/bin/ttyd --ssl --ssl-cert /etc/ttyd/server.crt --ssl-key /etc/ttyd/server.key --ssl-ca /mnt/internal/ca.crt -t disableLeaveAlert=true -W login -f droid
-ExecStartPost=/usr/bin/avahi-publish-service ttyd _http._tcp 7681
Type=simple
Restart=always
User=root
diff --git a/build/debian/fai_config/files/etc/systemd/user/weston.service/AVF b/build/debian/fai_config/files/etc/systemd/user/weston.service/AVF
new file mode 100644
index 0000000..088c561
--- /dev/null
+++ b/build/debian/fai_config/files/etc/systemd/user/weston.service/AVF
@@ -0,0 +1,23 @@
+[Unit]
+Description=Weston, a Wayland compositor, as a user service
+Documentation=man:weston(1) man:weston.ini(5)
+Documentation=https://wayland.freedesktop.org/
+
+# Activate using a systemd socket
+Requires=weston.socket
+After=weston.socket
+
+# Since we are part of the graphical session, make sure we are started before
+Before=graphical-session.target
+
+[Service]
+Type=notify
+# Defaults to journal
+StandardOutput=journal
+StandardError=journal
+
+# add a ~/.config/weston.ini and weston will pick-it up
+ExecStart=/usr/bin/weston --modules=systemd-notify.so --xwayland --shell=kiosk-shell.so --continue-without-input
+
+[Install]
+WantedBy=graphical-session.target
\ No newline at end of file
diff --git a/build/debian/fai_config/files/etc/systemd/user/weston.socket/AVF b/build/debian/fai_config/files/etc/systemd/user/weston.socket/AVF
new file mode 100644
index 0000000..c57ff88
--- /dev/null
+++ b/build/debian/fai_config/files/etc/systemd/user/weston.socket/AVF
@@ -0,0 +1,7 @@
+[Unit]
+Description=Weston, a Wayland compositor
+Documentation=man:weston(1) man:weston.ini(5)
+Documentation=https://wayland.freedesktop.org/
+
+[Socket]
+ListenStream=%t/wayland-0
\ No newline at end of file
diff --git a/build/debian/fai_config/files/usr/local/bin/enable_display/AVF b/build/debian/fai_config/files/usr/local/bin/enable_display/AVF
new file mode 100644
index 0000000..69dce6a
--- /dev/null
+++ b/build/debian/fai_config/files/usr/local/bin/enable_display/AVF
@@ -0,0 +1,4 @@
+#!/bin/bash
+sudo systemd-run --collect -E XDG_SESSION_TYPE=wayland --uid=1000 -p PAMName=login -p TTYPath=/dev/tty7 sleep 1d
+systemctl --user start weston
+export DISPLAY=:0
\ No newline at end of file
diff --git a/build/debian/fai_config/scripts/AVF/10-systemd b/build/debian/fai_config/scripts/AVF/10-systemd
index 121acc5..a087a48 100755
--- a/build/debian/fai_config/scripts/AVF/10-systemd
+++ b/build/debian/fai_config/scripts/AVF/10-systemd
@@ -1,7 +1,9 @@
#!/bin/bash
chmod +x $target/usr/local/bin/ttyd
+chmod +x $target/usr/local/bin/enable_display
ln -s /etc/systemd/system/ttyd.service $target/etc/systemd/system/multi-user.target.wants/ttyd.service
+ln -s /etc/systemd/system/avahi_ttyd.service $target/etc/systemd/system/multi-user.target.wants/avahi_ttyd.service
ln -s /etc/systemd/system/virtiofs.service $target/etc/systemd/system/multi-user.target.wants/virtiofs.service
ln -s /etc/systemd/system/virtiofs_internal.service $target/etc/systemd/system/multi-user.target.wants/virtiofs_internal.service
ln -s /etc/systemd/system/backup_mount.service $target/etc/systemd/system/multi-user.target.wants/backup_mount.service
diff --git a/build/debian/kokoro/gcp_ubuntu_docker/x86_64/build.sh b/build/debian/kokoro/gcp_ubuntu_docker/x86_64/build.sh
index a935591..0655046 100644
--- a/build/debian/kokoro/gcp_ubuntu_docker/x86_64/build.sh
+++ b/build/debian/kokoro/gcp_ubuntu_docker/x86_64/build.sh
@@ -2,6 +2,13 @@
set -e
+git clone https://github.com/pyenv/pyenv.git ~/.pyenv
+export PYENV_ROOT="$HOME/.pyenv"
+export PATH="$PYENV_ROOT/bin:$PATH"
+pyenv install 3.11
+pyenv global 3.11
+python --version
+
cd "${KOKORO_ARTIFACTS_DIR}/git/avf/build/debian/"
sudo losetup -D
grep vmx /proc/cpuinfo || true
diff --git a/build/debian/vm_config.json.x86_64 b/build/debian/vm_config.json.x86_64
index 496e684..c34a0f2 100644
--- a/build/debian/vm_config.json.x86_64
+++ b/build/debian/vm_config.json.x86_64
@@ -43,5 +43,6 @@
"debuggable": true,
"console_out": true,
"console_input_device": "ttyS0",
- "network": true
+ "network": true,
+ "auto_memory_balloon": true
}
diff --git a/build/microdroid/Android.bp b/build/microdroid/Android.bp
index dea0bf3..059077a 100644
--- a/build/microdroid/Android.bp
+++ b/build/microdroid/Android.bp
@@ -633,7 +633,7 @@
}
avb_add_hash_footer_defaults {
- name: "microdroid_gki_kernel_signed_defaults",
+ name: "microdroid_gki-android15-6.6_kernel_signed_defaults",
defaults: ["microdroid_kernel_signed_defaults"],
arch: {
arm64: {
@@ -652,7 +652,7 @@
avb_add_hash_footer {
name: "microdroid_gki-android15-6.6_kernel_signed",
defaults: [
- "microdroid_gki_kernel_signed_defaults",
+ "microdroid_gki-android15-6.6_kernel_signed_defaults",
"microdroid_kernel_cap_defaults",
],
filename: "microdroid_gki-android15-6.6_kernel_signed",
@@ -661,7 +661,7 @@
avb_add_hash_footer {
name: "microdroid_gki-android15-6.6_kernel_signed_supports_uefi_boot",
defaults: [
- "microdroid_gki_kernel_signed_defaults",
+ "microdroid_gki-android15-6.6_kernel_signed_defaults",
"microdroid_kernel_cap_with_uefi_defaults",
],
filename: "microdroid_gki-android15-6.6_kernel_signed_supports_uefi_boot",
@@ -709,6 +709,91 @@
src: ":microdroid_gki-android15-6.6_initrd_debuggable",
}
+///////////////////////////////////////
+// GKI-android16-6.12
+///////////////////////////////////////
+prebuilt_etc {
+ name: "microdroid_gki-android16-6.12.json",
+ src: "microdroid_gki-android16-6.12.json",
+}
+
+avb_add_hash_footer_defaults {
+ name: "microdroid_gki-android16-6.12_kernel_signed_defaults",
+ defaults: ["microdroid_kernel_signed_defaults"],
+ arch: {
+ arm64: {
+ src: ":microdroid_gki_kernel_prebuilts-android16-6.12-arm64",
+ },
+ x86_64: {
+ src: ":microdroid_gki_kernel_prebuilts-android16-6.12-x86_64",
+ },
+ },
+ include_descriptors_from_images: [
+ ":microdroid_gki-android16-6.12_initrd_normal_hashdesc",
+ ":microdroid_gki-android16-6.12_initrd_debug_hashdesc",
+ ],
+}
+
+avb_add_hash_footer {
+ name: "microdroid_gki-android16-6.12_kernel_signed",
+ defaults: [
+ "microdroid_gki-android16-6.12_kernel_signed_defaults",
+ "microdroid_kernel_cap_defaults",
+ ],
+ filename: "microdroid_gki-android16-6.12_kernel_signed",
+}
+
+avb_add_hash_footer {
+ name: "microdroid_gki-android16-6.12_kernel_signed_supports_uefi_boot",
+ defaults: [
+ "microdroid_gki-android16-6.12_kernel_signed_defaults",
+ "microdroid_kernel_cap_with_uefi_defaults",
+ ],
+ filename: "microdroid_gki-android16-6.12_kernel_signed_supports_uefi_boot",
+}
+
+// HACK: use cc_genrule for arch-specific properties
+cc_genrule {
+ name: "microdroid_gki-android16-6.12_kernel_signed-lz4",
+ out: ["microdroid_gki-android16-6.12_kernel_signed-lz4"],
+ srcs: [":empty_file"],
+ arch: {
+ arm64: {
+ srcs: [":microdroid_gki-android16-6.12_kernel_signed"],
+ exclude_srcs: [":empty_file"],
+ },
+ },
+ tools: ["lz4"],
+ cmd: "$(location lz4) -9 $(in) $(out)",
+}
+
+prebuilt_etc {
+ name: "microdroid_gki-android16-6.12_kernel",
+ filename: "microdroid_gki-android16-6.12_kernel",
+ src: ":empty_file",
+ relative_install_path: "fs",
+ arch: {
+ arm64: {
+ src: ":microdroid_gki-android16-6.12_kernel_signed",
+ },
+ x86_64: {
+ src: ":microdroid_gki-android16-6.12_kernel_signed",
+ },
+ },
+}
+
+avb_gen_vbmeta_image {
+ name: "microdroid_gki-android16-6.12_initrd_normal_hashdesc",
+ defaults: ["microdroid_initrd_normal_defaults"],
+ src: ":microdroid_gki-android16-6.12_initrd_normal",
+}
+
+avb_gen_vbmeta_image {
+ name: "microdroid_gki-android16-6.12_initrd_debug_hashdesc",
+ defaults: ["microdroid_initrd_debug_defaults"],
+ src: ":microdroid_gki-android16-6.12_initrd_debuggable",
+}
+
python_binary_host {
name: "extract_microdroid_kernel_hashes",
srcs: ["extract_microdroid_kernel_hashes.py"],
@@ -723,11 +808,13 @@
arm64: {
srcs: [
":microdroid_gki-android15-6.6_kernel_signed",
+ ":microdroid_gki-android16-6.12_kernel_signed",
],
},
x86_64: {
srcs: [
":microdroid_gki-android15-6.6_kernel_signed",
+ ":microdroid_gki-android16-6.12_kernel_signed",
],
},
},
diff --git a/build/microdroid/init.rc b/build/microdroid/init.rc
index 672f47d..ce26a35 100644
--- a/build/microdroid/init.rc
+++ b/build/microdroid/init.rc
@@ -35,6 +35,16 @@
# (In Android this happens inside apexd-bootstrap.)
wait_for_prop ro.cold_boot_done true
+ # We need to define ANDROID_TZDATA_ROOT otherwise libicu complains.
+ # For now set it to a non-existent value, because libicu APIs that don't depend on tzdata work
+ # correctly without it.
+ # Once loading tzdata APEX is supported, we can set this value to /apex/com.android.tzdata.
+ # This is no-op for most microdroid VMs, and is only used if com.android.i18n APEX was mounted.
+ export ANDROID_TZDATA_ROOT /does/not/exist
+ # Set ANDROID_I18N_ROOT otherwise libicu will complain.
+ # This is no-op for most microdroid VMs, and is only used if com.android.i18n APEX was mounted.
+ export ANDROID_I18N_ROOT /apex/com.android.i18n
+
on init
mkdir /mnt/apk 0755 root root
mkdir /mnt/extra-apk 0755 root root
diff --git a/build/microdroid/initrd/Android.bp b/build/microdroid/initrd/Android.bp
index 7331e0b..d9aa108 100644
--- a/build/microdroid/initrd/Android.bp
+++ b/build/microdroid/initrd/Android.bp
@@ -52,6 +52,17 @@
}
java_genrule {
+ name: "microdroid_gki-android16-6.12_initrd_gen_arm64",
+ srcs: [
+ ":microdroid_ramdisk",
+ ":microdroid_first_stage_ramdisk",
+ ":microdroid_gki_modules-android16-6.12-arm64",
+ ],
+ out: ["microdroid_initrd.img"],
+ cmd: "cat $(in) > $(out)",
+}
+
+java_genrule {
name: "microdroid_gki-android15-6.6_initrd_gen_x86_64",
srcs: [
":microdroid_ramdisk",
@@ -62,6 +73,17 @@
cmd: "cat $(in) > $(out)",
}
+java_genrule {
+ name: "microdroid_gki-android16-6.12_initrd_gen_x86_64",
+ srcs: [
+ ":microdroid_ramdisk",
+ ":microdroid_first_stage_ramdisk",
+ ":microdroid_gki_modules-android16-6.12-x86_64",
+ ],
+ out: ["microdroid_initrd.img"],
+ cmd: "cat $(in) > $(out)",
+}
+
// This contains vbmeta hashes & related (boot)configs which are passed to kernel/init
java_genrule {
name: "microdroid_vbmeta_bootconfig_gen",
@@ -111,6 +133,17 @@
}
java_genrule {
+ name: "microdroid_gki-android16-6.12_initrd_debuggable_arm64",
+ tools: ["initrd_bootconfig"],
+ srcs: [
+ ":microdroid_gki-android16-6.12_initrd_gen_arm64",
+ ":microdroid_bootconfig_debuggable_src",
+ ] + bootconfigs_arm64,
+ out: ["microdroid_gki-android16-6.12_initrd_debuggable_arm64"],
+ cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
+}
+
+java_genrule {
name: "microdroid_initrd_debuggable_x86_64",
tools: ["initrd_bootconfig"],
srcs: [
@@ -144,6 +177,17 @@
}
java_genrule {
+ name: "microdroid_gki-android16-6.12_initrd_debuggable_x86_64",
+ tools: ["initrd_bootconfig"],
+ srcs: [
+ ":microdroid_gki-android16-6.12_initrd_gen_x86_64",
+ ":microdroid_bootconfig_debuggable_src",
+ ] + bootconfigs_x86_64,
+ out: ["microdroid_gki-android16-6.12_initrd_debuggable_x86_64"],
+ cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
+}
+
+java_genrule {
name: "microdroid_initrd_normal_arm64",
tools: ["initrd_bootconfig"],
srcs: [
@@ -166,6 +210,17 @@
}
java_genrule {
+ name: "microdroid_gki-android16-6.12_initrd_normal_arm64",
+ tools: ["initrd_bootconfig"],
+ srcs: [
+ ":microdroid_gki-android16-6.12_initrd_gen_arm64",
+ ":microdroid_bootconfig_normal_src",
+ ] + bootconfigs_arm64,
+ out: ["microdroid_gki-android16-6.12_initrd_normal_arm64"],
+ cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
+}
+
+java_genrule {
name: "microdroid_initrd_normal_x86_64",
tools: ["initrd_bootconfig"],
srcs: [
@@ -198,6 +253,17 @@
cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
}
+java_genrule {
+ name: "microdroid_gki-android16-6.12_initrd_normal_x86_64",
+ tools: ["initrd_bootconfig"],
+ srcs: [
+ ":microdroid_gki-android16-6.12_initrd_gen_x86_64",
+ ":microdroid_bootconfig_normal_src",
+ ] + bootconfigs_x86_64,
+ out: ["microdroid_gki-android16-6.12_initrd_normal_x86_64"],
+ cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
+}
+
prebuilt_etc {
name: "microdroid_initrd_debuggable",
// We don't have ramdisk for architectures other than x86_64 & arm64
@@ -247,6 +313,21 @@
}
prebuilt_etc {
+ name: "microdroid_gki-android16-6.12_initrd_debuggable",
+ // We don't have ramdisk for architectures other than x86_64 & arm64
+ src: ":empty_file",
+ arch: {
+ arm64: {
+ src: ":microdroid_gki-android16-6.12_initrd_debuggable_arm64",
+ },
+ x86_64: {
+ src: ":microdroid_gki-android16-6.12_initrd_debuggable_x86_64",
+ },
+ },
+ filename: "microdroid_gki-android16-6.12_initrd_debuggable.img",
+}
+
+prebuilt_etc {
name: "microdroid_initrd_normal",
// We don't have ramdisk for architectures other than x86_64 & arm64
src: ":empty_file",
@@ -293,3 +374,18 @@
},
filename: "microdroid_gki-android15-6.6_initrd_normal.img",
}
+
+prebuilt_etc {
+ name: "microdroid_gki-android16-6.12_initrd_normal",
+ // We don't have ramdisk for architectures other than x86_64 & arm64
+ src: ":empty_file",
+ arch: {
+ arm64: {
+ src: ":microdroid_gki-android16-6.12_initrd_normal_arm64",
+ },
+ x86_64: {
+ src: ":microdroid_gki-android16-6.12_initrd_normal_x86_64",
+ },
+ },
+ filename: "microdroid_gki-android16-6.12_initrd_normal.img",
+}
diff --git a/build/microdroid/microdroid_gki-android16-6.12.json b/build/microdroid/microdroid_gki-android16-6.12.json
new file mode 100644
index 0000000..57adb24
--- /dev/null
+++ b/build/microdroid/microdroid_gki-android16-6.12.json
@@ -0,0 +1,20 @@
+{
+ "kernel": "/apex/com.android.virt/etc/fs/microdroid_gki-android16-6.12_kernel",
+ "disks": [
+ {
+ "partitions": [
+ {
+ "label": "vbmeta_a",
+ "path": "/apex/com.android.virt/etc/fs/microdroid_vbmeta.img"
+ },
+ {
+ "label": "super",
+ "path": "/apex/com.android.virt/etc/fs/microdroid_super.img"
+ }
+ ],
+ "writable": false
+ }
+ ],
+ "memory_mib": 256,
+ "platform_version": "~1.0"
+}
diff --git a/docs/custom_vm.md b/docs/custom_vm.md
index 2815bbf..148d368 100644
--- a/docs/custom_vm.md
+++ b/docs/custom_vm.md
@@ -26,23 +26,9 @@
`/apex/com.android.virt/bin/vm help` for details.
# Terminal app
-## Graphical environment (Wayland, VNC)
-By installing Wayland compositor and VNC backend, you can enable graphical environment.
-One of the options is `sway`, `wayvnc` and `xwayland`(if necessary).
-
-```
-sudo apt install sway wayvnc xwayland
-WLR_BACKENDS=headless WLR_LIBINPUT_NO_DEVICES=1 sway
-WAYLAND_DISPLAY=wayland-1 wayvnc 0.0.0.0 # or use port forwarding
-```
-
-And then, connect to 192.168.0.2:5900(or localhost:5900) with arbitrary VNC client.
-Or, `novnc`(https://github.com/novnc/noVNC/releases). For `novnc` you need to install
-`novnc`, and run `<novnc_path>/utils/novnc_proxy`, and then connect to `http://192.168.0.2:6080/vnc.html`
-(or `localhost:6080` if port forwarding is enabled.)
-
-`weston` with VNC backend might be another option, but it isn't available in
-Debian package repository for bookworm.
+## Run GUI apps
+Execute `source enable_display` and then click Display button above to enable display feature.
+And then, go back to the terminal, and run GUI apps.
## Hardware acceleration
If the file `/sdcard/linux/virglrenderer` exists on the device, it enables VirGL for VM.
diff --git a/docs/mainline_module_payload.md b/docs/mainline_module_payload.md
new file mode 100644
index 0000000..84617f0
--- /dev/null
+++ b/docs/mainline_module_payload.md
@@ -0,0 +1,19 @@
+# Delivery Microdroid pVM payload via Mainline modules
+
+There are several additional challenges when a Microdroid pVM payload is
+delivered inside a Mainline module.
+
+## Mainline rollbacks
+
+Mainline modules are expected to be rolled back on a device in case a problem
+with a Mainline release has been detected. This doesn't work well with the
+rollback protection of Microdroid pVMs - if a payload is updated, then a
+previous version of the payload is not allowed to access it's secrets.
+
+To work around this challenge, payloads delivered via Mainline modules are
+expected to request
+`android.permission.USE_RELAXED_MICRODROID_ROLLBACK_PROTECTION` privileged
+permission.
+
+TODO(ioffe): add more context on how permission is used once the implementation
+is done.
diff --git a/guest/forwarder_guest_launcher/src/main.rs b/guest/forwarder_guest_launcher/src/main.rs
index 963a531..f4c8ca9 100644
--- a/guest/forwarder_guest_launcher/src/main.rs
+++ b/guest/forwarder_guest_launcher/src/main.rs
@@ -36,14 +36,12 @@
const NON_PREVILEGED_PORT_RANGE_START: i32 = 1024;
const TTYD_PORT: i32 = 7681;
-const TCPSTATES_IP_4: i8 = 4;
const TCPSTATES_STATE_CLOSE: &str = "CLOSE";
const TCPSTATES_STATE_LISTEN: &str = "LISTEN";
#[derive(Debug, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
struct TcpStateRow {
- ip: i8,
lport: i32,
rport: i32,
#[serde(alias = "C-COMM")]
@@ -127,10 +125,8 @@
// TODO(b/340126051): Consider using NETLINK_SOCK_DIAG for the optimization.
let listeners = listeners::get_all()?;
- // TODO(b/340126051): Support distinguished port forwarding for ipv6 as well.
let mut listening_ports: HashMap<_, _> = listeners
.iter()
- .filter(|x| x.socket.is_ipv4())
.map(|x| {
(
x.socket.port().into(),
@@ -144,9 +140,6 @@
let mut records = csv_reader.records();
while let Some(record) = records.next().await {
let row: TcpStateRow = record?.deserialize(Some(&header))?;
- if row.ip != TCPSTATES_IP_4 {
- continue;
- }
if !is_forwardable_port(row.lport) {
continue;
}
diff --git a/guest/microdroid_manager/src/dice.rs b/guest/microdroid_manager/src/dice.rs
index bf89358..dd5375f 100644
--- a/guest/microdroid_manager/src/dice.rs
+++ b/guest/microdroid_manager/src/dice.rs
@@ -153,7 +153,11 @@
subcomponents.into_iter().map(Subcomponent::into_value).collect::<Result<Vec<_>>>()?;
map.push((cbor!(-71002)?, cbor!(values)?));
}
- // Add a placeholder security version as it is required by the open-dice profile "Android.16"
+ // Add a placeholder security version as it is required by the open-dice profile "Android.16".
+ // Note: The DICE certificate derived in microdroid_manager primarily describes the APKs/APEXs
+ // loaded by microdroid_manager. Each APK/APEX is described separately with its own security
+ // version as a subcomponent within the certificate's config descriptor.
+ // Therefore, the global security version below (for the entire certificate) is unused.
map.push((cbor!(-70005)?, cbor!(0)?));
Ok(Value::Map(map).to_vec()?)
}
diff --git a/libs/bssl/src/err.rs b/libs/bssl/src/err.rs
index a53ac8c..f64baee 100644
--- a/libs/bssl/src/err.rs
+++ b/libs/bssl/src/err.rs
@@ -17,8 +17,8 @@
use alloc::string::{String, ToString};
use bssl_avf_error::{CipherError, EcError, EcdsaError, GlobalError, ReasonCode};
use bssl_sys::{
- self, ERR_get_error_line, ERR_lib_error_string, ERR_reason_error_string, ERR_GET_LIB_RUST,
- ERR_GET_REASON_RUST,
+ self, ERR_get_error_line, ERR_lib_error_string, ERR_reason_error_string, ERR_GET_LIB,
+ ERR_GET_REASON,
};
use core::ffi::{c_char, CStr};
use core::ptr;
@@ -102,13 +102,13 @@
fn get_reason(packed_error: u32) -> i32 {
// SAFETY: This function only reads the given error code.
- unsafe { ERR_GET_REASON_RUST(packed_error) }
+ unsafe { ERR_GET_REASON(packed_error) }
}
/// Returns the library code for the error.
fn get_lib(packed_error: u32) -> i32 {
// SAFETY: This function only reads the given error code.
- unsafe { ERR_GET_LIB_RUST(packed_error) }
+ unsafe { ERR_GET_LIB(packed_error) }
}
fn map_to_reason_code(reason: i32, lib: i32) -> ReasonCode {
diff --git a/libs/libavf/include/android/virtualization.h b/libs/libavf/include/android/virtualization.h
index ef57325..8d96fac 100644
--- a/libs/libavf/include/android/virtualization.h
+++ b/libs/libavf/include/android/virtualization.h
@@ -357,7 +357,7 @@
* For a graceful shutdown, you could request the virtual machine to exit itself, and wait for the
* virtual machine to stop by `AVirtualMachine_waitForStop`.
*
- * A stopped virtual machine can be re-started by calling `AVirtualMachine_start`.
+ * A stopped virtual machine cannot be re-started.
*
* `AVirtualMachine_stop` stops a virtual machine by sending a signal to the process. This operation
* is synchronous and `AVirtualMachine_stop` may block.
diff --git a/libs/libvm_payload/include/vm_payload.h b/libs/libvm_payload/include/vm_payload.h
index e4609fa..a3bb577 100644
--- a/libs/libvm_payload/include/vm_payload.h
+++ b/libs/libvm_payload/include/vm_payload.h
@@ -58,7 +58,7 @@
typedef enum AVmAccessRollbackProtectedSecretStatus : int32_t {
/**
* Relevant Entry not found. This can happen either due to no value was ever written or because
- * Android maliciously deleted the value (deletions may not be authenticated).
+ * it was deleted by host.
*/
AVMACCESSROLLBACKPROTECTEDSECRETSTATUS_ENTRY_NOT_FOUND = -1,
/** Requested access size is not supported by the implementation */
@@ -282,9 +282,9 @@
__INTRODUCED_IN(__ANDROID_API_V__);
/**
* Writes up to n bytes from buffer starting at `buf`, on behalf of the payload, to rollback
- * detectable storage. The number of bytes written may be less than n if, for example, the
- * underlying storage has size constraints. This stored data is confidential to the pVM and
- * protected via appropriate DICE policy on the payload's DICE chain.
+ * detectable storage. The data is written from the start. The number of bytes written may be less
+ * than n if, for example, the underlying storage has size constraints. This stored data is
+ * confidential to the VM instance.
*
* \param buf A pointer to data to be written. This should have the size of at least n bytes.
* \param n The maximum number of bytes to be filled in `buf`.
@@ -296,7 +296,7 @@
int32_t AVmPayload_writeRollbackProtectedSecret(const void* _Nonnull buf, size_t n)
__INTRODUCED_IN(36);
/**
- * Read up to n bytes of payload's data in rollback detectable storage into `buf`.
+ * Read the first n bytes of payload's data in rollback detectable storage into `buf`.
*
* \param buf A pointer to buffer where the requested data is written. This should have the size of
* at least n bytes.
@@ -307,7 +307,6 @@
* number) is returned.
*/
int32_t AVmPayload_readRollbackProtectedSecret(void* _Nullable buf, size_t n) __INTRODUCED_IN(36);
-;
/**
* Checks whether the VM instance is new - i.e., if this is the first run of an instance.
@@ -316,6 +315,6 @@
*
* \return true if this is the first run of an instance, false otherwise.
*/
-bool AVmPayload_isNewInstance() __INTRODUCED_IN(36);
+bool AVmPayload_isNewInstance(void) __INTRODUCED_IN(36);
__END_DECLS
diff --git a/tests/aidl/com/android/microdroid/testservice/IBenchmarkService.aidl b/tests/aidl/com/android/microdroid/testservice/IBenchmarkService.aidl
index 4d043f6..ca752e1 100644
--- a/tests/aidl/com/android/microdroid/testservice/IBenchmarkService.aidl
+++ b/tests/aidl/com/android/microdroid/testservice/IBenchmarkService.aidl
@@ -27,6 +27,13 @@
*/
double measureReadRate(String filename, boolean isRand);
+ /**
+ * Measures the write rate for writing the given file, creating the file if necessary
+ *
+ * @return The write rate in MB/s.
+ */
+ double measureWriteRate(String filename, long size_bytes);
+
/** Returns an entry from /proc/meminfo. */
long getMemInfoEntry(String name);
diff --git a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
index 6a413d6..4c824f0 100644
--- a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
+++ b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
@@ -104,4 +104,9 @@
* @return true on the first boot of the instance & false on subsequent boot.
*/
boolean isNewInstance();
+
+ /**
+ * Checks that libicu is accessible to the payload that has com.android.i18n APEX mounted.
+ */
+ void checkLibIcuIsAccessible();
}
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 1d827b9..915fc82 100644
--- a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
+++ b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
@@ -101,6 +101,7 @@
private static final double NANO_TO_MICRO = 1_000.0;
private static final String MICRODROID_IMG_PREFIX = "microdroid_";
private static final String MICRODROID_IMG_SUFFIX = ".img";
+ static final long ENCRYPTED_STORE_SIZE = 1_073_741_824; // 1G
@Parameterized.Parameters(name = "protectedVm={0},os={1}")
public static Collection<Object[]> params() {
@@ -1097,4 +1098,58 @@
"latency/writeRollbackProtectedSecretWithRefreshSession",
"us");
}
+
+ @Test
+ public void encryptedstoreIoRate() throws Exception {
+ VirtualMachineConfig config =
+ newVmConfigBuilderWithPayloadConfig("assets/vm_config_io.json")
+ .setDebugLevel(DEBUG_LEVEL_NONE)
+ .setShouldUseHugepages(true)
+ .setEncryptedStorageBytes(ENCRYPTED_STORE_SIZE)
+ .build();
+ List<Double> writeThroughput = new ArrayList<>(IO_TEST_TRIAL_COUNT);
+ List<Double> readThroughput = new ArrayList<>(IO_TEST_TRIAL_COUNT);
+
+ for (int i = 0; i < IO_TEST_TRIAL_COUNT; ++i) {
+ String vmName = "vm_encryptedstore_io" + i;
+ VirtualMachine vm = forceCreateNewVirtualMachine(vmName, config);
+ BenchmarkVmListener.create(new EncryptedstoreBenchmarkListener(writeThroughput, true))
+ .runToFinish(TAG, vm);
+ // Rerun the VM & read the storage!
+ BenchmarkVmListener.create(new EncryptedstoreBenchmarkListener(readThroughput, false))
+ .runToFinish(TAG, vm);
+ }
+ reportMetrics(writeThroughput, "encryptedstore/sequential_write", "mb_per_sec");
+ reportMetrics(readThroughput, "encryptedstore/sequential_read", "mb_per_sec");
+ }
+
+ private static class EncryptedstoreBenchmarkListener
+ implements BenchmarkVmListener.InnerListener {
+ private static final String FILENAME = "/mnt/encryptedstore/test_file";
+
+ private final List<Double> mIoThroughput;
+ // Set to true iff write is to be measured, read throughput is measured otherwise
+ private final boolean mMeasureWrite;
+
+ EncryptedstoreBenchmarkListener(List<Double> ioThroughput, boolean measureWrite) {
+ mIoThroughput = ioThroughput;
+ mMeasureWrite = measureWrite;
+ }
+
+ @Override
+ public void onPayloadReady(VirtualMachine vm, IBenchmarkService benchmarkService)
+ throws RemoteException {
+ double rate;
+ if (mMeasureWrite) {
+ // Fill 3/4 of the storage by writing (random) data into a file!
+ rate =
+ benchmarkService.measureWriteRate(
+ FILENAME, /*sizeBytes */ (ENCRYPTED_STORE_SIZE * 3) / 4);
+ } else {
+ // Sequentially read the file, just written.
+ rate = benchmarkService.measureReadRate(FILENAME, /*isRand */ false);
+ }
+ mIoThroughput.add(rate);
+ }
+ }
}
diff --git a/tests/benchmark/src/native/benchmarkbinary.cpp b/tests/benchmark/src/native/benchmarkbinary.cpp
index 5d93b93..5e46712 100644
--- a/tests/benchmark/src/native/benchmarkbinary.cpp
+++ b/tests/benchmark/src/native/benchmarkbinary.cpp
@@ -67,6 +67,15 @@
return resultStatus(res);
}
+ ndk::ScopedAStatus measureWriteRate(const std::string& filename, int64_t size_bytes,
+ double* out) override {
+ auto res = measure_write_rate(filename, size_bytes);
+ if (res.ok()) {
+ *out = res.value();
+ }
+ return resultStatus(res);
+ }
+
ndk::ScopedAStatus getMemInfoEntry(const std::string& name, int64_t* out) override {
auto value = read_meminfo_entry(name);
if (!value.ok()) {
@@ -144,6 +153,49 @@
return {file_size_mb / elapsed_seconds};
}
+ /**
+ * Measures the throughput of writing random data to the given file.
+ * @return The write rate in MB/s.
+ */
+ Result<double> measure_write_rate(const std::string& filename, int64_t size_bytes) {
+ struct stat file_stats;
+ const int64_t block_count = size_bytes / kBlockSizeBytes;
+ char buf[kBlockSizeBytes];
+ int fd_rand = open("/dev/urandom", O_RDONLY);
+ read(fd_rand, buf, kBlockSizeBytes);
+
+ struct timespec start;
+ if (clock_gettime(CLOCK_MONOTONIC, &start) == -1) {
+ return ErrnoError() << "failed to clock_gettime";
+ }
+ // TODO(b/390648694): Ideally open with O_SYNC instead of syncfs().
+ unique_fd fd(open(filename.c_str(), O_CREAT | O_WRONLY, 00666));
+ if (fd.get() == -1) {
+ return ErrnoError() << "Write: opening " << filename << " failed";
+ }
+ if (stat(filename.c_str(), &file_stats) == -1) {
+ return Error() << "failed to get file stats";
+ }
+
+ for (auto i = 0; i < block_count; ++i) {
+ auto bytes = write(fd, buf, kBlockSizeBytes);
+ if (bytes == 0) {
+ return Error() << "unexpected end of file";
+ } else if (bytes == -1) {
+ return ErrnoError() << "failed to write";
+ }
+ }
+ syncfs(fd);
+ struct timespec finish;
+ if (clock_gettime(CLOCK_MONOTONIC, &finish) == -1) {
+ return ErrnoError() << "failed to clock_gettime";
+ }
+ double elapsed_seconds =
+ finish.tv_sec - start.tv_sec + (finish.tv_nsec - start.tv_nsec) / 1e9;
+ double file_size_mb = (double)size_bytes / kNumBytesPerMB;
+ return {file_size_mb / elapsed_seconds};
+ }
+
void* alloc_anon_memory(long mb) {
long bytes = mb << 20;
void* p = malloc(bytes);
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 99300e2..806592d 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -63,11 +63,22 @@
DATA = [
":MicrodroidTestAppUpdated",
+ ":MicrodroidTestHelperAppRelaxedRollbackProtection_correct_V5",
":MicrodroidVmShareApp",
":test_microdroid_vendor_image",
":test_microdroid_vendor_image_unsigned",
]
+android_test_helper_app {
+ name: "MicrodroidTestHelperAppRelaxedRollbackProtection_correct_V5",
+ defaults: ["MicrodroidTestAppsDefaults"],
+ manifest: "AndroidManifestV5_relaxed_rollback_protection.xml",
+ jni_libs: [
+ "MicrodroidTestNativeLibWithLibIcu",
+ ],
+ min_sdk_version: "33",
+}
+
android_test {
name: "MicrodroidTestApp",
defaults: ["MicrodroidVersionsTestAppDefaults"],
@@ -188,6 +199,17 @@
}
cc_library_shared {
+ name: "MicrodroidTestNativeLibWithLibIcu",
+ defaults: ["MicrodroidTestNativeLibDefaults"],
+ shared_libs: [
+ "libicu",
+ ],
+ cflags: [
+ "-D__MICRODROID_TEST_PAYLOAD_USES_LIBICU__",
+ ],
+}
+
+cc_library_shared {
name: "MicrodroidTestNativeLibSub",
defaults: ["avf_build_flags_cc"],
srcs: ["src/native/testlib.cpp"],
diff --git a/tests/testapk/AndroidManifestV5.xml b/tests/testapk/AndroidManifestV5.xml
index 7d97680..2ef1b6b 100644
--- a/tests/testapk/AndroidManifestV5.xml
+++ b/tests/testapk/AndroidManifestV5.xml
@@ -18,10 +18,12 @@
android:versionCode="5">
<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.USE_RELAXED_MICRODROID_ROLLBACK_PROTECTION" />
<uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
<uses-feature android:name="android.software.virtualization_framework" android:required="false" />
<queries>
<package android:name="com.android.microdroid.vmshare_app" />
+ <package android:name="com.android.microdroid.test_relaxed_rollback_protection_scheme" />
</queries>
<application />
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/testapk/AndroidManifestV5_relaxed_rollback_protection.xml b/tests/testapk/AndroidManifestV5_relaxed_rollback_protection.xml
new file mode 100644
index 0000000..619d158
--- /dev/null
+++ b/tests/testapk/AndroidManifestV5_relaxed_rollback_protection.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.microdroid.test_relaxed_rollback_protection_scheme"
+ android:versionCode="5">
+ <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.USE_RELAXED_MICRODROID_ROLLBACK_PROTECTION" />
+ <uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
+ <uses-feature android:name="android.software.virtualization_framework" android:required="false" />
+ <application />
+</manifest>
diff --git a/tests/testapk/AndroidManifestV6.xml b/tests/testapk/AndroidManifestV6.xml
index 19d5674..7dd0663 100644
--- a/tests/testapk/AndroidManifestV6.xml
+++ b/tests/testapk/AndroidManifestV6.xml
@@ -18,6 +18,7 @@
android:versionCode="6">
<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.USE_RELAXED_MICRODROID_ROLLBACK_PROTECTION" />
<uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
<uses-feature android:name="android.software.virtualization_framework" android:required="false" />
<queries>
diff --git a/tests/testapk/AndroidTestTemplate.xml b/tests/testapk/AndroidTestTemplate.xml
index 613ce28..6cdf984 100644
--- a/tests/testapk/AndroidTestTemplate.xml
+++ b/tests/testapk/AndroidTestTemplate.xml
@@ -21,6 +21,7 @@
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="test-file-name" value="{MODULE}.apk" />
<option name="test-file-name" value="MicrodroidVmShareApp.apk" />
+ <option name="test-file-name" value="MicrodroidTestHelperAppRelaxedRollbackProtection_correct_V5.apk" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="mkdir -p /data/local/tmp/cts/microdroid" />
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 3ece140..b492684 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -130,6 +130,9 @@
private static final String VM_ATTESTATION_MESSAGE = "Hello RKP from AVF!";
private static final int ENCRYPTED_STORAGE_BYTES = 4_000_000;
+ private static final String USE_RELAXED_MICRODROID_ROLLBACK_PROTECTION_PERMISSION =
+ "android.permission.USE_RELAXED_MICRODROID_ROLLBACK_PROTECTION";
+
@Rule public Timeout globalTimeout = Timeout.seconds(300);
@Parameterized.Parameters(name = "protectedVm={0},os={1}")
@@ -163,15 +166,19 @@
// Tests that rely on the state of the permission should explicitly grant or revoke it.
revokePermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION);
}
+ revokePermission(USE_RELAXED_MICRODROID_ROLLBACK_PROTECTION_PERMISSION);
}
@After
public void tearDown() {
revokePermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION);
+ revokePermission(USE_RELAXED_MICRODROID_ROLLBACK_PROTECTION_PERMISSION);
}
private static final String EXAMPLE_STRING = "Literally any string!! :)";
private static final String VM_SHARE_APP_PACKAGE_NAME = "com.android.microdroid.vmshare_app";
+ private static final String RELAXED_ROLLBACK_PROTECTION_SCHEME_PACKAGE_NAME =
+ "com.android.microdroid.test_relaxed_rollback_protection_scheme";
private void createAndConnectToVmHelper(int cpuTopology, boolean shouldUseHugepages)
throws Exception {
@@ -2747,6 +2754,39 @@
assertThat(testResults.mPageSize).isEqualTo(expectedPageSize);
}
+ @Test
+ public void libIcuIsLoadable() throws Exception {
+ assumeSupportedDevice();
+
+ // This test relies on the test apk having USE_RELAXED_MICRODROID_ROLLBACK_PROTECTION
+ // permission.
+ grantPermission(USE_RELAXED_MICRODROID_ROLLBACK_PROTECTION_PERMISSION);
+
+ Context otherAppCtx =
+ getContext()
+ .createPackageContext(RELAXED_ROLLBACK_PROTECTION_SCHEME_PACKAGE_NAME, 0);
+ VirtualMachineConfig config =
+ new VirtualMachineConfig.Builder(otherAppCtx)
+ .setDebugLevel(DEBUG_LEVEL_FULL)
+ .setPayloadBinaryName("MicrodroidTestNativeLibWithLibIcu.so")
+ .setProtectedVm(isProtectedVm())
+ .setOs(os())
+ .build();
+
+ VirtualMachine vm = forceCreateNewVirtualMachine("test_libicu_is_loadable", config);
+
+ TestResults testResults =
+ runVmTestService(
+ TAG,
+ vm,
+ (ts, tr) -> {
+ ts.checkLibIcuIsAccessible();
+ });
+
+ // checkLibIcuIsAccessible will throw an exception if something goes wrong.
+ assertThat(testResults.mException).isNull();
+ }
+
private static class VmShareServiceConnection implements ServiceConnection {
private final CountDownLatch mLatch = new CountDownLatch(1);
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index 2ab73a4..355cfb1 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -30,6 +30,9 @@
#include <stdio.h>
#include <sys/capability.h>
#include <sys/system_properties.h>
+#ifdef __MICRODROID_TEST_PAYLOAD_USES_LIBICU__
+#include <unicode/uchar.h>
+#endif
#include <unistd.h>
#include <vm_main.h>
#include <vm_payload_restricted.h>
@@ -384,6 +387,29 @@
}
}
+ ScopedAStatus checkLibIcuIsAccessible() override {
+#ifdef __MICRODROID_TEST_PAYLOAD_USES_LIBICU__
+ static constexpr const char* kLibIcuPath = "/apex/com.android.i18n/lib64/libicu.so";
+ if (access(kLibIcuPath, R_OK) == 0) {
+ if (!u_hasBinaryProperty(U'❤' /* Emoji heart U+2764 */, UCHAR_EMOJI)) {
+ return ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC,
+ "libicu broken!");
+ }
+ return ScopedAStatus::ok();
+ } else {
+ std::string msg = "failed to access " + std::string(kLibIcuPath) + "(" +
+ std::to_string(errno) + ")";
+ return ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC,
+ msg.c_str());
+ }
+#else
+ return ScopedAStatus::
+ fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC,
+ "should be only used together with "
+ "MicrodroidTestNativeLibWithLibIcu.so payload");
+#endif
+ }
+
ScopedAStatus quit() override { exit(0); }
};
auto testService = ndk::SharedRefBase::make<TestService>();
diff --git a/tests/testapk/src/native/testbinary.rs b/tests/testapk/src/native/testbinary.rs
index c9d46b8..3900cad 100644
--- a/tests/testapk/src/native/testbinary.rs
+++ b/tests/testapk/src/native/testbinary.rs
@@ -135,6 +135,9 @@
fn isNewInstance(&self) -> BinderResult<bool> {
unimplemented()
}
+ fn checkLibIcuIsAccessible(&self) -> BinderResult<()> {
+ unimplemented()
+ }
}
fn unimplemented<T>() -> BinderResult<T> {
diff --git a/tests/vmshareapp/src/java/com/android/microdroid/test/sharevm/VmShareServiceImpl.java b/tests/vmshareapp/src/java/com/android/microdroid/test/sharevm/VmShareServiceImpl.java
index 1f71888..7f44fa5 100644
--- a/tests/vmshareapp/src/java/com/android/microdroid/test/sharevm/VmShareServiceImpl.java
+++ b/tests/vmshareapp/src/java/com/android/microdroid/test/sharevm/VmShareServiceImpl.java
@@ -291,5 +291,10 @@
public boolean isNewInstance() {
throw new UnsupportedOperationException("Not supported");
}
+
+ @Override
+ public void checkLibIcuIsAccessible() {
+ throw new UnsupportedOperationException("Not supported");
+ }
}
}