Add support for encrypted storage expansion
Capability to configure the encrypted storage size
Partition resizing to the required size upon boot
New unit tests to validate this functionality
Bug: 381067202
Test: atest MicrodroidTests
Change-Id: I6f5737ee601e7c511bdd316b180bf50e3d102ab1
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 e4a3ff6..6fd0885 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -129,6 +129,7 @@
private static final String TEST_APP_PACKAGE_NAME = "com.android.microdroid.test";
private static final String VM_ATTESTATION_PAYLOAD_PATH = "libvm_attestation_test_payload.so";
private static final String VM_ATTESTATION_MESSAGE = "Hello RKP from AVF!";
+ private static final long TOLERANCE_BYTES = 400_000;
private static final int ENCRYPTED_STORAGE_BYTES = 4_000_000;
private static final String RELAXED_ROLLBACK_PROTECTION_SCHEME_TEST_PACKAGE_NAME =
@@ -741,11 +742,6 @@
// so in the API spec.
assertConfigCompatible(baseline, newBaselineBuilder().setApkPath("/different")).isTrue();
- // Changes that are currently incompatible for ease of implementation, but this might change
- // in the future.
- assertConfigCompatible(baseline, newBaselineBuilder().setEncryptedStorageBytes(100_000))
- .isFalse();
-
VirtualMachineConfig.Builder debuggableBuilder =
newBaselineBuilder().setDebugLevel(DEBUG_LEVEL_FULL);
VirtualMachineConfig debuggable = debuggableBuilder.build();
@@ -1866,6 +1862,169 @@
assertThat(testResults.mFileContent).isEqualTo(EXAMPLE_STRING);
}
+ @Test
+ @CddTest
+ public void encryptedStorageSupportsExpansion() throws Exception {
+ assumeSupportedDevice();
+
+ VirtualMachineConfig config =
+ newVmConfigBuilderWithPayloadBinary("MicrodroidTestNativeLib.so")
+ .setEncryptedStorageBytes(ENCRYPTED_STORAGE_BYTES)
+ .build();
+
+ VirtualMachine vm = forceCreateNewVirtualMachine("test_vm", config);
+ TestResults testResults =
+ runVmTestService(
+ TAG,
+ vm,
+ (ts, tr) -> {
+ tr.mEncryptedStorageSize = ts.getEncryptedStorageSize();
+ });
+ testResults.assertNoException();
+ assertThat(testResults.mEncryptedStorageSize)
+ .isWithin(TOLERANCE_BYTES)
+ .of(ENCRYPTED_STORAGE_BYTES);
+
+ // Re-run the VM with more storage size & verify the file persisted.
+ // Note, the previous `runVmTestService` stopped the VM
+ config = newVmConfigBuilderWithPayloadBinary("MicrodroidTestNativeLib.so")
+ .setEncryptedStorageBytes(ENCRYPTED_STORAGE_BYTES * 2)
+ .build();
+ vm.setConfig(config);
+ assertThat(vm.getConfig().getEncryptedStorageBytes())
+ .isEqualTo(ENCRYPTED_STORAGE_BYTES * 2);
+
+ testResults =
+ runVmTestService(
+ TAG,
+ vm,
+ (ts, tr) -> {
+ tr.mEncryptedStorageSize = ts.getEncryptedStorageSize();
+ });
+ testResults.assertNoException();
+ assertThat(testResults.mEncryptedStorageSize)
+ .isWithin(TOLERANCE_BYTES)
+ .of(ENCRYPTED_STORAGE_BYTES * 2);
+ }
+
+ @Test
+ @CddTest
+ public void encryptedStorageExpansionIsPersistent() throws Exception {
+ assumeSupportedDevice();
+
+ VirtualMachineConfig config =
+ newVmConfigBuilderWithPayloadBinary("MicrodroidTestNativeLib.so")
+ .setEncryptedStorageBytes(ENCRYPTED_STORAGE_BYTES)
+ .build();
+
+ VirtualMachine vm = forceCreateNewVirtualMachine("test_vm", config);
+ TestResults testResults =
+ runVmTestService(
+ TAG,
+ vm,
+ (ts, tr) -> {
+ ts.writeToFile(
+ /* content= */ EXAMPLE_STRING,
+ /* path= */ "/mnt/encryptedstore/test_file");
+ });
+ testResults.assertNoException();
+
+ // Re-run the VM with more storage size & verify the file persisted.
+ // Note, the previous `runVmTestService` stopped the VM
+ config = newVmConfigBuilderWithPayloadBinary("MicrodroidTestNativeLib.so")
+ .setEncryptedStorageBytes(ENCRYPTED_STORAGE_BYTES * 2)
+ .build();
+ vm.setConfig(config);
+
+ testResults =
+ runVmTestService(
+ TAG,
+ vm,
+ (ts, tr) -> {
+ tr.mFileContent = ts.readFromFile("/mnt/encryptedstore/test_file");
+ });
+ testResults.assertNoException();
+ assertThat(testResults.mFileContent).isEqualTo(EXAMPLE_STRING);
+ }
+
+ @Test
+ @CddTest
+ public void encryptedStorageSizeUnchanged() throws Exception {
+ assumeSupportedDevice();
+
+ VirtualMachineConfig config =
+ newVmConfigBuilderWithPayloadBinary("MicrodroidTestNativeLib.so")
+ .setEncryptedStorageBytes(ENCRYPTED_STORAGE_BYTES)
+ .build();
+
+ VirtualMachine vm = forceCreateNewVirtualMachine("test_vm", config);
+ TestResults testResults =
+ runVmTestService(
+ TAG,
+ vm,
+ (ts, tr) -> {
+ tr.mEncryptedStorageSize = ts.getEncryptedStorageSize();
+ });
+ testResults.assertNoException();
+ assertThat(testResults.mEncryptedStorageSize)
+ .isWithin(TOLERANCE_BYTES)
+ .of(ENCRYPTED_STORAGE_BYTES);
+
+ // Re-run the VM with more storage size & verify the file persisted.
+ // Note, the previous `runVmTestService` stopped the VM
+ config = newVmConfigBuilderWithPayloadBinary("MicrodroidTestNativeLib.so")
+ .setEncryptedStorageBytes(ENCRYPTED_STORAGE_BYTES)
+ .build();
+ vm.setConfig(config);
+ assertThat(vm.getConfig().getEncryptedStorageBytes())
+ .isEqualTo(ENCRYPTED_STORAGE_BYTES);
+
+ testResults =
+ runVmTestService(
+ TAG,
+ vm,
+ (ts, tr) -> {
+ tr.mEncryptedStorageSize = ts.getEncryptedStorageSize();
+ });
+ testResults.assertNoException();
+ assertThat(testResults.mEncryptedStorageSize)
+ .isWithin(TOLERANCE_BYTES)
+ .of(ENCRYPTED_STORAGE_BYTES);
+ }
+
+ @Test
+ @CddTest
+ public void encryptedStorageShrinkFails() throws Exception {
+ assumeSupportedDevice();
+
+ VirtualMachineConfig config =
+ newVmConfigBuilderWithPayloadBinary("MicrodroidTestNativeLib.so")
+ .setEncryptedStorageBytes(ENCRYPTED_STORAGE_BYTES)
+ .build();
+
+ VirtualMachine vm = forceCreateNewVirtualMachine("test_vm", config);
+ TestResults testResults =
+ runVmTestService(
+ TAG,
+ vm,
+ (ts, tr) -> {
+ tr.mEncryptedStorageSize = ts.getEncryptedStorageSize();
+ });
+ testResults.assertNoException();
+ assertThat(testResults.mEncryptedStorageSize)
+ .isWithin(TOLERANCE_BYTES)
+ .of(ENCRYPTED_STORAGE_BYTES);
+
+ // Re-run the VM with more storage size & verify the file persisted.
+ // Note, the previous `runVmTestService` stopped the VM
+ VirtualMachineConfig newConfig =
+ newVmConfigBuilderWithPayloadBinary("MicrodroidTestNativeLib.so")
+ .setEncryptedStorageBytes(ENCRYPTED_STORAGE_BYTES / 2)
+ .build();
+ assertThrowsVmExceptionContaining(
+ () -> vm.setConfig(newConfig), "incompatible config");
+ }
+
private boolean deviceCapableOfProtectedVm() {
int capabilities = getVirtualMachineManager().getCapabilities();
if ((capabilities & CAPABILITY_PROTECTED_VM) != 0) {
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index 355cfb1..13eafce 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -29,6 +29,7 @@
#include <stdint.h>
#include <stdio.h>
#include <sys/capability.h>
+#include <sys/statvfs.h>
#include <sys/system_properties.h>
#ifdef __MICRODROID_TEST_PAYLOAD_USES_LIBICU__
#include <unicode/uchar.h>
@@ -232,6 +233,23 @@
return ScopedAStatus::ok();
}
+ ScopedAStatus getEncryptedStorageSize(int64_t *out) override {
+ const char* path_c = AVmPayload_getEncryptedStoragePath();
+ if (path_c == nullptr) {
+ *out = 0;
+ return ScopedAStatus::ok();
+ }
+ struct statvfs buffer;
+ if (statvfs(path_c, &buffer) != 0) {
+ std::string msg = "statvfs " + std::string(path_c) + " failed : " +
+ std::strerror(errno);
+ return ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC,
+ msg.c_str());
+ }
+ *out= buffer.f_blocks * buffer.f_frsize;
+ return ScopedAStatus::ok();
+ }
+
ScopedAStatus getEffectiveCapabilities(std::vector<std::string>* out) override {
if (out == nullptr) {
return ScopedAStatus::ok();
diff --git a/tests/testapk/src/native/testbinary.rs b/tests/testapk/src/native/testbinary.rs
index 3900cad..6a7d96e 100644
--- a/tests/testapk/src/native/testbinary.rs
+++ b/tests/testapk/src/native/testbinary.rs
@@ -87,6 +87,10 @@
// Everything below here is unimplemented. Implementations may be added as needed.
+ fn getEncryptedStorageSize(&self) -> BinderResult<i64> {
+ unimplemented()
+ }
+
fn readProperty(&self, _: &str) -> BinderResult<String> {
unimplemented()
}