Merge "Moved binder_common to rpcbinder package under binder directory."
diff --git a/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb b/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb
index 6956ab1..7b2cad9 100644
--- a/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb
+++ b/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "8819384"
+    build_id: "8964254"
     target: "u-boot_pvmfw"
     source_file: "pvmfw.img"
   }
diff --git a/TEST_MAPPING b/TEST_MAPPING
index c967473..ea81bc4 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit": [
+  "avf-presubmit": [
     {
       "name": "MicrodroidHostTestCases"
     },
@@ -19,7 +19,7 @@
       "name": "compos_key_tests"
     }
   ],
-  "postsubmit": [
+  "avf-postsubmit": [
     {
       "name": "odsign_e2e_tests_full"
     },
diff --git a/apkdmverity/TEST_MAPPING b/apkdmverity/TEST_MAPPING
index 997b3f9..89750a8 100644
--- a/apkdmverity/TEST_MAPPING
+++ b/apkdmverity/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "postsubmit" : [
+  "avf-postsubmit" : [
     {
       "name" : "apkdmverity.test"
     }
diff --git a/authfs/TEST_MAPPING b/authfs/TEST_MAPPING
index 14f1824..3c84b76 100644
--- a/authfs/TEST_MAPPING
+++ b/authfs/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit": [
+  "avf-presubmit": [
     {
       "name": "authfs_device_test_src_lib"
     },
diff --git a/avmd/Android.bp b/avmd/Android.bp
index b09bed5..dc6a896 100644
--- a/avmd/Android.bp
+++ b/avmd/Android.bp
@@ -36,15 +36,11 @@
     ],
 }
 
-rust_test_host {
+rust_test {
     name: "avmdtool_tests",
     srcs: ["tests/*_test.rs"],
     test_suites: ["general-tests"],
-    prefer_rlib: true,
-    data: ["tests/data/*"],
+    compile_multilib: "first",
     data_bins: ["avmdtool"],
-    data_libs: [
-        "libcrypto",
-        "libz",
-    ],
+    data: ["tests/data/*"],
 }
diff --git a/avmd/TEST_MAPPING b/avmd/TEST_MAPPING
index dd687fe..ea58edb 100644
--- a/avmd/TEST_MAPPING
+++ b/avmd/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit" : [
+  "avf-presubmit" : [
     {
       "name" : "avmdtool_tests"
     }
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index 770f489..946bc5b 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -102,6 +102,7 @@
 
         let config_path = parameters.config_path.as_deref().unwrap_or(DEFAULT_VM_CONFIG_PATH);
         let config = VirtualMachineConfig::AppConfig(VirtualMachineAppConfig {
+            name: String::from("Compos"),
             apk: Some(apk_fd),
             idsig: Some(idsig_fd),
             instanceImage: Some(instance_fd),
diff --git a/compos/service/Android.bp b/compos/service/Android.bp
index 336ae9b..3dcf8be 100644
--- a/compos/service/Android.bp
+++ b/compos/service/Android.bp
@@ -36,6 +36,5 @@
     // Access to SystemService, ServiceManager#waitForService etc
     libs: ["services"],
     sdk_version: "",
-    platform_apis: true,
     installable: true,
 }
diff --git a/javalib/32/public/api/android.system.virtualmachine-removed.txt b/javalib/32/public/api/android.system.virtualmachine-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/javalib/32/public/api/android.system.virtualmachine-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/javalib/32/public/api/android.system.virtualmachine.txt b/javalib/32/public/api/android.system.virtualmachine.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/javalib/32/public/api/android.system.virtualmachine.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/javalib/32/system/api/android.system.virtualmachine-removed.txt b/javalib/32/system/api/android.system.virtualmachine-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/javalib/32/system/api/android.system.virtualmachine-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/javalib/32/system/api/android.system.virtualmachine.txt b/javalib/32/system/api/android.system.virtualmachine.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/javalib/32/system/api/android.system.virtualmachine.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/javalib/Android.bp b/javalib/Android.bp
index 26ad848..1df9b51 100644
--- a/javalib/Android.bp
+++ b/javalib/Android.bp
@@ -17,8 +17,11 @@
         "android.system.virtualmachine",
         "android.system.virtualizationservice",
     ],
-    // TODO(jiyong): remove the below once this gets public
-    unsafe_ignore_missing_latest_api: true,
+}
+
+prebuilt_apis {
+    name: "android-virtualization-framework-sdk",
+    api_dirs: ["32"],
 }
 
 android_app {
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index 955b350..234a5dc 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -100,21 +100,21 @@
     private final Object mLock = new Object();
 
     /** The package which owns this VM. */
-    private final @NonNull String mPackageName;
+    @NonNull private final String mPackageName;
 
     /** Name of this VM within the package. The name should be unique in the package. */
-    private final @NonNull String mName;
+    @NonNull private final String mName;
 
     /**
      * Path to the config file for this VM. The config file is where the configuration is persisted.
      */
-    private final @NonNull File mConfigFilePath;
+    @NonNull private final File mConfigFilePath;
 
     /** Path to the instance image file for this VM. */
-    private final @NonNull File mInstanceFilePath;
+    @NonNull private final File mInstanceFilePath;
 
     /** Path to the idsig file for this VM. */
-    private final @NonNull File mIdsigFilePath;
+    @NonNull private final File mIdsigFilePath;
 
     private static class ExtraApkSpec {
         public final File apk;
@@ -130,30 +130,32 @@
      * List of extra apks. Apks are specified by the vm config, and corresponding idsigs are to be
      * generated.
      */
-    private final @NonNull List<ExtraApkSpec> mExtraApks;
+    @NonNull private final List<ExtraApkSpec> mExtraApks;
 
     /** Size of the instance image. 10 MB. */
     private static final long INSTANCE_FILE_SIZE = 10 * 1024 * 1024;
 
     /** The configuration that is currently associated with this VM. */
-    private @NonNull VirtualMachineConfig mConfig;
+    @NonNull private VirtualMachineConfig mConfig;
 
     /** Handle to the "running" VM. */
-    private @Nullable IVirtualMachine mVirtualMachine;
+    @Nullable private IVirtualMachine mVirtualMachine;
 
     /** The registered callback */
     @GuardedBy("mLock")
-    private @Nullable VirtualMachineCallback mCallback;
+    @Nullable
+    private VirtualMachineCallback mCallback;
 
     /** The executor on which the callback will be executed */
     @GuardedBy("mLock")
-    private @Nullable Executor mCallbackExecutor;
+    @Nullable
+    private Executor mCallbackExecutor;
 
-    private @Nullable ParcelFileDescriptor mConsoleReader;
-    private @Nullable ParcelFileDescriptor mConsoleWriter;
+    @Nullable private ParcelFileDescriptor mConsoleReader;
+    @Nullable private ParcelFileDescriptor mConsoleWriter;
 
-    private @Nullable ParcelFileDescriptor mLogReader;
-    private @Nullable ParcelFileDescriptor mLogWriter;
+    @Nullable private ParcelFileDescriptor mLogReader;
+    @Nullable private ParcelFileDescriptor mLogWriter;
 
     private final ExecutorService mExecutorService = Executors.newCachedThreadPool();
 
@@ -181,7 +183,8 @@
      * it is persisted until it is deleted by calling {@link #delete()}. The created virtual machine
      * is in {@link Status#STOPPED} state. To run the VM, call {@link #run()}.
      */
-    /* package */ static @NonNull VirtualMachine create(
+    @NonNull
+    static VirtualMachine create(
             @NonNull Context context, @NonNull String name, @NonNull VirtualMachineConfig config)
             throws VirtualMachineException {
         if (config == null) {
@@ -232,7 +235,8 @@
     }
 
     /** Loads a virtual machine that is already created before. */
-    /* package */ static @Nullable VirtualMachine load(
+    @Nullable
+    static VirtualMachine load(
             @NonNull Context context, @NonNull String name) throws VirtualMachineException {
         File configFilePath = getConfigFilePath(context, name);
         VirtualMachineConfig config;
@@ -260,8 +264,11 @@
     /**
      * Returns the name of this virtual machine. The name is unique in the package and can't be
      * changed.
+     *
+     * @hide
      */
-    public @NonNull String getName() {
+    @NonNull
+    public String getName() {
         return mName;
     }
 
@@ -271,13 +278,21 @@
      * isolated from each other; one cannot share its secret to another virtual machine even if they
      * share the same config. It is also possible that a virtual machine can switch its config,
      * which can be done by calling {@link #setConfig(VirtualMachineConfig)}.
+     *
+     * @hide
      */
-    public @NonNull VirtualMachineConfig getConfig() {
+    @NonNull
+    public VirtualMachineConfig getConfig() {
         return mConfig;
     }
 
-    /** Returns the current status of this virtual machine. */
-    public @NonNull Status getStatus() throws VirtualMachineException {
+    /**
+     * Returns the current status of this virtual machine.
+     *
+     * @hide
+     */
+    @NonNull
+    public Status getStatus() throws VirtualMachineException {
         try {
             if (mVirtualMachine != null) {
                 switch (mVirtualMachine.getState()) {
@@ -304,6 +319,8 @@
     /**
      * Registers the callback object to get events from the virtual machine. If a callback was
      * already registered, it is replaced with the new one.
+     *
+     * @hide
      */
     public void setCallback(
             @NonNull @CallbackExecutor Executor executor,
@@ -314,7 +331,11 @@
         }
     }
 
-    /** Clears the currently registered callback. */
+    /**
+     * Clears the currently registered callback.
+     *
+     * @hide
+     */
     public void clearCallback() {
         synchronized (mLock) {
             mCallback = null;
@@ -345,6 +366,8 @@
      * Runs this virtual machine. The returning of this method however doesn't mean that the VM has
      * actually started running or the OS has booted there. Such events can be notified by
      * registering a callback object (not implemented currently).
+     *
+     * @hide
      */
     public void run() throws VirtualMachineException {
         if (getStatus() != Status.STOPPED) {
@@ -379,6 +402,7 @@
             }
 
             VirtualMachineAppConfig appConfig = getConfig().toParcel();
+            appConfig.name = mName;
 
             // Fill the idsig file by hashing the apk
             service.createOrUpdateIdsigFile(
@@ -405,13 +429,10 @@
             // The VM should only be observed to die once
             AtomicBoolean onDiedCalled = new AtomicBoolean(false);
 
-            IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
-                @Override
-                public void binderDied() {
-                    if (onDiedCalled.compareAndSet(false, true)) {
-                        executeCallback((cb) -> cb.onDied(VirtualMachine.this,
-                                VirtualMachineCallback.DEATH_REASON_VIRTUALIZATIONSERVICE_DIED));
-                    }
+            IBinder.DeathRecipient deathRecipient = () -> {
+                if (onDiedCalled.compareAndSet(false, true)) {
+                    executeCallback((cb) -> cb.onDied(VirtualMachine.this,
+                            VirtualMachineCallback.DEATH_REASON_VIRTUALIZATIONSERVICE_DIED));
                 }
             };
 
@@ -454,23 +475,31 @@
             );
             service.asBinder().linkToDeath(deathRecipient, 0);
             mVirtualMachine.start();
-        } catch (IOException e) {
-            throw new VirtualMachineException(e);
-        } catch (RemoteException e) {
+        } catch (IOException | RemoteException e) {
             throw new VirtualMachineException(e);
         }
     }
 
-    /** Returns the stream object representing the console output from the virtual machine. */
-    public @NonNull InputStream getConsoleOutputStream() throws VirtualMachineException {
+    /**
+     * Returns the stream object representing the console output from the virtual machine.
+     *
+     * @hide
+     */
+    @NonNull
+    public InputStream getConsoleOutputStream() throws VirtualMachineException {
         if (mConsoleReader == null) {
             throw new VirtualMachineException("Console output not available");
         }
         return new FileInputStream(mConsoleReader.getFileDescriptor());
     }
 
-    /** Returns the stream object representing the log output from the virtual machine. */
-    public @NonNull InputStream getLogOutputStream() throws VirtualMachineException {
+    /**
+     * Returns the stream object representing the log output from the virtual machine.
+     *
+     * @hide
+     */
+    @NonNull
+    public InputStream getLogOutputStream() throws VirtualMachineException {
         if (mLogReader == null) {
             throw new VirtualMachineException("Log output not available");
         }
@@ -482,6 +511,8 @@
      * computer; the machine halts immediately. Software running on the virtual machine is not
      * notified with the event. A stopped virtual machine can be re-started by calling {@link
      * #run()}.
+     *
+     * @hide
      */
     public void stop() throws VirtualMachineException {
         if (mVirtualMachine == null) return;
@@ -498,6 +529,8 @@
      * associated with it including the per-VM secret. This is an irreversable action. A virtual
      * machine once deleted can never be restored. A new virtual machine created with the same name
      * and the same config is different from an already deleted virtual machine.
+     *
+     * @hide
      */
     public void delete() throws VirtualMachineException {
         if (getStatus() != Status.STOPPED) {
@@ -513,8 +546,13 @@
         vmRootDir.delete();
     }
 
-    /** Returns the CID of this virtual machine, if it is running. */
-    public @NonNull Optional<Integer> getCid() throws VirtualMachineException {
+    /**
+     * Returns the CID of this virtual machine, if it is running.
+     *
+     * @hide
+     */
+    @NonNull
+    public Optional<Integer> getCid() throws VirtualMachineException {
         if (getStatus() != Status.RUNNING) {
             return Optional.empty();
         }
@@ -535,8 +573,11 @@
      * when an incompatible config is attempted.
      *
      * @return the old config
+     *
+     * @hide
      */
-    public @NonNull VirtualMachineConfig setConfig(@NonNull VirtualMachineConfig newConfig)
+    @NonNull
+    public VirtualMachineConfig setConfig(@NonNull VirtualMachineConfig newConfig)
             throws VirtualMachineException {
         final VirtualMachineConfig oldConfig = getConfig();
         if (!oldConfig.isCompatibleWith(newConfig)) {
@@ -566,6 +607,8 @@
      * expected to set up vsock servers in their payload. After the host app receives the {@link
      * VirtualMachineCallback#onPayloadReady(VirtualMachine)}, it can use this method to
      * establish an RPC session to the guest VMs.
+     *
+     * @hide
      */
     public Future<IBinder> connectToVsockServer(int port) throws VirtualMachineException {
         if (getStatus() != Status.RUNNING) {
@@ -586,7 +629,7 @@
 
     private static List<String> parseExtraApkListFromPayloadConfig(JsonReader reader)
             throws VirtualMachineException {
-        /**
+        /*
          * JSON schema from packages/modules/Virtualization/microdroid/payload/config/src/lib.rs:
          *
          * <p>{ "extra_apks": [ { "path": "/system/app/foo.apk", }, ... ], ... }
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index e0f74ec..b7c7a88 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.PackageInfoFlags;
 import android.content.pm.Signature; // This actually is certificate!
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
@@ -59,10 +60,14 @@
     private static final String KEY_CPU_AFFINITY = "cpuAffinity";
 
     // Paths to the APK file of this application.
-    private final @NonNull String mApkPath;
-    private final @NonNull Signature[] mCerts;
+    @NonNull private final String mApkPath;
+    @NonNull private final Signature[] mCerts;
 
-    /** A debug level defines the set of debug features that the VM can be configured to. */
+    /**
+     * A debug level defines the set of debug features that the VM can be configured to.
+     *
+     * @hide
+     */
     public enum DebugLevel {
         /**
          * Not debuggable at all. No log is exported from the VM. Debugger can't be attached to the
@@ -110,7 +115,7 @@
     /**
      * Path within the APK to the payload config file that defines software aspects of this config.
      */
-    private final @NonNull String mPayloadConfigPath;
+    @NonNull private final String mPayloadConfigPath;
 
     private VirtualMachineConfig(
             @NonNull String apkPath,
@@ -132,7 +137,8 @@
     }
 
     /** Loads a config from a stream, for example a file. */
-    /* package */ static @NonNull VirtualMachineConfig from(@NonNull InputStream input)
+    @NonNull
+    static VirtualMachineConfig from(@NonNull InputStream input)
             throws IOException, VirtualMachineException {
         PersistableBundle b = PersistableBundle.readFromStream(input);
         final int version = b.getInt(KEY_VERSION);
@@ -186,8 +192,13 @@
         b.writeToStream(output);
     }
 
-    /** Returns the path to the payload config within the owning application. */
-    public @NonNull String getPayloadConfigPath() {
+    /**
+     * Returns the path to the payload config within the owning application.
+     *
+     * @hide
+     */
+    @NonNull
+    public String getPayloadConfigPath() {
         return mPayloadConfigPath;
     }
 
@@ -197,6 +208,8 @@
      * number of CPUs and the size of the RAM, and change of the payload as long as the payload is
      * signed by the same signer. All other changes (e.g. using a payload from a different signer,
      * change of the debug mode, etc.) are considered as incompatible.
+     *
+     * @hide
      */
     public boolean isCompatibleWith(@NonNull VirtualMachineConfig other) {
         if (!Arrays.equals(this.mCerts, other.mCerts)) {
@@ -243,17 +256,25 @@
         return parcel;
     }
 
-    /** A builder used to create a {@link VirtualMachineConfig}. */
+    /**
+     * A builder used to create a {@link VirtualMachineConfig}.
+     *
+     * @hide
+     */
     public static class Builder {
-        private Context mContext;
-        private String mPayloadConfigPath;
+        private final Context mContext;
+        private final String mPayloadConfigPath;
         private DebugLevel mDebugLevel;
         private boolean mProtectedVm;
         private int mMemoryMib;
         private int mNumCpus;
         private String mCpuAffinity;
 
-        /** Creates a builder for the given context (APK), and the payload config file in APK. */
+        /**
+         * Creates a builder for the given context (APK), and the payload config file in APK.
+         *
+         * @hide
+         */
         public Builder(@NonNull Context context, @NonNull String payloadConfigPath) {
             mContext = Objects.requireNonNull(context);
             mPayloadConfigPath = Objects.requireNonNull(payloadConfigPath);
@@ -263,13 +284,21 @@
             mCpuAffinity = null;
         }
 
-        /** Sets the debug level */
+        /**
+         * Sets the debug level
+         *
+         * @hide
+         */
         public Builder debugLevel(DebugLevel debugLevel) {
             mDebugLevel = debugLevel;
             return this;
         }
 
-        /** Sets whether to protect the VM memory from the host. Defaults to false. */
+        /**
+         *  Sets whether to protect the VM memory from the host. Defaults to false.
+         *
+         * @hide
+         */
         public Builder protectedVm(boolean protectedVm) {
             mProtectedVm = protectedVm;
             return this;
@@ -278,6 +307,8 @@
         /**
          * Sets the amount of RAM to give the VM. If this is zero or negative then the default will
          * be used.
+         *
+         * @hide
          */
         public Builder memoryMib(int memoryMib) {
             mMemoryMib = memoryMib;
@@ -286,6 +317,8 @@
 
         /**
          * Sets the number of vCPUs in the VM. Defaults to 1.
+         *
+         * @hide
          */
         public Builder numCpus(int num) {
             mNumCpus = num;
@@ -297,24 +330,30 @@
          * or CPU ranges to run vCPUs on. e.g. "0,1-3,5" to choose host CPUs 0, 1, 2, 3, and 5.
          * Or this can be a colon-separated list of assignments of vCPU to host CPU assignments.
          * e.g. "0=0:1=1:2=2" to map vCPU 0 to host CPU 0, and so on.
+         *
+         * @hide
          */
         public Builder cpuAffinity(String affinity) {
             mCpuAffinity = affinity;
             return this;
         }
 
-        /** Builds an immutable {@link VirtualMachineConfig} */
-        public @NonNull VirtualMachineConfig build() {
+        /**
+         * Builds an immutable {@link VirtualMachineConfig}
+         *
+         * @hide
+         */
+        @NonNull
+        public VirtualMachineConfig build() {
             final String apkPath = mContext.getPackageCodePath();
             final String packageName = mContext.getPackageName();
             Signature[] certs;
             try {
-                certs =
-                        mContext.getPackageManager()
-                                .getPackageInfo(
-                                        packageName, PackageManager.GET_SIGNING_CERTIFICATES)
-                                .signingInfo
-                                .getSigningCertificateHistory();
+                certs = mContext.getPackageManager()
+                        .getPackageInfo(packageName,
+                                PackageInfoFlags.of(PackageManager.GET_SIGNING_CERTIFICATES))
+                        .signingInfo
+                        .getSigningCertificateHistory();
             } catch (PackageManager.NameNotFoundException e) {
                 // This cannot happen as `packageName` is from this app.
                 throw new RuntimeException(e);
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineManager.java b/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
index eaa383e..1ffc6bb 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
@@ -31,23 +31,29 @@
  * @hide
  */
 public class VirtualMachineManager {
-    private final @NonNull Context mContext;
+    @NonNull private final Context mContext;
 
     private VirtualMachineManager(@NonNull Context context) {
         mContext = context;
     }
 
-    static Map<Context, WeakReference<VirtualMachineManager>> sInstances = new WeakHashMap<>();
+    private static final Map<Context, WeakReference<VirtualMachineManager>> sInstances =
+            new WeakHashMap<>();
 
-    /** Returns the per-context instance. */
-    public static @NonNull VirtualMachineManager getInstance(@NonNull Context context) {
+    /**
+     * Returns the per-context instance.
+     *
+     * @hide
+     */
+    @NonNull
+    public static VirtualMachineManager getInstance(@NonNull Context context) {
         Objects.requireNonNull(context);
         synchronized (sInstances) {
             VirtualMachineManager vmm =
                     sInstances.containsKey(context) ? sInstances.get(context).get() : null;
             if (vmm == null) {
                 vmm = new VirtualMachineManager(context);
-                sInstances.put(context, new WeakReference(vmm));
+                sInstances.put(context, new WeakReference<>(vmm));
             }
             return vmm;
         }
@@ -62,8 +68,11 @@
      * machine has to be deleted before its name can be reused. Every call to this methods creates a
      * new (and different) virtual machine even if the name and the config are the same as the
      * deleted one.
+     *
+     * @hide
      */
-    public @NonNull VirtualMachine create(
+    @NonNull
+    public VirtualMachine create(
             @NonNull String name, @NonNull VirtualMachineConfig config)
             throws VirtualMachineException {
         synchronized (sCreateLock) {
@@ -74,16 +83,22 @@
     /**
      * Returns an existing {@link VirtualMachine} with the given name. Returns null if there is no
      * such virtual machine.
+     *
+     * @hide
      */
-    public @Nullable VirtualMachine get(@NonNull String name) throws VirtualMachineException {
+    @Nullable
+    public VirtualMachine get(@NonNull String name) throws VirtualMachineException {
         return VirtualMachine.load(mContext, name);
     }
 
     /**
      * Returns an existing {@link VirtualMachine} if it exists, or create a new one. The config
      * parameter is used only when a new virtual machine is created.
+     *
+     * @hide
      */
-    public @NonNull VirtualMachine getOrCreate(
+    @NonNull
+    public VirtualMachine getOrCreate(
             @NonNull String name, @NonNull VirtualMachineConfig config)
             throws VirtualMachineException {
         VirtualMachine vm;
diff --git a/libs/apkverify/TEST_MAPPING b/libs/apkverify/TEST_MAPPING
index 9248716..7224fc7 100644
--- a/libs/apkverify/TEST_MAPPING
+++ b/libs/apkverify/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit" : [
+  "avf-presubmit" : [
     {
       "name" : "libapkverify.test"
     },
diff --git a/libs/vbmeta/TEST_MAPPING b/libs/vbmeta/TEST_MAPPING
index adfcf89..e42aea0 100644
--- a/libs/vbmeta/TEST_MAPPING
+++ b/libs/vbmeta/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit" : [
+  "avf-presubmit" : [
     {
       "name" : "libvbmeta_rust.test"
     }
diff --git a/libs/vmconfig/src/lib.rs b/libs/vmconfig/src/lib.rs
index 607b347..7ca8272 100644
--- a/libs/vmconfig/src/lib.rs
+++ b/libs/vmconfig/src/lib.rs
@@ -33,6 +33,8 @@
 /// Configuration for a particular VM to be started.
 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
 pub struct VmConfig {
+    /// The name of VM.
+    pub name: Option<String>,
     /// The filename of the kernel image, if any.
     pub kernel: Option<PathBuf>,
     /// The filename of the initial ramdisk for the kernel, if any.
@@ -91,6 +93,7 @@
         } else {
             0
         };
+
         Ok(VirtualMachineRawConfig {
             kernel: maybe_open_parcel_file(&self.kernel, false)?,
             initrd: maybe_open_parcel_file(&self.initrd, false)?,
diff --git a/microdroid/README.md b/microdroid/README.md
index 6e5d709..fef71ce 100644
--- a/microdroid/README.md
+++ b/microdroid/README.md
@@ -162,8 +162,9 @@
 ## ADB
 
 On userdebug builds, you can have an adb connection to microdroid. To do so,
-first, add the `--debug=full` flag to the `/apex/com.android.virt/bin/vm
-run-app` command, and then
+first, delete `$TEST_ROOT/instance.img`; this is because changing debug settings
+requires a new instance. Then add the `--debug=full` flag to the
+`/apex/com.android.virt/bin/vm run-app` command, and then
 
 ```sh
 adb forward tcp:8000 vsock:$CID:5555
diff --git a/microdroid/init.rc b/microdroid/init.rc
index b0e5e46..42aa983 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -88,6 +88,10 @@
 
     setprop ro.debuggable ${ro.boot.microdroid.debuggable:-0}
 
+on property:dev.bootcomplete=1
+    # Stop ueventd to save memory
+    stop ueventd
+
 on init && property:ro.boot.microdroid.debuggable=1
     # Mount tracefs (with GID=AID_READTRACEFS)
     mount tracefs tracefs /sys/kernel/tracing gid=3012
diff --git a/microdroid/kdump/Android.bp b/microdroid/kdump/Android.bp
index 390886b..cc681a7 100644
--- a/microdroid/kdump/Android.bp
+++ b/microdroid/kdump/Android.bp
@@ -4,9 +4,10 @@
 
 cc_binary {
     name: "microdroid_kexec",
-    stem: "kexec",
+    stem: "kexec_load",
     srcs: ["kexec.c"],
     installable: false,
+    static_executable: true, // required because this runs before linkerconfig
     compile_multilib: "64",
 }
 
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index c226e9d..9e1890f 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -166,6 +166,8 @@
     let _ = kernlog::init();
     info!("started.");
 
+    load_crashkernel_if_supported().context("Failed to load crashkernel")?;
+
     let service = get_vms_rpc_binder()
         .context("cannot connect to VirtualMachineService")
         .map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
@@ -610,6 +612,20 @@
     Ok(serde_json::from_reader(file)?)
 }
 
+/// Loads the crashkernel into memory using kexec if the VM is loaded with `crashkernel=' parameter
+/// in the cmdline.
+fn load_crashkernel_if_supported() -> Result<()> {
+    let supported = std::fs::read_to_string("/proc/cmdline")?.contains(" crashkernel=");
+    info!("ramdump supported: {}", supported);
+    if supported {
+        let status = Command::new("/system/bin/kexec_load").status()?;
+        if !status.success() {
+            return Err(anyhow!("Failed to load crashkernel: {:?}", status));
+        }
+    }
+    Ok(())
+}
+
 /// Executes the given task. Stdout of the task is piped into the vsock stream to the
 /// virtualizationservice in the host side.
 fn exec_task(task: &Task, service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
diff --git a/pvmfw/pvmfw.img b/pvmfw/pvmfw.img
index b360bda..7cc8009 100644
--- a/pvmfw/pvmfw.img
+++ b/pvmfw/pvmfw.img
Binary files differ
diff --git a/rialto/TEST_MAPPING b/rialto/TEST_MAPPING
index 8e3c085..88f616e 100644
--- a/rialto/TEST_MAPPING
+++ b/rialto/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit": [
+  "avf-presubmit": [
     {
       "name": "rialto_test"
     }
diff --git a/rialto/tests/test.rs b/rialto/tests/test.rs
index fb6a1ad..8a78861 100644
--- a/rialto/tests/test.rs
+++ b/rialto/tests/test.rs
@@ -54,6 +54,7 @@
     let log = android_log_fd()?;
 
     let config = VirtualMachineConfig::RawConfig(VirtualMachineRawConfig {
+        name: String::from("RialtoTest"),
         kernel: None,
         initrd: None,
         params: None,
diff --git a/tests/aidl/Android.bp b/tests/aidl/Android.bp
index 893ec0b..d59ca7e 100644
--- a/tests/aidl/Android.bp
+++ b/tests/aidl/Android.bp
@@ -8,7 +8,6 @@
     unstable: true,
     backend: {
         java: {
-            platform_apis: true,
             gen_rpc: true,
         },
         cpp: {
diff --git a/tests/helper/Android.bp b/tests/helper/Android.bp
index e7760e2..200eb42 100644
--- a/tests/helper/Android.bp
+++ b/tests/helper/Android.bp
@@ -18,5 +18,4 @@
         "truth-prebuilt",
     ],
     libs: ["android.system.virtualmachine"],
-    platform_apis: true,
 }
diff --git a/tests/helper/src/java/com/android/microdroid/test/MicrodroidDeviceTestBase.java b/tests/helper/src/java/com/android/microdroid/test/MicrodroidDeviceTestBase.java
index efd7c85..1a573bb 100644
--- a/tests/helper/src/java/com/android/microdroid/test/MicrodroidDeviceTestBase.java
+++ b/tests/helper/src/java/com/android/microdroid/test/MicrodroidDeviceTestBase.java
@@ -23,7 +23,6 @@
 import android.os.ParcelFileDescriptor;
 import android.os.SystemProperties;
 import android.sysprop.HypervisorProperties;
-import android.system.virtualizationservice.DeathReason;
 import android.system.virtualmachine.VirtualMachine;
 import android.system.virtualmachine.VirtualMachineCallback;
 import android.system.virtualmachine.VirtualMachineConfig;
@@ -223,7 +222,7 @@
 
         @Override
         @CallSuper
-        public void onDied(VirtualMachine vm, @DeathReason int reason) {
+        public void onDied(VirtualMachine vm, int reason) {
             mExecutorService.shutdown();
         }
 
@@ -318,7 +317,7 @@
         listener.runToFinish(logTag, vm);
         return new BootResult(
                 payloadStarted.getNow(false),
-                deathReason.getNow(DeathReason.INFRASTRUCTURE_ERROR),
+                deathReason.getNow(VirtualMachineCallback.DEATH_REASON_INFRASTRUCTURE_ERROR),
                 apiCallNanoTime,
                 endTime.getNow(apiCallNanoTime) - apiCallNanoTime,
                 listener.getVcpuStartedNanoTime(),
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index 5ce19bd..d77f3de 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -15,6 +15,7 @@
     static_libs: [
         "MicrodroidHostTestHelper",
         "compatibility-host-util",
+        "cts-statsd-atom-host-test-utils",
     ],
     per_testcase_directory: true,
     data: [
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
index 722ec74..5ac36a0 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
@@ -25,17 +25,24 @@
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.ReportUtils;
+
 import com.android.compatibility.common.util.CddTest;
+import com.android.os.AtomsProto;
+import com.android.os.StatsLog;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.result.TestDescription;
 import com.android.tradefed.result.TestResult;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
 import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
 import com.android.tradefed.util.CommandResult;
 import com.android.tradefed.util.FileUtil;
@@ -58,6 +65,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -80,6 +88,7 @@
 
     @Rule public TestLogData mTestLogs = new TestLogData();
     @Rule public TestName mTestName = new TestName();
+    @Rule public TestMetrics mMetrics = new TestMetrics();
 
     private int minMemorySize() throws DeviceNotAvailableException {
         CommandRunner android = new CommandRunner(getDevice());
@@ -95,8 +104,7 @@
     }
 
     private boolean isProtectedVmSupported() throws DeviceNotAvailableException {
-        return getDevice().getBooleanProperty("ro.boot.hypervisor.protected_vm.supported",
-                false);
+        return getDevice().getBooleanProperty("ro.boot.hypervisor.protected_vm.supported", false);
     }
 
     private void waitForBootComplete() {
@@ -104,29 +112,27 @@
     }
 
     @Test
-    @CddTest(requirements = {
-            "9.17/C-1-1",
-            "9.17/C-1-2",
-            "9.17/C-1-4"
-    })
+    @CddTest(requirements = {"9.17/C-1-1", "9.17/C-1-2", "9.17/C-1-4"})
     public void testCreateVmRequiresPermission() throws Exception {
         // Revoke the MANAGE_VIRTUAL_MACHINE permission for the test app
         CommandRunner android = new CommandRunner(getDevice());
         android.run("pm", "revoke", PACKAGE_NAME, "android.permission.MANAGE_VIRTUAL_MACHINE");
 
         // Run MicrodroidTests#connectToVmService test, which should fail
-        final DeviceTestRunOptions options = new DeviceTestRunOptions(PACKAGE_NAME)
-                .setTestClassName(PACKAGE_NAME + ".MicrodroidTests")
-                .setTestMethodName("connectToVmService[protectedVm=false]")
-                .setCheckResults(false);
+        final DeviceTestRunOptions options =
+                new DeviceTestRunOptions(PACKAGE_NAME)
+                        .setTestClassName(PACKAGE_NAME + ".MicrodroidTests")
+                        .setTestMethodName("connectToVmService[protectedVm=false]")
+                        .setCheckResults(false);
         assertFalse(runDeviceTests(options));
 
         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",
+        assertTrue(
+                "The test should fail with a permission error",
                 result.getStackTrace()
-                .contains("android.permission.MANAGE_VIRTUAL_MACHINE permission"));
+                        .contains("android.permission.MANAGE_VIRTUAL_MACHINE permission"));
     }
 
     private static JSONObject newPartition(String label, String path) {
@@ -152,24 +158,23 @@
         command.add(signingKey.getPath());
         command.add(virtApexDir.getPath());
 
-        CommandResult result = runUtil.runTimedCmd(
-                                    // sign_virt_apex is so slow on CI server that this often times
-                                    // out. Until we can make it fast, use 50s for timeout
-                                    50 * 1000,
-                                    "/bin/bash",
-                                    "-c",
-                                    String.join(" ", command));
+        CommandResult result =
+                runUtil.runTimedCmd(
+                        // sign_virt_apex is so slow on CI server that this often times
+                        // out. Until we can make it fast, use 50s for timeout
+                        50 * 1000, "/bin/bash", "-c", String.join(" ", command));
         String out = result.getStdout();
         String err = result.getStderr();
         assertWithMessage(
-                "resigning the Virt APEX failed:\n\tout: " + out + "\n\terr: " + err + "\n")
+                        "resigning the Virt APEX failed:\n\tout: " + out + "\n\terr: " + err + "\n")
                 .about(command_results())
                 .that(result)
                 .isSuccess();
     }
 
-    private static <T> void assertThatEventually(long timeoutMillis, Callable<T> callable,
-            org.hamcrest.Matcher<T> matcher) throws Exception {
+    private static <T> void assertThatEventually(
+            long timeoutMillis, Callable<T> callable, org.hamcrest.Matcher<T> matcher)
+            throws Exception {
         long start = System.currentTimeMillis();
         while (true) {
             try {
@@ -188,6 +193,7 @@
     static class ActiveApexInfo {
         public String name;
         public String path;
+
         ActiveApexInfo(String name, String path) {
             this.name = name;
             this.path = path;
@@ -196,11 +202,13 @@
 
     static class ActiveApexInfoList {
         private List<ActiveApexInfo> mList;
+
         ActiveApexInfoList(List<ActiveApexInfo> list) {
             this.mList = list;
         }
+
         ActiveApexInfo get(String apexName) {
-            for (ActiveApexInfo info: mList) {
+            for (ActiveApexInfo info : mList) {
                 if (info.name.equals(apexName)) {
                     return info;
                 }
@@ -217,12 +225,14 @@
             protected DefaultHandler createXmlHandler() {
                 return new DefaultHandler() {
                     @Override
-                    public void startElement(String uri, String localName, String qName,
-                            Attributes attributes) {
+                    public void startElement(
+                            String uri, String localName, String qName, Attributes attributes) {
                         if (localName.equals("apex-info")
                                 && attributes.getValue("isActive").equals("true")) {
-                            list.add(new ActiveApexInfo(attributes.getValue("moduleName"),
-                                    attributes.getValue("modulePath")));
+                            list.add(
+                                    new ActiveApexInfo(
+                                            attributes.getValue("moduleName"),
+                                            attributes.getValue("modulePath")));
                         }
                     }
                 };
@@ -231,8 +241,12 @@
         return new ActiveApexInfoList(list);
     }
 
-    private String runMicrodroidWithResignedImages(File key, Map<String, File> keyOverrides,
-            boolean isProtected, boolean daemonize, String consolePath)
+    private String runMicrodroidWithResignedImages(
+            File key,
+            Map<String, File> keyOverrides,
+            boolean isProtected,
+            boolean daemonize,
+            String consolePath)
             throws Exception {
         CommandRunner android = new CommandRunner(getDevice());
 
@@ -256,8 +270,12 @@
 
         // Create the instance image for the VM
         final String instanceImgPath = TEST_ROOT + "instance.img";
-        android.run(VIRT_APEX + "bin/vm", "create-partition", "--type instance",
-                instanceImgPath, Integer.toString(10 * 1024 * 1024));
+        android.run(
+                VIRT_APEX + "bin/vm",
+                "create-partition",
+                "--type instance",
+                instanceImgPath,
+                Integer.toString(10 * 1024 * 1024));
 
         // payload-metadata is prepared on host with the two APEXes and APK
         final String payloadMetadataPath = TEST_ROOT + "payload-metadata.img";
@@ -299,7 +317,8 @@
         // Add partitions to the second disk
         final String vbmetaPath = TEST_ROOT + "etc/fs/microdroid_vbmeta_bootconfig.img";
         final String bootconfigPath = TEST_ROOT + "etc/fs/microdroid_bootconfig.full_debuggable";
-        disks.getJSONObject(1).getJSONArray("partitions")
+        disks.getJSONObject(1)
+                .getJSONArray("partitions")
                 .put(newPartition("vbmeta", vbmetaPath))
                 .put(newPartition("bootconfig", bootconfigPath))
                 .put(newPartition("vm-instance", instanceImgPath));
@@ -308,12 +327,17 @@
         // - payload-metadata
         // - apexes: com.android.os.statsd, com.android.adbd
         // - apk and idsig
-        disks.put(new JSONObject().put("writable", false).put("partitions", new JSONArray()
-                .put(newPartition("payload-metadata", payloadMetadataPath))
-                .put(newPartition("com.android.os.statsd", statsdApexPath))
-                .put(newPartition("com.android.adbd", adbdApexPath))
-                .put(newPartition("microdroid-apk", apkPath))
-                .put(newPartition("microdroid-apk-idsig", idSigPath))));
+        disks.put(
+                new JSONObject()
+                        .put("writable", false)
+                        .put(
+                                "partitions",
+                                new JSONArray()
+                                        .put(newPartition("payload-metadata", payloadMetadataPath))
+                                        .put(newPartition("com.android.os.statsd", statsdApexPath))
+                                        .put(newPartition("com.android.adbd", adbdApexPath))
+                                        .put(newPartition("microdroid-apk", apkPath))
+                                        .put(newPartition("microdroid-apk-idsig", idSigPath))));
 
         config.put("protected", isProtected);
 
@@ -322,13 +346,14 @@
         getDevice().pushString(config.toString(), configPath);
 
         final String logPath = LOG_PATH;
-        final String ret = android.runWithTimeout(
-                60 * 1000,
-                VIRT_APEX + "bin/vm run",
-                daemonize ? "--daemonize" : "",
-                (consolePath != null) ? "--console " + consolePath : "",
-                "--log " + logPath,
-                configPath);
+        final String ret =
+                android.runWithTimeout(
+                        60 * 1000,
+                        VIRT_APEX + "bin/vm run",
+                        daemonize ? "--daemonize" : "",
+                        (consolePath != null) ? "--console " + consolePath : "",
+                        "--log " + logPath,
+                        configPath);
         Pattern pattern = Pattern.compile("with CID (\\d+)");
         Matcher matcher = pattern.matcher(ret);
         assertTrue(matcher.find());
@@ -336,11 +361,7 @@
     }
 
     @Test
-    @CddTest(requirements = {
-            "9.17/C-2-1",
-            "9.17/C-2-2",
-            "9.17/C-2-6"
-    })
+    @CddTest(requirements = {"9.17/C-2-1", "9.17/C-2-2", "9.17/C-2-6"})
     public void testBootFailsWhenProtectedVmStartsWithImagesSignedWithDifferentKey()
             throws Exception {
         assumeTrue(isProtectedVmSupported());
@@ -348,18 +369,14 @@
         File key = findTestFile("test.com.android.virt.pem");
         Map<String, File> keyOverrides = Map.of();
         boolean isProtected = true;
-        boolean daemonize = false;  // VM should shut down due to boot failure.
+        boolean daemonize = false; // VM should shut down due to boot failure.
         String consolePath = TEST_ROOT + "console";
         runMicrodroidWithResignedImages(key, keyOverrides, isProtected, daemonize, consolePath);
-        assertThat(getDevice().pullFileContents(consolePath),
-                containsString("pvmfw boot failed"));
+        assertThat(getDevice().pullFileContents(consolePath), containsString("pvmfw boot failed"));
     }
 
     @Test
-    @CddTest(requirements = {
-            "9.17/C-2-2",
-            "9.17/C-2-6"
-    })
+    @CddTest(requirements = {"9.17/C-2-2", "9.17/C-2-6"})
     public void testBootSucceedsWhenNonProtectedVmStartsWithImagesSignedWithDifferentKey()
             throws Exception {
         File key = findTestFile("test.com.android.virt.pem");
@@ -367,57 +384,54 @@
         boolean isProtected = false;
         boolean daemonize = true;
         String consolePath = TEST_ROOT + "console";
-        String cid = runMicrodroidWithResignedImages(key, keyOverrides, isProtected, daemonize,
-                consolePath);
+        String cid =
+                runMicrodroidWithResignedImages(
+                        key, keyOverrides, isProtected, daemonize, consolePath);
         // Adb connection to the microdroid means that boot succeeded.
         adbConnectToMicrodroid(getDevice(), cid);
         shutdownMicrodroid(getDevice(), cid);
     }
 
     @Test
-    @CddTest(requirements = {
-            "9.17/C-2-2",
-            "9.17/C-2-6"
-    })
-    public void testBootFailsWhenBootloaderAndVbMetaAreSignedWithDifferentKeys()
-            throws Exception {
+    @CddTest(requirements = {"9.17/C-2-2", "9.17/C-2-6"})
+    public void testBootFailsWhenBootloaderAndVbMetaAreSignedWithDifferentKeys() throws Exception {
         // Sign everything with key1 except vbmeta
         File key = findTestFile("test.com.android.virt.pem");
         File key2 = findTestFile("test2.com.android.virt.pem");
-        Map<String, File> keyOverrides = Map.of(
-                "microdroid_vbmeta.img", key2);
-        boolean isProtected = false;  // Not interested in pvwfw
-        boolean daemonize = true;  // Bootloader fails and enters prompts.
-                                   // To be able to stop it, it should be a daemon.
+        Map<String, File> keyOverrides = Map.of("microdroid_vbmeta.img", key2);
+        boolean isProtected = false; // Not interested in pvwfw
+        boolean daemonize = true; // Bootloader fails and enters prompts.
+        // To be able to stop it, it should be a daemon.
         String consolePath = TEST_ROOT + "console";
-        String cid = runMicrodroidWithResignedImages(key, keyOverrides, isProtected, daemonize,
-                consolePath);
+        String cid =
+                runMicrodroidWithResignedImages(
+                        key, keyOverrides, isProtected, daemonize, consolePath);
         // Wail for a while so that bootloader prints errors to console
-        assertThatEventually(10000, () -> getDevice().pullFileContents(consolePath),
+        assertThatEventually(
+                10000,
+                () -> getDevice().pullFileContents(consolePath),
                 containsString("Public key was rejected"));
         shutdownMicrodroid(getDevice(), cid);
     }
 
     @Test
-    @CddTest(requirements = {
-            "9.17/C-2-2",
-            "9.17/C-2-6"
-    })
-    public void testBootSucceedsWhenBootloaderAndVbmetaHaveSameSigningKeys()
-            throws Exception {
+    @CddTest(requirements = {"9.17/C-2-2", "9.17/C-2-6"})
+    public void testBootSucceedsWhenBootloaderAndVbmetaHaveSameSigningKeys() throws Exception {
         // Sign everything with key1 except bootloader and vbmeta
         File key = findTestFile("test.com.android.virt.pem");
         File key2 = findTestFile("test2.com.android.virt.pem");
-        Map<String, File> keyOverrides = Map.of(
-                "microdroid_bootloader", key2,
-                "microdroid_vbmeta.img", key2,
-                "microdroid_vbmeta_bootconfig.img", key2);
-        boolean isProtected = false;  // Not interested in pvwfw
-        boolean daemonize = true;  // Bootloader should succeed.
-                                   // To be able to stop it, it should be a daemon.
+        Map<String, File> keyOverrides =
+                Map.of(
+                        "microdroid_bootloader", key2,
+                        "microdroid_vbmeta.img", key2,
+                        "microdroid_vbmeta_bootconfig.img", key2);
+        boolean isProtected = false; // Not interested in pvwfw
+        boolean daemonize = true; // Bootloader should succeed.
+        // To be able to stop it, it should be a daemon.
         String consolePath = TEST_ROOT + "console";
-        String cid = runMicrodroidWithResignedImages(key, keyOverrides, isProtected, daemonize,
-                consolePath);
+        String cid =
+                runMicrodroidWithResignedImages(
+                        key, keyOverrides, isProtected, daemonize, consolePath);
         // Adb connection to the microdroid means that boot succeeded.
         adbConnectToMicrodroid(getDevice(), cid);
         shutdownMicrodroid(getDevice(), cid);
@@ -439,17 +453,17 @@
                         Optional.of(CPU_AFFINITY));
         // check until microdroid is shut down
         CommandRunner android = new CommandRunner(getDevice());
-        android.runWithTimeout(
-                15000,
-                "logcat",
-                "-m",
-                "1",
-                "-e",
-                "'crosvm has exited normally'");
+        android.runWithTimeout(15000, "logcat", "-m", "1", "-e", "'crosvm has exited normally'");
         // Check that tombstone is received (from host logcat)
-        String result = runOnHost("adb", "-s", getDevice().getSerialNumber(),
-                "logcat", "-d", "-e",
-                "Received [0-9]+ bytes from guest & wrote to tombstone file");
+        String result =
+                runOnHost(
+                        "adb",
+                        "-s",
+                        getDevice().getSerialNumber(),
+                        "logcat",
+                        "-d",
+                        "-e",
+                        "Received [0-9]+ bytes from guest & wrote to tombstone file");
         return !result.trim().isEmpty();
     }
 
@@ -464,11 +478,90 @@
     }
 
     @Test
-    @CddTest(requirements = {
-            "9.17/C-1-1",
-            "9.17/C-1-2",
-            "9.17/C/1-3"
-    })
+    public void testTelemetryPushedAtoms() throws Exception {
+        // Reset statsd config and report before the test
+        ConfigUtils.removeConfig(getDevice());
+        ReportUtils.clearReports(getDevice());
+
+        // Setup statsd config
+        int[] atomIds = {
+            AtomsProto.Atom.VM_CREATION_REQUESTED_FIELD_NUMBER,
+            AtomsProto.Atom.VM_BOOTED_FIELD_NUMBER,
+            AtomsProto.Atom.VM_EXITED_FIELD_NUMBER,
+        };
+        ConfigUtils.uploadConfigForPushedAtoms(getDevice(), PACKAGE_NAME, atomIds);
+
+        // Create VM with microdroid
+        final String configPath = "assets/vm_config_apex.json"; // path inside the APK
+        final String cid =
+                startMicrodroid(
+                        getDevice(),
+                        getBuild(),
+                        APK_NAME,
+                        PACKAGE_NAME,
+                        configPath,
+                        /* debug */ true,
+                        minMemorySize(),
+                        Optional.of(NUM_VCPUS),
+                        Optional.of(CPU_AFFINITY));
+
+        // Check VmCreationRequested atom and clear the statsd report
+        List<StatsLog.EventMetricData> data;
+        data = ReportUtils.getEventMetricDataList(getDevice());
+        assertEquals(1, data.size());
+        assertEquals(
+                AtomsProto.Atom.VM_CREATION_REQUESTED_FIELD_NUMBER,
+                data.get(0).getAtom().getPushedCase().getNumber());
+        AtomsProto.VmCreationRequested atomVmCreationRequested =
+                data.get(0).getAtom().getVmCreationRequested();
+        assertEquals(
+                AtomsProto.VmCreationRequested.Hypervisor.PKVM,
+                atomVmCreationRequested.getHypervisor());
+        assertFalse(atomVmCreationRequested.getIsProtected());
+        assertTrue(atomVmCreationRequested.getCreationSucceeded());
+        assertEquals(0, atomVmCreationRequested.getBinderExceptionCode());
+        assertEquals("VmRunApp", atomVmCreationRequested.getVmIdentifier());
+        assertEquals(
+                AtomsProto.VmCreationRequested.ConfigType.VIRTUAL_MACHINE_APP_CONFIG,
+                atomVmCreationRequested.getConfigType());
+        assertEquals(NUM_VCPUS, atomVmCreationRequested.getNumCpus());
+        assertEquals(CPU_AFFINITY, atomVmCreationRequested.getCpuAffinity());
+        assertEquals(minMemorySize(), atomVmCreationRequested.getMemoryMib());
+        assertEquals(
+                "com.android.art:com.android.compos:com.android.sdkext",
+                atomVmCreationRequested.getApexes());
+
+        // Boot VM with microdroid
+        adbConnectToMicrodroid(getDevice(), cid);
+        waitForBootComplete();
+
+        // Check VmBooted atom and clear the statsd report
+        data = ReportUtils.getEventMetricDataList(getDevice());
+        assertEquals(1, data.size());
+        assertEquals(
+                AtomsProto.Atom.VM_BOOTED_FIELD_NUMBER,
+                data.get(0).getAtom().getPushedCase().getNumber());
+        AtomsProto.VmBooted atomVmBooted = data.get(0).getAtom().getVmBooted();
+        assertEquals("VmRunApp", atomVmBooted.getVmIdentifier());
+
+        // Shutdown VM with microdroid
+        shutdownMicrodroid(getDevice(), cid);
+        // TODO: make sure the VM is completely shut down while 'vm stop' command running.
+        Thread.sleep(1000);
+
+        // Check VmExited atom and clear the statsd report
+        data = ReportUtils.getEventMetricDataList(getDevice());
+        assertEquals(1, data.size());
+        assertEquals(
+                AtomsProto.Atom.VM_EXITED_FIELD_NUMBER,
+                data.get(0).getAtom().getPushedCase().getNumber());
+        AtomsProto.VmExited atomVmExited = data.get(0).getAtom().getVmExited();
+        assertEquals("VmRunApp", atomVmExited.getVmIdentifier());
+        assertEquals(AtomsProto.VmExited.DeathReason.KILLED, atomVmExited.getDeathReason());
+    }
+
+    @Test
+    @CddTest(requirements = {"9.17/C-1-1", "9.17/C-1-2", "9.17/C/1-3"})
     public void testMicrodroidBoots() throws Exception {
         final String configPath = "assets/vm_config.json"; // path inside the APK
         final String cid =
@@ -506,10 +599,11 @@
 
         // Check that no denials have happened so far
         CommandRunner android = new CommandRunner(getDevice());
-        assertThat(android.tryRun("egrep", "'avc:[[:space:]]{1,2}denied'", LOG_PATH),
-                is(nullValue()));
+        assertThat(
+                android.tryRun("egrep", "'avc:[[:space:]]{1,2}denied'", LOG_PATH), is(nullValue()));
 
-        assertThat(runOnMicrodroid("cat /proc/cpuinfo | grep processor | wc -l"),
+        assertThat(
+                runOnMicrodroid("cat /proc/cpuinfo | grep processor | wc -l"),
                 is(Integer.toString(NUM_VCPUS)));
 
         // Check that selinux is enabled
@@ -543,6 +637,92 @@
         shutdownMicrodroid(getDevice(), cid);
     }
 
+    private static class ProcessInfo {
+        public final String mName;
+        public final int mPid;
+
+        ProcessInfo(String name, int pid) {
+            mName = name;
+            mPid = pid;
+        }
+    }
+
+    private Map<String, Long> parseMemInfo(String file) {
+        Map<String, Long> stats = new HashMap<>();
+        file.lines().forEach(line -> {
+            if (line.endsWith(" kB")) line = line.substring(0, line.length() - 3);
+
+            String[] elems = line.split(":");
+            assertThat(elems.length).isEqualTo(2);
+            stats.put(elems[0].trim(), Long.parseLong(elems[1].trim()));
+        });
+        return stats;
+    }
+
+    private String skipFirstLine(String str) {
+        int index = str.indexOf("\n");
+        return (index < 0) ? "" : str.substring(index + 1);
+    }
+
+    private List<ProcessInfo> getRunningProcessesList() {
+        List<ProcessInfo> list = new ArrayList<ProcessInfo>();
+        skipFirstLine(runOnMicrodroid("ps", "-Ao", "PID,NAME")).lines().forEach(ps -> {
+            // Each line is '  <pid> <name>'.
+            ps = ps.trim();
+            int space = ps.indexOf(" ");
+            list.add(new ProcessInfo(
+                    ps.substring(space + 1),
+                    Integer.parseInt(ps.substring(0, space))));
+        });
+
+        return list;
+    }
+
+    private Map<String, Long> getProcMemInfo() {
+        return parseMemInfo(runOnMicrodroid("cat", "/proc/meminfo"));
+    }
+
+    private Map<String, Long> getProcSmapsRollup(int pid) {
+        String path = "/proc/" + pid + "/smaps_rollup";
+        return  parseMemInfo(skipFirstLine(runOnMicrodroid("cat", path, "||", "true")));
+    }
+
+    @Test
+    public void testMicrodroidRamUsage() throws Exception {
+        final String configPath = "assets/vm_config.json";
+        final String cid =
+                startMicrodroid(
+                        getDevice(),
+                        getBuild(),
+                        APK_NAME,
+                        PACKAGE_NAME,
+                        configPath,
+                        /* debug */ true,
+                        minMemorySize(),
+                        Optional.of(NUM_VCPUS),
+                        Optional.of(CPU_AFFINITY));
+        adbConnectToMicrodroid(getDevice(), cid);
+        waitForBootComplete();
+        rootMicrodroid();
+
+        for (Map.Entry<String, Long> stat : getProcMemInfo().entrySet()) {
+            mMetrics.addTestMetric(
+                    "avf_perf/microdroid/meminfo/" + stat.getKey().toLowerCase(),
+                    stat.getValue().toString());
+        }
+
+        for (ProcessInfo proc : getRunningProcessesList()) {
+            for (Map.Entry<String, Long> stat : getProcSmapsRollup(proc.mPid).entrySet()) {
+                String name = stat.getKey().toLowerCase();
+                mMetrics.addTestMetric(
+                        "avf_perf/microdroid/smaps/" + name + "/" + proc.mName,
+                        stat.getValue().toString());
+            }
+        }
+
+        shutdownMicrodroid(getDevice(), cid);
+    }
+
     @Test
     public void testCustomVirtualMachinePermission()
             throws DeviceNotAvailableException, IOException, JSONException {
@@ -571,8 +751,10 @@
         final String ret =
                 android.runForResult(VIRT_APEX + "bin/vm run", configPath).getStderr().trim();
 
-        assertThat(ret).contains("does not have the android.permission.USE_CUSTOM_VIRTUAL_MACHINE"
-                + " permission");
+        assertThat(ret)
+                .contains(
+                        "does not have the android.permission.USE_CUSTOM_VIRTUAL_MACHINE"
+                                + " permission");
     }
 
     @Before
@@ -591,13 +773,17 @@
     public void shutdown() throws Exception {
         cleanUpVirtualizationTestSetup(getDevice());
 
-        archiveLogThenDelete(mTestLogs, getDevice(), LOG_PATH,
-                "vm.log-" + mTestName.getMethodName());
+        archiveLogThenDelete(
+                mTestLogs, getDevice(), LOG_PATH, "vm.log-" + mTestName.getMethodName());
 
         getDevice().uninstallPackage(PACKAGE_NAME);
 
         // testCustomVirtualMachinePermission revokes this permission. Grant it again as cleanup
-        new CommandRunner(getDevice()).tryRun(
-                "pm", "grant", SHELL_PACKAGE_NAME, "android.permission.USE_CUSTOM_VIRTUAL_MACHINE");
+        new CommandRunner(getDevice())
+                .tryRun(
+                        "pm",
+                        "grant",
+                        SHELL_PACKAGE_NAME,
+                        "android.permission.USE_CUSTOM_VIRTUAL_MACHINE");
     }
 }
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 29a74ca..60912ea 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -25,6 +25,7 @@
     use_embedded_native_libs: true,
     // We only support 64-bit ABI, but CTS demands all APKs to be multi-ABI.
     compile_multilib: "both",
+    min_sdk_version: "33",
 }
 
 // TODO(jiyong): make this a binary, not a shared library
diff --git a/tests/testapk/AndroidManifest.xml b/tests/testapk/AndroidManifest.xml
index bc955d2..9c8b2d5 100644
--- a/tests/testapk/AndroidManifest.xml
+++ b/tests/testapk/AndroidManifest.xml
@@ -16,6 +16,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.android.microdroid.test">
     <uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
+    <uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
     <application>
         <uses-library android:name="android.system.virtualmachine" android:required="false" />
     </application>
diff --git a/tests/testapk/assets/vm_config_apex.json b/tests/testapk/assets/vm_config_apex.json
new file mode 100644
index 0000000..0f100aa
--- /dev/null
+++ b/tests/testapk/assets/vm_config_apex.json
@@ -0,0 +1,25 @@
+{
+  "os": {
+    "name": "microdroid"
+  },
+  "task": {
+    "type": "microdroid_launcher",
+    "command": "MicrodroidTestNativeLib.so",
+    "args": [
+      "hello",
+      "microdroid"
+    ]
+  },
+  "apexes": [
+    {
+      "name": "com.android.art"
+    },
+    {
+      "name": "com.android.compos"
+    },
+    {
+      "name": "com.android.sdkext"
+    }
+  ],
+  "export_tombstones": true
+}
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 db2ca0f..2c6d139 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -23,8 +23,8 @@
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemProperties;
-import android.system.virtualizationservice.DeathReason;
 import android.system.virtualmachine.VirtualMachine;
+import android.system.virtualmachine.VirtualMachineCallback;
 import android.system.virtualmachine.VirtualMachineConfig;
 import android.system.virtualmachine.VirtualMachineConfig.DebugLevel;
 import android.system.virtualmachine.VirtualMachineException;
@@ -172,13 +172,14 @@
         VmEventListener listener =
                 new VmEventListener() {
                     @Override
-                    public void onDied(VirtualMachine vm, @DeathReason int reason) {
+                    public void onDied(VirtualMachine vm,  int reason) {
                         exception.complete(reason);
                         super.onDied(vm, reason);
                     }
                 };
         listener.runToFinish(TAG, vm);
-        assertThat(exception.getNow(0)).isAnyOf(DeathReason.REBOOT, DeathReason.HANGUP);
+        assertThat(exception.getNow(0)).isAnyOf(VirtualMachineCallback.DEATH_REASON_REBOOT,
+                VirtualMachineCallback.DEATH_REASON_HANGUP);
     }
 
     @Test
@@ -358,9 +359,9 @@
         List<DataItem> rootArrayItems = ((Array) dataItems.get(0)).getDataItems();
         assertThat(rootArrayItems.size()).isAtLeast(2); // Public key and one certificate
         if (mProtectedVm) {
-            // When a true BCC is created, microdroid expects entries for at least: the root public
-            // key, pvmfw, u-boot, u-boot-env, microdroid, app payload and the service process.
-            assertThat(rootArrayItems.size()).isAtLeast(7);
+            // When a true DICE chain is created, microdroid expects entries for: u-boot,
+            // u-boot-env, microdroid, app payload and the service process.
+            assertThat(rootArrayItems.size()).isAtLeast(5);
         }
     }
 
@@ -434,7 +435,7 @@
         assertThat(result.payloadStarted).isFalse();
 
         // This failure should shut the VM down immediately and shouldn't trigger a hangup.
-        assertThat(result.deathReason).isNotEqualTo(DeathReason.HANGUP);
+        assertThat(result.deathReason).isNotEqualTo(VirtualMachineCallback.DEATH_REASON_HANGUP);
     }
 
     @Test
@@ -502,6 +503,7 @@
 
         BootResult bootResult = tryBootVm(TAG, "test_vm_invalid_config");
         assertThat(bootResult.payloadStarted).isFalse();
-        assertThat(bootResult.deathReason).isEqualTo(DeathReason.MICRODROID_INVALID_PAYLOAD_CONFIG);
+        assertThat(bootResult.deathReason).isEqualTo(
+                VirtualMachineCallback.DEATH_REASON_MICRODROID_INVALID_PAYLOAD_CONFIG);
     }
 }
diff --git a/virtualizationservice/TEST_MAPPING b/virtualizationservice/TEST_MAPPING
index 6456a98..8388ff2 100644
--- a/virtualizationservice/TEST_MAPPING
+++ b/virtualizationservice/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit": [
+  "avf-presubmit": [
     {
       "name": "virtualizationservice_device_test"
     }
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
index 22b8a94..8eb5497 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
@@ -17,6 +17,9 @@
 
 /** Configuration for running an App in a VM */
 parcelable VirtualMachineAppConfig {
+    /** Name of VM */
+    String name;
+
     /** Main APK */
     ParcelFileDescriptor apk;
 
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
index 83a81a0..d11de03 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
@@ -19,6 +19,9 @@
 
 /** Raw configuration for running a VM. */
 parcelable VirtualMachineRawConfig {
+    /** Name of VM */
+    String name;
+
     /** The kernel image, if any. */
     @nullable ParcelFileDescriptor kernel;
 
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 73b3ae5..890bda0 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -14,6 +14,7 @@
 
 //! Implementation of the AIDL interface of the VirtualizationService.
 
+use crate::atom::{write_vm_booted_stats, write_vm_creation_stats};
 use crate::composite::make_composite_image;
 use crate::crosvm::{CrosvmConfig, DiskFile, PayloadState, VmInstance, VmState};
 use crate::payload::add_microdroid_images;
@@ -48,11 +49,10 @@
 use rpcbinder::run_rpc_server_with_factory;
 use disk::QcowFile;
 use idsig::{HashAlgorithm, V4Signature};
-use log::{debug, error, info, warn, trace};
+use log::{debug, error, info, warn};
 use microdroid_payload_config::VmPayloadConfig;
 use rustutils::system_properties;
 use semver::VersionReq;
-use statslog_virtualization_rust::vm_creation_requested::{stats_write, Hypervisor};
 use std::convert::TryInto;
 use std::ffi::CStr;
 use std::fs::{create_dir, File, OpenOptions};
@@ -131,23 +131,7 @@
     ) -> binder::Result<Strong<dyn IVirtualMachine>> {
         let mut is_protected = false;
         let ret = self.create_vm_internal(config, console_fd, log_fd, &mut is_protected);
-        match ret {
-            Ok(_) => {
-                let ok_status = Status::ok();
-                write_vm_creation_stats(
-                    is_protected,
-                    /*creation_succeeded*/ true,
-                    ok_status.exception_code() as i32,
-                );
-            }
-            Err(ref e) => {
-                write_vm_creation_stats(
-                    is_protected,
-                    /*creation_succeeded*/ false,
-                    e.exception_code() as i32,
-                );
-            }
-        }
+        write_vm_creation_stats(config, is_protected, &ret);
         ret
     }
 
@@ -468,6 +452,7 @@
         // Actually start the VM.
         let crosvm_config = CrosvmConfig {
             cid,
+            name: config.name.clone(),
             bootloader: maybe_clone_file(&config.bootloader)?,
             kernel: maybe_clone_file(&config.kernel)?,
             initrd: maybe_clone_file(&config.initrd)?,
@@ -506,16 +491,6 @@
     }
 }
 
-/// Write the stats of VMCreation to statsd
-fn write_vm_creation_stats(is_protected: bool, creation_succeeded: bool, exception_code: i32) {
-    match stats_write(Hypervisor::Pkvm, is_protected, creation_succeeded, exception_code) {
-        Err(e) => {
-            warn!("statslog_rust failed with error: {}", e);
-        }
-        Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
-    }
-}
-
 /// Waits for incoming connections from VM. If a new connection is made, stores the stream in the
 /// corresponding `VmInstance`.
 fn handle_stream_connection_from_vm(state: Arc<Mutex<State>>) -> Result<()> {
@@ -649,6 +624,7 @@
         vm_config.memoryMib = config.memoryMib;
     }
 
+    vm_config.name = config.name.clone();
     vm_config.protectedVm = config.protectedVm;
     vm_config.numCpus = config.numCpus;
     vm_config.cpuAffinity = config.cpuAffinity.clone();
@@ -1003,7 +979,7 @@
 }
 
 /// Converts a `&ParcelFileDescriptor` to a `File` by cloning the file.
-fn clone_file(file: &ParcelFileDescriptor) -> Result<File, Status> {
+pub fn clone_file(file: &ParcelFileDescriptor) -> Result<File, Status> {
     file.as_ref().try_clone().map_err(|e| {
         Status::new_exception_str(
             ExceptionCode::BAD_PARCELABLE,
@@ -1069,6 +1045,8 @@
             })?;
             let stream = vm.stream.lock().unwrap().take();
             vm.callbacks.notify_payload_started(cid, stream);
+
+            write_vm_booted_stats(vm.requester_uid as i32, &vm.name);
             Ok(())
         } else {
             error!("notifyPayloadStarted is called from an unknown CID {}", cid);
diff --git a/virtualizationservice/src/atom.rs b/virtualizationservice/src/atom.rs
new file mode 100644
index 0000000..feaa72a
--- /dev/null
+++ b/virtualizationservice/src/atom.rs
@@ -0,0 +1,179 @@
+// 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.
+
+//! Functions for creating and collecting atoms.
+
+use crate::aidl::clone_file;
+use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+    DeathReason::DeathReason, IVirtualMachine::IVirtualMachine,
+    VirtualMachineAppConfig::VirtualMachineAppConfig, VirtualMachineConfig::VirtualMachineConfig,
+};
+use android_system_virtualizationservice::binder::{Status, Strong};
+use anyhow::{anyhow, Result};
+use binder::ThreadState;
+use log::{trace, warn};
+use microdroid_payload_config::VmPayloadConfig;
+use statslog_virtualization_rust::{vm_booted, vm_creation_requested, vm_exited};
+use zip::ZipArchive;
+
+fn get_vm_payload_config(config: &VirtualMachineAppConfig) -> Result<VmPayloadConfig> {
+    let apk = config.apk.as_ref().ok_or_else(|| anyhow!("APK is none"))?;
+    let apk_file = clone_file(apk)?;
+    let mut apk_zip = ZipArchive::new(&apk_file)?;
+    let config_file = apk_zip.by_name(&config.configPath)?;
+    let vm_payload_config: VmPayloadConfig = serde_json::from_reader(config_file)?;
+    Ok(vm_payload_config)
+}
+
+/// Write the stats of VMCreation to statsd
+pub fn write_vm_creation_stats(
+    config: &VirtualMachineConfig,
+    is_protected: bool,
+    ret: &binder::Result<Strong<dyn IVirtualMachine>>,
+) {
+    let creation_succeeded;
+    let binder_exception_code;
+    match ret {
+        Ok(_) => {
+            creation_succeeded = true;
+            binder_exception_code = Status::ok().exception_code() as i32;
+        }
+        Err(ref e) => {
+            creation_succeeded = false;
+            binder_exception_code = e.exception_code() as i32;
+        }
+    }
+
+    let vm_identifier;
+    let config_type;
+    let num_cpus;
+    let cpu_affinity;
+    let memory_mib;
+    let apexes;
+    match config {
+        VirtualMachineConfig::AppConfig(config) => {
+            vm_identifier = &config.name;
+            config_type = vm_creation_requested::ConfigType::VirtualMachineAppConfig;
+            num_cpus = config.numCpus;
+            cpu_affinity = config.cpuAffinity.clone().unwrap_or_default();
+            memory_mib = config.memoryMib;
+
+            let vm_payload_config = get_vm_payload_config(config);
+            if let Ok(vm_payload_config) = vm_payload_config {
+                apexes = vm_payload_config
+                    .apexes
+                    .iter()
+                    .map(|x| x.name.clone())
+                    .collect::<Vec<String>>()
+                    .join(":");
+            } else {
+                apexes = "INFO: Can't get VmPayloadConfig".into();
+            }
+        }
+        VirtualMachineConfig::RawConfig(config) => {
+            vm_identifier = &config.name;
+            config_type = vm_creation_requested::ConfigType::VirtualMachineRawConfig;
+            num_cpus = config.numCpus;
+            cpu_affinity = config.cpuAffinity.clone().unwrap_or_default();
+            memory_mib = config.memoryMib;
+            apexes = String::new();
+        }
+    }
+
+    let vm_creation_requested = vm_creation_requested::VmCreationRequested {
+        uid: ThreadState::get_calling_uid() as i32,
+        vm_identifier,
+        hypervisor: vm_creation_requested::Hypervisor::Pkvm,
+        is_protected,
+        creation_succeeded,
+        binder_exception_code,
+        config_type,
+        num_cpus,
+        cpu_affinity: &cpu_affinity,
+        memory_mib,
+        apexes: &apexes,
+        // TODO(seungjaeyoo) Fill information about task_profile
+        // TODO(seungjaeyoo) Fill information about disk_image for raw config
+    };
+
+    match vm_creation_requested.stats_write() {
+        Err(e) => {
+            warn!("statslog_rust failed with error: {}", e);
+        }
+        Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
+    }
+}
+
+/// Write the stats of VM boot to statsd
+pub fn write_vm_booted_stats(uid: i32, vm_identifier: &String) {
+    let vm_booted = vm_booted::VmBooted { uid, vm_identifier };
+    match vm_booted.stats_write() {
+        Err(e) => {
+            warn!("statslog_rust failed with error: {}", e);
+        }
+        Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
+    }
+}
+
+/// Write the stats of VM exit to statsd
+pub fn write_vm_exited_stats(uid: i32, vm_identifier: &String, reason: DeathReason) {
+    let vm_exited = vm_exited::VmExited {
+        uid,
+        vm_identifier,
+        death_reason: match reason {
+            DeathReason::INFRASTRUCTURE_ERROR => vm_exited::DeathReason::InfrastructureError,
+            DeathReason::KILLED => vm_exited::DeathReason::Killed,
+            DeathReason::UNKNOWN => vm_exited::DeathReason::Unknown,
+            DeathReason::SHUTDOWN => vm_exited::DeathReason::Shutdown,
+            DeathReason::ERROR => vm_exited::DeathReason::Error,
+            DeathReason::REBOOT => vm_exited::DeathReason::Reboot,
+            DeathReason::CRASH => vm_exited::DeathReason::Crash,
+            DeathReason::PVM_FIRMWARE_PUBLIC_KEY_MISMATCH => {
+                vm_exited::DeathReason::PvmFirmwarePublicKeyMismatch
+            }
+            DeathReason::PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED => {
+                vm_exited::DeathReason::PvmFirmwareInstanceImageChanged
+            }
+            DeathReason::BOOTLOADER_PUBLIC_KEY_MISMATCH => {
+                vm_exited::DeathReason::BootloaderPublicKeyMismatch
+            }
+            DeathReason::BOOTLOADER_INSTANCE_IMAGE_CHANGED => {
+                vm_exited::DeathReason::BootloaderInstanceImageChanged
+            }
+            DeathReason::MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE => {
+                vm_exited::DeathReason::MicrodroidFailedToConnectToVirtualizationService
+            }
+            DeathReason::MICRODROID_PAYLOAD_HAS_CHANGED => {
+                vm_exited::DeathReason::MicrodroidPayloadHasChanged
+            }
+            DeathReason::MICRODROID_PAYLOAD_VERIFICATION_FAILED => {
+                vm_exited::DeathReason::MicrodroidPayloadVerificationFailed
+            }
+            DeathReason::MICRODROID_INVALID_PAYLOAD_CONFIG => {
+                vm_exited::DeathReason::MicrodroidInvalidPayloadConfig
+            }
+            DeathReason::MICRODROID_UNKNOWN_RUNTIME_ERROR => {
+                vm_exited::DeathReason::MicrodroidUnknownRuntimeError
+            }
+            DeathReason::HANGUP => vm_exited::DeathReason::Hangup,
+            _ => vm_exited::DeathReason::Unknown,
+        },
+    };
+    match vm_exited.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 3e1a151..29d2fe7 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -15,8 +15,9 @@
 //! Functions for running instances of `crosvm`.
 
 use crate::aidl::VirtualMachineCallbacks;
+use crate::atom::write_vm_exited_stats;
 use crate::Cid;
-use anyhow::{bail, Context, Error};
+use anyhow::{anyhow, bail, Context, Error};
 use command_fds::CommandFdExt;
 use lazy_static::lazy_static;
 use log::{debug, error, info};
@@ -29,7 +30,7 @@
 use std::mem;
 use std::num::NonZeroU32;
 use std::os::unix::io::{AsRawFd, RawFd, FromRawFd};
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
 use std::process::{Command, ExitStatus};
 use std::sync::{Arc, Condvar, Mutex};
 use std::time::Duration;
@@ -38,6 +39,7 @@
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::DeathReason::DeathReason;
 use binder::Strong;
 use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
+use tombstoned_client::{TombstonedConnection, DebuggerdDumpType};
 
 const CROSVM_PATH: &str = "/apex/com.android.virt/bin/crosvm";
 
@@ -69,6 +71,7 @@
 #[derive(Debug)]
 pub struct CrosvmConfig {
     pub cid: Cid,
+    pub name: String,
     pub bootloader: Option<File>,
     pub kernel: Option<File>,
     pub initrd: Option<File>,
@@ -169,6 +172,8 @@
     pub vm_state: Mutex<VmState>,
     /// The CID assigned to the VM for vsock communication.
     pub cid: Cid,
+    /// The name of the VM.
+    pub name: String,
     /// Whether the VM is a protected VM.
     pub protected: bool,
     /// Directory of temporary files used by the VM while it is running.
@@ -203,10 +208,12 @@
     ) -> Result<VmInstance, Error> {
         validate_config(&config)?;
         let cid = config.cid;
+        let name = config.name.clone();
         let protected = config.protected;
         Ok(VmInstance {
             vm_state: Mutex::new(VmState::NotStarted { config }),
             cid,
+            name,
             protected,
             temporary_directory,
             requester_uid,
@@ -260,7 +267,10 @@
             };
 
         self.handle_ramdump().unwrap_or_else(|e| error!("Error handling ramdump: {}", e));
-        self.callbacks.callback_on_died(self.cid, death_reason(&result, &failure_reason));
+
+        let death_reason = death_reason(&result, &failure_reason);
+        self.callbacks.callback_on_died(self.cid, death_reason);
+        write_vm_exited_stats(self.requester_uid as i32, &self.name, death_reason);
 
         // Delete temporary files.
         if let Err(e) = remove_dir_all(&self.temporary_directory) {
@@ -336,9 +346,30 @@
             let ramdump = File::open(&ramdump_path)
                 .context(format!("Failed to open ramdump {:?} for reading", &ramdump_path))?;
             self.callbacks.callback_on_ramdump(self.cid, ramdump);
+
+            Self::send_ramdump_to_tombstoned(&ramdump_path)?;
         }
         Ok(())
     }
+
+    fn send_ramdump_to_tombstoned(ramdump_path: &Path) -> Result<(), Error> {
+        let mut input = File::open(ramdump_path)
+            .context(format!("Failed to open raudmp {:?} for reading", ramdump_path))?;
+
+        let pid = std::process::id() as i32;
+        let conn = TombstonedConnection::connect(pid, DebuggerdDumpType::Tombstone)
+            .context("Failed to connect to tombstoned")?;
+        let mut output = conn
+            .text_output
+            .as_ref()
+            .ok_or_else(|| anyhow!("Could not get file to write the tombstones on"))?;
+
+        std::io::copy(&mut input, &mut output).context("Failed to send ramdump to tombstoned")?;
+        info!("Ramdump {:?} sent to tombstoned", ramdump_path);
+
+        conn.notify_completion()?;
+        Ok(())
+    }
 }
 
 fn death_reason(result: &Result<ExitStatus, io::Error>, failure_reason: &str) -> DeathReason {
diff --git a/virtualizationservice/src/main.rs b/virtualizationservice/src/main.rs
index 3b0adb9..93a5966 100644
--- a/virtualizationservice/src/main.rs
+++ b/virtualizationservice/src/main.rs
@@ -15,6 +15,7 @@
 //! Android VirtualizationService
 
 mod aidl;
+mod atom;
 mod composite;
 mod crosvm;
 mod payload;
diff --git a/vm/src/main.rs b/vm/src/main.rs
index c421b04..ee0e2e6 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -40,6 +40,10 @@
 enum Opt {
     /// Run a virtual machine with a config in APK
     RunApp {
+        /// Name of VM
+        #[structopt(long)]
+        name: Option<String>,
+
         /// Path to VM Payload APK
         #[structopt(parse(from_os_str))]
         apk: PathBuf,
@@ -102,6 +106,10 @@
     },
     /// Run a virtual machine
     Run {
+        /// Name of VM
+        #[structopt(long)]
+        name: Option<String>,
+
         /// Path to VM config JSON
         #[structopt(parse(from_os_str))]
         config: PathBuf,
@@ -195,6 +203,7 @@
 
     match opt {
         Opt::RunApp {
+            name,
             apk,
             idsig,
             instance,
@@ -211,6 +220,7 @@
             task_profiles,
             extra_idsigs,
         } => command_run_app(
+            name,
             service.as_ref(),
             &apk,
             &idsig,
@@ -228,8 +238,9 @@
             task_profiles,
             &extra_idsigs,
         ),
-        Opt::Run { config, daemonize, cpus, cpu_affinity, task_profiles, console, log } => {
+        Opt::Run { name, config, daemonize, cpus, cpu_affinity, task_profiles, console, log } => {
             command_run(
+                name,
                 service.as_ref(),
                 &config,
                 daemonize,
diff --git a/vm/src/run.rs b/vm/src/run.rs
index 9bd7863..05a9390 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -35,6 +35,7 @@
 /// Run a VM from the given APK, idsig, and config.
 #[allow(clippy::too_many_arguments)]
 pub fn command_run_app(
+    name: Option<String>,
     service: &dyn IVirtualizationService,
     apk: &Path,
     idsig: &Path,
@@ -91,6 +92,7 @@
     let extra_idsig_fds = extra_idsig_files?.into_iter().map(ParcelFileDescriptor::new).collect();
 
     let config = VirtualMachineConfig::AppConfig(VirtualMachineAppConfig {
+        name: name.unwrap_or_else(|| String::from("VmRunApp")),
         apk: apk_fd.into(),
         idsig: idsig_fd.into(),
         extraIdsigs: extra_idsig_fds,
@@ -117,6 +119,7 @@
 /// Run a VM from the given configuration file.
 #[allow(clippy::too_many_arguments)]
 pub fn command_run(
+    name: Option<String>,
     service: &dyn IVirtualizationService,
     config_path: &Path,
     daemonize: bool,
@@ -136,6 +139,11 @@
     if let Some(cpus) = cpus {
         config.numCpus = cpus as i32;
     }
+    if let Some(name) = name {
+        config.name = name;
+    } else {
+        config.name = String::from("VmRun");
+    }
     config.cpuAffinity = cpu_affinity;
     config.taskProfiles = task_profiles;
     run(
diff --git a/vmbase/TEST_MAPPING b/vmbase/TEST_MAPPING
index 9b7e4cb..c315b4a 100644
--- a/vmbase/TEST_MAPPING
+++ b/vmbase/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit": [
+  "avf-presubmit": [
     {
       "name": "vmbase_example.integration_test"
     }
diff --git a/vmbase/example/tests/test.rs b/vmbase/example/tests/test.rs
index fd6eb8c..58fffff 100644
--- a/vmbase/example/tests/test.rs
+++ b/vmbase/example/tests/test.rs
@@ -48,7 +48,9 @@
         File::open(VMBASE_EXAMPLE_PATH)
             .with_context(|| format!("Failed to open VM image {}", VMBASE_EXAMPLE_PATH))?,
     );
+
     let config = VirtualMachineConfig::RawConfig(VirtualMachineRawConfig {
+        name: String::from("VmBaseTest"),
         kernel: None,
         initrd: None,
         params: None,
diff --git a/vmbase/exceptions_panic.S b/vmbase/exceptions_panic.S
index 6f73da8..4a3f2db 100644
--- a/vmbase/exceptions_panic.S
+++ b/vmbase/exceptions_panic.S
@@ -22,8 +22,8 @@
  */
 
 .macro exception_panic
-	mov	x0, 0x80400000
-	add	x0, x0, 9
+	mov	x0, 0x84000000
+	movk	x0, 9
 	mov	x1, 0
 	mov	x2, 0
 	mov	x3, 0
diff --git a/zipfuse/TEST_MAPPING b/zipfuse/TEST_MAPPING
index ef398a0..13055f0 100644
--- a/zipfuse/TEST_MAPPING
+++ b/zipfuse/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit" : [
+  "avf-presubmit" : [
     {
       "name" : "ZipFuseTest"
     }