Merge "Some UI change to the terminal app" into main
diff --git a/android/TerminalApp/generate_assets.sh b/android/TerminalApp/generate_assets.sh
index ff7444e..4001bfd 100755
--- a/android/TerminalApp/generate_assets.sh
+++ b/android/TerminalApp/generate_assets.sh
@@ -6,14 +6,15 @@
echo "image.raw can be built with packages/modules/Virtualization/build/debian/build.sh"
exit 1
fi
+image_raw_path=$(realpath $1)
pushd $(dirname $0) > /dev/null
tempdir=$(mktemp -d)
asset_dir=./assets/linux
mkdir -p ${asset_dir}
echo Copy files...
pushd ${tempdir} > /dev/null
-cp "$1" ${tempdir}
-tar czvS -f images.tar.gz $(basename $1)
+cp "${image_raw_path}" ${tempdir}
+tar czvS -f images.tar.gz $(basename ${image_raw_path})
popd > /dev/null
cp vm_config.json ${asset_dir}
mv ${tempdir}/images.tar.gz ${asset_dir}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt
index 4be291f..1b14ef2 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt
@@ -16,27 +16,49 @@
package com.android.virtualization.terminal
import android.os.Bundle
+import android.os.FileUtils
import android.widget.TextView
import android.widget.Toast
+import android.text.style.RelativeSizeSpan
+import android.text.Spannable
+import android.text.SpannableString
+import android.text.Spanned
+import android.text.format.Formatter
+import android.text.TextUtils
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import com.google.android.material.button.MaterialButton
import com.google.android.material.slider.Slider
+import java.util.regex.Matcher
+import java.util.regex.Pattern
+
class SettingsDiskResizeActivity : AppCompatActivity() {
+ private val maxDiskSize: Float = 256F
+ private val numberPattern: Pattern = Pattern.compile("[\\d]*[\\٫.,]?[\\d]+");
private var diskSize: Float = 104F
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.settings_disk_resize)
- val diskSizeText = findViewById<TextView>(R.id.settings_disk_resize_disk_size)
+ val diskSizeText = findViewById<TextView>(R.id.settings_disk_resize_resize_gb_assigned)
+ val diskMaxSizeText = findViewById<TextView>(R.id.settings_disk_resize_resize_gb_max)
+ diskMaxSizeText.text = getString(R.string.settings_disk_resize_resize_gb_max_format,
+ localizedFileSize(maxDiskSize));
+
val diskSizeSlider = findViewById<Slider>(R.id.settings_disk_resize_disk_size_slider)
+ diskSizeSlider.setValueTo(maxDiskSize)
val cancelButton = findViewById<MaterialButton>(R.id.settings_disk_resize_cancel_button)
val resizeButton = findViewById<MaterialButton>(R.id.settings_disk_resize_resize_button)
- diskSizeText.text = diskSize.toInt().toString()
diskSizeSlider.value = diskSize
+ diskSizeText.text = enlargeFontOfNumber(
+ getString(R.string.settings_disk_resize_resize_gb_assigned_format,
+ localizedFileSize(diskSize)))
diskSizeSlider.addOnChangeListener { _, value, _ ->
- diskSizeText.text = value.toInt().toString()
+ diskSizeText.text = enlargeFontOfNumber(
+ getString(R.string.settings_disk_resize_resize_gb_assigned_format,
+ localizedFileSize(value)))
cancelButton.isVisible = true
resizeButton.isVisible = true
}
@@ -54,4 +76,29 @@
.show()
}
}
+
+ fun localizedFileSize(sizeGb: Float): String {
+ // formatShortFileSize() uses SI unit (i.e. kB = 1000 bytes),
+ // so covert sizeGb with "GB" instead of "GIB".
+ val bytes = FileUtils.parseSize(sizeGb.toLong().toString() + "GB")
+ return Formatter.formatShortFileSize(this, bytes)
+ }
+
+ fun enlargeFontOfNumber(summary: CharSequence): CharSequence {
+ if (TextUtils.isEmpty(summary)) {
+ return ""
+ }
+
+ val matcher = numberPattern.matcher(summary);
+ if (matcher.find()) {
+ val spannableSummary = SpannableString(summary)
+ spannableSummary.setSpan(
+ RelativeSizeSpan(2f),
+ matcher.start(),
+ matcher.end(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
+ return spannableSummary
+ }
+ return summary
+ }
}
\ No newline at end of file
diff --git a/android/TerminalApp/res/layout/settings_disk_resize.xml b/android/TerminalApp/res/layout/settings_disk_resize.xml
index 3c09f52..f868b28 100644
--- a/android/TerminalApp/res/layout/settings_disk_resize.xml
+++ b/android/TerminalApp/res/layout/settings_disk_resize.xml
@@ -21,27 +21,19 @@
android:layout_height="match_parent">
<TextView
- android:id="@+id/settings_disk_resize_disk_size"
+ android:id="@+id/settings_disk_resize_resize_gb_assigned"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- android:textSize="36sp"
- app:layout_constraintLeft_toLeftOf="parent"
+ android:textSize="14sp"
+ app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@+id/settings_disk_resize_disk_size_slider"/>
<TextView
+ android:id="@+id/settings_disk_resize_resize_gb_max"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- android:text="@string/settings_disk_resize_resize_gb_assigned"
android:textSize="14sp"
- app:layout_constraintLeft_toRightOf="@+id/settings_disk_resize_disk_size"
- app:layout_constraintBottom_toTopOf="@+id/settings_disk_resize_disk_size_slider"/>
-
- <TextView
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="@string/settings_disk_resize_resize_gb_total"
- android:textSize="14sp"
- app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@+id/settings_disk_resize_disk_size_slider"/>
<com.google.android.material.slider.Slider
@@ -51,7 +43,6 @@
android:layout_marginBottom="36dp"
app:tickVisible="false"
android:valueFrom="0"
- android:valueTo="256"
android:stepSize="4"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
@@ -65,7 +56,7 @@
android:layout_marginHorizontal="8dp"
app:layout_constraintTop_toTopOf="@+id/settings_disk_resize_disk_size_slider"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintRight_toLeftOf="@+id/settings_disk_resize_resize_button" />
+ app:layout_constraintEnd_toStartOf="@+id/settings_disk_resize_resize_button" />
<com.google.android.material.button.MaterialButton
android:id="@+id/settings_disk_resize_resize_button"
@@ -76,6 +67,6 @@
android:layout_marginHorizontal="8dp"
app:layout_constraintTop_toTopOf="@+id/settings_disk_resize_disk_size_slider"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintRight_toRightOf="parent" />
+ app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/android/TerminalApp/res/values/strings.xml b/android/TerminalApp/res/values/strings.xml
index 7af9761..1cbaee8 100644
--- a/android/TerminalApp/res/values/strings.xml
+++ b/android/TerminalApp/res/values/strings.xml
@@ -36,10 +36,10 @@
<string name="settings_disk_resize_sub_title">Resize / Rootfs</string>
<!-- Toast message after new disk size is set. [CHAR LIMIT=none] -->
<string name="settings_disk_resize_resize_message">Disk size set</string>
- <!-- Settings menu option description for the current disk size, followed by a text box with the actual number [CHAR LIMIT=none] -->
- <string name="settings_disk_resize_resize_gb_assigned">GB Assigned</string>
- <!-- Settings menu option description for the maximum resizable disk size. [CHAR LIMIT=none] -->
- <string name="settings_disk_resize_resize_gb_total">256 GB total</string>
+ <!-- Settings menu option description format of the current disk size. [CHAR LIMIT=none] -->
+ <string name="settings_disk_resize_resize_gb_assigned_format"><xliff:g id="assigned_size" example="10GB">%1$s</xliff:g> assigned</string>
+ <!-- Settings menu option description format of the maximum resizable disk size. [CHAR LIMIT=none] -->
+ <string name="settings_disk_resize_resize_gb_max_format"><xliff:g id="max_size" example="256GB">%1$s</xliff:g> max</string>
<!-- Settings menu button to cancel disk resize. [CHAR LIMIT=32] -->
<string name="settings_disk_resize_resize_cancel">Cancel</string>
<!-- Settings menu button to apply change that requires to restart VM (abbrev of virtual machine). [CHAR LIMIT=64] -->
diff --git a/libs/libvmbase/src/entry.rs b/libs/libvmbase/src/entry.rs
index 99f28fc..f442a32 100644
--- a/libs/libvmbase/src/entry.rs
+++ b/libs/libvmbase/src/entry.rs
@@ -56,8 +56,7 @@
/// This is the entry point to the Rust code, called from the binary entry point in `entry.S`.
#[no_mangle]
extern "C" fn rust_entry(x0: u64, x1: u64, x2: u64, x3: u64) -> ! {
- // SAFETY: Only called once, from here, and inaccessible to client code.
- unsafe { heap::init() };
+ heap::init();
if try_console_init().is_err() {
// Don't panic (or log) here to avoid accessing the console.
diff --git a/libs/libvmbase/src/heap.rs b/libs/libvmbase/src/heap.rs
index 99c06aa..3a4e198 100644
--- a/libs/libvmbase/src/heap.rs
+++ b/libs/libvmbase/src/heap.rs
@@ -22,39 +22,78 @@
use core::ffi::c_void;
use core::mem;
use core::num::NonZeroUsize;
+use core::ops::Range;
use core::ptr;
use core::ptr::NonNull;
use buddy_system_allocator::LockedHeap;
+use spin::{
+ mutex::{SpinMutex, SpinMutexGuard},
+ Once,
+};
/// Configures the size of the global allocator.
#[macro_export]
macro_rules! configure_heap {
($len:expr) => {
- static mut __HEAP_ARRAY: [u8; $len] = [0; $len];
- #[export_name = "HEAP"]
- // SAFETY: HEAP will only be accessed once as mut, from init().
- static mut __HEAP: &'static mut [u8] = unsafe { &mut __HEAP_ARRAY };
+ static __HEAP: $crate::heap::HeapArray<{ $len }> = $crate::heap::HeapArray::new();
+ #[export_name = "get_heap"]
+ fn __get_heap() -> &'static mut [u8] {
+ __HEAP.get()
+ }
};
}
+/// An array to be used as a heap.
+///
+/// This should be stored in a static variable to have the appropriate lifetime.
+pub struct HeapArray<const SIZE: usize> {
+ array: SpinMutex<[u8; SIZE]>,
+}
+
+impl<const SIZE: usize> HeapArray<SIZE> {
+ /// Creates a new empty heap array.
+ #[allow(clippy::new_without_default)]
+ pub const fn new() -> Self {
+ Self { array: SpinMutex::new([0; SIZE]) }
+ }
+
+ /// Gets the heap as a slice.
+ ///
+ /// Panics if called more than once.
+ pub fn get(&self) -> &mut [u8] {
+ SpinMutexGuard::leak(self.array.try_lock().expect("Page heap was already taken"))
+ .as_mut_slice()
+ }
+}
+
extern "Rust" {
- /// Slice used by the global allocator, configured using configure_heap!().
- static mut HEAP: &'static mut [u8];
+ /// Gets slice used by the global allocator, configured using configure_heap!().
+ ///
+ /// Panics if called more than once.
+ fn get_heap() -> &'static mut [u8];
}
#[global_allocator]
static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::new();
+/// The range of addresses used for the heap.
+static HEAP_RANGE: Once<Range<usize>> = Once::new();
+
/// Initialize the global allocator.
///
-/// # Safety
-///
-/// Must be called no more than once.
-pub(crate) unsafe fn init() {
- // SAFETY: Nothing else accesses this memory, and we hand it over to the heap to manage and
- // never touch it again. The heap is locked, so there cannot be any races.
- let (start, size) = unsafe { (HEAP.as_mut_ptr() as usize, HEAP.len()) };
+/// Panics if called more than once.
+pub(crate) fn init() {
+ // SAFETY: This is in fact a safe Rust function.
+ let heap = unsafe { get_heap() };
+
+ HEAP_RANGE.call_once(|| {
+ let range = heap.as_ptr_range();
+ range.start as usize..range.end as usize
+ });
+
+ let start = heap.as_mut_ptr() as usize;
+ let size = heap.len();
let mut heap = HEAP_ALLOCATOR.lock();
// SAFETY: We are supplying a valid memory range, and we only do this once.
@@ -107,10 +146,9 @@
/// errors.
unsafe extern "C" fn free(ptr: *mut c_void) {
let Some(ptr) = NonNull::new(ptr) else { return };
- // SAFETY: The contents of the HEAP slice may change, but the address range never does.
- let heap_range = unsafe { HEAP.as_ptr_range() };
+ let heap_range = HEAP_RANGE.get().expect("free called before heap was initialised");
assert!(
- heap_range.contains(&(ptr.as_ptr() as *const u8)),
+ heap_range.contains(&(ptr.as_ptr() as usize)),
"free() called on a pointer that is not part of the HEAP: {ptr:?}"
);
// SAFETY: ptr is non-null and was allocated by allocate, which prepends a correctly aligned
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 2d55d66..ffcf338 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -36,6 +36,7 @@
import android.cts.statsdatom.lib.ReportUtils;
import com.android.compatibility.common.util.CddTest;
+import com.android.compatibility.common.util.PropertyUtil;
import com.android.compatibility.common.util.VsrTest;
import com.android.microdroid.test.common.ProcessUtil;
import com.android.microdroid.test.host.CommandRunner;
@@ -437,9 +438,8 @@
@VsrTest(requirements = {"VSR-7.1-001.008"})
public void UpgradedPackageIsAcceptedWithSecretkeeper() throws Exception {
// Preconditions
- assumeVmTypeSupported(true);
- assumeUpdatableVmSupported();
-
+ assumeVmTypeSupported(true); // Non-protected VMs may not support upgrades
+ ensureUpdatableVmSupported();
getDevice().uninstallPackage(PACKAGE_NAME);
getDevice().installPackage(findTestFile(APK_NAME), /* reinstall= */ true);
ensureProtectedMicrodroidBootsSuccessfully(INSTANCE_ID_FILE, INSTANCE_IMG);
@@ -1392,10 +1392,16 @@
&& device.doesFileExist("/sys/bus/platform/drivers/vfio-platform"));
}
- private void assumeUpdatableVmSupported() throws DeviceNotAvailableException {
- assumeTrue(
- "This test is only applicable if if Updatable VMs are supported",
- isUpdatableVmSupported());
+ private void ensureUpdatableVmSupported() throws DeviceNotAvailableException {
+ if (PropertyUtil.isVendorApiLevelAtLeast(getAndroidDevice(), 202504)) {
+ assertTrue(
+ "Missing Updatable VM support, have you declared Secretkeeper interface?",
+ isUpdatableVmSupported());
+ } else {
+ assumeTrue(
+ "Vendor API lower than 202504 may not support Updatable VM",
+ isUpdatableVmSupported());
+ }
}
private TestDevice getAndroidDevice() {