Merge "Test for checking persistence of encryptedstore"
diff --git a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
index c936e1b..7ee1f01 100644
--- a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
+++ b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
@@ -49,4 +49,10 @@
 
     /** Returns a mask of effective capabilities that the process running the payload binary has. */
     String[] getEffectiveCapabilities();
+
+    /* write the content into the specified file. */
+    void writeToFile(String content, String path);
+
+    /* get the content of the specified file. */
+    String readFromFile(String path);
 }
diff --git a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
index 9aed34d..536f663 100644
--- a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
+++ b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
@@ -126,6 +126,12 @@
         }
     }
 
+    protected enum EncryptedStoreOperation {
+        NONE,
+        READ,
+        WRITE,
+    }
+
     public abstract static class VmEventListener implements VirtualMachineCallback {
         private ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
         private OptionalLong mVcpuStartedNanoTime = OptionalLong.empty();
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 160b679..897879b 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -113,6 +113,7 @@
 
     private static final int MIN_MEM_ARM64 = 150;
     private static final int MIN_MEM_X86_64 = 196;
+    private static final String EXAMPLE_STRING = "Literally any string!! :)";
 
     @Test
     @CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-1"})
@@ -1142,6 +1143,29 @@
         assertThat(testResults.mEffectiveCapabilities).isEmpty();
     }
 
+    @Test
+    @CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-1"})
+    public void encryptedStorageIsPersistent() throws Exception {
+        assumeSupportedKernel();
+
+        VirtualMachineConfig config =
+                newVmConfigBuilder()
+                        .setPayloadBinaryPath("MicrodroidTestNativeLib.so")
+                        .setMemoryMib(minMemoryRequired())
+                        .setEncryptedStorageKib(4096)
+                        .setDebugLevel(DEBUG_LEVEL_FULL)
+                        .build();
+        VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_a", config);
+        TestResults testResults = runVmTestService(vm, EncryptedStoreOperation.WRITE);
+        assertThat(testResults.mException).isNull();
+
+        // Re-run the same VM & verify the file persisted. Note, the previous `runVmTestService`
+        // stopped the VM
+        testResults = runVmTestService(vm, EncryptedStoreOperation.READ);
+        assertThat(testResults.mException).isNull();
+        assertThat(testResults.mFileContent).isEqualTo(EXAMPLE_STRING);
+    }
+
     private void assertFileContentsAreEqualInTwoVms(String fileName, String vmName1, String vmName2)
             throws IOException {
         File file1 = getVmFile(vmName1, fileName);
@@ -1197,9 +1221,15 @@
         String mApkContentsPath;
         String mEncryptedStoragePath;
         String[] mEffectiveCapabilities;
+        String mFileContent;
     }
 
     private TestResults runVmTestService(VirtualMachine vm) throws Exception {
+        return runVmTestService(vm, EncryptedStoreOperation.NONE);
+    }
+
+    private TestResults runVmTestService(VirtualMachine vm, EncryptedStoreOperation mode)
+            throws Exception {
         CompletableFuture<Boolean> payloadStarted = new CompletableFuture<>();
         CompletableFuture<Boolean> payloadReady = new CompletableFuture<>();
         TestResults testResults = new TestResults();
@@ -1222,6 +1252,14 @@
                                     testService.getEncryptedStoragePath();
                             testResults.mEffectiveCapabilities =
                                     testService.getEffectiveCapabilities();
+                            if (mode == EncryptedStoreOperation.WRITE) {
+                                testService.writeToFile(
+                                        /*content*/ EXAMPLE_STRING,
+                                        /*path*/ "/mnt/encryptedstore/test_file");
+                            } else if (mode == EncryptedStoreOperation.READ) {
+                                testResults.mFileContent =
+                                        testService.readFromFile("/mnt/encryptedstore/test_file");
+                            }
                         } catch (Exception e) {
                             testResults.mException = e;
                         }
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index da408e4..4ba502a 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -232,6 +232,29 @@
                 return ScopedAStatus::fromServiceSpecificErrorWithMessage(-1, message.c_str());
             }
         }
+
+        ScopedAStatus writeToFile(const std::string& content, const std::string& path) override {
+            if (!android::base::WriteStringToFile(content, path)) {
+                std::string msg = "Failed to write " + content + " to file " + path +
+                        ". Errono: " + std::to_string(errno);
+                return ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC,
+                                                                   msg.c_str());
+            }
+            // TODO(b/264520098): Remove sync() once TestService supports quit() method
+            // and Microdroid manager flushes filesystem caches on shutdown.
+            sync();
+            return ScopedAStatus::ok();
+        }
+
+        ScopedAStatus readFromFile(const std::string& path, std::string* out) override {
+            if (!android::base::ReadFileToString(path, out)) {
+                std::string msg =
+                        "Failed to read " + path + " to string. Errono: " + std::to_string(errno);
+                return ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC,
+                                                                   msg.c_str());
+            }
+            return ScopedAStatus::ok();
+        }
     };
     auto testService = ndk::SharedRefBase::make<TestService>();