Merge "pvmfw: Use vmbase::logger and the log API"
diff --git a/apkdmverity/src/main.rs b/apkdmverity/src/main.rs
index 23457c4..a69b583 100644
--- a/apkdmverity/src/main.rs
+++ b/apkdmverity/src/main.rs
@@ -93,7 +93,7 @@
             bail!("The size of {:?} is not multiple of {}.", &apk, BLOCK_SIZE)
         }
         (
-            loopdevice::attach(&apk, 0, apk_size, /*direct_io*/ true)
+            loopdevice::attach(&apk, 0, apk_size, /*direct_io*/ true, /*writable*/ false)
                 .context("Failed to attach APK to a loop device")?,
             apk_size,
         )
@@ -108,8 +108,9 @@
     // Due to unknown reason(b/191344832), we can't enable "direct IO" for the IDSIG file (backing
     // the hash). For now we don't use "direct IO" but it seems OK since the IDSIG file is very
     // small and the benefit of direct-IO would be negliable.
-    let hash_device = loopdevice::attach(&idsig, offset, size, /*direct_io*/ false)
-        .context("Failed to attach idsig to a loop device")?;
+    let hash_device =
+        loopdevice::attach(&idsig, offset, size, /*direct_io*/ false, /*writable*/ false)
+            .context("Failed to attach idsig to a loop device")?;
 
     // Build a dm-verity target spec from the information from the idsig file. The apk and the
     // idsig files are used as the data device and the hash device, respectively.
@@ -324,9 +325,19 @@
         // already a block device, `enable_verity` uses the block device as it is. The detatching
         // of the data device is done in the scopeguard for the return value of `enable_verity`
         // below. Only the idsig_loop_device needs detatching.
-        let apk_loop_device = loopdevice::attach(&apk_path, 0, apk_size, true).unwrap();
+        let apk_loop_device = loopdevice::attach(
+            &apk_path, 0, apk_size, /*direct_io*/ true, /*writable*/ false,
+        )
+        .unwrap();
         let idsig_loop_device = scopeguard::guard(
-            loopdevice::attach(&idsig_path, 0, idsig_size, false).unwrap(),
+            loopdevice::attach(
+                &idsig_path,
+                0,
+                idsig_size,
+                /*direct_io*/ false,
+                /*writable*/ false,
+            )
+            .unwrap(),
             |dev| loopdevice::detach(dev).unwrap(),
         );
 
diff --git a/authfs/TEST_MAPPING b/authfs/TEST_MAPPING
index 3c84b76..ab6111b 100644
--- a/authfs/TEST_MAPPING
+++ b/authfs/TEST_MAPPING
@@ -6,5 +6,10 @@
     {
       "name": "AuthFsHostTest"
     }
+  ],
+  "avf-postsubmit": [
+    {
+      "name": "AuthFsBenchmarks"
+    }
   ]
 }
diff --git a/authfs/fd_server/src/main.rs b/authfs/fd_server/src/main.rs
index 93a788b..f1fffdd 100644
--- a/authfs/fd_server/src/main.rs
+++ b/authfs/fd_server/src/main.rs
@@ -29,7 +29,7 @@
 use clap::Parser;
 use log::debug;
 use nix::sys::stat::{umask, Mode};
-use rpcbinder::run_rpc_server;
+use rpcbinder::run_vsock_rpc_server;
 use std::collections::BTreeMap;
 use std::fs::File;
 use std::os::unix::io::{FromRawFd, OwnedFd};
@@ -137,7 +137,7 @@
 
     let service = FdService::new_binder(fd_pool).as_binder();
     debug!("fd_server is starting as a rpc service.");
-    let retval = run_rpc_server(service, RPC_SERVICE_PORT, || {
+    let retval = run_vsock_rpc_server(service, RPC_SERVICE_PORT, || {
         debug!("fd_server is ready");
         // Close the ready-fd if we were given one to signal our readiness.
         drop(ready_fd.take());
diff --git a/authfs/tests/Android.bp b/authfs/tests/Android.bp
deleted file mode 100644
index 0177254..0000000
--- a/authfs/tests/Android.bp
+++ /dev/null
@@ -1,55 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-java_test_host {
-    name: "AuthFsHostTest",
-    srcs: ["java/**/*.java"],
-    libs: [
-        "tradefed",
-        "compatibility-tradefed",
-        "compatibility-host-util",
-    ],
-    static_libs: [
-        "MicrodroidHostTestHelper",
-    ],
-    test_suites: ["general-tests"],
-    data_device_bins_first: [
-        "open_then_run",
-        "fsverity",
-    ],
-    per_testcase_directory: true,
-    data: [
-        ":authfs_test_files",
-        ":CtsApkVerityTestPrebuiltFiles",
-        ":MicrodroidTestApp",
-        ":measure_io",
-    ],
-}
-
-rust_test {
-    name: "open_then_run",
-    crate_name: "open_then_run",
-    srcs: ["open_then_run.rs"],
-    edition: "2021",
-    rustlibs: [
-        "libandroid_logger",
-        "libanyhow",
-        "libclap",
-        "libcommand_fds",
-        "liblibc",
-        "liblog_rust",
-    ],
-    test_suites: ["general-tests"],
-    test_harness: false,
-}
-
-cc_binary {
-    name: "measure_io",
-    srcs: [
-        "measure_io.cpp",
-    ],
-    shared_libs: [
-        "libbase",
-    ],
-}
diff --git a/authfs/tests/benchmarks/Android.bp b/authfs/tests/benchmarks/Android.bp
new file mode 100644
index 0000000..b198328
--- /dev/null
+++ b/authfs/tests/benchmarks/Android.bp
@@ -0,0 +1,37 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_test_host {
+    name: "AuthFsBenchmarks",
+    srcs: ["src/java/com/android/fs/benchmarks/*.java"],
+    libs: [
+        "tradefed",
+    ],
+    static_libs: [
+        "AuthFsHostTestCommon",
+        "MicrodroidHostTestHelper",
+    ],
+    test_suites: ["general-tests"],
+    data_device_bins_first: [
+        "open_then_run",
+        "fsverity",
+    ],
+    per_testcase_directory: true,
+    data: [
+        ":authfs_test_files",
+        ":CtsApkVerityTestPrebuiltFiles",
+        ":MicrodroidTestApp",
+        ":measure_io",
+    ],
+}
+
+cc_binary {
+    name: "measure_io",
+    srcs: [
+        "src/measure_io.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+}
diff --git a/authfs/tests/benchmarks/AndroidTest.xml b/authfs/tests/benchmarks/AndroidTest.xml
new file mode 100644
index 0000000..7ca3a80
--- /dev/null
+++ b/authfs/tests/benchmarks/AndroidTest.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+
+<configuration description="Config for authfs tests">
+    <!-- Need root to start virtualizationservice -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+    <!-- Still need to define SELinux policy for authfs and fd_server properly. -->
+    <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="throw-if-cmd-fail" value="true" />
+        <!-- Prepare test directories. -->
+        <option name="run-command" value="mkdir -p /data/local/tmp/authfs/mnt" />
+        <option name="teardown-command" value="rm -rf /data/local/tmp/authfs" />
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="abort-on-push-failure" value="true" />
+
+        <!-- Test executable -->
+        <option name="push-file" key="open_then_run" value="/data/local/tmp/open_then_run" />
+        <option name="push-file" key="fsverity" value="/data/local/tmp/fsverity" />
+
+        <!-- Test data files -->
+        <option name="push-file" key="cert.der" value="/data/local/tmp/authfs/cert.der" />
+        <option name="push-file" key="input.4m" value="/data/local/tmp/authfs/input.4m" />
+        <option name="push-file" key="input.4m.fsv_meta"
+            value="/data/local/tmp/authfs/input.4m.fsv_meta" />
+
+        <!-- Just pick a file with signature that can be trused on the device. -->
+        <option name="push-file" key="CtsApkVerityTestAppPrebuilt.apk"
+            value="/data/local/tmp/authfs/input.apk" />
+        <option name="push-file" key="CtsApkVerityTestAppPrebuilt.apk.fsv_sig"
+            value="/data/local/tmp/authfs/input.apk.fsv_sig" />
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="throw-if-cmd-fail" value="true" />
+        <!-- Now that the files are pushed to the device, enable fs-verity for the targeting file.
+             It works because the signature is trusted on all CTS compatible devices. -->
+        <option name="run-command"
+            value="cd /data/local/tmp/authfs;
+                   ../fsverity enable input.apk --signature=input.apk.fsv_sig" />
+    </target_preparer>
+
+    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+        <option name="jar" value="AuthFsBenchmarks.jar" />
+    </test>
+</configuration>
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsBenchmarks.java b/authfs/tests/benchmarks/src/java/com/android/fs/benchmarks/AuthFsBenchmarks.java
similarity index 98%
rename from authfs/tests/java/src/com/android/fs/AuthFsBenchmarks.java
rename to authfs/tests/benchmarks/src/java/com/android/fs/benchmarks/AuthFsBenchmarks.java
index af2c892..5e9073a 100644
--- a/authfs/tests/java/src/com/android/fs/AuthFsBenchmarks.java
+++ b/authfs/tests/benchmarks/src/java/com/android/fs/benchmarks/AuthFsBenchmarks.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.virt.fs;
+package com.android.virt.fs.benchmarks;
 
 import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
 
@@ -22,6 +22,7 @@
 
 import android.platform.test.annotations.RootPermissionTest;
 
+import com.android.fs.common.AuthFsTestRule;
 import com.android.microdroid.test.common.MetricsProcessor;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.invoker.TestInformation;
diff --git a/authfs/tests/measure_io.cpp b/authfs/tests/benchmarks/src/measure_io.cpp
similarity index 100%
rename from authfs/tests/measure_io.cpp
rename to authfs/tests/benchmarks/src/measure_io.cpp
diff --git a/authfs/tests/common/Android.bp b/authfs/tests/common/Android.bp
new file mode 100644
index 0000000..ec426c7
--- /dev/null
+++ b/authfs/tests/common/Android.bp
@@ -0,0 +1,33 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library_host {
+    name: "AuthFsHostTestCommon",
+    srcs: ["src/java/**/*.java"],
+    libs: [
+        "compatibility-host-util",
+        "compatibility-tradefed",
+        "tradefed",
+    ],
+    static_libs: [
+        "MicrodroidHostTestHelper",
+    ],
+}
+
+rust_test {
+    name: "open_then_run",
+    crate_name: "open_then_run",
+    srcs: ["src/open_then_run.rs"],
+    edition: "2021",
+    rustlibs: [
+        "libandroid_logger",
+        "libanyhow",
+        "libclap",
+        "libcommand_fds",
+        "liblibc",
+        "liblog_rust",
+    ],
+    test_suites: ["general-tests"],
+    test_harness: false,
+}
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsTestRule.java b/authfs/tests/common/src/java/com/android/fs/common/AuthFsTestRule.java
similarity index 90%
rename from authfs/tests/java/src/com/android/fs/AuthFsTestRule.java
rename to authfs/tests/common/src/java/com/android/fs/common/AuthFsTestRule.java
index a9fdd10..994f23b 100644
--- a/authfs/tests/java/src/com/android/fs/AuthFsTestRule.java
+++ b/authfs/tests/common/src/java/com/android/fs/common/AuthFsTestRule.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.virt.fs;
+package com.android.fs.common;
 
 import static com.android.microdroid.test.host.LogArchiver.archiveLogThenDelete;
 import static com.android.tradefed.device.TestDevice.MicrodroidBuilder;
@@ -50,22 +50,22 @@
 /** Custom TestRule for AuthFs tests. */
 public class AuthFsTestRule extends TestLogData {
     /** FUSE's magic from statfs(2) */
-    static final String FUSE_SUPER_MAGIC_HEX = "65735546";
+    public static final String FUSE_SUPER_MAGIC_HEX = "65735546";
 
     /** VM config entry path in the test APK */
     private static final String VM_CONFIG_PATH_IN_APK = "assets/vm_config.json";
 
     /** Test directory on Android where data are located */
-    static final String TEST_DIR = "/data/local/tmp/authfs";
+    public static final String TEST_DIR = "/data/local/tmp/authfs";
 
     /** File name of the test APK */
     private static final String TEST_APK_NAME = "MicrodroidTestApp.apk";
 
     /** Output directory where the test can generate output on Android */
-    static final String TEST_OUTPUT_DIR = "/data/local/tmp/authfs/output_dir";
+    public static final String TEST_OUTPUT_DIR = "/data/local/tmp/authfs/output_dir";
 
     /** Mount point of authfs on Microdroid during the test */
-    static final String MOUNT_DIR = "/data/local/tmp/mnt";
+    public static final String MOUNT_DIR = "/data/local/tmp/mnt";
 
     /** VM's log file */
     private static final String LOG_PATH = TEST_OUTPUT_DIR + "/log.txt";
@@ -91,7 +91,7 @@
 
     private final ExecutorService mThreadPool = Executors.newCachedThreadPool();
 
-    static void setUpAndroid(TestInformation testInfo) throws Exception {
+    public static void setUpAndroid(TestInformation testInfo) throws Exception {
         assertNotNull(testInfo.getDevice());
         if (!(testInfo.getDevice() instanceof TestDevice)) {
             CLog.w("Unexpected type of ITestDevice. Skipping.");
@@ -110,28 +110,28 @@
         }
     }
 
-    static void tearDownAndroid() {
+    public static void tearDownAndroid() {
         sAndroid = null;
     }
 
     /** This method is supposed to be called after {@link #setUpTest()}. */
-    static CommandRunner getAndroid() {
+    public static CommandRunner getAndroid() {
         assertThat(sAndroid).isNotNull();
         return sAndroid;
     }
 
     /** This method is supposed to be called after {@link #setUpTest()}. */
-    static CommandRunner getMicrodroid() {
+    public static CommandRunner getMicrodroid() {
         assertThat(sMicrodroid).isNotNull();
         return sMicrodroid;
     }
 
-    static ITestDevice getMicrodroidDevice() {
+    public static ITestDevice getMicrodroidDevice() {
         assertThat(sMicrodroidDevice).isNotNull();
         return sMicrodroidDevice;
     }
 
-    static void startMicrodroid() throws DeviceNotAvailableException {
+    public static void startMicrodroid() throws DeviceNotAvailableException {
         CLog.i("Starting the shared VM");
         assertThat(sMicrodroidDevice).isNull();
         sMicrodroidDevice =
@@ -151,7 +151,7 @@
         assertThat(sMicrodroidDevice.enableAdbRoot()).isTrue();
     }
 
-    static void shutdownMicrodroid() throws DeviceNotAvailableException {
+    public static void shutdownMicrodroid() throws DeviceNotAvailableException {
         assertNotNull(sMicrodroidDevice);
         getDevice().shutdownMicrodroid(sMicrodroidDevice);
         sMicrodroidDevice = null;
@@ -172,7 +172,7 @@
                 description);
     }
 
-    void runFdServerOnAndroid(String helperFlags, String fdServerFlags)
+    public void runFdServerOnAndroid(String helperFlags, String fdServerFlags)
             throws DeviceNotAvailableException {
         String cmd =
                 "cd "
@@ -188,7 +188,7 @@
         Future<?> unusedFuture = mThreadPool.submit(() -> runForResult(sAndroid, cmd, "fd_server"));
     }
 
-    void runAuthFsOnMicrodroid(String flags) {
+    public void runAuthFsOnMicrodroid(String flags) {
         String cmd = AUTHFS_BIN + " " + MOUNT_DIR + " " + flags + " --cid " + VMADDR_CID_HOST;
 
         AtomicBoolean starting = new AtomicBoolean(true);
@@ -215,7 +215,7 @@
         }
     }
 
-    static File findTestFile(IBuildInfo buildInfo, String fileName) {
+    public static File findTestFile(IBuildInfo buildInfo, String fileName) {
         try {
             return (new CompatibilityBuildHelper(buildInfo)).getTestFile(fileName);
         } catch (FileNotFoundException e) {
diff --git a/authfs/tests/open_then_run.rs b/authfs/tests/common/src/open_then_run.rs
similarity index 100%
rename from authfs/tests/open_then_run.rs
rename to authfs/tests/common/src/open_then_run.rs
diff --git a/authfs/tests/hosttests/Android.bp b/authfs/tests/hosttests/Android.bp
new file mode 100644
index 0000000..4b8151d
--- /dev/null
+++ b/authfs/tests/hosttests/Android.bp
@@ -0,0 +1,26 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_test_host {
+    name: "AuthFsHostTest",
+    srcs: ["java/src/com/android/fs/*.java"],
+    libs: [
+        "tradefed",
+    ],
+    static_libs: [
+        "MicrodroidHostTestHelper",
+        "AuthFsHostTestCommon",
+    ],
+    test_suites: ["general-tests"],
+    data_device_bins_first: [
+        "open_then_run",
+        "fsverity",
+    ],
+    per_testcase_directory: true,
+    data: [
+        ":authfs_test_files",
+        ":CtsApkVerityTestPrebuiltFiles",
+        ":MicrodroidTestApp",
+    ],
+}
diff --git a/authfs/tests/AndroidTest.xml b/authfs/tests/hosttests/AndroidTest.xml
similarity index 100%
rename from authfs/tests/AndroidTest.xml
rename to authfs/tests/hosttests/AndroidTest.xml
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java b/authfs/tests/hosttests/java/src/com/android/fs/AuthFsHostTest.java
similarity index 99%
rename from authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
rename to authfs/tests/hosttests/java/src/com/android/fs/AuthFsHostTest.java
index c4169f6..3157dfd 100644
--- a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
+++ b/authfs/tests/hosttests/java/src/com/android/fs/AuthFsHostTest.java
@@ -24,6 +24,7 @@
 
 import android.platform.test.annotations.RootPermissionTest;
 
+import com.android.fs.common.AuthFsTestRule;
 import com.android.microdroid.test.host.CommandRunner;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.invoker.TestInformation;
diff --git a/compos/src/compsvc_main.rs b/compos/src/compsvc_main.rs
index 991725d..a4e3903 100644
--- a/compos/src/compsvc_main.rs
+++ b/compos/src/compsvc_main.rs
@@ -25,7 +25,7 @@
 use anyhow::{bail, Result};
 use compos_common::COMPOS_VSOCK_PORT;
 use log::{debug, error};
-use rpcbinder::run_rpc_server;
+use rpcbinder::run_vsock_rpc_server;
 use std::panic;
 use vm_payload_bindgen::AVmPayload_notifyPayloadReady;
 
@@ -48,7 +48,7 @@
     let service = compsvc::new_binder()?.as_binder();
     debug!("compsvc is starting as a rpc service.");
     // SAFETY: Invokes a method from the bindgen library `vm_payload_bindgen`.
-    let retval = run_rpc_server(service, COMPOS_VSOCK_PORT, || unsafe {
+    let retval = run_vsock_rpc_server(service, COMPOS_VSOCK_PORT, || unsafe {
         AVmPayload_notifyPayloadReady();
     });
     if retval {
diff --git a/libs/devicemapper/src/loopdevice.rs b/libs/devicemapper/src/loopdevice.rs
index bdbc0f6..5533e17 100644
--- a/libs/devicemapper/src/loopdevice.rs
+++ b/libs/devicemapper/src/loopdevice.rs
@@ -66,6 +66,7 @@
     offset: u64,
     size_limit: u64,
     direct_io: bool,
+    writable: bool,
 ) -> Result<PathBuf> {
     // Attaching a file to a loop device can make a race condition; a loop device number obtained
     // from LOOP_CTL_GET_FREE might have been used by another thread or process. In that case the
@@ -80,7 +81,7 @@
 
     let begin = Instant::now();
     loop {
-        match try_attach(&path, offset, size_limit, direct_io) {
+        match try_attach(&path, offset, size_limit, direct_io, writable) {
             Ok(loop_dev) => return Ok(loop_dev),
             Err(e) => {
                 if begin.elapsed() > TIMEOUT {
@@ -103,6 +104,7 @@
     offset: u64,
     size_limit: u64,
     direct_io: bool,
+    writable: bool,
 ) -> Result<PathBuf> {
     // Get a free loop device
     wait_for_path(LOOP_CONTROL)?;
@@ -116,6 +118,7 @@
     // Construct the loop_info64 struct
     let backing_file = OpenOptions::new()
         .read(true)
+        .write(writable)
         .custom_flags(if direct_io { O_DIRECT } else { 0 })
         .open(&path)
         .context(format!("failed to open {:?}", path.as_ref()))?;
@@ -126,7 +129,11 @@
     config.block_size = 4096;
     config.info.lo_offset = offset;
     config.info.lo_sizelimit = size_limit;
-    config.info.lo_flags = Flag::LO_FLAGS_READ_ONLY;
+
+    if !writable {
+        config.info.lo_flags = Flag::LO_FLAGS_READ_ONLY;
+    }
+
     if direct_io {
         config.info.lo_flags.insert(Flag::LO_FLAGS_DIRECT_IO);
     }
@@ -168,13 +175,19 @@
         "1" == fs::read_to_string(&dio).unwrap().trim()
     }
 
+    // kernel exposes /sys/block/loop*/ro which gives the read-only value
+    fn is_direct_io_writable(dev: &Path) -> bool {
+        let ro = Path::new("/sys/block").join(dev.file_name().unwrap()).join("ro");
+        "0" == fs::read_to_string(&ro).unwrap().trim()
+    }
+
     #[test]
     fn attach_loop_device_with_dio() {
         let a_dir = tempfile::TempDir::new().unwrap();
         let a_file = a_dir.path().join("test");
         let a_size = 4096u64;
         create_empty_file(&a_file, a_size);
-        let dev = attach(a_file, 0, a_size, /*direct_io*/ true).unwrap();
+        let dev = attach(a_file, 0, a_size, /*direct_io*/ true, /*writable*/ false).unwrap();
         scopeguard::defer! {
             detach(&dev).unwrap();
         }
@@ -187,10 +200,24 @@
         let a_file = a_dir.path().join("test");
         let a_size = 4096u64;
         create_empty_file(&a_file, a_size);
-        let dev = attach(a_file, 0, a_size, /*direct_io*/ false).unwrap();
+        let dev = attach(a_file, 0, a_size, /*direct_io*/ false, /*writable*/ false).unwrap();
         scopeguard::defer! {
             detach(&dev).unwrap();
         }
         assert!(!is_direct_io(&dev));
     }
+
+    #[test]
+    fn attach_loop_device_with_dio_writable() {
+        let a_dir = tempfile::TempDir::new().unwrap();
+        let a_file = a_dir.path().join("test");
+        let a_size = 4096u64;
+        create_empty_file(&a_file, a_size);
+        let dev = attach(a_file, 0, a_size, /*direct_io*/ true, /*writable*/ true).unwrap();
+        scopeguard::defer! {
+            detach(&dev).unwrap();
+        }
+        assert!(is_direct_io(&dev));
+        assert!(is_direct_io_writable(&dev));
+    }
 }
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index 8741fb8..4b06b3e 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -34,7 +34,6 @@
         "libonce_cell",
         "libopenssl",
         "libprotobuf",
-        "libregex",
         "librpcbinder_rs",
         "librustutils",
         "libscopeguard",
diff --git a/microdroid_manager/src/procutil.rs b/microdroid_manager/src/procutil.rs
index b323ca9..f9479c4 100644
--- a/microdroid_manager/src/procutil.rs
+++ b/microdroid_manager/src/procutil.rs
@@ -12,10 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use anyhow::{Context, Result};
+use anyhow::{bail, Result};
 use libc::{sysconf, _SC_CLK_TCK};
-use regex::Regex;
-use std::fs::{self, File};
+use std::fs::File;
 use std::io::{BufRead, BufReader};
 
 const MILLIS_PER_SEC: i64 = 1000;
@@ -57,19 +56,20 @@
 //   sys: 10771070
 //   idle: 10480973587
 pub fn get_cpu_time() -> Result<CpuTime> {
-    let re = Regex::new(r"^cpu\s+([\d]+)\s([\d]+)\s([\d]+)\s([\d]+)").unwrap();
-
     let mut proc_stat = BufReader::new(File::open("/proc/stat")?);
     let mut line = String::new();
     proc_stat.read_line(&mut line)?;
-    let data_list = re.captures(&line).context("Failed to capture values")?;
+    let data_list: Vec<_> = line.split_whitespace().filter_map(|s| s.parse::<i64>().ok()).collect();
+    if data_list.len() < 4 {
+        bail!("Failed to extract numeric values in /proc/stat :\n{}", line);
+    }
 
     let ticks_per_sec = unsafe { sysconf(_SC_CLK_TCK) } as i64;
     let cpu_time = CpuTime {
-        user: data_list.get(1).unwrap().as_str().parse::<i64>()? * MILLIS_PER_SEC / ticks_per_sec,
-        nice: data_list.get(2).unwrap().as_str().parse::<i64>()? * MILLIS_PER_SEC / ticks_per_sec,
-        sys: data_list.get(3).unwrap().as_str().parse::<i64>()? * MILLIS_PER_SEC / ticks_per_sec,
-        idle: data_list.get(4).unwrap().as_str().parse::<i64>()? * MILLIS_PER_SEC / ticks_per_sec,
+        user: data_list[0] * MILLIS_PER_SEC / ticks_per_sec,
+        nice: data_list[1] * MILLIS_PER_SEC / ticks_per_sec,
+        sys: data_list[2] * MILLIS_PER_SEC / ticks_per_sec,
+        idle: data_list[3] * MILLIS_PER_SEC / ticks_per_sec,
     };
     Ok(cpu_time)
 }
@@ -105,21 +105,23 @@
 //   buffer: 10231296
 //   cached: 189502836
 pub fn get_mem_info() -> Result<MemInfo> {
-    let re = Regex::new(r"^.*?:\s+([0-9]+)\skB").unwrap();
-
-    let proc_mem_info = fs::read_to_string("/proc/meminfo")?;
-    let data_list: Vec<_> = proc_mem_info
-        .trim()
-        .splitn(6, '\n')
-        .map(|s| re.captures(s).context("Failed to capture values").ok()?.get(1))
-        .collect();
+    let mut proc_stat = BufReader::new(File::open("/proc/meminfo")?);
+    let mut lines = String::new();
+    for _ in 0..5 {
+        proc_stat.read_line(&mut lines)?;
+    }
+    let data_list: Vec<_> =
+        lines.split_whitespace().filter_map(|s| s.parse::<i64>().ok()).collect();
+    if data_list.len() != 5 {
+        bail!("Failed to extract numeric values in /proc/meminfo :\n{}", lines);
+    }
 
     let mem_info = MemInfo {
-        total: data_list[0].unwrap().as_str().parse::<i64>()?,
-        free: data_list[1].unwrap().as_str().parse::<i64>()?,
-        available: data_list[2].unwrap().as_str().parse::<i64>()?,
-        buffer: data_list[3].unwrap().as_str().parse::<i64>()?,
-        cached: data_list[4].unwrap().as_str().parse::<i64>()?,
+        total: data_list[0],
+        free: data_list[1],
+        available: data_list[2],
+        buffer: data_list[3],
+        cached: data_list[4],
     };
     Ok(mem_info)
 }
diff --git a/pvmfw/src/exceptions.rs b/pvmfw/src/exceptions.rs
index 0fb2911..03fc220 100644
--- a/pvmfw/src/exceptions.rs
+++ b/pvmfw/src/exceptions.rs
@@ -20,14 +20,14 @@
 use vmbase::{console::emergency_write_str, eprintln, power::reboot};
 
 const ESR_32BIT_EXT_DABT: u64 = 0x96000010;
-const UART_PAGE: u64 = page_4kb_of(console::BASE_ADDRESS as u64);
+const UART_PAGE: usize = page_4kb_of(console::BASE_ADDRESS);
 
 #[no_mangle]
 extern "C" fn sync_exception_current(_elr: u64, _spsr: u64) {
     let esr = read_esr();
     let far = read_far();
     // Don't print to the UART if we're handling the exception it could raise.
-    if esr != ESR_32BIT_EXT_DABT || page_4kb_of(far) != UART_PAGE {
+    if esr != ESR_32BIT_EXT_DABT || page_4kb_of(far as usize) != UART_PAGE {
         emergency_write_str("sync_exception_current\n");
         print_esr(esr);
     }
diff --git a/pvmfw/src/helpers.rs b/pvmfw/src/helpers.rs
index 781c1ac..ac3a48e 100644
--- a/pvmfw/src/helpers.rs
+++ b/pvmfw/src/helpers.rs
@@ -14,13 +14,15 @@
 
 //! Miscellaneous helper functions.
 
+pub const SIZE_4KB: usize = 4 << 10;
+
 /// Computes the address of the page containing a given address.
-pub const fn page_of(addr: u64, page_size: u64) -> u64 {
+pub const fn page_of(addr: usize, page_size: usize) -> usize {
     addr & !(page_size - 1)
 }
 
 /// Validates a page size and computes the address of the page containing a given address.
-pub const fn checked_page_of(addr: u64, page_size: u64) -> Option<u64> {
+pub const fn checked_page_of(addr: usize, page_size: usize) -> Option<usize> {
     if page_size.is_power_of_two() {
         Some(page_of(addr, page_size))
     } else {
@@ -29,8 +31,6 @@
 }
 
 /// Computes the address of the 4KiB page containing a given address.
-pub const fn page_4kb_of(addr: u64) -> u64 {
-    const PAGE_SIZE: u64 = 4 << 10;
-
-    page_of(addr, PAGE_SIZE)
+pub const fn page_4kb_of(addr: usize) -> usize {
+    page_of(addr, SIZE_4KB)
 }
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 4fdd30e..55604e5 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -43,10 +43,10 @@
 
 fn main(fdt_address: u64, payload_start: u64, payload_size: u64, arg3: u64) -> Result<(), Error> {
     // We need to inform the hypervisor that the MMIO page containing the UART may be shared back.
-    let uart = console::BASE_ADDRESS as u64;
     let mmio_granule = smccc::mmio_guard_info().map_err(|_| Error::FailedUartSetup)?;
-    let uart_page = checked_page_of(uart, mmio_granule).ok_or(Error::FailedUartSetup)?;
-    smccc::mmio_guard_map(uart_page).map_err(|_| Error::FailedUartSetup)?;
+    let uart_page = checked_page_of(console::BASE_ADDRESS, mmio_granule as usize)
+        .ok_or(Error::FailedUartSetup)?;
+    smccc::mmio_guard_map(uart_page as u64).map_err(|_| Error::FailedUartSetup)?;
 
     info!("pVM firmware");
     debug!(
diff --git a/pvmfw/src/smccc.rs b/pvmfw/src/smccc.rs
index e3a2b05..18128e1 100644
--- a/pvmfw/src/smccc.rs
+++ b/pvmfw/src/smccc.rs
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use core::fmt;
+use core::{fmt, result};
 
 // TODO(b/245889995): use psci-0.1.1 crate
 #[inline(always)]
@@ -69,20 +69,22 @@
             Self::NotSupported => write!(f, "SMCCC call not supported"),
             Self::NotRequired => write!(f, "SMCCC call not required"),
             Self::InvalidParameter => write!(f, "SMCCC call received non-supported value"),
-            Self::Unexpected(v) => write!(f, "Unexpected SMCCC return value '{}'", v),
-            Self::Unknown(e) => write!(f, "Unknown SMCCC return value '{}'", e),
+            Self::Unexpected(v) => write!(f, "Unexpected SMCCC return value {} ({0:#x})", v),
+            Self::Unknown(e) => write!(f, "Unknown SMCCC return value {} ({0:#x})", e),
         }
     }
 }
 
-fn check_smccc_err(ret: i64) -> Result<(), Error> {
+type Result<T> = result::Result<T, Error>;
+
+fn check_smccc_err(ret: i64) -> Result<()> {
     match check_smccc_value(ret)? {
         0 => Ok(()),
         v => Err(Error::Unexpected(v)),
     }
 }
 
-fn check_smccc_value(ret: i64) -> Result<u64, Error> {
+fn check_smccc_value(ret: i64) -> Result<u64> {
     match ret {
         x if x >= 0 => Ok(ret as u64),
         -1 => Err(Error::NotSupported),
@@ -96,7 +98,7 @@
 const VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID: u32 = 0xc6000007;
 
 /// Issue pKVM-specific MMIO_GUARD_INFO HVC64.
-pub fn mmio_guard_info() -> Result<u64, Error> {
+pub fn mmio_guard_info() -> Result<u64> {
     let args = [0u64; 17];
 
     let res = hvc64(VENDOR_HYP_KVM_MMIO_GUARD_INFO_FUNC_ID, args);
@@ -105,7 +107,7 @@
 }
 
 /// Issue pKVM-specific MMIO_GUARD_MAP HVC64.
-pub fn mmio_guard_map(ipa: u64) -> Result<(), Error> {
+pub fn mmio_guard_map(ipa: u64) -> Result<()> {
     let mut args = [0u64; 17];
     args[0] = ipa;
 
diff --git a/tests/benchmark/src/native/benchmarkbinary.cpp b/tests/benchmark/src/native/benchmarkbinary.cpp
index 842d713..c394756 100644
--- a/tests/benchmark/src/native/benchmarkbinary.cpp
+++ b/tests/benchmark/src/native/benchmarkbinary.cpp
@@ -165,8 +165,8 @@
         }
     };
 
-    if (!RunRpcServerCallback(test_service->asBinder().get(), test_service->SERVICE_PORT, callback,
-                              nullptr)) {
+    if (!RunVsockRpcServerCallback(test_service->asBinder().get(), test_service->SERVICE_PORT,
+                                   callback, nullptr)) {
         return Error() << "RPC Server failed to run";
     }
     return {};
diff --git a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
index f3aa199..17f773e 100644
--- a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
+++ b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
@@ -159,6 +159,8 @@
             throws DeviceNotAvailableException, InterruptedException {
         CommandRunner android = new CommandRunner(getDevice());
         unlockScreen(android);
+        // Ensure we are killing the app to get the cold app startup time
+        android.run("am force-stop " + pkgName);
         android.run("echo 3 > /proc/sys/vm/drop_caches");
         String vmStartAppLog = android.run("am", "start -W -S " + pkgName);
         assertNotNull(vmStartAppLog);
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
index 57882ce..0be3a38 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
@@ -131,10 +131,9 @@
         Map<TestDescription, TestResult> results = getLastDeviceRunResults().getTestResults();
         assertThat(results).hasSize(1);
         TestResult result = results.values().toArray(new TestResult[0])[0];
-        assertTrue(
-                "The test should fail with a permission error",
-                result.getStackTrace()
-                        .contains("android.permission.MANAGE_VIRTUAL_MACHINE permission"));
+        assertWithMessage("The test should fail with a permission error")
+                .that(result.getStackTrace())
+                .contains("android.permission.MANAGE_VIRTUAL_MACHINE permission");
     }
 
     private static JSONObject newPartition(String label, String path) {
@@ -601,7 +600,8 @@
         // Check UID and elapsed_time by comparing each other.
         assertEquals(atomVmCreationRequested.getUid(), atomVmBooted.getUid());
         assertEquals(atomVmCreationRequested.getUid(), atomVmExited.getUid());
-        assertTrue(atomVmBooted.getElapsedTimeMillis() < atomVmExited.getElapsedTimeMillis());
+        assertThat(atomVmBooted.getElapsedTimeMillis())
+                .isLessThan(atomVmExited.getElapsedTimeMillis());
     }
 
     @Test
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index d810e3c..5d6ca8b 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -122,8 +122,8 @@
             abort();
         }
     };
-    if (!RunRpcServerCallback(testService->asBinder().get(), testService->SERVICE_PORT, callback,
-                              nullptr)) {
+    if (!RunVsockRpcServerCallback(testService->asBinder().get(), testService->SERVICE_PORT,
+                                   callback, nullptr)) {
         return Error() << "RPC Server failed to run";
     }
 
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index bff4f36..1eec765 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -57,7 +57,7 @@
 use disk::QcowFile;
 use log::{debug, error, info, warn};
 use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
-use rpcbinder::run_rpc_server_with_factory;
+use rpcbinder::run_vsock_rpc_server_with_factory;
 use rustutils::system_properties;
 use semver::VersionReq;
 use std::convert::TryInto;
@@ -320,7 +320,7 @@
         let state = service.state.clone();
         std::thread::spawn(move || {
             debug!("VirtualMachineService is starting as an RPC service.");
-            if run_rpc_server_with_factory(VM_BINDER_SERVICE_PORT as u32, |cid| {
+            if run_vsock_rpc_server_with_factory(VM_BINDER_SERVICE_PORT as u32, |cid| {
                 VirtualMachineService::factory(cid, &state)
             }) {
                 debug!("RPC server has shut down gracefully");