Set encrypted storage size in bytes
Change the API to deal in bytes not KiB. Internally, round up to a
size that the crosvm will accept.
Modify callers appropriately. (Deliberately using round numbers in
base 10 not base 2.)
In passing, totally gratuitously switch from sleep to pause; it's
cleaner.
Bug: 262449687
Test: atest MicrodroidTests
Change-Id: I539ceb7845e7345e7e4f5b5b849afaba21497087
diff --git a/javalib/api/system-current.txt b/javalib/api/system-current.txt
index fe9943d..3482fb5 100644
--- a/javalib/api/system-current.txt
+++ b/javalib/api/system-current.txt
@@ -58,7 +58,7 @@
public final class VirtualMachineConfig {
method @Nullable public String getApkPath();
method @NonNull public int getDebugLevel();
- method @IntRange(from=0) public long getEncryptedStorageKib();
+ method @IntRange(from=0) public long getEncryptedStorageBytes();
method @IntRange(from=0) public int getMemoryMib();
method @IntRange(from=1) public int getNumCpus();
method @Nullable public String getPayloadBinaryName();
@@ -75,7 +75,7 @@
method @NonNull public android.system.virtualmachine.VirtualMachineConfig build();
method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setApkPath(@NonNull String);
method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setDebugLevel(int);
- method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setEncryptedStorageKib(@IntRange(from=1) long);
+ method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setEncryptedStorageBytes(@IntRange(from=1) long);
method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setMemoryMib(@IntRange(from=1) int);
method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setNumCpus(@IntRange(from=1) int);
method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setPayloadBinaryName(@NonNull String);
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index ffb2e14..ba7174e 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -476,7 +476,7 @@
try {
service.initializeWritablePartition(
ParcelFileDescriptor.open(vm.mEncryptedStoreFilePath, MODE_READ_WRITE),
- config.getEncryptedStorageKib() * 1024L,
+ config.getEncryptedStorageBytes(),
PartitionType.ENCRYPTEDSTORE);
} catch (FileNotFoundException e) {
throw new VirtualMachineException("encrypted storage image missing", e);
@@ -919,7 +919,7 @@
* Stops this virtual machine. Stopping a virtual machine is like pulling the plug on a real
* computer; the machine halts immediately. Software running on the virtual machine is not
* notified of the event. Writes to {@linkplain
- * VirtualMachineConfig.Builder#setEncryptedStorageKib encrypted storage} might not be
+ * VirtualMachineConfig.Builder#setEncryptedStorageBytes encrypted storage} might not be
* persisted, and the instance might be left in an inconsistent state.
*
* <p>For a graceful shutdown, you could request the payload to call {@code exit()}, e.g. via a
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index b358f9e..18dd23f 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -60,7 +60,7 @@
private static final String[] EMPTY_STRING_ARRAY = {};
// These define the schema of the config file persisted on disk.
- private static final int VERSION = 4;
+ private static final int VERSION = 5;
private static final String KEY_VERSION = "version";
private static final String KEY_PACKAGENAME = "packageName";
private static final String KEY_APKPATH = "apkPath";
@@ -70,7 +70,7 @@
private static final String KEY_PROTECTED_VM = "protectedVm";
private static final String KEY_MEMORY_MIB = "memoryMib";
private static final String KEY_NUM_CPUS = "numCpus";
- private static final String KEY_ENCRYPTED_STORAGE_KIB = "encryptedStorageKib";
+ private static final String KEY_ENCRYPTED_STORAGE_BYTES = "encryptedStorageBytes";
private static final String KEY_VM_OUTPUT_CAPTURED = "vmOutputCaptured";
/** @hide */
@@ -128,8 +128,8 @@
/** Name of the payload binary file within the APK that will be executed within the VM. */
@Nullable private final String mPayloadBinaryName;
- /** The size of storage in KiB. 0 indicates that encryptedStorage is not required */
- private final long mEncryptedStorageKib;
+ /** The size of storage in bytes. 0 indicates that encryptedStorage is not required */
+ private final long mEncryptedStorageBytes;
/** Whether the app can read console and log output. */
private final boolean mVmOutputCaptured;
@@ -143,7 +143,7 @@
boolean protectedVm,
int memoryMib,
int numCpus,
- long encryptedStorageKib,
+ long encryptedStorageBytes,
boolean vmOutputCaptured) {
// This is only called from Builder.build(); the builder handles parameter validation.
mPackageName = packageName;
@@ -154,7 +154,7 @@
mProtectedVm = protectedVm;
mMemoryMib = memoryMib;
mNumCpus = numCpus;
- mEncryptedStorageKib = encryptedStorageKib;
+ mEncryptedStorageBytes = encryptedStorageBytes;
mVmOutputCaptured = vmOutputCaptured;
}
@@ -225,9 +225,9 @@
builder.setMemoryMib(memoryMib);
}
builder.setNumCpus(b.getInt(KEY_NUM_CPUS));
- long encryptedStorageKib = b.getLong(KEY_ENCRYPTED_STORAGE_KIB);
- if (encryptedStorageKib != 0) {
- builder.setEncryptedStorageKib(encryptedStorageKib);
+ long encryptedStorageBytes = b.getLong(KEY_ENCRYPTED_STORAGE_BYTES);
+ if (encryptedStorageBytes != 0) {
+ builder.setEncryptedStorageBytes(encryptedStorageBytes);
}
builder.setVmOutputCaptured(b.getBoolean(KEY_VM_OUTPUT_CAPTURED));
@@ -261,8 +261,8 @@
if (mMemoryMib > 0) {
b.putInt(KEY_MEMORY_MIB, mMemoryMib);
}
- if (mEncryptedStorageKib > 0) {
- b.putLong(KEY_ENCRYPTED_STORAGE_KIB, mEncryptedStorageKib);
+ if (mEncryptedStorageBytes > 0) {
+ b.putLong(KEY_ENCRYPTED_STORAGE_BYTES, mEncryptedStorageBytes);
}
b.putBoolean(KEY_VM_OUTPUT_CAPTURED, mVmOutputCaptured);
b.writeToStream(output);
@@ -357,26 +357,26 @@
*/
@SystemApi
public boolean isEncryptedStorageEnabled() {
- return mEncryptedStorageKib > 0;
+ return mEncryptedStorageBytes > 0;
}
/**
- * Returns the size of encrypted storage (in KiB) available in the VM, or 0 if encrypted storage
- * is not enabled
+ * Returns the size of encrypted storage (in bytes) available in the VM, or 0 if encrypted
+ * storage is not enabled
*
* @hide
*/
@SystemApi
@IntRange(from = 0)
- public long getEncryptedStorageKib() {
- return mEncryptedStorageKib;
+ public long getEncryptedStorageBytes() {
+ return mEncryptedStorageBytes;
}
/**
* Returns whether the app can read the VM console or log output. If not, the VM output is
* automatically forwarded to the host logcat.
*
- * @see #setVmOutputCaptured
+ * @see Builder#setVmOutputCaptured
* @hide
*/
@SystemApi
@@ -398,7 +398,7 @@
public boolean isCompatibleWith(@NonNull VirtualMachineConfig other) {
return this.mDebugLevel == other.mDebugLevel
&& this.mProtectedVm == other.mProtectedVm
- && this.mEncryptedStorageKib == other.mEncryptedStorageKib
+ && this.mEncryptedStorageBytes == other.mEncryptedStorageBytes
&& this.mVmOutputCaptured == other.mVmOutputCaptured
&& Objects.equals(this.mPayloadConfigPath, other.mPayloadConfigPath)
&& Objects.equals(this.mPayloadBinaryName, other.mPayloadBinaryName)
@@ -476,7 +476,7 @@
private boolean mProtectedVmSet;
private int mMemoryMib;
private int mNumCpus = 1;
- private long mEncryptedStorageKib;
+ private long mEncryptedStorageBytes;
private boolean mVmOutputCaptured = false;
/**
@@ -545,7 +545,7 @@
mProtectedVm,
mMemoryMib,
mNumCpus,
- mEncryptedStorageKib,
+ mEncryptedStorageBytes,
mVmOutputCaptured);
}
@@ -699,8 +699,8 @@
}
/**
- * Sets the size (in KiB) of encrypted storage available to the VM. If not set, no encrypted
- * storage is provided.
+ * Sets the size (in bytes) of encrypted storage available to the VM. If not set, no
+ * encrypted storage is provided.
*
* <p>The storage is encrypted with a key deterministically derived from the VM identity
*
@@ -716,11 +716,11 @@
*/
@SystemApi
@NonNull
- public Builder setEncryptedStorageKib(@IntRange(from = 1) long encryptedStorageKib) {
- if (encryptedStorageKib <= 0) {
+ public Builder setEncryptedStorageBytes(@IntRange(from = 1) long encryptedStorageBytes) {
+ if (encryptedStorageBytes <= 0) {
throw new IllegalArgumentException("Encrypted Storage size must be positive");
}
- mEncryptedStorageKib = encryptedStorageKib;
+ mEncryptedStorageBytes = encryptedStorageBytes;
return this;
}
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 5cd0cb1..512f136 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -351,7 +351,7 @@
assertThat(minimal.getPayloadConfigPath()).isNull();
assertThat(minimal.isProtectedVm()).isEqualTo(isProtectedVm());
assertThat(minimal.isEncryptedStorageEnabled()).isFalse();
- assertThat(minimal.getEncryptedStorageKib()).isEqualTo(0);
+ assertThat(minimal.getEncryptedStorageBytes()).isEqualTo(0);
assertThat(minimal.isVmOutputCaptured()).isEqualTo(false);
// Maximal has everything that can be set to some non-default value. (And has different
@@ -365,7 +365,7 @@
.setNumCpus(maxCpus)
.setDebugLevel(DEBUG_LEVEL_FULL)
.setMemoryMib(42)
- .setEncryptedStorageKib(1024)
+ .setEncryptedStorageBytes(1_000_000)
.setVmOutputCaptured(true);
VirtualMachineConfig maximal = maximalBuilder.build();
@@ -377,7 +377,7 @@
assertThat(maximal.getPayloadConfigPath()).isEqualTo("config/path");
assertThat(maximal.isProtectedVm()).isEqualTo(isProtectedVm());
assertThat(maximal.isEncryptedStorageEnabled()).isTrue();
- assertThat(maximal.getEncryptedStorageKib()).isEqualTo(1024);
+ assertThat(maximal.getEncryptedStorageBytes()).isEqualTo(1_000_000);
assertThat(maximal.isVmOutputCaptured()).isEqualTo(true);
assertThat(minimal.isCompatibleWith(maximal)).isFalse();
@@ -405,7 +405,7 @@
assertThrows(IllegalArgumentException.class, () -> builder.setDebugLevel(-1));
assertThrows(IllegalArgumentException.class, () -> builder.setMemoryMib(0));
assertThrows(IllegalArgumentException.class, () -> builder.setNumCpus(0));
- assertThrows(IllegalArgumentException.class, () -> builder.setEncryptedStorageKib(0));
+ assertThrows(IllegalArgumentException.class, () -> builder.setEncryptedStorageBytes(0));
// Consistency checks enforced at build time.
Exception e;
@@ -457,7 +457,7 @@
// Changes that are currently incompatible for ease of implementation, but this might change
// in the future.
assertConfigCompatible(baseline, newBaselineBuilder().setApkPath("/different")).isFalse();
- assertConfigCompatible(baseline, newBaselineBuilder().setEncryptedStorageKib(100))
+ assertConfigCompatible(baseline, newBaselineBuilder().setEncryptedStorageBytes(100_000))
.isFalse();
VirtualMachineConfig.Builder debuggableBuilder =
@@ -1230,7 +1230,7 @@
.setPayloadBinaryName("MicrodroidTestNativeLib.so")
.setDebugLevel(DEBUG_LEVEL_FULL);
if (encryptedStoreEnabled) {
- builder.setEncryptedStorageKib(4096);
+ builder.setEncryptedStorageBytes(4_000_000);
}
VirtualMachineConfig config = builder.build();
String vmNameOrig = "test_vm_orig";
@@ -1285,7 +1285,7 @@
newVmConfigBuilder()
.setPayloadBinaryName("MicrodroidTestNativeLib.so")
.setMemoryMib(minMemoryRequired())
- .setEncryptedStorageKib(4096)
+ .setEncryptedStorageBytes(4_000_000)
.setDebugLevel(DEBUG_LEVEL_FULL)
.build();
VirtualMachine vm = forceCreateNewVirtualMachine("test_vm", config);
@@ -1332,7 +1332,7 @@
newVmConfigBuilder()
.setPayloadBinaryName("MicrodroidTestNativeLib.so")
.setMemoryMib(minMemoryRequired())
- .setEncryptedStorageKib(4096)
+ .setEncryptedStorageBytes(4_000_000)
.setDebugLevel(DEBUG_LEVEL_FULL)
.build();
VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_a", config);
diff --git a/tests/testapk/src/native/idlebinary.cpp b/tests/testapk/src/native/idlebinary.cpp
index 9499d94..366120c 100644
--- a/tests/testapk/src/native/idlebinary.cpp
+++ b/tests/testapk/src/native/idlebinary.cpp
@@ -20,6 +20,6 @@
extern "C" int AVmPayload_main() {
// do nothing; just leave it alive. good night.
for (;;) {
- sleep(1000);
+ pause();
}
}
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index ca42999..cab7c71 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -90,6 +90,9 @@
const UNFORMATTED_STORAGE_MAGIC: &str = "UNFORMATTED-STORAGE";
+/// crosvm requires all partitions to be a multiple of 4KiB.
+const PARTITION_GRANULARITY_BYTES: u64 = 4096;
+
lazy_static! {
pub static ref GLOBAL_SERVICE: Strong<dyn IVirtualizationServiceInternal> =
wait_for_interface(BINDER_SERVICE_IDENTIFIER)
@@ -172,16 +175,17 @@
fn initializeWritablePartition(
&self,
image_fd: &ParcelFileDescriptor,
- size: i64,
+ size_bytes: i64,
partition_type: PartitionType,
) -> binder::Result<()> {
check_manage_access()?;
- let size = size.try_into().map_err(|e| {
+ let size_bytes = size_bytes.try_into().map_err(|e| {
Status::new_exception_str(
ExceptionCode::ILLEGAL_ARGUMENT,
- Some(format!("Invalid size {}: {:?}", size, e)),
+ Some(format!("Invalid size {}: {:?}", size_bytes, e)),
)
})?;
+ let size_bytes = round_up(size_bytes, PARTITION_GRANULARITY_BYTES);
let image = clone_file(image_fd)?;
// initialize the file. Any data in the file will be erased.
image.set_len(0).map_err(|e| {
@@ -190,7 +194,7 @@
Some(format!("Failed to reset a file: {:?}", e)),
)
})?;
- let mut part = QcowFile::new(image, size).map_err(|e| {
+ let mut part = QcowFile::new(image, size_bytes).map_err(|e| {
Status::new_service_specific_error_str(
-1,
Some(format!("Failed to create QCOW2 image: {:?}", e)),
@@ -460,6 +464,15 @@
File::create(ramdump_path).context(format!("Failed to create ramdump file {:?}", &ramdump_path))
}
+fn round_up(input: u64, granularity: u64) -> u64 {
+ if granularity == 0 {
+ return input;
+ }
+ // If the input is absurdly large we round down instead of up; it's going to fail anyway.
+ let result = input.checked_add(granularity - 1).unwrap_or(input);
+ (result / granularity) * granularity
+}
+
/// Given the configuration for a disk image, assembles the `DiskFile` to pass to crosvm.
///
/// This may involve assembling a composite disk from a set of partition images.
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
index fc4c9e7..d72d5ac 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
@@ -37,7 +37,7 @@
* The file must be open with both read and write permissions, and should be a new empty file.
*/
void initializeWritablePartition(
- in ParcelFileDescriptor imageFd, long size, PartitionType type);
+ in ParcelFileDescriptor imageFd, long sizeBytes, PartitionType type);
/**
* Create or update an idsig file that digests the given APK file. The idsig file follows the