Merge "Add README at top of repository with links to others."
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 13c68d7..69d4568 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,8 +1,13 @@
 {
-  "postsubmit": [
+  "presubmit": [
     {
       "name": "MicrodroidHostTestCases"
-    },
+    }
+  ],
+  "postsubmit": [
+    // TODO(jiyong): promote this to presubmit. That currently doesn't work because
+    // this test is skipped for cf_x86_64_phone (not aosp_cf_x86_64_phone), but tradefed
+    // somehow thinks that the test wasn't executed at all and reports it as a failure.
     {
       "name": "VirtualizationTestCases"
     }
diff --git a/apex/Android.bp b/apex/Android.bp
index 2194c67..bbd4aa5 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -7,6 +7,7 @@
 
     // TODO(jiyong): make it updatable
     updatable: false,
+    platform_apis: true,
 
     manifest: "manifest.json",
 
@@ -48,10 +49,6 @@
         "fd_server",
         "vm",
         "compos_key_cmd",
-
-        // tools to create composite images
-        "mk_cdisk",
-        "mk_payload",
     ],
     prebuilts: [
         "com.android.virt.init.rc",
diff --git a/apkdmverity/TEST_MAPPING b/apkdmverity/TEST_MAPPING
index 997b3f9..1bbec76 100644
--- a/apkdmverity/TEST_MAPPING
+++ b/apkdmverity/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "postsubmit" : [
+  "presubmit" : [
     {
       "name" : "apkdmverity.test"
     }
diff --git a/authfs/TEST_MAPPING b/authfs/TEST_MAPPING
index cabd5df..d0c0b09 100644
--- a/authfs/TEST_MAPPING
+++ b/authfs/TEST_MAPPING
@@ -3,10 +3,5 @@
     {
       "name": "authfs_device_test_src_lib"
     }
-  ],
-  "postsubmit": [
-    {
-      "name": "MicrodroidHostTestCases"
-    }
   ]
 }
diff --git a/authfs/src/file/remote_file.rs b/authfs/src/file/remote_file.rs
index 0b6c007..037b8ec 100644
--- a/authfs/src/file/remote_file.rs
+++ b/authfs/src/file/remote_file.rs
@@ -17,13 +17,12 @@
 use std::cmp::min;
 use std::convert::TryFrom;
 use std::io;
-use std::sync::{Arc, Mutex};
 
 use super::{ChunkBuffer, RandomWrite, ReadByChunk, VirtFdService};
 use crate::common::CHUNK_SIZE;
 
 fn remote_read_chunk(
-    service: &Arc<Mutex<VirtFdService>>,
+    service: &VirtFdService,
     remote_fd: i32,
     chunk_index: u64,
     buf: &mut ChunkBuffer,
@@ -32,8 +31,6 @@
         .map_err(|_| io::Error::from_raw_os_error(libc::EOVERFLOW))?;
 
     let chunk = service
-        .lock()
-        .unwrap()
         .readFile(remote_fd, offset, buf.len() as i32)
         .map_err(|e| io::Error::new(io::ErrorKind::Other, e.get_description()))?;
     let size = min(buf.len(), chunk.len());
@@ -43,12 +40,12 @@
 
 pub struct RemoteFileReader {
     // This needs to have Sync trait to be used in fuse::worker::start_message_loop.
-    service: Arc<Mutex<VirtFdService>>,
+    service: VirtFdService,
     file_fd: i32,
 }
 
 impl RemoteFileReader {
-    pub fn new(service: Arc<Mutex<VirtFdService>>, file_fd: i32) -> Self {
+    pub fn new(service: VirtFdService, file_fd: i32) -> Self {
         RemoteFileReader { service, file_fd }
     }
 }
@@ -61,13 +58,12 @@
 
 pub struct RemoteMerkleTreeReader {
     // This needs to be a Sync to be used in fuse::worker::start_message_loop.
-    // TODO(victorhsieh): change to Strong<> once binder supports it.
-    service: Arc<Mutex<VirtFdService>>,
+    service: VirtFdService,
     file_fd: i32,
 }
 
 impl RemoteMerkleTreeReader {
-    pub fn new(service: Arc<Mutex<VirtFdService>>, file_fd: i32) -> Self {
+    pub fn new(service: VirtFdService, file_fd: i32) -> Self {
         RemoteMerkleTreeReader { service, file_fd }
     }
 }
@@ -79,8 +75,6 @@
 
         let chunk = self
             .service
-            .lock()
-            .unwrap()
             .readFsverityMerkleTree(self.file_fd, offset, buf.len() as i32)
             .map_err(|e| io::Error::new(io::ErrorKind::Other, e.get_description()))?;
         let size = min(buf.len(), chunk.len());
@@ -91,12 +85,12 @@
 
 pub struct RemoteFileEditor {
     // This needs to have Sync trait to be used in fuse::worker::start_message_loop.
-    service: Arc<Mutex<VirtFdService>>,
+    service: VirtFdService,
     file_fd: i32,
 }
 
 impl RemoteFileEditor {
-    pub fn new(service: Arc<Mutex<VirtFdService>>, file_fd: i32) -> Self {
+    pub fn new(service: VirtFdService, file_fd: i32) -> Self {
         RemoteFileEditor { service, file_fd }
     }
 }
@@ -107,8 +101,6 @@
             i64::try_from(offset).map_err(|_| io::Error::from_raw_os_error(libc::EOVERFLOW))?;
         let size = self
             .service
-            .lock()
-            .unwrap()
             .writeFile(self.file_fd, &buf, offset)
             .map_err(|e| io::Error::new(io::ErrorKind::Other, e.get_description()))?;
         Ok(size as usize) // within range because size is supposed to <= buf.len(), which is a usize
@@ -118,8 +110,6 @@
         let size =
             i64::try_from(size).map_err(|_| io::Error::from_raw_os_error(libc::EOVERFLOW))?;
         self.service
-            .lock()
-            .unwrap()
             .resize(self.file_fd, size)
             .map_err(|e| io::Error::new(io::ErrorKind::Other, e.get_description()))?;
         Ok(())
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index 593fa74..9d36c3f 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -32,7 +32,6 @@
 use std::fs::File;
 use std::io::Read;
 use std::path::{Path, PathBuf};
-use std::sync::{Arc, Mutex};
 use structopt::StructOpt;
 
 mod auth;
@@ -92,6 +91,14 @@
     debug: bool,
 }
 
+impl Args {
+    fn has_remote_files(&self) -> bool {
+        !self.remote_ro_file.is_empty()
+            || !self.remote_ro_file_unverified.is_empty()
+            || !self.remote_new_rw_file.is_empty()
+    }
+}
+
 struct OptionRemoteRoFile {
     ino: Inode,
 
@@ -216,15 +223,14 @@
 ) -> Result<FileConfig> {
     let signature = service.readFsveritySignature(remote_id).context("Failed to read signature")?;
 
-    let service = Arc::new(Mutex::new(service));
     let authenticator = FakeAuthenticator::always_succeed();
     Ok(FileConfig::RemoteVerifiedReadonlyFile {
         reader: VerifiedFileReader::new(
             &authenticator,
-            RemoteFileReader::new(Arc::clone(&service), remote_id),
+            RemoteFileReader::new(service.clone(), remote_id),
             file_size,
             signature,
-            RemoteMerkleTreeReader::new(Arc::clone(&service), remote_id),
+            RemoteMerkleTreeReader::new(service.clone(), remote_id),
         )?,
         file_size,
     })
@@ -235,7 +241,7 @@
     remote_id: i32,
     file_size: u64,
 ) -> Result<FileConfig> {
-    let reader = RemoteFileReader::new(Arc::new(Mutex::new(service)), remote_id);
+    let reader = RemoteFileReader::new(service, remote_id);
     Ok(FileConfig::RemoteUnverifiedReadonlyFile { reader, file_size })
 }
 
@@ -266,34 +272,44 @@
     service: file::VirtFdService,
     remote_id: i32,
 ) -> Result<FileConfig> {
-    let remote_file = RemoteFileEditor::new(Arc::new(Mutex::new(service)), remote_id);
+    let remote_file = RemoteFileEditor::new(service, remote_id);
     Ok(FileConfig::RemoteVerifiedNewFile { editor: VerifiedFileEditor::new(remote_file) })
 }
 
 fn prepare_file_pool(args: &Args) -> Result<BTreeMap<Inode, FileConfig>> {
     let mut file_pool = BTreeMap::new();
 
-    let service = file::get_binder_service(args.cid)?;
+    if args.has_remote_files() {
+        let service = file::get_binder_service(args.cid)?;
 
-    for config in &args.remote_ro_file {
-        file_pool.insert(
-            config.ino,
-            new_config_remote_verified_file(service.clone(), config.remote_id, config.file_size)?,
-        );
-    }
+        for config in &args.remote_ro_file {
+            file_pool.insert(
+                config.ino,
+                new_config_remote_verified_file(
+                    service.clone(),
+                    config.remote_id,
+                    config.file_size,
+                )?,
+            );
+        }
 
-    for config in &args.remote_ro_file_unverified {
-        file_pool.insert(
-            config.ino,
-            new_config_remote_unverified_file(service.clone(), config.remote_id, config.file_size)?,
-        );
-    }
+        for config in &args.remote_ro_file_unverified {
+            file_pool.insert(
+                config.ino,
+                new_config_remote_unverified_file(
+                    service.clone(),
+                    config.remote_id,
+                    config.file_size,
+                )?,
+            );
+        }
 
-    for config in &args.remote_new_rw_file {
-        file_pool.insert(
-            config.ino,
-            new_config_remote_new_verified_file(service.clone(), config.remote_id)?,
-        );
+        for config in &args.remote_new_rw_file {
+            file_pool.insert(
+                config.ino,
+                new_config_remote_new_verified_file(service.clone(), config.remote_id)?,
+            );
+        }
     }
 
     for config in &args.local_ro_file {
diff --git a/compos/Android.bp b/compos/Android.bp
index 0cb6894..1eb6716 100644
--- a/compos/Android.bp
+++ b/compos/Android.bp
@@ -83,10 +83,3 @@
     prefer_rlib: true,
     apex_available: ["com.android.compos"],
 }
-
-// TODO(b/190503456) Remove this when vm/virtualizationservice generates payload.img from vm_config
-prebuilt_etc {
-    name: "compos_payload_config",
-    src: "payload_config.json",
-    filename: "payload_config.json",
-}
diff --git a/compos/apex/Android.bp b/compos/apex/Android.bp
index 95463d0..1fffa2e 100644
--- a/compos/apex/Android.bp
+++ b/compos/apex/Android.bp
@@ -34,6 +34,7 @@
 
     // TODO(victorhsieh): make it updatable
     updatable: false,
+    platform_apis: true,
 
     binaries: [
         "compos_key_service",
@@ -47,6 +48,6 @@
     ],
 
     prebuilts: [
-        "compos_payload_config",
+        "CompOSPayloadApp.apk.idsig",
     ],
 }
diff --git a/compos/apk/Android.bp b/compos/apk/Android.bp
index c6192b9..c5d0c31 100644
--- a/compos/apk/Android.bp
+++ b/compos/apk/Android.bp
@@ -3,7 +3,42 @@
 }
 
 android_app {
-    name: "CompOSPayloadApp",
+    name: "CompOSPayloadApp.unsigned",
     sdk_version: "current",
     apex_available: ["com.android.compos"],
 }
+
+// TODO(b/190409306) this is temporal until we have a solid way to pass merkle tree
+java_genrule {
+    name: "CompOSPayloadApp.signing",
+    out: [
+        "CompOSPayloadApp.apk",
+        "CompOSPayloadApp.apk.idsig",
+    ],
+    srcs: [":CompOSPayloadApp.unsigned"],
+    tools: ["apksigner"],
+    tool_files: ["test.keystore"],
+    cmd: "$(location apksigner) sign " +
+        "--ks $(location test.keystore) " +
+        "--ks-pass=pass:testkey --key-pass=pass:testkey " +
+        "--in $(in) " +
+        "--out $(genDir)/CompOSPayloadApp.apk",
+    // $(genDir)/MicrodroidTestApp.apk.idsig is generated implicitly
+}
+
+android_app_import {
+    name: "CompOSPayloadApp",
+    // Make sure the build system doesn't try to resign the APK
+    dex_preopt: {
+        enabled: false,
+    },
+    apk: ":CompOSPayloadApp.signing{CompOSPayloadApp.apk}",
+    presigned: true,
+    filename: "CompOSPayloadApp.apk",
+    apex_available: ["com.android.compos"],
+}
+
+prebuilt_etc {
+    name: "CompOSPayloadApp.apk.idsig",
+    src: ":CompOSPayloadApp.signing{CompOSPayloadApp.apk.idsig}",
+}
diff --git a/compos/apk/assets/vm_config.json b/compos/apk/assets/vm_config.json
index a8dca71..f9f1f90 100644
--- a/compos/apk/assets/vm_config.json
+++ b/compos/apk/assets/vm_config.json
@@ -13,22 +13,10 @@
   },
   "apexes": [
     {
-      "name": "com.android.adbd"
-    },
-    {
       "name": "com.android.art"
     },
     {
       "name": "com.android.compos"
-    },
-    {
-      "name": "com.android.i18n"
-    },
-    {
-      "name": "com.android.os.statsd"
-    },
-    {
-      "name": "com.android.sdkext"
     }
   ]
 }
\ No newline at end of file
diff --git a/compos/apk/test.keystore b/compos/apk/test.keystore
new file mode 100644
index 0000000..2f024d8
--- /dev/null
+++ b/compos/apk/test.keystore
Binary files differ
diff --git a/compos/payload_config.json b/compos/payload_config.json
deleted file mode 100644
index 588ccca..0000000
--- a/compos/payload_config.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "apk": {
-    "path": "/apex/com.android.compos/app/CompOSPayloadApp/CompOSPayloadApp.apk",
-    "name": "com.android.compos.payload"
-  },
-  "system_apexes": [
-    "com.android.adbd",
-    "com.android.art",
-    "com.android.compos",
-    "com.android.i18n",
-    "com.android.os.statsd",
-    "com.android.sdkext"
-  ],
-  "payload_config_path": "/mnt/apk/assets/vm_config.json"
-}
\ No newline at end of file
diff --git a/compos/src/compos_key_service.rs b/compos/src/compos_key_service.rs
index 97fd855..0cbe8de 100644
--- a/compos/src/compos_key_service.rs
+++ b/compos/src/compos_key_service.rs
@@ -39,7 +39,6 @@
 use ring::signature;
 use scopeguard::ScopeGuard;
 use std::ffi::CString;
-use std::sync::Mutex;
 
 const LOG_TAG: &str = "CompOsKeyService";
 const OUR_SERVICE_NAME: &str = "android.system.composkeyservice";
@@ -68,10 +67,6 @@
 
 struct CompOsKeyService {
     random: SystemRandom,
-    state: Mutex<State>,
-}
-
-struct State {
     security_level: Strong<dyn IKeystoreSecurityLevel>,
 }
 
@@ -102,20 +97,12 @@
     fn new(keystore_service: &Strong<dyn IKeystoreService>) -> Self {
         Self {
             random: SystemRandom::new(),
-            state: Mutex::new(State {
-                security_level: keystore_service
-                    .getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT)
-                    .unwrap(),
-            }),
+            security_level: keystore_service
+                .getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT)
+                .unwrap(),
         }
     }
 
-    fn security_level(&self) -> Strong<dyn IKeystoreSecurityLevel> {
-        // We need the Mutex because Strong<_> isn't sync. But we don't need to keep it locked
-        // to make the call, once we've cloned the pointer.
-        self.state.lock().unwrap().security_level.clone()
-    }
-
     fn do_generate(&self) -> Result<CompOsKeyData> {
         let key_parameters =
             [PURPOSE_SIGN, ALGORITHM, PADDING, DIGEST, KEY_SIZE, EXPONENT, NO_AUTH_REQUIRED];
@@ -124,7 +111,7 @@
         let entropy = [];
 
         let key_metadata = self
-            .security_level()
+            .security_level
             .generateKey(&KEY_DESCRIPTOR, attestation_key, &key_parameters, flags, &entropy)
             .context("Generating key failed")?;
 
@@ -154,7 +141,7 @@
         let forced = false;
 
         let response = self
-            .security_level()
+            .security_level
             .createOperation(&key_descriptor, &operation_parameters, forced)
             .context("Creating key failed")?;
         let operation = scopeguard::guard(
diff --git a/compositediskconfig/src/lib.rs b/compositediskconfig/src/lib.rs
deleted file mode 100644
index 3546dd3..0000000
--- a/compositediskconfig/src/lib.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2021, 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.
-
-//! JSON configuration for composite disks, as used for running `mk_cdisk` and by the `vm` tool.
-
-use serde::{Deserialize, Serialize};
-use std::io::Write;
-use std::path::PathBuf;
-
-/// Configuration for running `mk_cdisk`.
-#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
-pub struct Config {
-    /// The set of partitions to be assembled into a composite image.
-    pub partitions: Vec<Partition>,
-}
-
-/// A partition to be assembled into a composite image.
-#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
-pub struct Partition {
-    /// A label for the partition.
-    pub label: String,
-    /// The filename of the partition image.
-    pub path: PathBuf,
-    /// Whether the partition should be writable.
-    #[serde(default)]
-    pub writable: bool,
-}
-
-impl Config {
-    /// Write the configuration as JSON, in the format used by `mk_cdisk`.
-    pub fn write_json(&self, writer: impl Write) -> serde_json::Result<()> {
-        serde_json::to_writer(writer, self)
-    }
-}
diff --git a/microdroid/README.md b/microdroid/README.md
index 802e847..f48a35c 100644
--- a/microdroid/README.md
+++ b/microdroid/README.md
@@ -74,20 +74,13 @@
     "type": "microdroid_launcher",
     "command": "MyMicrodroidApp.so"
   },
-  "apexes": [
-    {"name": "com.android.adbd"},
-    {"name": "com.android.i18n"},
-    {"name": "com.android.os.statsd"},
-    {"name": "com.android.sdkext"}
-  ]
+  "apexes" : [ ... ]
 }
 ```
 
 The value of `task.command` should match with the name of the shared library
 defined above. The `apexes` array is the APEXes that will be imported to
-microdroid. The above four APEXes are essential ones and therefore shouldn't be
-omitted. In the future, you wouldn't need to add the default ones manually. If
-more APEXes are required for you app, add their names too.
+microdroid.
 
 Embed the shared library and the VM configuration file in an APK:
 
@@ -130,77 +123,39 @@
 adb install out/dist/MyApp.apk
 ```
 
-### Creating `payload.img` manually (temporary step)
-
-This is a step that needs to be done manually for now. Eventually, this will be
-automatically done by a service named `virtualizationservice` which is part of
-the `com.android.virt` APEX.
-
-Create `payload.json` file:
-
-```json
-{
-  "payload_config_path": "/mnt/apk/assets/VM_CONFIG_NAME,
-  "system_apexes": [
-    "com.android.adbd",
-    "com.android.i18n",
-    "com.android.os.statsd",
-    "com.android.sdkext"
-  ],
-  "apk": {
-    "name": "PACKAGE_NAME_OF_YOUR_APP",
-    "path": "PATH_TO_YOUR_APP",
-    "idsig_path": "PATH_TO_APK_IDSIG"
-  }
-}
-```
-
-`ALL_CAP`s in the above are placeholders. They need to be replaced with correct
+`ALL_CAP`s below are placeholders. They need to be replaced with correct
 values:
-
 * `VM_CONFIG_FILE`: the name of the VM config file that you embedded in the APK.
-* `PACKAGE_NAME_OF_YOUR_APP`: package name of your app(e.g. `com.acme.app`).
+  (e.g. `vm_config.json`)
+* `PACKAGE_NAME_OF_YOUR_APP`: package name of your app (e.g. `com.acme.app`).
 * `PATH_TO_YOUR_APP`: path to the installed APK on the device. Can be obtained
   via the following command.
+  ```sh
+  adb shell pm path PACKAGE_NAME_OF_YOUR_APP
+  ```
+  It shall report a cryptic path similar to `/data/app/~~OgZq==/com.acme.app-HudMahQ==/base.apk`.
 
-```sh
-adb shell pm path PACKAGE_NAME_OF_YOUR_APP
-```
-
-It shall report a cryptic path similar to
-`/data/app/~~OgZq==/com.acme.app-HudMahQ==/base.apk`.
-
-* `PATH_TO_APK_IDSIG`: path to the pushed APK idsig on the device. See below
-  `adb push` command: it will be `/data/local/tmp/virt/MyApp.apk.idsig` in this
-  example.
-
-Once the file is done, execute the following command to push it to the device
-and run `mk_payload` to create `payload.img`:
+Push idsig of the APK to the device.
 
 ```sh
 TEST_ROOT=/data/local/tmp/virt
-adb push out/dist/MyApp.apk.idsig $TEST_ROOT/MyApp.apk.idsig
-adb push path_to_payload.json $TEST_ROOT/payload.json
-adb shell /apex/com.android.virt/bin/my_payload $TEST_ROOT/payload.json $TEST_ROOT/payload.img
-adb shell chmod go+r $TEST_ROOT/payload*
+adb push out/dist/MyApp.apk.idsig $TEST_ROOT
 ```
 
-### Running the VM
-
 Execute the following commands to launch a VM. The VM will boot to microdroid
 and then automatically execute your app (the shared library
 `MyMicrodroidApp.so`).
 
 ```sh
 TEST_ROOT=/data/local/tmp/virt
-adb push packages/modules/Virtualization/microdroid/microdroid.json $TEST_ROOT/microdroid.json
 adb root
 adb shell setenforce 0
 adb shell start virtualizationservice
-adb shell /apex/com.android.virt/bin/vm run $TEST_ROOT/microdroid.json
+adb shell /apex/com.android.virt/bin/vm run-app --daemonize --log $TEST_ROOT/log.txt PATH_TO_YOUR_APP $TEST_ROOT/MyApp.apk.idsig assets/VM_CONFIG_FILE
 ```
 
-The last command lets you know the CID assigned to the VM.
+The last command lets you know the CID assigned to the VM. The console output
+from the VM is stored to `$TEST_ROOT/log.txt` file for debugging purpose.
 
 Note: the disabling of SELinux is a temporary step. The restriction will
 eventually be removed.
@@ -211,7 +166,8 @@
 adb shell /apex/com.android.virt/bin/vm stop CID
 ```
 
-, where `CID` is the reported CID value.
+, where `CID` is the reported CID value. This works only when the `vm` was
+invoked with the `--daemonize` flag.
 
 ## ADB
 
diff --git a/microdroid/microdroid.json b/microdroid/microdroid.json
index 7dc4b6a..b1ef86a 100644
--- a/microdroid/microdroid.json
+++ b/microdroid/microdroid.json
@@ -5,23 +5,23 @@
       "partitions": [
         {
           "label": "boot_a",
-          "path": "/apex/com.android.virt/etc/fs/microdroid_boot-5.10.img"
+          "paths": ["/apex/com.android.virt/etc/fs/microdroid_boot-5.10.img"]
         },
         {
           "label": "vendor_boot_a",
-          "path": "/apex/com.android.virt/etc/fs/microdroid_vendor_boot-5.10.img"
+          "paths": ["/apex/com.android.virt/etc/fs/microdroid_vendor_boot-5.10.img"]
         },
         {
           "label": "vbmeta_a",
-          "path": "/apex/com.android.virt/etc/fs/microdroid_vbmeta.img"
+          "paths": ["/apex/com.android.virt/etc/fs/microdroid_vbmeta.img"]
         },
         {
           "label": "vbmeta_system_a",
-          "path": "/apex/com.android.virt/etc/fs/microdroid_vbmeta_system.img"
+          "paths": ["/apex/com.android.virt/etc/fs/microdroid_vbmeta_system.img"]
         },
         {
           "label": "super",
-          "path": "/apex/com.android.virt/etc/fs/microdroid_super.img"
+          "paths": ["/apex/com.android.virt/etc/fs/microdroid_super.img"]
         }
       ],
       "writable": false
@@ -30,14 +30,10 @@
       "partitions": [
         {
           "label": "uboot_env",
-          "path": "/apex/com.android.virt/etc/uboot_env.img"
+          "paths": ["/apex/com.android.virt/etc/uboot_env.img"]
         }
       ],
       "writable": false
-    },
-    {
-      "image": "/data/local/tmp/virt/payload.img",
-      "writable": false
     }
   ]
 }
diff --git a/microdroid/payload/Android.bp b/microdroid/payload/Android.bp
index 5ea6c10..c7bc415 100644
--- a/microdroid/payload/Android.bp
+++ b/microdroid/payload/Android.bp
@@ -44,6 +44,9 @@
     protos: ["metadata.proto"],
     source_stem: "microdroid_metadata",
     host_supported: true,
+    apex_available: [
+        "com.android.virt",
+    ],
 }
 
 cc_binary {
diff --git a/microdroid/payload/config/Android.bp b/microdroid/payload/config/Android.bp
index da58bdf..827f6e3 100644
--- a/microdroid/payload/config/Android.bp
+++ b/microdroid/payload/config/Android.bp
@@ -13,4 +13,7 @@
         "libserde_json",
         "libserde",
     ],
+    apex_available: [
+        "com.android.virt",
+    ],
 }
diff --git a/microdroid/payload/metadata/Android.bp b/microdroid/payload/metadata/Android.bp
new file mode 100644
index 0000000..d3ec625
--- /dev/null
+++ b/microdroid/payload/metadata/Android.bp
@@ -0,0 +1,19 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_library {
+    name: "libmicrodroid_metadata",
+    host_supported: true,
+    crate_name: "microdroid_metadata",
+    srcs: ["src/lib.rs"],
+    prefer_rlib: true,
+    edition: "2018",
+    rustlibs: [
+        "libmicrodroid_metadata_proto_rust",
+        "libprotobuf",
+    ],
+    apex_available: [
+        "com.android.virt",
+    ],
+}
diff --git a/microdroid/payload/metadata/src/lib.rs b/microdroid/payload/metadata/src/lib.rs
new file mode 100644
index 0000000..9c97411
--- /dev/null
+++ b/microdroid/payload/metadata/src/lib.rs
@@ -0,0 +1,42 @@
+// Copyright 2021, 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.
+
+//! Read/write metadata blob for VM payload image. The blob is supposed to be used as a metadata
+//! partition in the VM payload image.
+//! The layout of metadata blob is like:
+//!   4 bytes : size(N) in big endian
+//!   N bytes : protobuf message for Metadata
+
+use protobuf::Message;
+use std::io;
+use std::io::Read;
+use std::io::Write;
+
+pub use microdroid_metadata::metadata::{ApexPayload, ApkPayload, Metadata};
+
+/// Reads a metadata from a reader
+pub fn read_metadata<T: Read>(mut r: T) -> io::Result<Metadata> {
+    let mut buf = [0u8; 4];
+    r.read_exact(&mut buf)?;
+    let size = i32::from_be_bytes(buf);
+    Ok(Metadata::parse_from_reader(&mut r.take(size as u64))?)
+}
+
+/// Writes a metadata to a writer
+pub fn write_metadata<T: Write>(metadata: &Metadata, mut w: T) -> io::Result<()> {
+    let mut buf = Vec::new();
+    metadata.write_to_writer(&mut buf)?;
+    w.write_all(&(buf.len() as i32).to_be_bytes())?;
+    w.write_all(&buf)
+}
diff --git a/microdroid/sepolicy/system/private/hwservice_contexts b/microdroid/sepolicy/system/private/hwservice_contexts
index 5b6e79d..f4583e2 100644
--- a/microdroid/sepolicy/system/private/hwservice_contexts
+++ b/microdroid/sepolicy/system/private/hwservice_contexts
@@ -1,85 +1,6 @@
-android.frameworks.automotive.display::IAutomotiveDisplayProxyService u:object_r:fwk_automotive_display_hwservice:s0
-android.frameworks.bufferhub::IBufferHub                        u:object_r:fwk_bufferhub_hwservice:s0
-android.frameworks.cameraservice.service::ICameraService        u:object_r:fwk_camera_hwservice:s0
-android.frameworks.displayservice::IDisplayService              u:object_r:fwk_display_hwservice:s0
-android.frameworks.schedulerservice::ISchedulingPolicyService   u:object_r:fwk_scheduler_hwservice:s0
-android.frameworks.sensorservice::ISensorManager                u:object_r:fwk_sensor_hwservice:s0
-android.frameworks.stats::IStats                                u:object_r:fwk_stats_hwservice:s0
-android.hardware.atrace::IAtraceDevice                          u:object_r:hal_atrace_hwservice:s0
-android.hardware.audio.effect::IEffectsFactory                  u:object_r:hal_audio_hwservice:s0
-android.hardware.audio::IDevicesFactory                         u:object_r:hal_audio_hwservice:s0
-android.hardware.authsecret::IAuthSecret                        u:object_r:hal_authsecret_hwservice:s0
-android.hardware.automotive.audiocontrol::IAudioControl         u:object_r:hal_audiocontrol_hwservice:s0
-android.hardware.automotive.can::ICanController                 u:object_r:hal_can_controller_hwservice:s0
-android.hardware.automotive.can::ICanBus                        u:object_r:hal_can_bus_hwservice:s0
-android.hardware.automotive.evs::IEvsEnumerator                 u:object_r:hal_evs_hwservice:s0
-android.hardware.automotive.vehicle::IVehicle                   u:object_r:hal_vehicle_hwservice:s0
-android.hardware.biometrics.face::IBiometricsFace               u:object_r:hal_face_hwservice:s0
-android.hardware.biometrics.fingerprint::IBiometricsFingerprint u:object_r:hal_fingerprint_hwservice:s0
-android.hardware.bluetooth::IBluetoothHci                       u:object_r:hal_bluetooth_hwservice:s0
-android.hardware.bluetooth.a2dp::IBluetoothAudioOffload         u:object_r:hal_audio_hwservice:s0
-android.hardware.bluetooth.audio::IBluetoothAudioProvidersFactory   u:object_r:hal_audio_hwservice:s0
-android.hardware.boot::IBootControl                             u:object_r:hal_bootctl_hwservice:s0
-android.hardware.broadcastradio::IBroadcastRadio                u:object_r:hal_broadcastradio_hwservice:s0
-android.hardware.broadcastradio::IBroadcastRadioFactory         u:object_r:hal_broadcastradio_hwservice:s0
-android.hardware.camera.provider::ICameraProvider               u:object_r:hal_camera_hwservice:s0
-android.hardware.configstore::ISurfaceFlingerConfigs            u:object_r:hal_configstore_ISurfaceFlingerConfigs:s0
-android.hardware.confirmationui::IConfirmationUI                u:object_r:hal_confirmationui_hwservice:s0
-android.hardware.contexthub::IContexthub                        u:object_r:hal_contexthub_hwservice:s0
-android.hardware.cas::IMediaCasService                          u:object_r:hal_cas_hwservice:s0
-android.hardware.drm::ICryptoFactory                            u:object_r:hal_drm_hwservice:s0
-android.hardware.drm::IDrmFactory                               u:object_r:hal_drm_hwservice:s0
-android.hardware.dumpstate::IDumpstateDevice                    u:object_r:hal_dumpstate_hwservice:s0
-android.hardware.gatekeeper::IGatekeeper                        u:object_r:hal_gatekeeper_hwservice:s0
-android.hardware.gnss::IGnss                                    u:object_r:hal_gnss_hwservice:s0
-android.hardware.graphics.allocator::IAllocator                 u:object_r:hal_graphics_allocator_hwservice:s0
-android.hardware.graphics.composer::IComposer                   u:object_r:hal_graphics_composer_hwservice:s0
-android.hardware.graphics.mapper::IMapper                       u:object_r:hal_graphics_mapper_hwservice:s0
-android.hardware.health::IHealth                                u:object_r:hal_health_hwservice:s0
-android.hardware.health.storage::IStorage                       u:object_r:hal_health_storage_hwservice:s0
-android.hardware.input.classifier::IInputClassifier             u:object_r:hal_input_classifier_hwservice:s0
-android.hardware.ir::IConsumerIr                                u:object_r:hal_ir_hwservice:s0
-android.hardware.keymaster::IKeymasterDevice                    u:object_r:hal_keymaster_hwservice:s0
-android.hardware.tests.lazy::ILazy                              u:object_r:hal_lazy_test_hwservice:s0
-android.hardware.light::ILight                                  u:object_r:hal_light_hwservice:s0
-android.hardware.lowpan::ILowpanDevice                          u:object_r:hal_lowpan_hwservice:s0
-android.hardware.media.omx::IOmx                                u:object_r:hal_omx_hwservice:s0
-android.hardware.media.omx::IOmxStore                           u:object_r:hal_omx_hwservice:s0
-android.hardware.media.c2::IComponentStore                      u:object_r:hal_codec2_hwservice:s0
-android.hardware.memtrack::IMemtrack                            u:object_r:hal_memtrack_hwservice:s0
-android.hardware.neuralnetworks::IDevice                        u:object_r:hal_neuralnetworks_hwservice:s0
-android.hardware.nfc::INfc                                      u:object_r:hal_nfc_hwservice:s0
-android.hardware.oemlock::IOemLock                              u:object_r:hal_oemlock_hwservice:s0
-android.hardware.power::IPower                                  u:object_r:hal_power_hwservice:s0
-android.hardware.power.stats::IPowerStats                       u:object_r:hal_power_stats_hwservice:s0
-android.hardware.radio.config::IRadioConfig                     u:object_r:hal_telephony_hwservice:s0
-android.hardware.radio.deprecated::IOemHook                     u:object_r:hal_telephony_hwservice:s0
-android.hardware.radio::IRadio                                  u:object_r:hal_telephony_hwservice:s0
-android.hardware.radio::ISap                                    u:object_r:hal_telephony_hwservice:s0
-android.hardware.renderscript::IDevice                          u:object_r:hal_renderscript_hwservice:s0
-android.hardware.secure_element::ISecureElement                 u:object_r:hal_secure_element_hwservice:s0
-android.hardware.sensors::ISensors                              u:object_r:hal_sensors_hwservice:s0
-android.hardware.soundtrigger::ISoundTriggerHw                  u:object_r:hal_audio_hwservice:s0
-android.hardware.tetheroffload.config::IOffloadConfig           u:object_r:hal_tetheroffload_hwservice:s0
-android.hardware.tetheroffload.control::IOffloadControl         u:object_r:hal_tetheroffload_hwservice:s0
-android.hardware.thermal::IThermal                              u:object_r:hal_thermal_hwservice:s0
-android.hardware.tv.cec::IHdmiCec                               u:object_r:hal_tv_cec_hwservice:s0
-android.hardware.tv.input::ITvInput                             u:object_r:hal_tv_input_hwservice:s0
-android.hardware.tv.tuner::ITuner                             	u:object_r:hal_tv_tuner_hwservice:s0
-android.hardware.usb::IUsb                                      u:object_r:hal_usb_hwservice:s0
-android.hardware.usb.gadget::IUsbGadget                         u:object_r:hal_usb_gadget_hwservice:s0
-android.hardware.vibrator::IVibrator                            u:object_r:hal_vibrator_hwservice:s0
-android.hardware.vr::IVr                                        u:object_r:hal_vr_hwservice:s0
-android.hardware.weaver::IWeaver                                u:object_r:hal_weaver_hwservice:s0
-android.hardware.wifi::IWifi                                    u:object_r:hal_wifi_hwservice:s0
-android.hardware.wifi.hostapd::IHostapd                         u:object_r:hal_wifi_hostapd_hwservice:s0
-android.hardware.wifi.supplicant::ISupplicant                   u:object_r:hal_wifi_supplicant_hwservice:s0
 android.hidl.allocator::IAllocator                              u:object_r:hidl_allocator_hwservice:s0
 android.hidl.base::IBase                                        u:object_r:hidl_base_hwservice:s0
 android.hidl.manager::IServiceManager                           u:object_r:hidl_manager_hwservice:s0
 android.hidl.memory::IMapper                                    u:object_r:hidl_memory_hwservice:s0
 android.hidl.token::ITokenManager                               u:object_r:hidl_token_hwservice:s0
-android.system.net.netd::INetd                                  u:object_r:system_net_netd_hwservice:s0
-android.system.suspend::ISystemSuspend                          u:object_r:system_suspend_hwservice:s0
-android.system.wifi.keystore::IKeystore                         u:object_r:system_wifi_keystore_hwservice:s0
 *                                                               u:object_r:default_android_hwservice:s0
diff --git a/microdroid/sepolicy/system/private/keystore2_key_contexts b/microdroid/sepolicy/system/private/keystore2_key_contexts
index 4e7c260..02cdd5e 100644
--- a/microdroid/sepolicy/system/private/keystore2_key_contexts
+++ b/microdroid/sepolicy/system/private/keystore2_key_contexts
@@ -4,27 +4,6 @@
 # <namespace> <label>
 #
 # <namespace> must be an integer in the interval [0 ...  2^31)
-# su_key is a keystore_key namespace for the su domain intended for native tests.
-0              u:object_r:su_key:s0
-
-# shell_key is a keystore_key namespace for the shell domain intended for native tests.
-1              u:object_r:shell_key:s0
-
-# vold_key is a keystore2_key namespace for vold. It allows using raw Keymint blobs.
-100            u:object_r:vold_key:s0
-
-# odsign_key is a keystore2_key namespace for the on-device signing daemon.
-101            u:object_r:odsign_key:s0
-
-# wifi_key is a keystore2_key namespace for the WI-FI subsystem. It replaces the WIFI_UID
-# namespace in keystore.
-102            u:object_r:wifi_key:s0
-
-# locksettings_key is a keystore2_key namespace for the LockSettingsService.
-103            u:object_r:locksettings_key:s0
-
-# resume_on_reboot_key is a keystore2_key namespace intended for resume on reboot.
-120            u:object_r:resume_on_reboot_key:s0
 
 # vm_payload_key is a keystore2_key namespace intended for microdroid VM payloads.
 # TODO(b/191843770): sort out a longer term policy
diff --git a/microdroid/sepolicy/system/private/microdroid_manager.te b/microdroid/sepolicy/system/private/microdroid_manager.te
index fba3e71..81a6839 100644
--- a/microdroid/sepolicy/system/private/microdroid_manager.te
+++ b/microdroid/sepolicy/system/private/microdroid_manager.te
@@ -22,10 +22,7 @@
 allow microdroid_manager {shell_exec toolbox_exec}:file rx_file_perms;
 
 # Let microdroid_manager kernel-log.
-# TODO(b/189805435) when ready this should be kmsg_device rather than kmsg_debug_device
-userdebug_or_eng(`
-  allow microdroid_manager kmsg_debug_device:chr_file write;
-')
+allow microdroid_manager kmsg_device:chr_file w_file_perms;
 
 # Let microdroid_manager read a config file from /mnt/apk (fusefs)
 # TODO(b/188400186) remove the below two rules
diff --git a/microdroid/sepolicy/system/private/port_contexts b/microdroid/sepolicy/system/private/port_contexts
index b473c0c..2f40b38 100644
--- a/microdroid/sepolicy/system/private/port_contexts
+++ b/microdroid/sepolicy/system/private/port_contexts
@@ -1,3 +1 @@
-# portcon statements go here, e.g.
-# portcon tcp 80 u:object_r:http_port:s0
-
+# This file can't be empty, but is unused on microdroid
diff --git a/microdroid/sepolicy/system/private/property_contexts b/microdroid/sepolicy/system/private/property_contexts
index 1483f6f..c2a3a62 100644
--- a/microdroid/sepolicy/system/private/property_contexts
+++ b/microdroid/sepolicy/system/private/property_contexts
@@ -25,9 +25,6 @@
 ctl.console u:object_r:ctl_console_prop:s0
 ctl.        u:object_r:ctl_default_prop:s0
 
-dev.mnt.blk.root   u:object_r:system_prop:s0 exact string
-dev.mnt.blk.vendor u:object_r:system_prop:s0 exact string
-
 sys.init.perf_lsm_hooks u:object_r:init_perf_lsm_hooks_prop:s0 exact bool
 
 service.adb.root u:object_r:shell_prop:s0 exact bool
diff --git a/microdroid/sepolicy/system/private/seapp_contexts b/microdroid/sepolicy/system/private/seapp_contexts
index b8e42ea..2f40b38 100644
--- a/microdroid/sepolicy/system/private/seapp_contexts
+++ b/microdroid/sepolicy/system/private/seapp_contexts
@@ -1,178 +1 @@
-# The entries in this file define how security contexts for apps are determined.
-# Each entry lists input selectors, used to match the app, and outputs which are
-# used to determine the security contexts for matching apps.
-#
-# Input selectors:
-#       isSystemServer (boolean)
-#       isEphemeralApp (boolean)
-#       isOwner (boolean)
-#       user (string)
-#       seinfo (string)
-#       name (string)
-#       path (string)
-#       isPrivApp (boolean)
-#       minTargetSdkVersion (unsigned integer)
-#       fromRunAs (boolean)
-#
-# All specified input selectors in an entry must match (i.e. logical AND).
-# An unspecified string or boolean selector with no default will match any
-# value.
-# A user, name, or path string selector that ends in * will perform a prefix
-# match.
-# String matching is case-insensitive.
-# See external/selinux/libselinux/src/android/android_platform.c,
-# seapp_context_lookup().
-#
-# isSystemServer=true only matches the system server.
-# An unspecified isSystemServer defaults to false.
-# isEphemeralApp=true will match apps marked by PackageManager as Ephemeral
-# isOwner=true will only match for the owner/primary user.
-# user=_app will match any regular app process.
-# user=_isolated will match any isolated service process.
-# Other values of user are matched against the name associated with the process
-# UID.
-# seinfo= matches aginst the seinfo tag for the app, determined from
-# mac_permissions.xml files.
-# The ':' character is reserved and may not be used in seinfo.
-# name= matches against the package name of the app.
-# path= matches against the directory path when labeling app directories.
-# isPrivApp=true will only match for applications preinstalled in
-#       /system/priv-app.
-# minTargetSdkVersion will match applications with a targetSdkVersion
-#       greater than or equal to the specified value. If unspecified,
-#       it has a default value of 0.
-# fromRunAs=true means the process being labeled is started by run-as. Default
-# is false.
-#
-# Precedence: entries are compared using the following rules, in the order shown
-# (see external/selinux/libselinux/src/android/android_platform.c,
-# seapp_context_cmp()).
-#       (1) isSystemServer=true before isSystemServer=false.
-#       (2) Specified isEphemeralApp= before unspecified isEphemeralApp=
-#             boolean.
-#       (3) Specified isOwner= before unspecified isOwner= boolean.
-#       (4) Specified user= string before unspecified user= string;
-#             more specific user= string before less specific user= string.
-#       (5) Specified seinfo= string before unspecified seinfo= string.
-#       (6) Specified name= string before unspecified name= string;
-#             more specific name= string before less specific name= string.
-#       (7) Specified path= string before unspecified path= string.
-#             more specific name= string before less specific name= string.
-#       (8) Specified isPrivApp= before unspecified isPrivApp= boolean.
-#       (9) Higher value of minTargetSdkVersion= before lower value of
-#              minTargetSdkVersion= integer. Note that minTargetSdkVersion=
-#              defaults to 0 if unspecified.
-#       (10) fromRunAs=true before fromRunAs=false.
-# (A fixed selector is more specific than a prefix, i.e. ending in *, and a
-# longer prefix is more specific than a shorter prefix.)
-# Apps are checked against entries in precedence order until the first match,
-# regardless of their order in this file.
-#
-# Duplicate entries, i.e. with identical input selectors, are not allowed.
-#
-# Outputs:
-#       domain (string)
-#       type (string)
-#       levelFrom (string; one of none, all, app, or user)
-#       level (string)
-#
-# domain= determines the label to be used for the app process; entries
-# without domain= are ignored for this purpose.
-# type= specifies the label to be used for the app data directory; entries
-# without type= are ignored for this purpose. The label specified must
-# have the app_data_file_type attribute.
-# levelFrom and level are used to determine the level (sensitivity + categories)
-# for MLS/MCS.
-# levelFrom=none omits the level.
-# levelFrom=app determines the level from the process UID.
-# levelFrom=user determines the level from the user ID.
-# levelFrom=all determines the level from both UID and user ID.
-#
-# levelFrom=user is only supported for _app or _isolated UIDs.
-# levelFrom=app or levelFrom=all is only supported for _app UIDs.
-# level may be used to specify a fixed level for any UID.
-#
-# For backwards compatibility levelFromUid=true is equivalent to levelFrom=app
-# and levelFromUid=false is equivalent to levelFrom=none.
-#
-#
-# Neverallow Assertions
-# Additional compile time assertion checks for the rules in this file can be
-# added as well. The assertion
-# rules are lines beginning with the keyword neverallow. Full support for PCRE
-# regular expressions exists on all input and output selectors. Neverallow
-# rules are never output to the built seapp_contexts file. Like all keywords,
-# neverallows are case-insensitive. A neverallow is asserted when all key value
-# inputs are matched on a key value rule line.
-#
-
-# only the system server can be in system_server domain
-neverallow isSystemServer=false domain=system_server
-neverallow isSystemServer="" domain=system_server
-
-# system domains should never be assigned outside of system uid
-neverallow user=((?!system).)* domain=system_app
-neverallow user=((?!system).)* type=system_app_data_file
-
-# any non priv-app with a non-known uid with a specified name should have a specified
-# seinfo
-neverallow user=_app isPrivApp=false name=.* seinfo=""
-neverallow user=_app isPrivApp=false name=.* seinfo=default
-
-# neverallow shared relro to any other domain
-# and neverallow any other uid into shared_relro
-neverallow user=shared_relro domain=((?!shared_relro).)*
-neverallow user=((?!shared_relro).)* domain=shared_relro
-
-# neverallow non-isolated uids into isolated_app domain
-# and vice versa
-neverallow user=_isolated domain=((?!isolated_app).)*
-neverallow user=((?!_isolated).)* domain=isolated_app
-
-# uid shell should always be in shell domain, however non-shell
-# uid's can be in shell domain
-neverallow user=shell domain=((?!shell).)*
-
-# only the package named com.android.shell can run in the shell domain
-neverallow domain=shell name=((?!com\.android\.shell).)*
-neverallow user=shell name=((?!com\.android\.shell).)*
-
-# Ephemeral Apps must run in the ephemeral_app domain
-neverallow isEphemeralApp=true domain=((?!ephemeral_app).)*
-
-isSystemServer=true domain=system_server_startup
-
-user=_app isPrivApp=true name=com.android.traceur domain=traceur_app type=app_data_file levelFrom=all
-user=_app isPrivApp=true name=com.android.remoteprovisioner domain=remote_prov_app type=app_data_file levelFrom=all
-user=system seinfo=platform domain=system_app type=system_app_data_file
-user=bluetooth seinfo=platform domain=bluetooth type=bluetooth_data_file
-user=network_stack seinfo=network_stack domain=network_stack type=radio_data_file
-user=nfc seinfo=platform domain=nfc type=nfc_data_file
-user=secure_element seinfo=platform domain=secure_element levelFrom=all
-user=radio seinfo=platform domain=radio type=radio_data_file
-user=shared_relro domain=shared_relro levelFrom=all
-user=shell seinfo=platform domain=shell name=com.android.shell type=shell_data_file
-user=webview_zygote seinfo=webview_zygote domain=webview_zygote
-user=_isolated domain=isolated_app levelFrom=user
-user=_app seinfo=app_zygote domain=app_zygote levelFrom=user
-user=_app seinfo=media domain=mediaprovider type=app_data_file levelFrom=user
-user=_app seinfo=platform domain=platform_app type=app_data_file levelFrom=user
-user=_app isEphemeralApp=true domain=ephemeral_app type=app_data_file levelFrom=all
-user=_app minTargetSdkVersion=31 isPrivApp=true domain=priv_app type=privapp_data_file levelFrom=all
-user=_app isPrivApp=true domain=priv_app type=privapp_data_file levelFrom=user
-user=_app isPrivApp=true name=com.google.android.permissioncontroller domain=permissioncontroller_app type=privapp_data_file levelFrom=all
-user=_app seinfo=media isPrivApp=true name=com.android.providers.media.module domain=mediaprovider_app type=privapp_data_file levelFrom=all
-user=_app isPrivApp=true name=com.google.android.providers.media.module domain=mediaprovider_app type=privapp_data_file levelFrom=all
-user=_app seinfo=platform isPrivApp=true name=com.android.permissioncontroller domain=permissioncontroller_app type=privapp_data_file levelFrom=all
-user=_app isPrivApp=true name=com.android.vzwomatrigger domain=vzwomatrigger_app type=privapp_data_file levelFrom=all
-user=_app isPrivApp=true name=com.google.android.gms domain=gmscore_app type=privapp_data_file levelFrom=user
-user=_app isPrivApp=true name=com.google.android.gms.* domain=gmscore_app type=privapp_data_file levelFrom=user
-user=_app isPrivApp=true name=com.google.android.gms:* domain=gmscore_app type=privapp_data_file levelFrom=user
-user=_app isPrivApp=true name=com.google.android.gsf domain=gmscore_app type=privapp_data_file levelFrom=user
-user=_app minTargetSdkVersion=30 domain=untrusted_app type=app_data_file levelFrom=all
-user=_app minTargetSdkVersion=29 domain=untrusted_app_29 type=app_data_file levelFrom=all
-user=_app minTargetSdkVersion=28 domain=untrusted_app_27 type=app_data_file levelFrom=all
-user=_app minTargetSdkVersion=26 domain=untrusted_app_27 type=app_data_file levelFrom=user
-user=_app domain=untrusted_app_25 type=app_data_file levelFrom=user
-user=_app minTargetSdkVersion=28 fromRunAs=true domain=runas_app levelFrom=all
-user=_app fromRunAs=true domain=runas_app levelFrom=user
+# This file can't be empty, but is unused on microdroid
diff --git a/microdroid/sepolicy/system/private/service_contexts b/microdroid/sepolicy/system/private/service_contexts
index b410b18..965b688 100644
--- a/microdroid/sepolicy/system/private/service_contexts
+++ b/microdroid/sepolicy/system/private/service_contexts
@@ -1,37 +1,10 @@
-android.hardware.authsecret.IAuthSecret/default                      u:object_r:hal_authsecret_service:s0
-android.hardware.automotive.audiocontrol.IAudioControl/default       u:object_r:hal_audiocontrol_service:s0
-android.hardware.biometrics.face.IFace/default                       u:object_r:hal_face_service:s0
-android.hardware.biometrics.fingerprint.IFingerprint/default         u:object_r:hal_fingerprint_service:s0
-android.hardware.gnss.IGnss/default                                  u:object_r:hal_gnss_service:s0
-android.hardware.health.storage.IStorage/default                     u:object_r:hal_health_storage_service:s0
-android.hardware.identity.IIdentityCredentialStore/default           u:object_r:hal_identity_service:s0
-android.hardware.light.ILights/default                               u:object_r:hal_light_service:s0
-android.hardware.memtrack.IMemtrack/default                          u:object_r:hal_memtrack_service:s0
-android.hardware.oemlock.IOemLock/default                            u:object_r:hal_oemlock_service:s0
-android.hardware.power.IPower/default                                u:object_r:hal_power_service:s0
-android.hardware.power.stats.IPowerStats/default                     u:object_r:hal_power_stats_service:s0
-android.hardware.rebootescrow.IRebootEscrow/default                  u:object_r:hal_rebootescrow_service:s0
 android.hardware.security.keymint.IKeyMintDevice/default             u:object_r:hal_keymint_service:s0
 android.hardware.security.keymint.IRemotelyProvisionedComponent/default u:object_r:hal_remotelyprovisionedcomponent_service:s0
 android.hardware.security.secureclock.ISecureClock/default             u:object_r:hal_secureclock_service:s0
 android.hardware.security.sharedsecret.ISharedSecret/default             u:object_r:hal_sharedsecret_service:s0
-android.hardware.soundtrigger3.ISoundTriggerHw/default               u:object_r:hal_audio_service:s0
-android.hardware.vibrator.IVibrator/default                          u:object_r:hal_vibrator_service:s0
-android.hardware.vibrator.IVibratorManager/default                   u:object_r:hal_vibrator_service:s0
-android.hardware.weaver.IWeaver/default                              u:object_r:hal_weaver_service:s0
-android.frameworks.stats.IStats/default                              u:object_r:fwk_stats_service:s0
 android.system.keystore2.IKeystoreService/default                    u:object_r:keystore_service:s0
 
-accessibility                             u:object_r:accessibility_service:s0
-account                                   u:object_r:account_service:s0
-activity                                  u:object_r:activity_service:s0
-activity_task                             u:object_r:activity_task_service:s0
 adb                                       u:object_r:adb_service:s0
-aidl_lazy_test_1                          u:object_r:aidl_lazy_test_service:s0
-aidl_lazy_test_2                          u:object_r:aidl_lazy_test_service:s0
-alarm                                     u:object_r:alarm_service:s0
-android.os.UpdateEngineService            u:object_r:update_engine_service:s0
-android.os.UpdateEngineStableService      u:object_r:update_engine_stable_service:s0
 android.security.apc                      u:object_r:apc_service:s0
 android.security.authorization            u:object_r:authorization_service:s0
 android.security.compat                   u:object_r:keystore_compat_hal_service:s0
@@ -40,270 +13,5 @@
 android.security.maintenance              u:object_r:keystore_maintenance_service:s0
 android.security.remoteprovisioning       u:object_r:remoteprovisioning_service:s0
 android.security.vpnprofilestore          u:object_r:vpnprofilestore_service:s0
-android.service.gatekeeper.IGateKeeperService    u:object_r:gatekeeper_service:s0
-app_binding                               u:object_r:app_binding_service:s0
-app_hibernation                           u:object_r:app_hibernation_service:s0
-app_integrity                             u:object_r:app_integrity_service:s0
-app_prediction                            u:object_r:app_prediction_service:s0
-app_search                                u:object_r:app_search_service:s0
 apexservice                               u:object_r:apex_service:s0
-blob_store                                u:object_r:blob_store_service:s0
-gsiservice                                u:object_r:gsi_service:s0
-appops                                    u:object_r:appops_service:s0
-appwidget                                 u:object_r:appwidget_service:s0
-artd                                      u:object_r:artd_service:s0
-assetatlas                                u:object_r:assetatlas_service:s0
-attention                                 u:object_r:attention_service:s0
-audio                                     u:object_r:audio_service:s0
-auth                                      u:object_r:auth_service:s0
-autofill                                  u:object_r:autofill_service:s0
-backup                                    u:object_r:backup_service:s0
-batteryproperties                         u:object_r:batteryproperties_service:s0
-batterystats                              u:object_r:batterystats_service:s0
-battery                                   u:object_r:battery_service:s0
-binder_calls_stats                        u:object_r:binder_calls_stats_service:s0
-biometric                                 u:object_r:biometric_service:s0
-bluetooth_manager                         u:object_r:bluetooth_manager_service:s0
-bluetooth                                 u:object_r:bluetooth_service:s0
-broadcastradio                            u:object_r:broadcastradio_service:s0
-bugreport                                 u:object_r:bugreport_service:s0
-cacheinfo                                 u:object_r:cacheinfo_service:s0
-carrier_config                            u:object_r:radio_service:s0
-clipboard                                 u:object_r:clipboard_service:s0
-com.android.net.IProxyService             u:object_r:IProxyService_service:s0
-android.system.virtualizationservice      u:object_r:virtualization_service:s0
-companiondevice                           u:object_r:companion_device_service:s0
-platform_compat                           u:object_r:platform_compat_service:s0
-platform_compat_native                    u:object_r:platform_compat_service:s0
-connectivity                              u:object_r:connectivity_service:s0
-connmetrics                               u:object_r:connmetrics_service:s0
-consumer_ir                               u:object_r:consumer_ir_service:s0
-content                                   u:object_r:content_service:s0
-content_capture                           u:object_r:content_capture_service:s0
-content_suggestions                       u:object_r:content_suggestions_service:s0
-contexthub                                u:object_r:contexthub_service:s0
-country_detector                          u:object_r:country_detector_service:s0
-coverage                                  u:object_r:coverage_service:s0
-cpuinfo                                   u:object_r:cpuinfo_service:s0
-crossprofileapps                          u:object_r:crossprofileapps_service:s0
-dataloader_manager                        u:object_r:dataloader_manager_service:s0
-dbinfo                                    u:object_r:dbinfo_service:s0
-device_config                             u:object_r:device_config_service:s0
-device_policy                             u:object_r:device_policy_service:s0
-device_identifiers                        u:object_r:device_identifiers_service:s0
-deviceidle                                u:object_r:deviceidle_service:s0
-device_state                              u:object_r:device_state_service:s0
-devicestoragemonitor                      u:object_r:devicestoragemonitor_service:s0
-diskstats                                 u:object_r:diskstats_service:s0
-display                                   u:object_r:display_service:s0
-dnsresolver                               u:object_r:dnsresolver_service:s0
-domain_verification                       u:object_r:domain_verification_service:s0
-color_display                             u:object_r:color_display_service:s0
-netd_listener                             u:object_r:netd_listener_service:s0
-network_watchlist                         u:object_r:network_watchlist_service:s0
-DockObserver                              u:object_r:DockObserver_service:s0
-dreams                                    u:object_r:dreams_service:s0
-drm.drmManager                            u:object_r:drmserver_service:s0
-dropbox                                   u:object_r:dropbox_service:s0
-dumpstate                                 u:object_r:dumpstate_service:s0
-dynamic_system                            u:object_r:dynamic_system_service:s0
-econtroller                               u:object_r:radio_service:s0
-emergency_affordance                      u:object_r:emergency_affordance_service:s0
-euicc_card_controller                     u:object_r:radio_service:s0
-external_vibrator_service                 u:object_r:external_vibrator_service:s0
-lowpan                                    u:object_r:lowpan_service:s0
-ethernet                                  u:object_r:ethernet_service:s0
-face                                      u:object_r:face_service:s0
-file_integrity                            u:object_r:file_integrity_service:s0
-fingerprint                               u:object_r:fingerprint_service:s0
-font                                      u:object_r:font_service:s0
-android.hardware.fingerprint.IFingerprintDaemon u:object_r:fingerprintd_service:s0
-game                                      u:object_r:game_service:s0
-gfxinfo                                   u:object_r:gfxinfo_service:s0
-graphicsstats                             u:object_r:graphicsstats_service:s0
-gpu                                       u:object_r:gpu_service:s0
-hardware                                  u:object_r:hardware_service:s0
-hardware_properties                       u:object_r:hardware_properties_service:s0
-hdmi_control                              u:object_r:hdmi_control_service:s0
-ions                                      u:object_r:radio_service:s0
-idmap                                     u:object_r:idmap_service:s0
-incident                                  u:object_r:incident_service:s0
-incidentcompanion                         u:object_r:incidentcompanion_service:s0
-inputflinger                              u:object_r:inputflinger_service:s0
-input_method                              u:object_r:input_method_service:s0
-input                                     u:object_r:input_service:s0
-installd                                  u:object_r:installd_service:s0
-iorapd                                    u:object_r:iorapd_service:s0
-iphonesubinfo_msim                        u:object_r:radio_service:s0
-iphonesubinfo2                            u:object_r:radio_service:s0
-iphonesubinfo                             u:object_r:radio_service:s0
-ims                                       u:object_r:radio_service:s0
-imms                                      u:object_r:imms_service:s0
-incremental                               u:object_r:incremental_service:s0
-ipsec                                     u:object_r:ipsec_service:s0
-ircsmessage                               u:object_r:radio_service:s0
-iris                                      u:object_r:iris_service:s0
-isms_msim                                 u:object_r:radio_service:s0
-isms2                                     u:object_r:radio_service:s0
-isms                                      u:object_r:radio_service:s0
-isub                                      u:object_r:radio_service:s0
-jobscheduler                              u:object_r:jobscheduler_service:s0
-launcherapps                              u:object_r:launcherapps_service:s0
-legacy_permission                         u:object_r:legacy_permission_service:s0
-lights                                    u:object_r:light_service:s0
-location                                  u:object_r:location_service:s0
-location_time_zone_manager                u:object_r:location_time_zone_manager_service:s0
-lock_settings                             u:object_r:lock_settings_service:s0
-looper_stats                              u:object_r:looper_stats_service:s0
-lpdump_service                            u:object_r:lpdump_service:s0
-media.aaudio                              u:object_r:audioserver_service:s0
-media.audio_flinger                       u:object_r:audioserver_service:s0
-media.audio_policy                        u:object_r:audioserver_service:s0
-media.camera                              u:object_r:cameraserver_service:s0
-media.camera.proxy                        u:object_r:cameraproxy_service:s0
-media.log                                 u:object_r:audioserver_service:s0
-media.player                              u:object_r:mediaserver_service:s0
-media.metrics                             u:object_r:mediametrics_service:s0
-media.extractor                           u:object_r:mediaextractor_service:s0
-media.transcoding                         u:object_r:mediatranscoding_service:s0
-media.resource_manager                    u:object_r:mediaserver_service:s0
-media.resource_observer                   u:object_r:mediaserver_service:s0
-media.sound_trigger_hw                    u:object_r:audioserver_service:s0
-media.drm                                 u:object_r:mediadrmserver_service:s0
-media.tuner                               u:object_r:mediatuner_service:s0
-media_communication                       u:object_r:media_communication_service:s0
-media_metrics                             u:object_r:media_metrics_service:s0
-media_projection                          u:object_r:media_projection_service:s0
-media_resource_monitor                    u:object_r:media_session_service:s0
-media_router                              u:object_r:media_router_service:s0
-media_session                             u:object_r:media_session_service:s0
-meminfo                                   u:object_r:meminfo_service:s0
-memtrack.proxy                            u:object_r:memtrackproxy_service:s0
-midi                                      u:object_r:midi_service:s0
-mount                                     u:object_r:mount_service:s0
-music_recognition                         u:object_r:music_recognition_service:s0
-netd                                      u:object_r:netd_service:s0
-netpolicy                                 u:object_r:netpolicy_service:s0
-netstats                                  u:object_r:netstats_service:s0
-network_stack                             u:object_r:network_stack_service:s0
-network_management                        u:object_r:network_management_service:s0
-network_score                             u:object_r:network_score_service:s0
-network_time_update_service               u:object_r:network_time_update_service:s0
-nfc                                       u:object_r:nfc_service:s0
-notification                              u:object_r:notification_service:s0
-oem_lock                                  u:object_r:oem_lock_service:s0
-otadexopt                                 u:object_r:otadexopt_service:s0
-overlay                                   u:object_r:overlay_service:s0
-pac_proxy                                 u:object_r:pac_proxy_service:s0
-package                                   u:object_r:package_service:s0
-package_native                            u:object_r:package_native_service:s0
-people                                    u:object_r:people_service:s0
-performance_hint                          u:object_r:hint_service:s0
-permission                                u:object_r:permission_service:s0
-permissionmgr                             u:object_r:permissionmgr_service:s0
-permission_checker                        u:object_r:permission_checker_service:s0
-persistent_data_block                     u:object_r:persistent_data_block_service:s0
-phone_msim                                u:object_r:radio_service:s0
-phone1                                    u:object_r:radio_service:s0
-phone2                                    u:object_r:radio_service:s0
-phone                                     u:object_r:radio_service:s0
-pinner                                    u:object_r:pinner_service:s0
-power_stats                               u:object_r:power_stats_service:s0
-power                                     u:object_r:power_service:s0
-print                                     u:object_r:print_service:s0
-processinfo                               u:object_r:processinfo_service:s0
-procstats                                 u:object_r:procstats_service:s0
-profcollectd                              u:object_r:profcollectd_service:s0
-radio.phonesubinfo                        u:object_r:radio_service:s0
-radio.phone                               u:object_r:radio_service:s0
-radio.sms                                 u:object_r:radio_service:s0
-rcs                                       u:object_r:radio_service:s0
-reboot_readiness                          u:object_r:reboot_readiness_service:s0
-recovery                                  u:object_r:recovery_service:s0
-resolver                                  u:object_r:resolver_service:s0
-restrictions                              u:object_r:restrictions_service:s0
-role                                      u:object_r:role_service:s0
-rollback                                  u:object_r:rollback_service:s0
-rttmanager                                u:object_r:rttmanager_service:s0
-runtime                                   u:object_r:runtime_service:s0
-samplingprofiler                          u:object_r:samplingprofiler_service:s0
-scheduling_policy                         u:object_r:scheduling_policy_service:s0
-search                                    u:object_r:search_service:s0
-search_ui                                 u:object_r:search_ui_service:s0
-secure_element                            u:object_r:secure_element_service:s0
-sec_key_att_app_id_provider               u:object_r:sec_key_att_app_id_provider_service:s0
-sensorservice                             u:object_r:sensorservice_service:s0
-sensor_privacy                            u:object_r:sensor_privacy_service:s0
-serial                                    u:object_r:serial_service:s0
-servicediscovery                          u:object_r:servicediscovery_service:s0
-manager                                   u:object_r:service_manager_service:s0
-settings                                  u:object_r:settings_service:s0
-shortcut                                  u:object_r:shortcut_service:s0
-simphonebook_msim                         u:object_r:radio_service:s0
-simphonebook2                             u:object_r:radio_service:s0
-simphonebook                              u:object_r:radio_service:s0
-sip                                       u:object_r:radio_service:s0
-slice                                     u:object_r:slice_service:s0
-smartspace                                u:object_r:smartspace_service:s0
-speech_recognition                        u:object_r:speech_recognition_service:s0
-stats                                     u:object_r:stats_service:s0
-statscompanion                            u:object_r:statscompanion_service:s0
-statsmanager                              u:object_r:statsmanager_service:s0
-soundtrigger                              u:object_r:voiceinteraction_service:s0
-soundtrigger_middleware                   u:object_r:soundtrigger_middleware_service:s0
-statusbar                                 u:object_r:statusbar_service:s0
-storaged                                  u:object_r:storaged_service:s0
-storaged_pri                              u:object_r:storaged_service:s0
-storagestats                              u:object_r:storagestats_service:s0
-SurfaceFlinger                            u:object_r:surfaceflinger_service:s0
-suspend_control                           u:object_r:system_suspend_control_service:s0
-suspend_control_internal                  u:object_r:system_suspend_control_internal_service:s0
-system_config                             u:object_r:system_config_service:s0
-system_server_dumper                      u:object_r:system_server_dumper_service:s0
-system_update                             u:object_r:system_update_service:s0
-task                                      u:object_r:task_service:s0
-telecom                                   u:object_r:telecom_service:s0
-telephony.registry                        u:object_r:registry_service:s0
-telephony_ims                             u:object_r:radio_service:s0
-testharness                               u:object_r:testharness_service:s0
-tethering                                 u:object_r:tethering_service:s0
-textclassification                        u:object_r:textclassification_service:s0
-textservices                              u:object_r:textservices_service:s0
-texttospeech                              u:object_r:texttospeech_service:s0
-time_detector                             u:object_r:timedetector_service:s0
-time_zone_detector                        u:object_r:timezonedetector_service:s0
-timezone                                  u:object_r:timezone_service:s0
-thermalservice                            u:object_r:thermal_service:s0
-tracing.proxy                             u:object_r:tracingproxy_service:s0
-transformer                               u:object_r:transformer_service:s0
-trust                                     u:object_r:trust_service:s0
-tv_input                                  u:object_r:tv_input_service:s0
-tv_tuner_resource_mgr                     u:object_r:tv_tuner_resource_mgr_service:s0
-uce                                       u:object_r:uce_service:s0
-uimode                                    u:object_r:uimode_service:s0
-updatelock                                u:object_r:updatelock_service:s0
-uri_grants                                u:object_r:uri_grants_service:s0
-usagestats                                u:object_r:usagestats_service:s0
-usb                                       u:object_r:usb_service:s0
-user                                      u:object_r:user_service:s0
-uwb                                       u:object_r:uwb_service:s0
-vcn_management                            u:object_r:vcn_management_service:s0
-vibrator                                  u:object_r:vibrator_service:s0
-vibrator_manager                          u:object_r:vibrator_manager_service:s0
-virtual_touchpad                          u:object_r:virtual_touchpad_service:s0
-voiceinteraction                          u:object_r:voiceinteraction_service:s0
-vold                                      u:object_r:vold_service:s0
-vpn_management                            u:object_r:vpn_management_service:s0
-vr_hwc                                    u:object_r:vr_hwc_service:s0
-vrflinger_vsync                           u:object_r:vrflinger_vsync_service:s0
-vrmanager                                 u:object_r:vr_manager_service:s0
-wallpaper                                 u:object_r:wallpaper_service:s0
-webviewupdate                             u:object_r:webviewupdate_service:s0
-wifip2p                                   u:object_r:wifip2p_service:s0
-wifiscanner                               u:object_r:wifiscanner_service:s0
-wifi                                      u:object_r:wifi_service:s0
-wifinl80211                               u:object_r:wifinl80211_service:s0
-wifiaware                                 u:object_r:wifiaware_service:s0
-wifirtt                                   u:object_r:rttmanager_service:s0
-window                                    u:object_r:window_service:s0
 *                                         u:object_r:default_android_service:s0
diff --git a/microdroid/sepolicy/system/private/virtualizationservice.te b/microdroid/sepolicy/system/private/virtualizationservice.te
index 4c6f1f9..097f0a0 100644
--- a/microdroid/sepolicy/system/private/virtualizationservice.te
+++ b/microdroid/sepolicy/system/private/virtualizationservice.te
@@ -14,9 +14,6 @@
 # When virtualizationservice execs a file with the crosvm_exec label, run it in the crosvm domain.
 domain_auto_trans(virtualizationservice, crosvm_exec, crosvm)
 
-# Let virtualizationservice exec other files (e.g. mk_cdisk) in the same domain.
-allow virtualizationservice system_file:file execute_no_trans;
-
 # Let virtualizationservice kill crosvm.
 allow virtualizationservice crosvm:process sigkill;
 
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index 30f8481..267147f 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -9,15 +9,15 @@
     edition: "2018",
     prefer_rlib: true,
     rustlibs: [
-        "libenv_logger",
         "libanyhow",
+        "libkernlog",
         "libkeystore2_system_property-rust",
         "liblog_rust",
-        "libmicrodroid_metadata_proto_rust",
+        "libmicrodroid_metadata",
         "libmicrodroid_payload_config",
         "libprotobuf",
-        "libserde_json",
         "libserde",
+        "libserde_json",
     ],
     init_rc: ["microdroid_manager.rc"],
 }
diff --git a/microdroid_manager/microdroid_manager.rc b/microdroid_manager/microdroid_manager.rc
index 4f194a3..60d8ab7 100644
--- a/microdroid_manager/microdroid_manager.rc
+++ b/microdroid_manager/microdroid_manager.rc
@@ -1,7 +1,6 @@
 service microdroid_manager /system/bin/microdroid_manager
     disabled
-    # TODO(b/189805435) for now redirect stdio to kmsg
-    stdio_to_kmsg
+    file /dev/kmsg w
     setenv RUST_LOG info
     # TODO(jooyung) remove this when microdroid_manager becomes a daemon
-    oneshot
\ No newline at end of file
+    oneshot
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 10731c5..9bcfa67 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -29,9 +29,7 @@
 const WAIT_TIMEOUT: Duration = Duration::from_secs(10);
 
 fn main() -> Result<()> {
-    // TODO(b/189805435) use kernlog
-    env_logger::init();
-
+    kernlog::init()?;
     info!("started.");
 
     let metadata = metadata::load()?;
diff --git a/microdroid_manager/src/metadata.rs b/microdroid_manager/src/metadata.rs
index 4f7d7af..81d9cc4 100644
--- a/microdroid_manager/src/metadata.rs
+++ b/microdroid_manager/src/metadata.rs
@@ -15,25 +15,14 @@
 //! Payload metadata from /dev/block/by-name/metadata
 
 use log::info;
-use microdroid_metadata::metadata::Metadata;
-use protobuf::Message;
+use microdroid_metadata::{read_metadata, Metadata};
 use std::fs::File;
 use std::io;
-use std::io::Read;
 
 const METADATA_PATH: &str = "/dev/block/by-name/metadata";
 
 /// loads payload metadata from /dev/block/by-name/metadata
 pub fn load() -> io::Result<Metadata> {
     info!("loading payload metadata...");
-
-    let mut f = File::open(METADATA_PATH)?;
-    // metadata partition is
-    //  4 bytes : size(N) in big endian
-    //  N bytes : message for Metadata
-    let mut buf = [0u8; 4];
-    f.read_exact(&mut buf)?;
-    let size = i32::from_be_bytes(buf);
-
-    Ok(Metadata::parse_from_reader(&mut f.take(size as u64))?)
+    read_metadata(File::open(METADATA_PATH)?)
 }
diff --git a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
index 2457797..a3288a1 100644
--- a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
@@ -18,7 +18,6 @@
 
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -31,25 +30,17 @@
 import com.android.tradefed.util.CommandStatus;
 import com.android.tradefed.util.RunUtil;
 
-import org.json.JSONArray;
-import org.json.JSONObject;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileWriter;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-import java.util.zip.ZipFile;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class MicrodroidTestCase extends BaseHostJUnit4Test {
@@ -132,7 +123,7 @@
         return result.getStdout().trim();
     }
 
-    // Run a shell command on Android
+    // Run a shell command on Android. the default timeout is 2 min by tradefed
     private String runOnAndroid(String... cmd) throws Exception {
         CommandResult result = getDevice().executeShellV2Command(join(cmd));
         if (result.getStatus() != CommandStatus.SUCCESS) {
@@ -141,12 +132,25 @@
         return result.getStdout().trim();
     }
 
-    // Same as runOnAndroid, but failutre is not an error
+    // Same as runOnAndroid, but failure is not an error
     private String tryRunOnAndroid(String... cmd) throws Exception {
         CommandResult result = getDevice().executeShellV2Command(join(cmd));
         return result.getStdout().trim();
     }
 
+    private String runOnAndroidWithTimeout(long timeoutMillis, String... cmd) throws Exception {
+        CommandResult result =
+                getDevice()
+                        .executeShellV2Command(
+                                join(cmd),
+                                timeoutMillis,
+                                java.util.concurrent.TimeUnit.MILLISECONDS);
+        if (result.getStatus() != CommandStatus.SUCCESS) {
+            fail(join(cmd) + " has failed: " + result);
+        }
+        return result.getStdout().trim();
+    }
+
     // Run a shell command on Microdroid
     private String runOnMicrodroid(String... cmd) {
         final long timeout = 30000; // 30 sec. Microdroid is extremely slow on GCE-on-CF.
@@ -163,26 +167,16 @@
         return String.join(" ", Arrays.asList(strs));
     }
 
-    private String createPayloadImage(String apkName, String packageName, String configPath)
+    private File findTestFile(String name) throws Exception {
+        return (new CompatibilityBuildHelper(getBuild())).getTestFile(name);
+    }
+
+    private String startMicrodroid(String apkName, String packageName, String configPath)
             throws Exception {
+        // Install APK
         File apkFile = findTestFile(apkName);
         getDevice().installPackage(apkFile, /* reinstall */ true);
 
-        // Read the config file from the apk and parse it to know the list of APEXes needed
-        ZipFile apkAsZip = new ZipFile(apkFile);
-        InputStream is = apkAsZip.getInputStream(apkAsZip.getEntry(configPath));
-        String configString =
-                new BufferedReader(new InputStreamReader(is))
-                        .lines()
-                        .collect(Collectors.joining("\n"));
-        JSONObject configObject = new JSONObject(configString);
-        JSONArray apexes = configObject.getJSONArray("apexes");
-        List<String> apexNames = new ArrayList<>();
-        for (int i = 0; i < apexes.length(); i++) {
-            JSONObject anApex = apexes.getJSONObject(i);
-            apexNames.add(anApex.getString("name"));
-        }
-
         // Get the path to the installed apk. Note that
         // getDevice().getAppPackageInfo(...).getCodePath() doesn't work due to the incorrect
         // parsing of the "=" character. (b/190975227). So we use the `pm path` command directly.
@@ -195,53 +189,37 @@
         final String apkIdsigPath = TEST_ROOT + apkName + ".idsig";
         getDevice().pushFile(idsigOnHost, apkIdsigPath);
 
-        // Create payload.json from the gathered data
-        JSONObject payloadObject = new JSONObject();
-        payloadObject.put("system_apexes", new JSONArray(apexNames));
-        payloadObject.put("payload_config_path", "/mnt/apk/" + configPath);
-        JSONObject apkObject = new JSONObject();
-        apkObject.put("name", packageName);
-        apkObject.put("path", apkPath);
-        apkObject.put("idsig_path", apkIdsigPath);
-        payloadObject.put("apk", apkObject);
-
-        // Copy the json file to Android
-        File payloadJsonOnHost = File.createTempFile("payload", "json");
-        FileWriter writer = new FileWriter(payloadJsonOnHost);
-        writer.write(payloadObject.toString());
-        writer.close();
-        final String payloadJson = TEST_ROOT + "payload.json";
-        getDevice().pushFile(payloadJsonOnHost, payloadJson);
-
-        // Finally run mk_payload to create payload.img
-        final String mkPayload = VIRT_APEX + "bin/mk_payload";
-        final String payloadImg = TEST_ROOT + "payload.img";
-        runOnAndroid(mkPayload, payloadJson, payloadImg);
-        assertThat(runOnAndroid("du", "-b", payloadImg), is(not("")));
-
-        // The generated files are owned by root. Allow the virtualizationservice to read them.
-        runOnAndroid("chmod", "go+r", TEST_ROOT + "payload*");
-
-        return payloadImg;
-    }
-
-    private File findTestFile(String name) throws Exception {
-        return (new CompatibilityBuildHelper(getBuild())).getTestFile(name);
-    }
-
-    private String startMicrodroid(String apkName, String packageName, String configPath)
-            throws Exception {
-        // Create payload.img
-        createPayloadImage(apkName, packageName, configPath);
+        final String logPath = TEST_ROOT + "log.txt";
 
         // Run the VM
         runOnAndroid("start", "virtualizationservice");
         String ret =
                 runOnAndroid(
                         VIRT_APEX + "bin/vm",
-                        "run",
+                        "run-app",
                         "--daemonize",
-                        VIRT_APEX + "etc/microdroid.json");
+                        "--log " + logPath,
+                        apkPath,
+                        apkIdsigPath,
+                        configPath);
+
+        // Redirect log.txt to logd using logwrapper
+        ExecutorService executor = Executors.newFixedThreadPool(1);
+        executor.execute(
+                () -> {
+                    try {
+                        // Keep redirecting sufficiently long enough
+                        runOnAndroidWithTimeout(
+                                MICRODROID_BOOT_TIMEOUT_MINUTES * 60 * 1000,
+                                "logwrapper",
+                                "tail",
+                                "-f",
+                                "-n +0",
+                                logPath);
+                    } catch (Exception e) {
+                        // Consume
+                    }
+                });
 
         // Retrieve the CID from the vm tool output
         Pattern pattern = Pattern.compile("with CID (\\d+)");
diff --git a/tests/testapk/assets/vm_config.json b/tests/testapk/assets/vm_config.json
index 8312f4d..b814394 100644
--- a/tests/testapk/assets/vm_config.json
+++ b/tests/testapk/assets/vm_config.json
@@ -9,19 +9,5 @@
       "hello",
       "microdroid"
     ]
-  },
-  "apexes": [
-    {
-      "name": "com.android.adbd"
-    },
-    {
-      "name": "com.android.i18n"
-    },
-    {
-      "name": "com.android.os.statsd"
-    },
-    {
-      "name": "com.android.sdkext"
-    }
-  ]
+  }
 }
diff --git a/tests/vsock_test.cc b/tests/vsock_test.cc
index 233c6dd..d9b8f21 100644
--- a/tests/vsock_test.cc
+++ b/tests/vsock_test.cc
@@ -32,6 +32,7 @@
 #include "android-base/parseint.h"
 #include "android-base/unique_fd.h"
 #include "android/system/virtualizationservice/VirtualMachineConfig.h"
+#include "android/system/virtualizationservice/VirtualMachineRawConfig.h"
 #include "virt/VirtualizationTest.h"
 
 #define KVM_CAP_ARM_PROTECTED_VM 0xffbadab1
@@ -83,12 +84,13 @@
     ret = TEMP_FAILURE_RETRY(listen(server_fd, 1));
     ASSERT_EQ(ret, 0) << strerror(errno);
 
-    VirtualMachineConfig config;
-    config.kernel = ParcelFileDescriptor(unique_fd(open(kVmKernelPath, O_RDONLY | O_CLOEXEC)));
-    config.initrd = ParcelFileDescriptor(unique_fd(open(kVmInitrdPath, O_RDONLY | O_CLOEXEC)));
-    config.params = String16(kVmParams);
-    config.protected_vm = protected_vm;
+    VirtualMachineRawConfig raw_config;
+    raw_config.kernel = ParcelFileDescriptor(unique_fd(open(kVmKernelPath, O_RDONLY | O_CLOEXEC)));
+    raw_config.initrd = ParcelFileDescriptor(unique_fd(open(kVmInitrdPath, O_RDONLY | O_CLOEXEC)));
+    raw_config.params = kVmParams;
+    raw_config.protected_vm = protected_vm;
 
+    VirtualMachineConfig config(std::move(raw_config));
     sp<IVirtualMachine> vm;
     status = virtualization_service->startVm(config, std::nullopt, &vm);
     ASSERT_TRUE(status.isOk()) << "Error starting VM: " << status;
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 700d0fc..a941742 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -27,12 +27,16 @@
         "libcrc32fast",
         "libdisk",
         "liblog_rust",
+        "libmicrodroid_metadata",
+        "libmicrodroid_payload_config",
         "libprotobuf",
         "libprotos",
         "libserde_json",
         "libserde",
         "libshared_child",
         "libuuid",
+        "libvmconfig",
+        "libzip",
     ],
 }
 
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl
index 782c239..9b8658b 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl
@@ -18,10 +18,10 @@
 /** A partition to be assembled into a composite image. */
 parcelable Partition {
     /** A label for the partition. */
-    String label;
+    @utf8InCpp String label;
 
-    /** The backing file descriptor of the partition image. */
-    ParcelFileDescriptor image;
+    /** The backing file descriptors of the partition images. */
+    ParcelFileDescriptor[] images;
 
     /** Whether the partition should be writable by the VM. */
     boolean writable;
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
new file mode 100644
index 0000000..5b270a3
--- /dev/null
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2021 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.virtualizationservice;
+
+/** Configuration for running an App in a VM */
+parcelable VirtualMachineAppConfig {
+    /** Main APK */
+    ParcelFileDescriptor apk;
+
+    /** idsig for an APK */
+    ParcelFileDescriptor idsig;
+
+    /** Path to a configuration in an APK. This is the actual configuration for a VM. */
+    @utf8InCpp String configPath;
+}
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineConfig.aidl
index cb28856..00a7937 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineConfig.aidl
@@ -15,31 +15,14 @@
  */
 package android.system.virtualizationservice;
 
-import android.system.virtualizationservice.DiskImage;
+import android.system.virtualizationservice.VirtualMachineAppConfig;
+import android.system.virtualizationservice.VirtualMachineRawConfig;
 
-/** Configuration for running a VM. */
-parcelable VirtualMachineConfig {
-    /** The kernel image, if any. */
-    @nullable ParcelFileDescriptor kernel;
+/** Configuration for running a VM */
+union VirtualMachineConfig {
+    /** Configuration for a VM to run an APP */
+    VirtualMachineAppConfig appConfig;
 
-    /** The initial ramdisk for the kernel, if any. */
-    @nullable ParcelFileDescriptor initrd;
-
-    /**
-     * Parameters to pass to the kernel. As far as the VMM and boot protocol are concerned this is
-     * just a string, but typically it will contain multiple parameters separated by spaces.
-     */
-    @nullable String params;
-
-    /**
-     * The bootloader to use. If this is supplied then the kernel and initrd must not be supplied;
-     * the bootloader is instead responsibly for loading the kernel from one of the disks.
-     */
-    @nullable ParcelFileDescriptor bootloader;
-
-    /** Disk images to be made available to the VM. */
-    DiskImage[] disks;
-
-    /** Whether the VM should be a protected VM. */
-    boolean protected_vm;
+    /** Configuration for a VM with low-level configuration */
+    VirtualMachineRawConfig rawConfig;
 }
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineDebugInfo.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineDebugInfo.aidl
index 18b01ce..d081b8d 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineDebugInfo.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineDebugInfo.aidl
@@ -21,13 +21,13 @@
     int cid;
 
     /** Directory of temporary files used by the VM while it is running. */
-    String temporaryDirectory;
+    @utf8InCpp String temporaryDirectory;
 
     /** The UID of the process which requested the VM. */
     int requesterUid;
 
     /** The SID of the process which requested the VM. */
-    String requesterSid;
+    @utf8InCpp String requesterSid;
 
     /**
      * The PID of the process which requested the VM. Note that this process may no longer exist and
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
new file mode 100644
index 0000000..7848ed5
--- /dev/null
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2021 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.virtualizationservice;
+
+import android.system.virtualizationservice.DiskImage;
+
+/** Raw Configuration for running a VM. */
+parcelable VirtualMachineRawConfig {
+    /** The kernel image, if any. */
+    @nullable ParcelFileDescriptor kernel;
+
+    /** The initial ramdisk for the kernel, if any. */
+    @nullable ParcelFileDescriptor initrd;
+
+    /**
+     * Parameters to pass to the kernel. As far as the VMM and boot protocol are concerned this is
+     * just a string, but typically it will contain multiple parameters separated by spaces.
+     */
+    @nullable @utf8InCpp String params;
+
+    /**
+     * The bootloader to use. If this is supplied then the kernel and initrd must not be supplied;
+     * the bootloader is instead responsibly for loading the kernel from one of the disks.
+     */
+    @nullable ParcelFileDescriptor bootloader;
+
+    /** Disk images to be made available to the VM. */
+    DiskImage[] disks;
+
+    /** Whether the VM should be a protected VM. */
+    boolean protected_vm;
+}
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index b1b0b38..a0b5217 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -16,27 +16,35 @@
 
 use crate::composite::make_composite_image;
 use crate::crosvm::{CrosvmConfig, DiskFile, VmInstance};
+use crate::payload;
 use crate::{Cid, FIRST_GUEST_CID};
+
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualizationService::IVirtualizationService;
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::DiskImage::DiskImage;
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualMachine::{
     BnVirtualMachine, IVirtualMachine,
 };
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualMachineCallback::IVirtualMachineCallback;
-use android_system_virtualizationservice::aidl::android::system::virtualizationservice::VirtualMachineConfig::VirtualMachineConfig;
+use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+    VirtualMachineAppConfig::VirtualMachineAppConfig,
+    VirtualMachineConfig::VirtualMachineConfig,
+    VirtualMachineRawConfig::VirtualMachineRawConfig,
+};
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::VirtualMachineDebugInfo::VirtualMachineDebugInfo;
 use android_system_virtualizationservice::binder::{
     self, BinderFeatures, ExceptionCode, Interface, ParcelFileDescriptor, Status, Strong, ThreadState,
 };
-use command_fds::FdMapping;
+use anyhow::Result;
 use disk::QcowFile;
 use log::{debug, error, warn};
+use microdroid_payload_config::{ApexConfig, VmPayloadConfig};
 use std::convert::TryInto;
 use std::ffi::CString;
 use std::fs::{File, create_dir};
 use std::os::unix::io::AsRawFd;
 use std::path::{Path, PathBuf};
 use std::sync::{Arc, Mutex, Weak};
+use vmconfig::VmConfig;
 
 pub const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtualizationservice";
 
@@ -93,6 +101,22 @@
             )
         })?;
 
+        let mut opt_raw_config = None;
+        let config = match config {
+            VirtualMachineConfig::AppConfig(config) => {
+                let raw_config = load_app_config(config, &temporary_directory).map_err(|e| {
+                    error!("Failed to load app config from {}: {}", &config.configPath, e);
+                    new_binder_exception(
+                        ExceptionCode::SERVICE_SPECIFIC,
+                        format!("Failed to load app config from {}: {}", &config.configPath, e),
+                    )
+                })?;
+                opt_raw_config.replace(raw_config);
+                opt_raw_config.as_ref().unwrap()
+            }
+            VirtualMachineConfig::RawConfig(config) => config,
+        };
+
         // Assemble disk images if needed.
         let disks = config
             .disks
@@ -117,17 +141,12 @@
             params: config.params.to_owned(),
             protected: config.protected_vm,
         };
-        let composite_disk_mappings: Vec<_> = indirect_files
-            .iter()
-            .map(|file| {
-                let fd = file.as_raw_fd();
-                FdMapping { parent_fd: fd, child_fd: fd }
-            })
-            .collect();
+        let composite_disk_fds: Vec<_> =
+            indirect_files.iter().map(|file| file.as_raw_fd()).collect();
         let instance = VmInstance::start(
             &crosvm_config,
             log_fd,
-            &composite_disk_mappings,
+            &composite_disk_fds,
             temporary_directory,
             requester_uid,
             requester_sid,
@@ -262,6 +281,46 @@
     Ok(DiskFile { image, writable: disk.writable })
 }
 
+fn load_app_config(
+    config: &VirtualMachineAppConfig,
+    temporary_directory: &Path,
+) -> Result<VirtualMachineRawConfig> {
+    let apk_file = config.apk.as_ref().unwrap().as_ref();
+    let idsig_file = config.idsig.as_ref().unwrap().as_ref();
+    let config_path = &config.configPath;
+
+    let mut apk_zip = zip::ZipArchive::new(apk_file)?;
+    let config_file = apk_zip.by_name(config_path)?;
+    let vm_payload_config: VmPayloadConfig = serde_json::from_reader(config_file)?;
+
+    let os_name = &vm_payload_config.os.name;
+    let vm_config_path = PathBuf::from(format!("/apex/com.android.virt/etc/{}.json", os_name));
+    let vm_config_file = File::open(vm_config_path)?;
+    let mut vm_config = VmConfig::load(&vm_config_file)?;
+
+    // Microdroid requires additional payload disk image
+    if os_name == "microdroid" {
+        // TODO (b/192200378) move this to microdroid.json?
+        let mut apexes = vm_payload_config.apexes.clone();
+        apexes.extend(
+            ["com.android.adbd", "com.android.i18n", "com.android.os.statsd", "com.android.sdkext"]
+                .iter()
+                .map(|name| ApexConfig { name: name.to_string() }),
+        );
+        apexes.dedup_by(|a, b| a.name == b.name);
+
+        vm_config.disks.push(payload::make_disk_image(
+            format!("/proc/self/fd/{}", apk_file.as_raw_fd()).into(),
+            format!("/proc/self/fd/{}", idsig_file.as_raw_fd()).into(),
+            config_path,
+            &apexes,
+            temporary_directory,
+        )?);
+    }
+
+    vm_config.to_parcelable()
+}
+
 /// Generates a unique filename to use for a composite disk image.
 fn make_composite_image_filenames(
     temporary_directory: &Path,
diff --git a/virtualizationservice/src/composite.rs b/virtualizationservice/src/composite.rs
index 5f792fd..1af0eed 100644
--- a/virtualizationservice/src/composite.rs
+++ b/virtualizationservice/src/composite.rs
@@ -72,6 +72,11 @@
     ((val + (align - 1)) / align) * align
 }
 
+/// Round `val` to partition size(4K)
+pub fn align_to_partition_size(val: u64) -> u64 {
+    align_to_power_of_2(val, PARTITION_SIZE_SHIFT)
+}
+
 impl PartitionInfo {
     fn aligned_size(&self) -> u64 {
         align_to_power_of_2(self.files.iter().map(|file| file.size).sum(), PARTITION_SIZE_SHIFT)
@@ -336,9 +341,9 @@
     Ok((composite_image, files))
 }
 
-/// Given the AIDL config containing a list of partitions, with a [`ParcelFileDescriptor`] for each
-/// partition, return the list of file descriptors which must be passed to the mk_cdisk child
-/// process and the composite disk image partition configuration for it.
+/// Given the AIDL config containing a list of partitions, with [`ParcelFileDescriptor`]s for each
+/// partition, return the list of file descriptors which must be passed to the composite disk image
+/// partition configuration for it.
 fn convert_partitions(partitions: &[Partition]) -> Result<(Vec<PartitionInfo>, Vec<File>), Error> {
     // File descriptors to pass to child process.
     let mut files = vec![];
@@ -346,24 +351,27 @@
     let partitions = partitions
         .iter()
         .map(|partition| {
-            // TODO(b/187187765): This shouldn't be an Option.
-            let file = partition
-                .image
-                .as_ref()
-                .context("Invalid partition image file descriptor")?
-                .as_ref()
-                .try_clone()
-                .context("Failed to clone partition image file descriptor")?;
-            let size = get_partition_size(&file)?;
-            let fd = file.as_raw_fd();
-            files.push(file);
+            let image_files = partition
+                .images
+                .iter()
+                .map(|image| {
+                    let file = image
+                        .as_ref()
+                        .try_clone()
+                        .context("Failed to clone partition image file descriptor")?;
+
+                    let size = get_partition_size(&file)?;
+                    let fd = file.as_raw_fd();
+                    let partition_info_file =
+                        PartitionFileInfo { path: format!("/proc/self/fd/{}", fd).into(), size };
+                    files.push(file);
+                    Ok(partition_info_file)
+                })
+                .collect::<Result<Vec<_>, Error>>()?;
 
             Ok(PartitionInfo {
                 label: partition.label.to_owned(),
-                files: vec![PartitionFileInfo {
-                    path: format!("/proc/self/fd/{}", fd).into(),
-                    size,
-                }],
+                files: image_files,
                 partition_type: ImagePartitionType::LinuxFilesystem,
                 writable: partition.writable,
             })
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index 669c631..ae8388a 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -17,11 +17,11 @@
 use crate::aidl::VirtualMachineCallbacks;
 use crate::Cid;
 use anyhow::{bail, Error};
-use command_fds::{CommandFdExt, FdMapping};
+use command_fds::CommandFdExt;
 use log::{debug, error, info};
 use shared_child::SharedChild;
 use std::fs::{remove_dir_all, File};
-use std::os::unix::io::AsRawFd;
+use std::os::unix::io::{AsRawFd, RawFd};
 use std::path::PathBuf;
 use std::process::Command;
 use std::sync::atomic::{AtomicBool, Ordering};
@@ -102,13 +102,13 @@
     pub fn start(
         config: &CrosvmConfig,
         log_fd: Option<File>,
-        composite_disk_mappings: &[FdMapping],
+        composite_disk_fds: &[RawFd],
         temporary_directory: PathBuf,
         requester_uid: u32,
         requester_sid: String,
         requester_debug_pid: i32,
     ) -> Result<Arc<VmInstance>, Error> {
-        let child = run_vm(config, log_fd, composite_disk_mappings)?;
+        let child = run_vm(config, log_fd, composite_disk_fds)?;
         let instance = Arc::new(VmInstance::new(
             child,
             config.cid,
@@ -161,7 +161,7 @@
 fn run_vm(
     config: &CrosvmConfig,
     log_fd: Option<File>,
-    composite_disk_mappings: &[FdMapping],
+    composite_disk_fds: &[RawFd],
 ) -> Result<SharedChild, Error> {
     validate_config(config)?;
 
@@ -181,14 +181,14 @@
     }
 
     // Keep track of what file descriptors should be mapped to the crosvm process.
-    let mut fd_mappings = composite_disk_mappings.to_vec();
+    let mut preserved_fds = composite_disk_fds.to_vec();
 
     if let Some(bootloader) = &config.bootloader {
-        command.arg("--bios").arg(add_fd_mapping(&mut fd_mappings, bootloader));
+        command.arg("--bios").arg(add_preserved_fd(&mut preserved_fds, bootloader));
     }
 
     if let Some(initrd) = &config.initrd {
-        command.arg("--initrd").arg(add_fd_mapping(&mut fd_mappings, initrd));
+        command.arg("--initrd").arg(add_preserved_fd(&mut preserved_fds, initrd));
     }
 
     if let Some(params) = &config.params {
@@ -198,15 +198,15 @@
     for disk in &config.disks {
         command
             .arg(if disk.writable { "--rwdisk" } else { "--disk" })
-            .arg(add_fd_mapping(&mut fd_mappings, &disk.image));
+            .arg(add_preserved_fd(&mut preserved_fds, &disk.image));
     }
 
     if let Some(kernel) = &config.kernel {
-        command.arg(add_fd_mapping(&mut fd_mappings, kernel));
+        command.arg(add_preserved_fd(&mut preserved_fds, kernel));
     }
 
-    debug!("Setting mappings {:?}", fd_mappings);
-    command.fd_mappings(fd_mappings)?;
+    debug!("Preserving FDs {:?}", preserved_fds);
+    command.preserved_fds(preserved_fds);
 
     info!("Running {:?}", command);
     let result = SharedChild::spawn(&mut command)?;
@@ -224,10 +224,10 @@
     Ok(())
 }
 
-/// Adds a mapping for `file` to `fd_mappings`, and returns a string of the form "/proc/self/fd/N"
-/// where N is the file descriptor for the child process.
-fn add_fd_mapping(fd_mappings: &mut Vec<FdMapping>, file: &File) -> String {
+/// Adds the file descriptor for `file` to `preserved_fds`, and returns a string of the form
+/// "/proc/self/fd/N" where N is the file descriptor.
+fn add_preserved_fd(preserved_fds: &mut Vec<RawFd>, file: &File) -> String {
     let fd = file.as_raw_fd();
-    fd_mappings.push(FdMapping { parent_fd: fd, child_fd: fd });
+    preserved_fds.push(fd);
     format!("/proc/self/fd/{}", fd)
 }
diff --git a/virtualizationservice/src/main.rs b/virtualizationservice/src/main.rs
index 43b5fe4..658203b 100644
--- a/virtualizationservice/src/main.rs
+++ b/virtualizationservice/src/main.rs
@@ -18,6 +18,7 @@
 mod composite;
 mod crosvm;
 mod gpt;
+mod payload;
 
 use crate::aidl::{VirtualizationService, BINDER_SERVICE_IDENTIFIER};
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualizationService::BnVirtualizationService;
diff --git a/virtualizationservice/src/payload.rs b/virtualizationservice/src/payload.rs
new file mode 100644
index 0000000..aaf43e4
--- /dev/null
+++ b/virtualizationservice/src/payload.rs
@@ -0,0 +1,139 @@
+// Copyright 2021, 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.
+
+//! Payload disk image
+
+use crate::composite::align_to_partition_size;
+
+use anyhow::{Error, Result};
+use microdroid_metadata::{ApexPayload, ApkPayload, Metadata};
+use microdroid_payload_config::ApexConfig;
+use std::fs;
+use std::fs::OpenOptions;
+use std::io::{Seek, SeekFrom, Write};
+use std::path::{Path, PathBuf};
+use std::process::Command;
+use vmconfig::{DiskImage, Partition};
+
+// TODO(b/191601801): look up /apex/apex-info-list.xml
+fn get_path(package_name: &str) -> Result<PathBuf> {
+    let output = Command::new("pm").arg("path").arg(package_name).output()?;
+    let output = String::from_utf8(output.stdout)?;
+    Ok(PathBuf::from(output.strip_prefix("package:").unwrap().trim()))
+}
+
+/// When passing a host APEX file as a block device in a payload disk image,
+/// the size of the original file needs to be stored in the last 4 bytes so that
+/// other programs (e.g. apexd) can read it as a zip.
+fn make_size_filler(size: u64, filler_path: &Path) -> Result<bool> {
+    let partition_size = align_to_partition_size(size + 4);
+    let mut file = OpenOptions::new().create_new(true).write(true).open(filler_path)?;
+    file.set_len(partition_size - size)?;
+    file.seek(SeekFrom::End(-4))?;
+    file.write_all(&(size as i32).to_be_bytes())?;
+    Ok(true)
+}
+
+/// When passing a host APK file as a block device in a payload disk image and it is
+/// mounted via dm-verity, we need to make the device zero-padded up to 4K boundary.
+/// Otherwise, intergrity checks via hashtree will fail.
+fn make_zero_filler(size: u64, filler_path: &Path) -> Result<bool> {
+    let partition_size = align_to_partition_size(size);
+    if partition_size <= size {
+        return Ok(false);
+    }
+    let file = OpenOptions::new().create_new(true).write(true).open(filler_path)?;
+    file.set_len(partition_size - size)?;
+    Ok(true)
+}
+
+/// When passing a host idsig file as a block device, we don't need any filler because it is read
+/// in length-prefixed way.
+fn make_no_filler(_size: u64, _filler_path: &Path) -> Result<bool> {
+    Ok(false)
+}
+
+/// Creates a DiskImage with partitions:
+///   metadata: metadata
+///   microdroid-apex-0: [apex 0, size filler]
+///   microdroid-apex-1: [apex 1, size filler]
+///   ..
+///   microdroid-apk: [apk, zero filler]
+///   microdroid-apk-idsig: idsig
+pub fn make_disk_image(
+    apk_file: PathBuf,
+    idsig_file: PathBuf,
+    config_path: &str,
+    apexes: &[ApexConfig],
+    temporary_directory: &Path,
+) -> Result<DiskImage> {
+    let metadata_path = temporary_directory.join("metadata");
+    let metadata = Metadata {
+        version: 1u32,
+        apexes: apexes
+            .iter()
+            .map(|apex| ApexPayload { name: String::from(&apex.name), ..Default::default() })
+            .collect(),
+        apk: Some(ApkPayload {
+            name: String::from("apk"),
+            payload_partition_name: String::from("microdroid-apk"),
+            idsig_partition_name: String::from("microdroid-apk-idsig"),
+            ..Default::default()
+        })
+        .into(),
+        payload_config_path: format!("/mnt/apk/{}", config_path),
+        ..Default::default()
+    };
+    let mut metadata_file =
+        OpenOptions::new().create_new(true).read(true).write(true).open(&metadata_path)?;
+    microdroid_metadata::write_metadata(&metadata, &mut metadata_file)?;
+
+    // put metadata at the first partition
+    let mut partitions = vec![Partition {
+        label: String::from("metadata"),
+        paths: vec![metadata_path],
+        writable: false,
+    }];
+
+    let mut filler_count = 0;
+    let mut make_partition = |label: String,
+                              path: PathBuf,
+                              make_filler: &dyn Fn(u64, &Path) -> Result<bool, Error>|
+     -> Result<Partition> {
+        let filler_path = temporary_directory.join(format!("filler-{}", filler_count));
+        let size = fs::metadata(&path)?.len();
+
+        if make_filler(size, &filler_path)? {
+            filler_count += 1;
+            Ok(Partition { label, paths: vec![path, filler_path], writable: false })
+        } else {
+            Ok(Partition { label, paths: vec![path], writable: false })
+        }
+    };
+    for (i, apex) in apexes.iter().enumerate() {
+        partitions.push(make_partition(
+            format!("microdroid-apex-{}", i),
+            get_path(&apex.name)?,
+            &make_size_filler,
+        )?);
+    }
+    partitions.push(make_partition(String::from("microdroid-apk"), apk_file, &make_zero_filler)?);
+    partitions.push(make_partition(
+        String::from("microdroid-apk-idsig"),
+        idsig_file,
+        &make_no_filler,
+    )?);
+
+    Ok(DiskImage { image: None, partitions, writable: false })
+}
diff --git a/vm/Android.bp b/vm/Android.bp
index c07beb2..734f2d3 100644
--- a/vm/Android.bp
+++ b/vm/Android.bp
@@ -10,13 +10,13 @@
     rustlibs: [
         "android.system.virtualizationservice-rust",
         "libanyhow",
-        "libcompositediskconfig",
         "libenv_logger",
         "liblibc",
         "liblog_rust",
         "libserde_json",
         "libserde",
         "libstructopt",
+        "libvmconfig",
     ],
     apex_available: [
         "com.android.virt",
diff --git a/vm/src/main.rs b/vm/src/main.rs
index 2c93ec4..d7bae30 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -14,14 +14,13 @@
 
 //! Android VM control tool.
 
-mod config;
 mod run;
 mod sync;
 
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualizationService::IVirtualizationService;
 use android_system_virtualizationservice::binder::{wait_for_interface, ProcessState, Strong, ParcelFileDescriptor};
 use anyhow::{Context, Error};
-use run::command_run;
+use run::{command_run, command_run_app};
 use std::convert::TryInto;
 use std::fs::OpenOptions;
 use std::path::{PathBuf, Path};
@@ -34,6 +33,27 @@
 #[derive(StructOpt)]
 #[structopt(no_version, global_settings = &[AppSettings::DisableVersion])]
 enum Opt {
+    /// Run a virtual machine with a config in APK
+    RunApp {
+        /// Path to VM Payload APK
+        #[structopt(parse(from_os_str))]
+        apk: PathBuf,
+
+        /// Path to idsig of the APK
+        #[structopt(parse(from_os_str))]
+        idsig: PathBuf,
+
+        /// Path to VM config JSON within APK (e.g. assets/vm_config.json)
+        config_path: String,
+
+        /// Detach VM from the terminal and run in the background
+        #[structopt(short, long)]
+        daemonize: bool,
+
+        /// Path to file for VM log output.
+        #[structopt(short, long)]
+        log: Option<PathBuf>,
+    },
     /// Run a virtual machine
     Run {
         /// Path to VM config JSON
@@ -43,6 +63,10 @@
         /// Detach VM from the terminal and run in the background
         #[structopt(short, long)]
         daemonize: bool,
+
+        /// Path to file for VM log output.
+        #[structopt(short, long)]
+        log: Option<PathBuf>,
     },
     /// Stop a virtual machine running in the background
     Stop {
@@ -73,7 +97,12 @@
         .context("Failed to find VirtualizationService")?;
 
     match opt {
-        Opt::Run { config, daemonize } => command_run(service, &config, daemonize),
+        Opt::RunApp { apk, idsig, config_path, daemonize, log } => {
+            command_run_app(service, &apk, &idsig, &config_path, daemonize, log.as_deref())
+        }
+        Opt::Run { config, daemonize, log } => {
+            command_run(service, &config, daemonize, log.as_deref())
+        }
         Opt::Stop { cid } => command_stop(service, cid),
         Opt::List => command_list(service),
         Opt::CreatePartition { path, size } => command_create_partition(service, &path, size),
diff --git a/vm/src/run.rs b/vm/src/run.rs
index ab4222f..1ae94ea 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -14,13 +14,16 @@
 
 //! Command to run a VM.
 
-use crate::config::VmConfig;
 use crate::sync::AtomicFlag;
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualizationService::IVirtualizationService;
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualMachine::IVirtualMachine;
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualMachineCallback::{
     BnVirtualMachineCallback, IVirtualMachineCallback,
 };
+use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+    VirtualMachineAppConfig::VirtualMachineAppConfig,
+    VirtualMachineConfig::VirtualMachineConfig,
+};
 use android_system_virtualizationservice::binder::{
     BinderFeatures, DeathRecipient, IBinder, ParcelFileDescriptor, Strong,
 };
@@ -30,19 +33,64 @@
 use std::io;
 use std::os::unix::io::{AsRawFd, FromRawFd};
 use std::path::Path;
+use vmconfig::VmConfig;
+
+/// Run a VM from the given APK, idsig, and config.
+pub fn command_run_app(
+    service: Strong<dyn IVirtualizationService>,
+    apk: &Path,
+    idsig: &Path,
+    config_path: &str,
+    daemonize: bool,
+    log_path: Option<&Path>,
+) -> Result<(), Error> {
+    let apk_file = File::open(apk).context("Failed to open APK file")?;
+    let idsig_file = File::open(idsig).context("Failed to open idsig file")?;
+    let config = VirtualMachineConfig::AppConfig(VirtualMachineAppConfig {
+        apk: ParcelFileDescriptor::new(apk_file).into(),
+        idsig: ParcelFileDescriptor::new(idsig_file).into(),
+        configPath: config_path.to_owned(),
+    });
+    run(service, &config, &format!("{:?}!{:?}", apk, config_path), daemonize, log_path)
+}
 
 /// Run a VM from the given configuration file.
 pub fn command_run(
     service: Strong<dyn IVirtualizationService>,
     config_path: &Path,
     daemonize: bool,
+    log_path: Option<&Path>,
 ) -> Result<(), Error> {
     let config_file = File::open(config_path).context("Failed to open config file")?;
     let config =
         VmConfig::load(&config_file).context("Failed to parse config file")?.to_parcelable()?;
-    let stdout =
-        if daemonize { None } else { Some(ParcelFileDescriptor::new(duplicate_stdout()?)) };
-    let vm = service.startVm(&config, stdout.as_ref()).context("Failed to start VM")?;
+    run(
+        service,
+        &VirtualMachineConfig::RawConfig(config),
+        &format!("{:?}", config_path),
+        daemonize,
+        log_path,
+    )
+}
+
+fn run(
+    service: Strong<dyn IVirtualizationService>,
+    config: &VirtualMachineConfig,
+    config_path: &str,
+    daemonize: bool,
+    log_path: Option<&Path>,
+) -> Result<(), Error> {
+    let stdout = if let Some(log_path) = log_path {
+        Some(ParcelFileDescriptor::new(
+            File::create(log_path)
+                .with_context(|| format!("Failed to open log file {:?}", log_path))?,
+        ))
+    } else if daemonize {
+        None
+    } else {
+        Some(ParcelFileDescriptor::new(duplicate_stdout()?))
+    };
+    let vm = service.startVm(config, stdout.as_ref()).context("Failed to start VM")?;
 
     let cid = vm.getCid().context("Failed to get CID")?;
     println!("Started VM from {:?} with CID {}.", config_path, cid);
diff --git a/compositediskconfig/Android.bp b/vmconfig/Android.bp
similarity index 66%
rename from compositediskconfig/Android.bp
rename to vmconfig/Android.bp
index 4608323..321eba0 100644
--- a/compositediskconfig/Android.bp
+++ b/vmconfig/Android.bp
@@ -3,12 +3,13 @@
 }
 
 rust_library {
-    name: "libcompositediskconfig",
-    host_supported: true,
-    crate_name: "compositediskconfig",
+    name: "libvmconfig",
+    crate_name: "vmconfig",
     srcs: ["src/lib.rs"],
     edition: "2018",
     rustlibs: [
+        "android.system.virtualizationservice-rust",
+        "libanyhow",
         "libserde_json",
         "libserde",
     ],
diff --git a/vm/src/config.rs b/vmconfig/src/lib.rs
similarity index 79%
rename from vm/src/config.rs
rename to vmconfig/src/lib.rs
index 8ea0d8f..7b0d2a5 100644
--- a/vm/src/config.rs
+++ b/vmconfig/src/lib.rs
@@ -12,16 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! Struct for VM configuration.
+//! Struct for VM configuration with JSON (de)serialization and AIDL parcelables
 
 use android_system_virtualizationservice::{
     aidl::android::system::virtualizationservice::DiskImage::DiskImage as AidlDiskImage,
     aidl::android::system::virtualizationservice::Partition::Partition as AidlPartition,
-    aidl::android::system::virtualizationservice::VirtualMachineConfig::VirtualMachineConfig,
+    aidl::android::system::virtualizationservice::VirtualMachineRawConfig::VirtualMachineRawConfig,
     binder::ParcelFileDescriptor,
 };
-use anyhow::{bail, Context, Error};
-use compositediskconfig::Partition;
+
+use anyhow::{bail, Context, Error, Result};
 use serde::{Deserialize, Serialize};
 use std::fs::{File, OpenOptions};
 use std::io::BufReader;
@@ -76,8 +76,8 @@
 
     /// Convert the `VmConfig` to a [`VirtualMachineConfig`] which can be passed to the Virt
     /// Manager.
-    pub fn to_parcelable(&self) -> Result<VirtualMachineConfig, Error> {
-        Ok(VirtualMachineConfig {
+    pub fn to_parcelable(&self) -> Result<VirtualMachineRawConfig, Error> {
+        Ok(VirtualMachineRawConfig {
             kernel: maybe_open_parcel_file(&self.kernel, false)?,
             initrd: maybe_open_parcel_file(&self.initrd, false)?,
             params: self.params.clone(),
@@ -105,7 +105,7 @@
 impl DiskImage {
     fn to_parcelable(&self) -> Result<AidlDiskImage, Error> {
         let partitions =
-            self.partitions.iter().map(partition_to_parcelable).collect::<Result<_, Error>>()?;
+            self.partitions.iter().map(Partition::to_parcelable).collect::<Result<_>>()?;
         Ok(AidlDiskImage {
             image: maybe_open_parcel_file(&self.image, self.writable)?,
             writable: self.writable,
@@ -114,16 +114,35 @@
     }
 }
 
-fn partition_to_parcelable(partition: &Partition) -> Result<AidlPartition, Error> {
-    Ok(AidlPartition {
-        image: Some(open_parcel_file(&partition.path, partition.writable)?),
-        writable: partition.writable,
-        label: partition.label.to_owned(),
-    })
+/// A partition to be assembled into a composite image.
+#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
+pub struct Partition {
+    /// A label for the partition.
+    pub label: String,
+    /// The filename of the partition image.
+    #[serde(default)]
+    pub paths: Vec<PathBuf>,
+    /// Whether the partition should be writable.
+    #[serde(default)]
+    pub writable: bool,
+}
+
+impl Partition {
+    fn to_parcelable(&self) -> Result<AidlPartition> {
+        if self.paths.is_empty() {
+            bail!("Partition {} contains no paths", &self.label);
+        }
+        let images = self
+            .paths
+            .iter()
+            .map(|path| open_parcel_file(&path, self.writable))
+            .collect::<Result<Vec<_>, _>>()?;
+        Ok(AidlPartition { images, writable: self.writable, label: self.label.to_owned() })
+    }
 }
 
 /// Try to open the given file and wrap it in a [`ParcelFileDescriptor`].
-fn open_parcel_file(filename: &Path, writable: bool) -> Result<ParcelFileDescriptor, Error> {
+fn open_parcel_file(filename: &Path, writable: bool) -> Result<ParcelFileDescriptor> {
     Ok(ParcelFileDescriptor::new(
         OpenOptions::new()
             .read(true)
@@ -137,6 +156,6 @@
 fn maybe_open_parcel_file(
     filename: &Option<PathBuf>,
     writable: bool,
-) -> Result<Option<ParcelFileDescriptor>, Error> {
+) -> Result<Option<ParcelFileDescriptor>> {
     filename.as_deref().map(|filename| open_parcel_file(filename, writable)).transpose()
 }
diff --git a/zipfuse/TEST_MAPPING b/zipfuse/TEST_MAPPING
index 5b313c1..81a8aeb 100644
--- a/zipfuse/TEST_MAPPING
+++ b/zipfuse/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "postsubmit" : [
+  "presubmit" : [
     {
       "name" : "ZipFuseTest"
     }