Merge "Use ro.product.vendor.device to determine if running on Cuttlefish"
diff --git a/OWNERS b/OWNERS
index 5bd97f3..ecd24ed 100644
--- a/OWNERS
+++ b/OWNERS
@@ -9,6 +9,7 @@
 
 # Other owners
 alanstokes@google.com
+aliceywang@google.com
 ardb@google.com
 ascull@google.com
 inseob@google.com
diff --git a/authfs/tests/Android.bp b/authfs/tests/Android.bp
index b662bee..0177254 100644
--- a/authfs/tests/Android.bp
+++ b/authfs/tests/Android.bp
@@ -23,6 +23,7 @@
         ":authfs_test_files",
         ":CtsApkVerityTestPrebuiltFiles",
         ":MicrodroidTestApp",
+        ":measure_io",
     ],
 }
 
@@ -42,3 +43,13 @@
     test_suites: ["general-tests"],
     test_harness: false,
 }
+
+cc_binary {
+    name: "measure_io",
+    srcs: [
+        "measure_io.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+}
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsBenchmarks.java b/authfs/tests/java/src/com/android/fs/AuthFsBenchmarks.java
index 0dd4613..af2c892 100644
--- a/authfs/tests/java/src/com/android/fs/AuthFsBenchmarks.java
+++ b/authfs/tests/java/src/com/android/fs/AuthFsBenchmarks.java
@@ -18,6 +18,8 @@
 
 import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.platform.test.annotations.RootPermissionTest;
 
 import com.android.microdroid.test.common.MetricsProcessor;
@@ -31,11 +33,13 @@
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -44,8 +48,12 @@
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class AuthFsBenchmarks extends BaseHostJUnit4Test {
     private static final int TRIAL_COUNT = 5;
-    private static final double NANO_SECS_PER_SEC = 1_000_000_000.;
-    private static final double INPUT_SIZE_IN_MB = 4.;
+
+    /** Name of the measure_io binary on host. */
+    private static final String MEASURE_IO_BIN_NAME = "measure_io";
+
+    /** Path to measure_io on Microdroid. */
+    private static final String MEASURE_IO_BIN_PATH = "/data/local/tmp/measure_io";
 
     /** fs-verity digest (sha256) of testdata/input.4m */
     private static final String DIGEST_4M =
@@ -57,7 +65,7 @@
 
     @BeforeClassWithInfo
     public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
-        AuthFsTestRule.setUpClass(testInfo);
+        AuthFsTestRule.setUpAndroid(testInfo);
     }
 
     @Before
@@ -66,37 +74,84 @@
                 MetricsProcessor.getMetricPrefix(
                         getDevice().getProperty("debug.hypervisor.metrics_tag"));
         mMetricsProcessor = new MetricsProcessor(metricsPrefix + "authfs/");
+        AuthFsTestRule.startMicrodroid();
+    }
+
+    @After
+    public void tearDown() throws DeviceNotAvailableException {
+        AuthFsTestRule.shutdownMicrodroid();
     }
 
     @AfterClassWithInfo
-    public static void afterClassWithDevice(TestInformation testInfo)
-            throws DeviceNotAvailableException {
-        AuthFsTestRule.tearDownClass(testInfo);
+    public static void afterClassWithDevice(TestInformation testInfo) {
+        AuthFsTestRule.tearDownAndroid();
     }
 
     @Test
     public void seqReadRemoteFile() throws Exception {
-        List<Double> transferRates = new ArrayList<>(TRIAL_COUNT);
+        readRemoteFile("seq");
+    }
+
+    @Test
+    public void randReadRemoteFile() throws Exception {
+        readRemoteFile("rand");
+    }
+
+    @Test
+    public void seqWriteRemoteFile() throws Exception {
+        writeRemoteFile("seq");
+    }
+
+    @Test
+    public void randWriteRemoteFile() throws Exception {
+        writeRemoteFile("rand");
+    }
+
+    private void readRemoteFile(String mode) throws DeviceNotAvailableException {
+        pushMeasureIoBinToMicrodroid();
         // Cache the file in memory for the host.
-        String cmd = "cat " + mAuthFsTestRule.TEST_DIR + "/input.4m > /dev/null";
-        mAuthFsTestRule.getAndroid().run(cmd);
+        mAuthFsTestRule
+                .getAndroid()
+                .run("cat " + mAuthFsTestRule.TEST_DIR + "/input.4m > /dev/null");
+
+        String filePath = mAuthFsTestRule.MOUNT_DIR + "/3";
+        int fileSizeMb = 4;
+        String cmd = MEASURE_IO_BIN_PATH + " " + filePath + " " + fileSizeMb + " " + mode + " r";
+        List<Double> rates = new ArrayList<>(TRIAL_COUNT);
         for (int i = 0; i < TRIAL_COUNT + 1; ++i) {
             mAuthFsTestRule.runFdServerOnAndroid(
                     "--open-ro 3:input.4m --open-ro 4:input.4m.fsv_meta", "--ro-fds 3:4");
             mAuthFsTestRule.runAuthFsOnMicrodroid("--remote-ro-file 3:" + DIGEST_4M);
-            double elapsedSeconds = measureSeqReadOnMicrodroid("3");
-            transferRates.add(INPUT_SIZE_IN_MB / elapsedSeconds);
+
+            String rate = mAuthFsTestRule.getMicrodroid().run(cmd);
+            rates.add(Double.parseDouble(rate));
         }
-        reportMetrics(transferRates, "seq_read", "mb_per_sec");
+        reportMetrics(rates, mode + "_read", "mb_per_sec");
     }
 
-    private double measureSeqReadOnMicrodroid(String filename) throws DeviceNotAvailableException {
-        String cmd = "cat " + mAuthFsTestRule.MOUNT_DIR + "/" + filename + " > /dev/null";
-        // Ideally, we should measure the time in the VM to avoid the adb and host tests latency.
-        double startTime = System.nanoTime();
-        mAuthFsTestRule.getMicrodroid().run(cmd);
-        double elapsedSeconds = (System.nanoTime() - startTime) / NANO_SECS_PER_SEC;
-        return elapsedSeconds;
+    private void writeRemoteFile(String mode) throws DeviceNotAvailableException {
+        pushMeasureIoBinToMicrodroid();
+        String filePath = mAuthFsTestRule.MOUNT_DIR + "/5";
+        int fileSizeMb = 8;
+        String cmd = MEASURE_IO_BIN_PATH + " " + filePath + " " + fileSizeMb + " " + mode + " w";
+        List<Double> rates = new ArrayList<>(TRIAL_COUNT);
+        for (int i = 0; i < TRIAL_COUNT + 1; ++i) {
+            mAuthFsTestRule.runFdServerOnAndroid(
+                    "--open-rw 5:" + mAuthFsTestRule.TEST_OUTPUT_DIR + "/out.file", "--rw-fds 5");
+            mAuthFsTestRule.runAuthFsOnMicrodroid("--remote-new-rw-file 5");
+
+            String rate = mAuthFsTestRule.getMicrodroid().run(cmd);
+            rates.add(Double.parseDouble(rate));
+        }
+        reportMetrics(rates, mode + "_write", "mb_per_sec");
+    }
+
+    private void pushMeasureIoBinToMicrodroid() throws DeviceNotAvailableException {
+        File measureReadBin = mAuthFsTestRule.findTestFile(getBuild(), MEASURE_IO_BIN_NAME);
+        assertThat(measureReadBin.exists()).isTrue();
+        mAuthFsTestRule.getMicrodroidDevice().pushFile(measureReadBin, MEASURE_IO_BIN_PATH);
+        assertThat(mAuthFsTestRule.getMicrodroid().run("ls " + MEASURE_IO_BIN_PATH))
+                .isEqualTo(MEASURE_IO_BIN_PATH);
     }
 
     private void reportMetrics(List<Double> metrics, String name, String unit) {
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
index 2deb490..c4169f6 100644
--- a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
+++ b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
@@ -52,7 +52,7 @@
     private static final String FSVERITY_BIN = "/data/local/tmp/fsverity";
 
     /** Mount point of authfs on Microdroid during the test */
-    private static final String MOUNT_DIR = "/data/local/tmp";
+    private static final String MOUNT_DIR = AuthFsTestRule.MOUNT_DIR;
 
     /** Input manifest path in the VM. */
     private static final String INPUT_MANIFEST_PATH = "/mnt/apk/assets/input_manifest.pb";
@@ -72,7 +72,8 @@
 
     @BeforeClassWithInfo
     public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
-        AuthFsTestRule.setUpClass(testInfo);
+        AuthFsTestRule.setUpAndroid(testInfo);
+        AuthFsTestRule.startMicrodroid();
         sAndroid = AuthFsTestRule.getAndroid();
         sMicrodroid = AuthFsTestRule.getMicrodroid();
     }
@@ -80,7 +81,8 @@
     @AfterClassWithInfo
     public static void afterClassWithDevice(TestInformation testInfo)
             throws DeviceNotAvailableException {
-        AuthFsTestRule.tearDownClass(testInfo);
+        AuthFsTestRule.shutdownMicrodroid();
+        AuthFsTestRule.tearDownAndroid();
     }
 
     @Test
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsTestRule.java b/authfs/tests/java/src/com/android/fs/AuthFsTestRule.java
index 34a403e..a9fdd10 100644
--- a/authfs/tests/java/src/com/android/fs/AuthFsTestRule.java
+++ b/authfs/tests/java/src/com/android/fs/AuthFsTestRule.java
@@ -62,10 +62,10 @@
     private static final String TEST_APK_NAME = "MicrodroidTestApp.apk";
 
     /** Output directory where the test can generate output on Android */
-    private static final String TEST_OUTPUT_DIR = "/data/local/tmp/authfs/output_dir";
+    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";
+    static final String MOUNT_DIR = "/data/local/tmp/mnt";
 
     /** VM's log file */
     private static final String LOG_PATH = TEST_OUTPUT_DIR + "/log.txt";
@@ -85,12 +85,13 @@
     private static final int VMADDR_CID_HOST = 2;
 
     private static TestInformation sTestInfo;
+    private static ITestDevice sMicrodroidDevice;
     private static CommandRunner sAndroid;
     private static CommandRunner sMicrodroid;
 
     private final ExecutorService mThreadPool = Executors.newCachedThreadPool();
 
-    static void setUpClass(TestInformation testInfo) throws Exception {
+    static void setUpAndroid(TestInformation testInfo) throws Exception {
         assertNotNull(testInfo.getDevice());
         if (!(testInfo.getDevice() instanceof TestDevice)) {
             CLog.w("Unexpected type of ITestDevice. Skipping.");
@@ -107,32 +108,9 @@
             CLog.i("Microdroid not supported. Skipping.");
             return;
         }
-
-        // For each test case, boot and adb connect to a new Microdroid
-        CLog.i("Starting the shared VM");
-        ITestDevice microdroidDevice =
-                MicrodroidBuilder.fromFile(
-                                findTestApk(testInfo.getBuildInfo()), VM_CONFIG_PATH_IN_APK)
-                        .debugLevel("full")
-                        .build((TestDevice) androidDevice);
-
-        // From this point on, we need to tear down the Microdroid instance
-        sMicrodroid = new CommandRunner(microdroidDevice);
-
-        // Root because authfs (started from shell in this test) currently require root to open
-        // /dev/fuse and mount the FUSE.
-        assertThat(microdroidDevice.enableAdbRoot()).isTrue();
     }
 
-    static void tearDownClass(TestInformation testInfo) throws DeviceNotAvailableException {
-        assertNotNull(sAndroid);
-
-        if (sMicrodroid != null) {
-            CLog.i("Shutting down shared VM");
-            ((TestDevice) testInfo.getDevice()).shutdownMicrodroid(sMicrodroid.getDevice());
-            sMicrodroid = null;
-        }
-
+    static void tearDownAndroid() {
         sAndroid = null;
     }
 
@@ -148,6 +126,38 @@
         return sMicrodroid;
     }
 
+    static ITestDevice getMicrodroidDevice() {
+        assertThat(sMicrodroidDevice).isNotNull();
+        return sMicrodroidDevice;
+    }
+
+    static void startMicrodroid() throws DeviceNotAvailableException {
+        CLog.i("Starting the shared VM");
+        assertThat(sMicrodroidDevice).isNull();
+        sMicrodroidDevice =
+                MicrodroidBuilder.fromFile(
+                                findTestFile(sTestInfo.getBuildInfo(), TEST_APK_NAME),
+                                VM_CONFIG_PATH_IN_APK)
+                        .debugLevel("full")
+                        .build(getDevice());
+
+        // From this point on, we need to tear down the Microdroid instance
+        sMicrodroid = new CommandRunner(sMicrodroidDevice);
+
+        sMicrodroid.runForResult("mkdir -p " + MOUNT_DIR);
+
+        // Root because authfs (started from shell in this test) currently require root to open
+        // /dev/fuse and mount the FUSE.
+        assertThat(sMicrodroidDevice.enableAdbRoot()).isTrue();
+    }
+
+    static void shutdownMicrodroid() throws DeviceNotAvailableException {
+        assertNotNull(sMicrodroidDevice);
+        getDevice().shutdownMicrodroid(sMicrodroidDevice);
+        sMicrodroidDevice = null;
+        sMicrodroid = null;
+    }
+
     @Override
     public Statement apply(final Statement base, Description description) {
         return super.apply(
@@ -205,11 +215,11 @@
         }
     }
 
-    private static File findTestApk(IBuildInfo buildInfo) {
+    static File findTestFile(IBuildInfo buildInfo, String fileName) {
         try {
-            return (new CompatibilityBuildHelper(buildInfo)).getTestFile(TEST_APK_NAME);
+            return (new CompatibilityBuildHelper(buildInfo)).getTestFile(fileName);
         } catch (FileNotFoundException e) {
-            fail("Missing test file: " + TEST_APK_NAME);
+            fail("Missing test file: " + fileName);
             return null;
         }
     }
diff --git a/authfs/tests/measure_io.cpp b/authfs/tests/measure_io.cpp
new file mode 100644
index 0000000..e1f2fb8
--- /dev/null
+++ b/authfs/tests/measure_io.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#include <android-base/unique_fd.h>
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <iomanip>
+#include <iostream>
+#include <random>
+
+using android::base::unique_fd;
+
+constexpr int kBlockSizeBytes = 4096;
+constexpr int kNumBytesPerMB = 1024 * 1024;
+
+int main(int argc, const char *argv[]) {
+    if (argc != 5 || !(strcmp(argv[3], "rand") == 0 || strcmp(argv[3], "seq") == 0) ||
+        !(strcmp(argv[4], "r") == 0 || strcmp(argv[4], "w") == 0)) {
+        errx(EXIT_FAILURE, "Usage: %s <filename> <file_size_mb> <rand|seq> <r|w>", argv[0]);
+    }
+    int file_size_mb = std::stoi(argv[2]);
+    bool is_rand = (strcmp(argv[3], "rand") == 0);
+    bool is_read = (strcmp(argv[4], "r") == 0);
+    const int block_count = file_size_mb * kNumBytesPerMB / kBlockSizeBytes;
+    std::vector<int> offsets(block_count);
+    for (auto i = 0; i < block_count; ++i) {
+        offsets[i] = i * kBlockSizeBytes;
+    }
+    if (is_rand) {
+        std::mt19937 rd{std::random_device{}()};
+        std::shuffle(offsets.begin(), offsets.end(), rd);
+    }
+    unique_fd fd(open(argv[1], (is_read ? O_RDONLY : O_WRONLY) | O_CLOEXEC));
+    if (fd.get() == -1) {
+        errx(EXIT_FAILURE, "failed to open file: %s", argv[1]);
+    }
+
+    char buf[kBlockSizeBytes];
+    clock_t start = clock();
+    for (auto i = 0; i < block_count; ++i) {
+        auto bytes = is_read ? pread(fd, buf, kBlockSizeBytes, offsets[i])
+                             : pwrite(fd, buf, kBlockSizeBytes, offsets[i]);
+        if (bytes == 0) {
+            errx(EXIT_FAILURE, "unexpected end of file");
+        } else if (bytes == -1) {
+            errx(EXIT_FAILURE, "failed to read");
+        }
+    }
+    if (!is_read) {
+        // Writes all the buffered modifications to the open file.
+        assert(syncfs(fd) == 0);
+    }
+    double elapsed_seconds = ((double)clock() - start) / CLOCKS_PER_SEC;
+    double rate = (double)file_size_mb / elapsed_seconds;
+    std::cout << std::setprecision(12) << rate << std::endl;
+
+    return EXIT_SUCCESS;
+}
diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md
index 34a990d..8a1e6dd 100644
--- a/docs/getting_started/index.md
+++ b/docs/getting_started/index.md
@@ -33,7 +33,7 @@
 ### Pixel 6 and 6 Pro
 
 If the device is running Android 12, join the [Android Beta
-Program](https://www.google.com/android/beta) to uprade to Android 13 Beta.
+Program](https://www.google.com/android/beta) to upgrade to Android 13 Beta.
 
 Once upgraded to Android 13, execute the following command to enable pKVM.
 
@@ -66,11 +66,19 @@
 ```
 
 Finally, if the `pvmfw` partition has been corrupted, both slots may be flashed
-using the [`pvmfw.img` pre-built](https://android.googlesource.com/platform/packages/modules/Virtualization/+/refs/heads/master/pvmfw/pvmfw.img)
+using the [`pvmfw.img` pre-built](https://android.googlesource.com/platform/packages/modules/Virtualization/+/08deac98acefd62e222edfa374d5292458cf97eb%5E/pvmfw/pvmfw.img)
 as long as the bootloader remains unlocked. Otherwise, a fresh install of
 Android 13 followed by the manual steps above for flashing the `other` slot
 should be used as a last resort.
 
+Starting in Android 14, `pvmfw.img` can be built using the Android Build system:
+```
+lunch <target>  # where PRODUCT_BUILD_PVMFW_IMAGE=true
+m pvmfwimage    # partition image under ${ANDROID_PRODUCT_OUT}/pvmfw.img
+```
+Note that the result is not intended to be used in Android 13 as not
+backward-compatibility is guaranteed.
+
 ## Running demo app
 
 The instruction is [here](../../demo/README.md).
diff --git a/libs/apkverify/Android.bp b/libs/apkverify/Android.bp
index 78192d2..1862820 100644
--- a/libs/apkverify/Android.bp
+++ b/libs/apkverify/Android.bp
@@ -33,6 +33,7 @@
     name: "libapkverify.test",
     defaults: ["libapkverify.defaults"],
     test_suites: ["general-tests"],
+    rustlibs: ["libhex"],
     data: ["tests/data/*"],
 }
 
diff --git a/libs/apkverify/src/v4.rs b/libs/apkverify/src/v4.rs
index 715f742..6c085f6 100644
--- a/libs/apkverify/src/v4.rs
+++ b/libs/apkverify/src/v4.rs
@@ -312,10 +312,6 @@
 
     const TEST_APK_PATH: &str = "tests/data/v4-digest-v3-Sha256withEC.apk";
 
-    fn hexstring_from(s: &[u8]) -> String {
-        s.iter().map(|byte| format!("{:02x}", byte)).reduce(|i, j| i + &j).unwrap_or_default()
-    }
-
     #[test]
     fn parse_idsig_file() {
         let parsed = V4Signature::from_idsig_path(format!("{}.idsig", TEST_APK_PATH)).unwrap();
@@ -325,22 +321,22 @@
         let hi = parsed.hashing_info;
         assert_eq!(HashAlgorithm::SHA256, hi.hash_algorithm);
         assert_eq!(12, hi.log2_blocksize);
-        assert_eq!("", hexstring_from(hi.salt.as_ref()));
+        assert_eq!("", hex::encode(hi.salt.as_ref()));
         assert_eq!(
             "77f063b48b63f846690fa76450a8d3b61a295b6158f50592e873f76dbeeb0201",
-            hexstring_from(hi.raw_root_hash.as_ref())
+            hex::encode(hi.raw_root_hash.as_ref())
         );
 
         let si = parsed.signing_info;
         assert_eq!(
             "c02fe2eddeb3078801828b930de546ea4f98d37fb98b40c7c7ed169b0d713583",
-            hexstring_from(si.apk_digest.as_ref())
+            hex::encode(si.apk_digest.as_ref())
         );
-        assert_eq!("", hexstring_from(si.additional_data.as_ref()));
+        assert_eq!("", hex::encode(si.additional_data.as_ref()));
         assert_eq!(
             "3046022100fb6383ba300dc7e1e6931a25b381398a16e5575baefd82afd12ba88660d9a6\
             4c022100ebdcae13ab18c4e30bf6ae634462e526367e1ba26c2647a1d87a0f42843fc128",
-            hexstring_from(si.signature.as_ref())
+            hex::encode(si.signature.as_ref())
         );
         assert_eq!(SignatureAlgorithmID::EcdsaWithSha256, si.signature_algorithm_id);
 
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index 4b06b3e..8741fb8 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -34,6 +34,7 @@
         "libonce_cell",
         "libopenssl",
         "libprotobuf",
+        "libregex",
         "librpcbinder_rs",
         "librustutils",
         "libscopeguard",
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 78f8bb4..4326f5b 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -18,6 +18,7 @@
 mod instance;
 mod ioutil;
 mod payload;
+mod procutil;
 mod swap;
 mod vm_payload_service;
 
@@ -25,8 +26,12 @@
 use crate::instance::{ApexData, ApkData, InstanceDisk, MicrodroidData, RootHash};
 use crate::vm_payload_service::register_vm_payload_service;
 use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::ErrorCode::ErrorCode;
-use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
-    VM_BINDER_SERVICE_PORT, VM_STREAM_SERVICE_PORT, IVirtualMachineService,
+use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::{
+    IVirtualMachineService::{
+        IVirtualMachineService, VM_BINDER_SERVICE_PORT, VM_STREAM_SERVICE_PORT,
+    },
+    VirtualMachineCpuStatus::VirtualMachineCpuStatus,
+    VirtualMachineMemStatus::VirtualMachineMemStatus,
 };
 use anyhow::{anyhow, bail, ensure, Context, Error, Result};
 use apkverify::{get_public_key_der, verify, V4Signature};
@@ -36,13 +41,15 @@
 use itertools::sorted;
 use log::{error, info};
 use microdroid_metadata::{write_metadata, Metadata, PayloadMetadata};
-use microdroid_payload_config::{Task, TaskType, VmPayloadConfig, OsConfig};
+use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
 use openssl::sha::Sha512;
 use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
+use procutil::{get_cpu_time, get_mem_info};
 use rand::Fill;
 use rpcbinder::get_vsock_rpc_interface;
 use rustutils::system_properties;
 use rustutils::system_properties::PropertyWatcher;
+use std::borrow::Cow::{Borrowed, Owned};
 use std::convert::TryInto;
 use std::fs::{self, create_dir, File, OpenOptions};
 use std::io::Write;
@@ -50,6 +57,7 @@
 use std::path::Path;
 use std::process::{Child, Command, Stdio};
 use std::str;
+use std::thread;
 use std::time::{Duration, SystemTime};
 use vsock::VsockStream;
 
@@ -89,6 +97,42 @@
     InvalidConfig(String),
 }
 
+fn send_vm_status() -> Result<()> {
+    let service = get_vms_rpc_binder()
+        .context("cannot connect to VirtualMachineService")
+        .map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
+
+    let one_second = Duration::from_millis(1000);
+    loop {
+        // Collect VM CPU time information and creating VmCpuStatus atom for metrics.
+        let cpu_time = get_cpu_time()?;
+        let vm_cpu_status = VirtualMachineCpuStatus {
+            cpu_time_user: cpu_time.user,
+            cpu_time_nice: cpu_time.nice,
+            cpu_time_sys: cpu_time.sys,
+            cpu_time_idle: cpu_time.idle,
+        };
+        service
+            .notifyCpuStatus(&vm_cpu_status)
+            .expect("Can't send information about VM CPU status");
+
+        // Collect VM memory information and creating VmMemStatus atom for metrics.
+        let mem_info = get_mem_info()?;
+        let vm_mem_status = VirtualMachineMemStatus {
+            mem_total: mem_info.total,
+            mem_free: mem_info.free,
+            mem_available: mem_info.available,
+            mem_buffer: mem_info.buffer,
+            mem_cached: mem_info.cached,
+        };
+        service
+            .notifyMemStatus(&vm_mem_status)
+            .expect("Can't send information about VM memory status");
+
+        thread::sleep(one_second);
+    }
+}
+
 fn translate_error(err: &Error) -> (ErrorCode, String) {
     if let Some(e) = err.downcast_ref::<MicrodroidError>() {
         match e {
@@ -112,7 +156,7 @@
 
 fn write_death_reason_to_serial(err: &Error) -> Result<()> {
     let death_reason = if let Some(e) = err.downcast_ref::<MicrodroidError>() {
-        match e {
+        Borrowed(match e {
             MicrodroidError::FailedToConnectToVirtualizationService(_) => {
                 "MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE"
             }
@@ -121,9 +165,12 @@
                 "MICRODROID_PAYLOAD_VERIFICATION_FAILED"
             }
             MicrodroidError::InvalidConfig(_) => "MICRODROID_INVALID_PAYLOAD_CONFIG",
-        }
+        })
     } else {
-        "MICRODROID_UNKNOWN_RUNTIME_ERROR"
+        // Send context information back after a separator, to ease diagnosis.
+        // These errors occur before the payload runs, so this should not leak sensitive
+        // information.
+        Owned(format!("MICRODROID_UNKNOWN_RUNTIME_ERROR|{:?}", err))
     };
 
     let death_reason_bytes = death_reason.as_bytes();
@@ -177,6 +224,13 @@
     let service = get_vms_rpc_binder()
         .context("cannot connect to VirtualMachineService")
         .map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
+
+    thread::spawn(move || {
+        if let Err(e) = send_vm_status() {
+            error!("failed to get virtual machine status: {:?}", e);
+        }
+    });
+
     match try_run_payload(&service) {
         Ok(code) => {
             info!("notifying payload finished");
@@ -351,7 +405,7 @@
     )
     .context("Failed to run zipfuse")?;
 
-    let config = load_config(payload_metadata)?;
+    let config = load_config(payload_metadata).context("Failed to load payload metadata")?;
 
     let task = config
         .task
@@ -386,10 +440,12 @@
     // Wait until zipfuse has mounted the APK so we can access the payload
     wait_for_property_true(APK_MOUNT_DONE_PROP).context("Failed waiting for APK mount done")?;
 
-    system_properties::write("dev.bootcomplete", "1").context("set dev.bootcomplete")?;
     register_vm_payload_service(service.clone(), dice)?;
     ProcessState::start_thread_pool();
-    exec_task(task, service)
+
+    system_properties::write("dev.bootcomplete", "1").context("set dev.bootcomplete")?;
+
+    exec_task(task, service).context("Failed to run payload")
 }
 
 fn control_service(action: &str, service: &str) -> Result<()> {
@@ -674,7 +730,8 @@
         PayloadMetadata::config_path(path) => {
             let path = Path::new(&path);
             info!("loading config from {:?}...", path);
-            let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
+            let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)
+                .with_context(|| format!("Failed to read {:?}", path))?;
             Ok(serde_json::from_reader(file)?)
         }
         PayloadMetadata::config(payload_config) => {
@@ -769,7 +826,7 @@
     let abi = value.split(',').next().ok_or_else(|| anyhow!("no abilist"))?;
     let path = format!("/mnt/apk/lib/{}/{}", abi, name);
 
-    let metadata = fs::metadata(&path)?;
+    let metadata = fs::metadata(&path).with_context(|| format!("Unable to access {}", path))?;
     if !metadata.is_file() {
         bail!("{} is not a file", &path);
     }
diff --git a/microdroid_manager/src/procutil.rs b/microdroid_manager/src/procutil.rs
new file mode 100644
index 0000000..b323ca9
--- /dev/null
+++ b/microdroid_manager/src/procutil.rs
@@ -0,0 +1,125 @@
+// Copyright 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.
+
+use anyhow::{Context, Result};
+use libc::{sysconf, _SC_CLK_TCK};
+use regex::Regex;
+use std::fs::{self, File};
+use std::io::{BufRead, BufReader};
+
+const MILLIS_PER_SEC: i64 = 1000;
+
+pub struct CpuTime {
+    pub user: i64,
+    pub nice: i64,
+    pub sys: i64,
+    pub idle: i64,
+}
+
+pub struct MemInfo {
+    pub total: i64,
+    pub free: i64,
+    pub available: i64,
+    pub buffer: i64,
+    pub cached: i64,
+}
+
+// Get CPU time information from /proc/stat
+//
+// /proc/stat example(omitted):
+//   cpu  24790952 21104390 10771070 10480973587 1700955 0 410931 0 316532 0
+//   cpu0 169636 141307 61153 81785791 9605 0 183524 0 1345 0
+//   cpu1 182431 198327 68273 81431817 10445 0 32392 0 2616 0
+//   cpu2 183209 174917 68591 81933935 12239 0 10042 0 2415 0
+//   cpu3 183413 177758 69908 81927474 13354 0 5853 0 2491 0
+//   intr 7913477443 39 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+//   ctxt 10326710014
+//   btime 1664123605
+//   processes 9225712
+//   procs_running 1
+//   procs_blocked 0
+//   softirq 2683914305 14595298 304837101 1581 327291100 16397051 0 208857783 1024640365 787932 786506094
+//
+// expected output:
+//   user: 24790952
+//   nice: 21104390
+//   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 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,
+    };
+    Ok(cpu_time)
+}
+
+// Get memory information from /proc/meminfo
+//
+// /proc/meminfo example(omitted):
+//   MemTotal:       263742736 kB
+//   MemFree:        37144204 kB
+//   MemAvailable:   249168700 kB
+//   Buffers:        10231296 kB
+//   Cached:         189502836 kB
+//   SwapCached:       113848 kB
+//   Active:         132266424 kB
+//   Inactive:       73587504 kB
+//   Active(anon):    1455240 kB
+//   Inactive(anon):  6993584 kB
+//   Active(file):   130811184 kB
+//   Inactive(file): 66593920 kB
+//   Unevictable:       56436 kB
+//   Mlocked:           56436 kB
+//   SwapTotal:      255123452 kB
+//   SwapFree:       254499068 kB
+//   Dirty:               596 kB
+//   Writeback:             0 kB
+//   AnonPages:       5295864 kB
+//   Mapped:          3512608 kB
+//
+// expected output:
+//   total: 263742736
+//   free: 37144204
+//   available: 249168700
+//   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 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>()?,
+    };
+    Ok(mem_info)
+}
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 51c145e..466acc7 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -418,6 +418,19 @@
     }
 
     @Test
+    public void bootFailsWhenBinaryPathIsInvalid() throws Exception {
+        VirtualMachineConfig.Builder builder = mInner.newVmConfigBuilder()
+                .setPayloadBinaryPath("DoesNotExist.so");
+        VirtualMachineConfig normalConfig = builder.setDebugLevel(DEBUG_LEVEL_FULL).build();
+        mInner.forceCreateNewVirtualMachine("test_vm_invalid_binary_path", normalConfig);
+
+        BootResult bootResult = tryBootVm(TAG, "test_vm_invalid_binary_path");
+        assertThat(bootResult.payloadStarted).isFalse();
+        assertThat(bootResult.deathReason).isEqualTo(
+                VirtualMachineCallback.STOP_REASON_MICRODROID_UNKNOWN_RUNTIME_ERROR);
+    }
+
+    @Test
     public void sameInstancesShareTheSameVmObject() throws Exception {
         VirtualMachineConfig config = mInner.newVmConfigBuilder()
                 .setPayloadBinaryPath("MicrodroidTestNativeLib.so")
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineDebugInfo.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineDebugInfo.aidl
index bed4097..424eec1 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineDebugInfo.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineDebugInfo.aidl
@@ -28,9 +28,6 @@
     /** The UID of the process which requested the VM. */
     int requesterUid;
 
-    /** The SID of the process which requested the VM. */
-    @utf8InCpp String requesterSid;
-
     /**
      * The PID of the process which requested the VM. Note that this process may no longer exist and
      * the PID may have been reused for a different process, so this should not be trusted.
diff --git a/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl b/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
index e8c1724..4fa5fa0 100644
--- a/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
+++ b/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
@@ -16,6 +16,8 @@
 package android.system.virtualmachineservice;
 
 import android.system.virtualizationcommon.ErrorCode;
+import android.system.virtualmachineservice.VirtualMachineCpuStatus;
+import android.system.virtualmachineservice.VirtualMachineMemStatus;
 
 /** {@hide} */
 interface IVirtualMachineService {
@@ -56,4 +58,14 @@
      * Notifies that an error has occurred inside the VM..
      */
     void notifyError(ErrorCode errorCode, in String message);
+
+    /**
+     * Notifies the current CPU status of the VM.
+     */
+    void notifyCpuStatus(in VirtualMachineCpuStatus cpuStatus);
+
+    /**
+     * Notifies the current memory status of the VM.
+     */
+    void notifyMemStatus(in VirtualMachineMemStatus memStatus);
 }
diff --git a/virtualizationservice/aidl/android/system/virtualmachineservice/VirtualMachineCpuStatus.aidl b/virtualizationservice/aidl/android/system/virtualmachineservice/VirtualMachineCpuStatus.aidl
new file mode 100644
index 0000000..307c3f9
--- /dev/null
+++ b/virtualizationservice/aidl/android/system/virtualmachineservice/VirtualMachineCpuStatus.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright 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.
+ */
+package android.system.virtualmachineservice;
+
+parcelable VirtualMachineCpuStatus {
+    long cpu_time_user;
+    long cpu_time_nice;
+    long cpu_time_sys;
+    long cpu_time_idle;
+}
diff --git a/virtualizationservice/aidl/android/system/virtualmachineservice/VirtualMachineMemStatus.aidl b/virtualizationservice/aidl/android/system/virtualmachineservice/VirtualMachineMemStatus.aidl
new file mode 100644
index 0000000..3de57c6
--- /dev/null
+++ b/virtualizationservice/aidl/android/system/virtualmachineservice/VirtualMachineMemStatus.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright 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.
+ */
+package android.system.virtualmachineservice;
+
+parcelable VirtualMachineMemStatus {
+    long mem_total;
+    long mem_free;
+    long mem_available;
+    long mem_buffer;
+    long mem_cached;
+}
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index f956062..eb2bac4 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -14,13 +14,17 @@
 
 //! Implementation of the AIDL interface of the VirtualizationService.
 
-use crate::atom::{write_vm_booted_stats, write_vm_creation_stats};
+use crate::atom::{
+    write_vm_booted_stats, write_vm_cpu_status_stats, write_vm_creation_stats,
+    write_vm_mem_status_stats,
+};
 use crate::composite::make_composite_image;
 use crate::crosvm::{CrosvmConfig, DiskFile, PayloadState, VmInstance, VmState};
 use crate::payload::{add_microdroid_payload_images, add_microdroid_system_images};
+use crate::selinux::{getfilecon, SeContext};
 use crate::{Cid, FIRST_GUEST_CID, SYSPROP_LAST_CID};
-use crate::selinux::{SeContext, getfilecon};
 use android_os_permissions_aidl::aidl::android::os::IPermissionController;
+use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::ErrorCode::ErrorCode;
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
     DeathReason::DeathReason,
     DiskImage::DiskImage,
@@ -29,39 +33,42 @@
     IVirtualizationService::IVirtualizationService,
     Partition::Partition,
     PartitionType::PartitionType,
-    VirtualMachineAppConfig::{VirtualMachineAppConfig, Payload::Payload},
+    VirtualMachineAppConfig::{Payload::Payload, VirtualMachineAppConfig},
     VirtualMachineConfig::VirtualMachineConfig,
     VirtualMachineDebugInfo::VirtualMachineDebugInfo,
     VirtualMachinePayloadConfig::VirtualMachinePayloadConfig,
     VirtualMachineRawConfig::VirtualMachineRawConfig,
     VirtualMachineState::VirtualMachineState,
 };
+use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::{
+    IVirtualMachineService::{
+        BnVirtualMachineService, IVirtualMachineService, VM_BINDER_SERVICE_PORT,
+        VM_STREAM_SERVICE_PORT, VM_TOMBSTONES_SERVICE_PORT,
+    },
+    VirtualMachineCpuStatus::VirtualMachineCpuStatus,
+    VirtualMachineMemStatus::VirtualMachineMemStatus,
+};
+use anyhow::{anyhow, bail, Context, Result};
+use apkverify::{HashAlgorithm, V4Signature};
 use binder::{
     self, BinderFeatures, ExceptionCode, Interface, LazyServiceGuard, ParcelFileDescriptor,
     SpIBinder, Status, StatusCode, Strong, ThreadState,
 };
-use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
-        BnVirtualMachineService, IVirtualMachineService, VM_BINDER_SERVICE_PORT,
-        VM_STREAM_SERVICE_PORT, VM_TOMBSTONES_SERVICE_PORT,
-};
-use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::ErrorCode::ErrorCode;
-use anyhow::{anyhow, bail, Context, Result};
-use rpcbinder::run_rpc_server_with_factory;
 use disk::QcowFile;
-use apkverify::{HashAlgorithm, V4Signature};
 use log::{debug, error, info, warn};
-use microdroid_payload_config::{VmPayloadConfig, OsConfig, Task, TaskType};
+use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
+use rpcbinder::run_rpc_server_with_factory;
 use rustutils::system_properties;
 use semver::VersionReq;
 use std::convert::TryInto;
 use std::ffi::CStr;
 use std::fs::{create_dir, File, OpenOptions};
-use std::io::{Error, ErrorKind, Write, Read};
+use std::io::{Error, ErrorKind, Read, Write};
 use std::num::NonZeroU32;
 use std::os::unix::io::{FromRawFd, IntoRawFd};
 use std::path::{Path, PathBuf};
 use std::sync::{Arc, Mutex, Weak};
-use tombstoned_client::{TombstonedConnection, DebuggerdDumpType};
+use tombstoned_client::{DebuggerdDumpType, TombstonedConnection};
 use vmconfig::VmConfig;
 use vsock::{VsockListener, VsockStream};
 use zip::ZipArchive;
@@ -111,8 +118,6 @@
                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
             writeln!(file, "\trequester_uid: {}", vm.requester_uid)
                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
-            writeln!(file, "\trequester_sid: {}", vm.requester_sid)
-                .or(Err(StatusCode::UNKNOWN_ERROR))?;
             writeln!(file, "\trequester_debug_pid: {}", vm.requester_debug_pid)
                 .or(Err(StatusCode::UNKNOWN_ERROR))?;
         }
@@ -217,7 +222,6 @@
                 cid: vm.cid as i32,
                 temporaryDirectory: vm.temporary_directory.to_string_lossy().to_string(),
                 requesterUid: vm.requester_uid as i32,
-                requesterSid: vm.requester_sid.clone(),
                 requesterPid: vm.requester_debug_pid,
                 state: get_state(&vm),
             })
@@ -346,7 +350,6 @@
         let console_fd = console_fd.map(clone_file).transpose()?;
         let log_fd = log_fd.map(clone_file).transpose()?;
         let requester_uid = ThreadState::get_calling_uid();
-        let requester_sid = get_calling_sid()?;
         let requester_debug_pid = ThreadState::get_calling_pid();
         let cid = next_cid().or(Err(ExceptionCode::ILLEGAL_STATE))?;
 
@@ -466,20 +469,14 @@
             detect_hangup: is_app_config,
         };
         let instance = Arc::new(
-            VmInstance::new(
-                crosvm_config,
-                temporary_directory,
-                requester_uid,
-                requester_sid,
-                requester_debug_pid,
-            )
-            .map_err(|e| {
-                error!("Failed to create VM with config {:?}: {:?}", config, e);
-                Status::new_service_specific_error_str(
-                    -1,
-                    Some(format!("Failed to create VM: {:?}", e)),
-                )
-            })?,
+            VmInstance::new(crosvm_config, temporary_directory, requester_uid, requester_debug_pid)
+                .map_err(|e| {
+                    error!("Failed to create VM with config {:?}: {:?}", config, e);
+                    Status::new_service_specific_error_str(
+                        -1,
+                        Some(format!("Failed to create VM: {:?}", e)),
+                    )
+                })?,
         );
         state.add_vm(Arc::downgrade(&instance));
         Ok(VirtualMachine::create(instance))
@@ -705,27 +702,6 @@
     footer: PathBuf,
 }
 
-/// Gets the calling SID of the current Binder thread.
-fn get_calling_sid() -> Result<String, Status> {
-    ThreadState::with_calling_sid(|sid| {
-        if let Some(sid) = sid {
-            match sid.to_str() {
-                Ok(sid) => Ok(sid.to_owned()),
-                Err(e) => {
-                    error!("SID was not valid UTF-8: {}", e);
-                    Err(Status::new_exception_str(
-                        ExceptionCode::ILLEGAL_ARGUMENT,
-                        Some(format!("SID was not valid UTF-8: {}", e)),
-                    ))
-                }
-            }
-        } else {
-            error!("Missing SID on createVm");
-            Err(Status::new_exception_str(ExceptionCode::SECURITY, Some("Missing SID on createVm")))
-        }
-    })
-}
-
 /// Checks whether the caller has a specific permission
 fn check_permission(perm: &str) -> binder::Result<()> {
     let calling_pid = ThreadState::get_calling_pid();
@@ -1168,6 +1144,36 @@
             ))
         }
     }
+
+    fn notifyCpuStatus(&self, status: &VirtualMachineCpuStatus) -> binder::Result<()> {
+        let cid = self.cid;
+        if let Some(vm) = self.state.lock().unwrap().get_vm(cid) {
+            info!("VM having CID {} encountered an error", cid);
+            write_vm_cpu_status_stats(vm.requester_uid as i32, &vm.name, status);
+            Ok(())
+        } else {
+            error!("notifyCurrentStatus is called from an unknown CID {}", cid);
+            Err(Status::new_service_specific_error_str(
+                -1,
+                Some(format!("cannot find a VM with CID {}", cid)),
+            ))
+        }
+    }
+
+    fn notifyMemStatus(&self, status: &VirtualMachineMemStatus) -> binder::Result<()> {
+        let cid = self.cid;
+        if let Some(vm) = self.state.lock().unwrap().get_vm(cid) {
+            info!("VM having CID {} encountered an error", cid);
+            write_vm_mem_status_stats(vm.requester_uid as i32, &vm.name, status);
+            Ok(())
+        } else {
+            error!("notifyCurrentStatus is called from an unknown CID {}", cid);
+            Err(Status::new_service_specific_error_str(
+                -1,
+                Some(format!("cannot find a VM with CID {}", cid)),
+            ))
+        }
+    }
 }
 
 impl VirtualMachineService {
diff --git a/virtualizationservice/src/atom.rs b/virtualizationservice/src/atom.rs
index eabb4cc..8c46ac5 100644
--- a/virtualizationservice/src/atom.rs
+++ b/virtualizationservice/src/atom.rs
@@ -22,11 +22,17 @@
     VirtualMachineConfig::VirtualMachineConfig,
 };
 use android_system_virtualizationservice::binder::{Status, Strong};
+use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::{
+    VirtualMachineCpuStatus::VirtualMachineCpuStatus,
+    VirtualMachineMemStatus::VirtualMachineMemStatus,
+};
 use anyhow::{anyhow, Result};
 use binder::{ParcelFileDescriptor, ThreadState};
 use log::{trace, warn};
 use microdroid_payload_config::VmPayloadConfig;
-use statslog_virtualization_rust::{vm_booted, vm_creation_requested, vm_exited};
+use statslog_virtualization_rust::{
+    vm_booted, vm_cpu_status_reported, vm_creation_requested, vm_exited, vm_mem_status_reported,
+};
 use std::time::{Duration, SystemTime};
 use zip::ZipArchive;
 
@@ -206,3 +212,48 @@
         Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
     }
 }
+
+/// Write the stats of VM cpu status to statsd
+pub fn write_vm_cpu_status_stats(
+    uid: i32,
+    vm_identifier: &String,
+    cpu_status: &VirtualMachineCpuStatus,
+) {
+    let vm_cpu_status_reported = vm_cpu_status_reported::VmCpuStatusReported {
+        uid,
+        vm_identifier,
+        cpu_time_user_millis: cpu_status.cpu_time_user,
+        cpu_time_nice_millis: cpu_status.cpu_time_nice,
+        cpu_time_sys_millis: cpu_status.cpu_time_sys,
+        cpu_time_idle_millis: cpu_status.cpu_time_idle,
+    };
+    match vm_cpu_status_reported.stats_write() {
+        Err(e) => {
+            warn!("statslog_rust failed with error: {}", e);
+        }
+        Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
+    }
+}
+
+/// Write the stats of VM memory status to statsd
+pub fn write_vm_mem_status_stats(
+    uid: i32,
+    vm_identifier: &String,
+    mem_status: &VirtualMachineMemStatus,
+) {
+    let vm_mem_status_reported = vm_mem_status_reported::VmMemStatusReported {
+        uid,
+        vm_identifier,
+        mem_total_kb: mem_status.mem_total,
+        mem_free_kb: mem_status.mem_free,
+        mem_available_kb: mem_status.mem_available,
+        mem_buffer_kb: mem_status.mem_buffer,
+        mem_cached_kb: mem_status.mem_cached,
+    };
+    match vm_mem_status_reported.stats_write() {
+        Err(e) => {
+            warn!("statslog_rust failed with error: {}", e);
+        }
+        Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
+    }
+}
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index 54cdeb6..749970c 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -185,8 +185,6 @@
     pub temporary_directory: PathBuf,
     /// The UID of the process which requested the VM.
     pub requester_uid: u32,
-    /// The SID of the process which requested the VM.
-    pub requester_sid: String,
     /// The PID of the process which requested the VM. Note that this process may no longer exist
     /// and the PID may have been reused for a different process, so this should not be trusted.
     pub requester_debug_pid: i32,
@@ -210,7 +208,6 @@
         config: CrosvmConfig,
         temporary_directory: PathBuf,
         requester_uid: u32,
-        requester_sid: String,
         requester_debug_pid: i32,
     ) -> Result<VmInstance, Error> {
         validate_config(&config)?;
@@ -224,7 +221,6 @@
             protected,
             temporary_directory,
             requester_uid,
-            requester_sid,
             requester_debug_pid,
             callbacks: Default::default(),
             stream: Mutex::new(None),
@@ -395,7 +391,12 @@
     }
 }
 
-fn death_reason(result: &Result<ExitStatus, io::Error>, failure_reason: &str) -> DeathReason {
+fn death_reason(result: &Result<ExitStatus, io::Error>, mut failure_reason: &str) -> DeathReason {
+    if let Some(position) = failure_reason.find('|') {
+        // Separator indicates extra context information is present after the failure name.
+        error!("Failure info: {}", &failure_reason[(position + 1)..]);
+        failure_reason = &failure_reason[..position];
+    }
     if let Ok(status) = result {
         match failure_reason {
             "PVM_FIRMWARE_PUBLIC_KEY_MISMATCH" => {
diff --git a/virtualizationservice/src/main.rs b/virtualizationservice/src/main.rs
index cb10eff..327a45d 100644
--- a/virtualizationservice/src/main.rs
+++ b/virtualizationservice/src/main.rs
@@ -50,10 +50,7 @@
     clear_temporary_files().expect("Failed to delete old temporary files");
 
     let service = VirtualizationService::init();
-    let service = BnVirtualizationService::new_binder(
-        service,
-        BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
-    );
+    let service = BnVirtualizationService::new_binder(service, BinderFeatures::default());
     register_lazy_service(BINDER_SERVICE_IDENTIFIER, service.as_binder()).unwrap();
     info!("Registered Binder service, joining threadpool.");
     ProcessState::join_thread_pool();