Merge "[service-vm] Persist the service VM instance image in VS" into main
diff --git a/Android.bp b/Android.bp
index b655551..1fae793 100644
--- a/Android.bp
+++ b/Android.bp
@@ -27,3 +27,24 @@
     ],
     // large-scale-change unable to identify any license_text files
 }
+
+soong_config_module_type {
+    name: "avf_flag_aware_rust_defaults",
+    module_type: "rust_defaults",
+    config_namespace: "ANDROID",
+    bool_variables: [
+        "release_avf_enable_multi_tenant_microdroid_vm",
+    ],
+    properties: [
+        "cfgs",
+    ],
+}
+
+avf_flag_aware_rust_defaults {
+    name: "avf_build_flags_rust",
+    soong_config_variables: {
+        release_avf_enable_multi_tenant_microdroid_vm: {
+            cfgs: ["payload_not_root"],
+        },
+    },
+}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 323b827..3bc7aba 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -12,6 +12,9 @@
       "name": "MicrodroidTestApp"
     },
     {
+      "name": "CustomPvmfwHostTestCases"
+    },
+    {
       "name": "art_standalone_dexpreopt_tests"
     },
     {
diff --git a/apkdmverity/Android.bp b/apkdmverity/Android.bp
index 8429263..cc54d2e 100644
--- a/apkdmverity/Android.bp
+++ b/apkdmverity/Android.bp
@@ -5,6 +5,7 @@
 rust_defaults {
     name: "apkdmverity.defaults",
     crate_name: "apkdmverity",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/main.rs"],
     edition: "2021",
     prefer_rlib: true,
diff --git a/authfs/Android.bp b/authfs/Android.bp
index 154a1d6..a4151c2 100644
--- a/authfs/Android.bp
+++ b/authfs/Android.bp
@@ -33,7 +33,10 @@
             enabled: false,
         },
     },
-    defaults: ["crosvm_defaults"],
+    defaults: [
+        "crosvm_defaults",
+        "avf_build_flags_rust",
+    ],
 }
 
 rust_binary {
diff --git a/authfs/fd_server/Android.bp b/authfs/fd_server/Android.bp
index db1fd44..b02c104 100644
--- a/authfs/fd_server/Android.bp
+++ b/authfs/fd_server/Android.bp
@@ -4,6 +4,7 @@
 
 rust_binary {
     name: "fd_server",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/main.rs"],
     rustlibs: [
         "authfs_aidl_interface-rust",
@@ -24,6 +25,7 @@
 
 rust_test {
     name: "fd_server.test",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/main.rs"],
     rustlibs: [
         "authfs_aidl_interface-rust",
diff --git a/authfs/src/fsverity/metadata/Android.bp b/authfs/src/fsverity/metadata/Android.bp
index 3df7519..c874c2b 100644
--- a/authfs/src/fsverity/metadata/Android.bp
+++ b/authfs/src/fsverity/metadata/Android.bp
@@ -5,6 +5,7 @@
 rust_bindgen {
     name: "libauthfs_fsverity_metadata_bindgen",
     wrapper_src: "metadata.hpp",
+    defaults: ["avf_build_flags_rust"],
     crate_name: "authfs_fsverity_metadata_bindgen",
     source_stem: "metadata_bindings",
     apex_available: ["com.android.virt"],
@@ -13,6 +14,7 @@
 rust_library {
     name: "libauthfs_fsverity_metadata",
     crate_name: "authfs_fsverity_metadata",
+    defaults: ["avf_build_flags_rust"],
     srcs: [
         "metadata.rs",
     ],
diff --git a/authfs/tests/common/Android.bp b/authfs/tests/common/Android.bp
index 01ebcfd..bba329e 100644
--- a/authfs/tests/common/Android.bp
+++ b/authfs/tests/common/Android.bp
@@ -18,6 +18,7 @@
 rust_test {
     name: "open_then_run",
     crate_name: "open_then_run",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/open_then_run.rs"],
     edition: "2021",
     rustlibs: [
@@ -35,6 +36,7 @@
 rust_test {
     name: "open_then_run.test",
     crate_name: "open_then_run",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/open_then_run.rs"],
     edition: "2021",
     rustlibs: [
diff --git a/compos/Android.bp b/compos/Android.bp
index 2f6be98..19123dd 100644
--- a/compos/Android.bp
+++ b/compos/Android.bp
@@ -5,6 +5,7 @@
 rust_defaults {
     name: "compsvc_defaults",
     edition: "2021",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/compsvc_main.rs"],
     rustlibs: [
         "authfs_aidl_interface-rust",
diff --git a/compos/common/Android.bp b/compos/common/Android.bp
index 05bc093..01ab7c9 100644
--- a/compos/common/Android.bp
+++ b/compos/common/Android.bp
@@ -5,6 +5,7 @@
 rust_library {
     name: "libcompos_common",
     crate_name: "compos_common",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["lib.rs"],
     edition: "2021",
     rustlibs: [
diff --git a/compos/composd/native/Android.bp b/compos/composd/native/Android.bp
index ccd8651..f35517f 100644
--- a/compos/composd/native/Android.bp
+++ b/compos/composd/native/Android.bp
@@ -5,6 +5,7 @@
 rust_library {
     name: "libcomposd_native_rust",
     crate_name: "composd_native",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["lib.rs"],
     rustlibs: [
         "libanyhow",
diff --git a/compos/composd_cmd/Android.bp b/compos/composd_cmd/Android.bp
index 77caad8..4d3ed5f 100644
--- a/compos/composd_cmd/Android.bp
+++ b/compos/composd_cmd/Android.bp
@@ -5,6 +5,7 @@
 rust_defaults {
     name: "composd_cmd_defaults",
     srcs: ["composd_cmd.rs"],
+    defaults: ["avf_build_flags_rust"],
     edition: "2021",
     rustlibs: [
         "android.system.composd-rust",
diff --git a/compos/verify/Android.bp b/compos/verify/Android.bp
index 9e30b0d..f4d8695 100644
--- a/compos/verify/Android.bp
+++ b/compos/verify/Android.bp
@@ -5,6 +5,7 @@
 rust_binary {
     name: "compos_verify",
     srcs: ["verify.rs"],
+    defaults: ["avf_build_flags_rust"],
     edition: "2021",
     rustlibs: [
         "compos_aidl_interface-rust",
@@ -26,6 +27,7 @@
 rust_test {
     name: "compos_verify.test",
     srcs: ["verify.rs"],
+    defaults: ["avf_build_flags_rust"],
     edition: "2021",
     rustlibs: [
         "compos_aidl_interface-rust",
diff --git a/compos/verify/native/Android.bp b/compos/verify/native/Android.bp
index 969c9f4..438d93a 100644
--- a/compos/verify/native/Android.bp
+++ b/compos/verify/native/Android.bp
@@ -5,6 +5,7 @@
 rust_library {
     name: "libcompos_verify_native_rust",
     crate_name: "compos_verify_native",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["lib.rs"],
     rustlibs: [
         "libanyhow",
diff --git a/encryptedstore/Android.bp b/encryptedstore/Android.bp
index 8ba5016..aa46c35 100644
--- a/encryptedstore/Android.bp
+++ b/encryptedstore/Android.bp
@@ -4,6 +4,7 @@
 
 rust_defaults {
     name: "encryptedstore.defaults",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/main.rs"],
     edition: "2021",
     prefer_rlib: true,
diff --git a/encryptedstore/src/main.rs b/encryptedstore/src/main.rs
index 2a698ea..db3d4f6 100644
--- a/encryptedstore/src/main.rs
+++ b/encryptedstore/src/main.rs
@@ -94,13 +94,21 @@
     }
     mount(&crypt_device, mountpoint)
         .with_context(|| format!("Unable to mount {:?}", crypt_device))?;
-    if needs_formatting {
-        std::fs::set_permissions(mountpoint, PermissionsExt::from_mode(0o770))
-            .context("Failed to chmod root directory")?;
+    if cfg!(payload_not_root) && needs_formatting {
+        set_root_dir_permissions(mountpoint)?;
     }
     Ok(())
 }
 
+fn set_root_dir_permissions(mountpoint: &Path) -> Result<()> {
+    // mke2fs hardwires the root dir permissions as 0o755 which doesn't match what we want.
+    // We want to allow full access by both root and the payload group, and no access by anything
+    // else. And we want the sticky bit set, so different payload UIDs can create sub-directories
+    // that other payloads can't delete.
+    let permissions = PermissionsExt::from_mode(0o770 | libc::S_ISVTX);
+    std::fs::set_permissions(mountpoint, permissions).context("Failed to chmod root directory")
+}
+
 fn enable_crypt(data_device: &Path, key: &str, name: &str) -> Result<PathBuf> {
     let dev_size = util::blkgetsize64(data_device)?;
     let key = hex::decode(key).context("Unable to decode hex key")?;
diff --git a/libs/apexutil/Android.bp b/libs/apexutil/Android.bp
index 4a4a673..92d4e80 100644
--- a/libs/apexutil/Android.bp
+++ b/libs/apexutil/Android.bp
@@ -5,6 +5,7 @@
 rust_defaults {
     name: "libapexutil_rust.defaults",
     crate_name: "apexutil",
+    defaults: ["avf_build_flags_rust"],
     host_supported: true,
     srcs: ["src/lib.rs"],
     edition: "2021",
diff --git a/libs/apkverify/Android.bp b/libs/apkverify/Android.bp
index 83dbff6..d3aa7ee 100644
--- a/libs/apkverify/Android.bp
+++ b/libs/apkverify/Android.bp
@@ -5,6 +5,7 @@
 rust_defaults {
     name: "libapkverify.defaults",
     crate_name: "apkverify",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/lib.rs"],
     prefer_rlib: true,
     edition: "2021",
@@ -40,6 +41,7 @@
 rust_test {
     name: "libapkverify.integration_test",
     crate_name: "apkverify_test",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["tests/*_test.rs"],
     prefer_rlib: true,
     edition: "2021",
diff --git a/libs/avflog/Android.bp b/libs/avflog/Android.bp
index 1ddfc7a..695a6c6 100644
--- a/libs/avflog/Android.bp
+++ b/libs/avflog/Android.bp
@@ -5,6 +5,7 @@
 rust_defaults {
     name: "libavflog.defaults",
     crate_name: "avflog",
+    defaults: ["avf_build_flags_rust"],
     host_supported: true,
     srcs: ["src/lib.rs"],
     edition: "2021",
diff --git a/libs/capabilities/Android.bp b/libs/capabilities/Android.bp
index db3f4d4..55112e1 100644
--- a/libs/capabilities/Android.bp
+++ b/libs/capabilities/Android.bp
@@ -4,6 +4,7 @@
 
 rust_bindgen {
     name: "libcap_bindgen",
+    defaults: ["avf_build_flags_rust"],
     edition: "2021",
     wrapper_src: "bindgen/libcap.h",
     crate_name: "cap_bindgen",
@@ -20,6 +21,7 @@
 rust_test {
     name: "libcap_bindgen_test",
     srcs: [":libcap_bindgen"],
+    defaults: ["avf_build_flags_rust"],
     crate_name: "cap_bindgen_test",
     test_suites: ["general-tests"],
     auto_gen_config: true,
@@ -30,6 +32,7 @@
 rust_defaults {
     name: "libcap_rust.defaults",
     crate_name: "cap",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/caps.rs"],
     rustlibs: [
         "libanyhow",
diff --git a/libs/devicemapper/Android.bp b/libs/devicemapper/Android.bp
index 29f2f5f..8f9c25c 100644
--- a/libs/devicemapper/Android.bp
+++ b/libs/devicemapper/Android.bp
@@ -5,6 +5,7 @@
 rust_defaults {
     name: "libdm_rust.defaults",
     crate_name: "dm",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/lib.rs"],
     edition: "2021",
     prefer_rlib: true,
diff --git a/libs/fdtpci/Android.bp b/libs/fdtpci/Android.bp
index f368b08..e12c24f 100644
--- a/libs/fdtpci/Android.bp
+++ b/libs/fdtpci/Android.bp
@@ -8,6 +8,7 @@
     no_stdlibs: true,
     host_supported: false,
     crate_name: "fdtpci",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/lib.rs"],
     rustlibs: [
         "liblibfdt",
diff --git a/libs/hyp/Android.bp b/libs/hyp/Android.bp
index 8baf9dd..404269a 100644
--- a/libs/hyp/Android.bp
+++ b/libs/hyp/Android.bp
@@ -5,6 +5,7 @@
 rust_library_rlib {
     name: "libhyp",
     crate_name: "hyp",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/lib.rs"],
     prefer_rlib: true,
     rustlibs: [
diff --git a/libs/libfdt/Android.bp b/libs/libfdt/Android.bp
index 0540f26..402040c 100644
--- a/libs/libfdt/Android.bp
+++ b/libs/libfdt/Android.bp
@@ -5,6 +5,7 @@
 rust_bindgen {
     name: "liblibfdt_bindgen",
     crate_name: "libfdt_bindgen",
+    defaults: ["avf_build_flags_rust"],
     wrapper_src: "bindgen/fdt.h",
     source_stem: "bindings",
     bindgen_flags: [
@@ -24,6 +25,7 @@
 rust_library_rlib {
     name: "liblibfdt",
     crate_name: "libfdt",
+    defaults: ["avf_build_flags_rust"],
     srcs: [
         "src/lib.rs",
         ":liblibfdt_bindgen",
@@ -47,6 +49,7 @@
 rust_test {
     name: "liblibfdt.integration_test",
     crate_name: "libfdt_test",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["tests/*.rs"],
     test_suites: ["general-tests"],
     data: [
diff --git a/libs/microdroid_uids/Android.bp b/libs/microdroid_uids/Android.bp
index 497948d..ce62217 100644
--- a/libs/microdroid_uids/Android.bp
+++ b/libs/microdroid_uids/Android.bp
@@ -7,8 +7,7 @@
     crate_name: "microdroid_uids",
     srcs: ["src/lib.rs"],
     edition: "2021",
-    // TODO(b/296393106): Figure out how/when to enable this
-    // cfgs: ["payload_not_root"],
+    defaults: ["avf_build_flags_rust"],
     apex_available: [
         "com.android.virt",
     ],
diff --git a/libs/microdroid_uids/src/lib.rs b/libs/microdroid_uids/src/lib.rs
index 1f09c65..04dc190 100644
--- a/libs/microdroid_uids/src/lib.rs
+++ b/libs/microdroid_uids/src/lib.rs
@@ -17,6 +17,17 @@
 /// Always the user ID of Root.
 pub const ROOT_UID: u32 = 0;
 
+// Android reserves UID/GIDs 6000-6499 for use by the system partition -
+// see AID_SYSTEM_RESERVED_START.
+// Within Microdroid we own the system partition, so they are free for our
+// use. The Microdroid system image includes /system/ext/passwd and
+// /system/ext/group files that allocate names to the IDs that we are
+// using, so that tools like `ps` handle them correctly - see build targets
+// microdroid_etc_passwd and microdroid_etc_group.
+// (Our UIDs are entirely separate from Android's, but we use the same
+// Bionic, and it uses the Android definitions - so using a reserved range
+// helps avoid confusion.)
+
 /// Group ID shared by all payload users.
 pub const MICRODROID_PAYLOAD_GID: u32 = if cfg!(payload_not_root) { 6000 } else { 0 };
 
diff --git a/libs/nested_virt/Android.bp b/libs/nested_virt/Android.bp
index 72393ea..74dd38f 100644
--- a/libs/nested_virt/Android.bp
+++ b/libs/nested_virt/Android.bp
@@ -5,6 +5,7 @@
 rust_library {
     name: "libnested_virt",
     crate_name: "nested_virt",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/lib.rs"],
     edition: "2021",
     rustlibs: [
diff --git a/libs/service_vm_comm/Android.bp b/libs/service_vm_comm/Android.bp
index 18397c5..cdb8fc3 100644
--- a/libs/service_vm_comm/Android.bp
+++ b/libs/service_vm_comm/Android.bp
@@ -5,6 +5,7 @@
 rust_defaults {
     name: "libservice_vm_comm_defaults",
     crate_name: "service_vm_comm",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/lib.rs"],
     prefer_rlib: true,
     apex_available: [
diff --git a/libs/service_vm_comm/src/lib.rs b/libs/service_vm_comm/src/lib.rs
index c3d3ed5..ef5e8bb 100644
--- a/libs/service_vm_comm/src/lib.rs
+++ b/libs/service_vm_comm/src/lib.rs
@@ -20,5 +20,7 @@
 extern crate alloc;
 
 mod message;
+mod vsock;
 
 pub use message::{Request, Response};
+pub use vsock::host_port;
diff --git a/libs/service_vm_comm/src/vsock.rs b/libs/service_vm_comm/src/vsock.rs
new file mode 100644
index 0000000..fd6f088
--- /dev/null
+++ b/libs/service_vm_comm/src/vsock.rs
@@ -0,0 +1,27 @@
+// Copyright 2023, 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.
+
+//! Vsock setup shared between the host and the service VM.
+
+/// Returns the host port number for the given VM protection state.
+pub fn host_port(is_protected_vm: bool) -> u32 {
+    const PROTECTED_VM_PORT: u32 = 5679;
+    const NON_PROTECTED_VM_PORT: u32 = 5680;
+
+    if is_protected_vm {
+        PROTECTED_VM_PORT
+    } else {
+        NON_PROTECTED_VM_PORT
+    }
+}
diff --git a/libs/statslog_virtualization/Android.bp b/libs/statslog_virtualization/Android.bp
index a702ea1..2860e6c 100644
--- a/libs/statslog_virtualization/Android.bp
+++ b/libs/statslog_virtualization/Android.bp
@@ -29,6 +29,7 @@
 
 rust_defaults {
     name: "libstatslog_virtualization_rust_defaults",
+    defaults: ["avf_build_flags_rust"],
     edition: "2021",
     rustlibs: [
         "libstatspull_bindgen",
diff --git a/libs/vbmeta/Android.bp b/libs/vbmeta/Android.bp
index a487097..ae83703 100644
--- a/libs/vbmeta/Android.bp
+++ b/libs/vbmeta/Android.bp
@@ -5,6 +5,7 @@
 rust_defaults {
     name: "libvbmeta_rust.defaults",
     crate_name: "vbmeta",
+    defaults: ["avf_build_flags_rust"],
     host_supported: true,
     srcs: ["src/lib.rs"],
     edition: "2021",
diff --git a/libs/vmconfig/Android.bp b/libs/vmconfig/Android.bp
index fe541d3..728033c 100644
--- a/libs/vmconfig/Android.bp
+++ b/libs/vmconfig/Android.bp
@@ -5,6 +5,7 @@
 rust_library {
     name: "libvmconfig",
     crate_name: "vmconfig",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/lib.rs"],
     edition: "2021",
     rustlibs: [
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 1e594b7..00831dd 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -44,7 +44,19 @@
     },
 ]
 
-android_system_image {
+soong_config_module_type {
+    name: "flag_aware_microdroid_system_image",
+    module_type: "android_system_image",
+    config_namespace: "ANDROID",
+    bool_variables: [
+        "release_avf_enable_multi_tenant_microdroid_vm",
+    ],
+    properties: [
+        "deps",
+    ],
+}
+
+flag_aware_microdroid_system_image {
     name: "microdroid",
     use_avb: true,
     avb_private_key: ":microdroid_sign_key",
@@ -54,8 +66,6 @@
     deps: [
         "init_second_stage.microdroid",
         "microdroid_build_prop",
-        "microdroid_etc_passwd",
-        "microdroid_etc_group",
         "microdroid_init_debug_policy",
         "microdroid_init_rc",
         "microdroid_ueventd_rc",
@@ -141,6 +151,16 @@
     fake_timestamp: "1611569676",
     // python -c "import uuid; print(uuid.uuid5(uuid.NAMESPACE_URL, 'www.android.com/avf/microdroid/system'))"
     uuid: "5fe079c6-f01a-52be-87d3-d415231a72ad",
+
+    // Below are dependencies that are conditionally enabled depending on value of build flags.
+    soong_config_variables: {
+        release_avf_enable_multi_tenant_microdroid_vm: {
+            deps: [
+                "microdroid_etc_passwd",
+                "microdroid_etc_group",
+            ],
+        },
+    },
 }
 
 prebuilt_etc {
diff --git a/microdroid/init_debug_policy/Android.bp b/microdroid/init_debug_policy/Android.bp
index afc2e73..ed017e5 100644
--- a/microdroid/init_debug_policy/Android.bp
+++ b/microdroid/init_debug_policy/Android.bp
@@ -4,6 +4,7 @@
 
 rust_binary {
     name: "microdroid_init_debug_policy",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/init_debug_policy.rs"],
     stem: "init_debug_policy",
     rustlibs: [
diff --git a/microdroid/initrd/Android.bp b/microdroid/initrd/Android.bp
index 699a28a..de28d8a 100644
--- a/microdroid/initrd/Android.bp
+++ b/microdroid/initrd/Android.bp
@@ -4,6 +4,7 @@
 
 rust_binary_host {
     name: "initrd_bootconfig",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/main.rs"],
     rustlibs: [
         "libanyhow",
@@ -14,6 +15,7 @@
 
 rust_test_host {
     name: "initrd_bootconfig.test",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/main.rs"],
     rustlibs: [
         "libanyhow",
diff --git a/microdroid/linker.config.json b/microdroid/linker.config.json
index fd90821..7b59ca2 100644
--- a/microdroid/linker.config.json
+++ b/microdroid/linker.config.json
@@ -1,18 +1,5 @@
 {
   "requireLibs": [
-    "libdexfile.so",
-    "libdexfiled.so",
-    "libnativebridge.so",
-    "libnativehelper.so",
-    "libnativeloader.so",
-    "libsigchain.so",
-    "libandroidicu.so",
-    "libicu.so",
-    "libicui18n.so",
-    "libicuuc.so",
-    "libnetd_resolv.so",
-    "libstatspull.so",
-    "libstatssocket.so",
     "libadb_pairing_auth.so",
     "libadb_pairing_connection.so",
     "libadb_pairing_server.so"
diff --git a/microdroid/payload/config/Android.bp b/microdroid/payload/config/Android.bp
index 7e60cd4..4c72b97 100644
--- a/microdroid/payload/config/Android.bp
+++ b/microdroid/payload/config/Android.bp
@@ -6,6 +6,7 @@
     name: "libmicrodroid_payload_config",
     host_supported: true,
     crate_name: "microdroid_payload_config",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/lib.rs"],
     prefer_rlib: true,
     edition: "2021",
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index fe0cf6a..c91519c 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -5,6 +5,7 @@
 rust_defaults {
     name: "microdroid_manager_defaults",
     crate_name: "microdroid_manager",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/main.rs"],
     edition: "2021",
     prefer_rlib: true,
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 5a5b34a..1c79452 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -855,7 +855,7 @@
     info!("executing main task {:?}...", task);
     let mut command = match task.type_ {
         TaskType::Executable => {
-            // TODO(b/296393106): Run system payloads as non-root.
+            // TODO(b/297501338): Figure out how to handle non-root for system payloads.
             Command::new(&task.command)
         }
         TaskType::MicrodroidLauncher => {
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index 1aa5935..523334f 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -48,6 +48,7 @@
     host_supported: true,
     // For now, only bootargs.rs is written to be conditionally compiled with std.
     srcs: ["src/bootargs.rs"],
+    defaults: ["avf_build_flags_rust"],
     test_suites: ["general-tests"],
     test_options: {
         unit_test: true,
diff --git a/pvmfw/avb/Android.bp b/pvmfw/avb/Android.bp
index 4efee6a..73d188b 100644
--- a/pvmfw/avb/Android.bp
+++ b/pvmfw/avb/Android.bp
@@ -5,10 +5,12 @@
 rust_library_rlib {
     name: "libpvmfw_avb_nostd",
     crate_name: "pvmfw_avb",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/lib.rs"],
     prefer_rlib: true,
     rustlibs: [
         "libavb_bindgen_nostd",
+        "libavb_rs_nostd",
         "libtinyvec_nostd",
     ],
     whole_static_libs: [
@@ -23,6 +25,7 @@
 rust_test {
     name: "libpvmfw_avb.integration_test",
     crate_name: "pvmfw_avb_test",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["tests/*.rs"],
     test_suites: ["general-tests"],
     data: [
@@ -45,6 +48,7 @@
     rustlibs: [
         "libanyhow",
         "libavb_bindgen",
+        "libavb_rs_nostd",
         "libhex",
         "libpvmfw_avb_nostd",
         "libopenssl",
diff --git a/pvmfw/avb/src/descriptor/collection.rs b/pvmfw/avb/src/descriptor/collection.rs
index 14c47b1..f47bfbd 100644
--- a/pvmfw/avb/src/descriptor/collection.rs
+++ b/pvmfw/avb/src/descriptor/collection.rs
@@ -17,9 +17,9 @@
 use super::common::get_valid_descriptor;
 use super::hash::HashDescriptor;
 use super::property::PropertyDescriptor;
-use crate::error::{AvbIOError, AvbSlotVerifyError};
 use crate::partition::PartitionName;
 use crate::utils::{self, is_not_null, to_usize, usize_checked_add};
+use crate::PvmfwVerifyError;
 use avb_bindgen::{
     avb_descriptor_foreach, avb_descriptor_validate_and_byteswap, AvbDescriptor, AvbDescriptorTag,
     AvbVBMetaData,
@@ -45,9 +45,9 @@
     /// Behavior is undefined if any of the following conditions are violated:
     /// * `vbmeta.vbmeta_data` must be non-null and points to a valid VBMeta.
     /// * `vbmeta.vbmeta_data` must be valid for reading `vbmeta.vbmeta_size` bytes.
-    pub(crate) unsafe fn from_vbmeta(vbmeta: AvbVBMetaData) -> Result<Self, AvbSlotVerifyError> {
-        is_not_null(vbmeta.vbmeta_data).map_err(|_| AvbSlotVerifyError::Io)?;
-        let mut res: Result<Self, AvbIOError> = Ok(Self::default());
+    pub(crate) unsafe fn from_vbmeta(vbmeta: AvbVBMetaData) -> Result<Self, PvmfwVerifyError> {
+        is_not_null(vbmeta.vbmeta_data).map_err(|_| avb::SlotVerifyError::Io)?;
+        let mut res: Result<Self, avb::IoError> = Ok(Self::default());
         // SAFETY: It is safe as the raw pointer `vbmeta.vbmeta_data` is a non-null pointer and
         // points to a valid VBMeta structure.
         let output = unsafe {
@@ -59,9 +59,9 @@
             )
         };
         if output == res.is_ok() {
-            res.map_err(AvbSlotVerifyError::InvalidDescriptors)
+            res.map_err(PvmfwVerifyError::InvalidDescriptors)
         } else {
-            Err(AvbSlotVerifyError::InvalidMetadata)
+            Err(avb::SlotVerifyError::InvalidMetadata.into())
         }
     }
 
@@ -74,11 +74,11 @@
     pub(crate) fn find_hash_descriptor(
         &self,
         partition_name: PartitionName,
-    ) -> Result<&HashDescriptor, AvbSlotVerifyError> {
+    ) -> Result<&HashDescriptor, avb::SlotVerifyError> {
         self.hash_descriptors
             .iter()
             .find(|d| d.partition_name == partition_name)
-            .ok_or(AvbSlotVerifyError::InvalidMetadata)
+            .ok_or(avb::SlotVerifyError::InvalidMetadata)
     }
 
     pub(crate) fn has_property_descriptor(&self) -> bool {
@@ -98,7 +98,7 @@
 
     fn push_hash_descriptor(&mut self, descriptor: HashDescriptor<'a>) -> utils::Result<()> {
         if self.hash_descriptors.iter().any(|d| d.partition_name == descriptor.partition_name) {
-            return Err(AvbIOError::Io);
+            return Err(avb::IoError::Io);
         }
         self.hash_descriptors.push(descriptor);
         Ok(())
@@ -109,7 +109,7 @@
         descriptor: PropertyDescriptor<'a>,
     ) -> utils::Result<()> {
         if self.prop_descriptor.is_some() {
-            return Err(AvbIOError::Io);
+            return Err(avb::IoError::Io);
         }
         self.prop_descriptor.replace(descriptor);
         Ok(())
@@ -120,7 +120,8 @@
 ///
 /// Behavior is undefined if any of the following conditions are violated:
 /// * The `descriptor` pointer must be non-null and points to a valid `AvbDescriptor` struct.
-/// * The `user_data` pointer must be non-null, points to a valid `Result<Descriptors, AvbIOError>`
+/// * The `user_data` pointer must be non-null, points to a valid
+///   `Result<Descriptors, avb::IoError>`
 ///  struct and is initialized.
 unsafe extern "C" fn check_and_save_descriptor(
     descriptor: *const AvbDescriptor,
@@ -128,7 +129,8 @@
 ) -> bool {
     // SAFETY: It is safe because the caller ensures that `user_data` points to a valid struct and
     // is initialized.
-    let Some(res) = (unsafe { (user_data as *mut Result<Descriptors, AvbIOError>).as_mut() }) else {
+    let Some(res) = (unsafe { (user_data as *mut Result<Descriptors, avb::IoError>).as_mut() })
+    else {
         return false;
     };
     let Ok(descriptors) = res else {
@@ -195,7 +197,7 @@
                     unsafe { PropertyDescriptor::from_descriptor_ptr(descriptor, data)? };
                 Ok(Self::Property(descriptor))
             }
-            _ => Err(AvbIOError::NoSuchValue),
+            _ => Err(avb::IoError::NoSuchValue),
         }
     }
 }
diff --git a/pvmfw/avb/src/descriptor/common.rs b/pvmfw/avb/src/descriptor/common.rs
index 9d91553..31ee0a5 100644
--- a/pvmfw/avb/src/descriptor/common.rs
+++ b/pvmfw/avb/src/descriptor/common.rs
@@ -14,7 +14,6 @@
 
 //! Structs and functions used by all the descriptors.
 
-use crate::error::AvbIOError;
 use crate::utils::{self, is_not_null};
 use core::mem::MaybeUninit;
 
@@ -32,7 +31,7 @@
     let descriptor = unsafe {
         let mut desc = MaybeUninit::uninit();
         if !descriptor_validate_and_byteswap(descriptor_ptr, desc.as_mut_ptr()) {
-            return Err(AvbIOError::Io);
+            return Err(avb::IoError::Io);
         }
         desc.assume_init()
     };
diff --git a/pvmfw/avb/src/descriptor/hash.rs b/pvmfw/avb/src/descriptor/hash.rs
index 2a00479..089268f 100644
--- a/pvmfw/avb/src/descriptor/hash.rs
+++ b/pvmfw/avb/src/descriptor/hash.rs
@@ -15,7 +15,6 @@
 //! Structs and functions relating to the hash descriptor.
 
 use super::common::get_valid_descriptor;
-use crate::error::AvbIOError;
 use crate::partition::PartitionName;
 use crate::utils::{self, to_usize, usize_checked_add};
 use avb_bindgen::{
@@ -54,13 +53,13 @@
         let h = unsafe { HashDescriptorHeader::from_descriptor_ptr(descriptor)? };
         let partition_name = data
             .get(h.partition_name_range()?)
-            .ok_or(AvbIOError::RangeOutsidePartition)?
+            .ok_or(avb::IoError::RangeOutsidePartition)?
             .try_into()?;
         let digest = data
             .get(h.digest_range()?)
-            .ok_or(AvbIOError::RangeOutsidePartition)?
+            .ok_or(avb::IoError::RangeOutsidePartition)?
             .try_into()
-            .map_err(|_| AvbIOError::InvalidValueSize)?;
+            .map_err(|_| avb::IoError::InvalidValueSize)?;
         Ok(Self { partition_name, digest })
     }
 }
diff --git a/pvmfw/avb/src/descriptor/property.rs b/pvmfw/avb/src/descriptor/property.rs
index 589f2b2..336623a 100644
--- a/pvmfw/avb/src/descriptor/property.rs
+++ b/pvmfw/avb/src/descriptor/property.rs
@@ -15,7 +15,6 @@
 //! Structs and functions relating to the property descriptor.
 
 use super::common::get_valid_descriptor;
-use crate::error::AvbIOError;
 use crate::utils::{self, to_usize, usize_checked_add};
 use avb_bindgen::{
     avb_property_descriptor_validate_and_byteswap, AvbDescriptor, AvbPropertyDescriptor,
@@ -48,8 +47,8 @@
         const NUL_BYTE: u8 = b'\0';
 
         match data.get(end) {
-            Some(&NUL_BYTE) => data.get(start..end).ok_or(AvbIOError::RangeOutsidePartition),
-            _ => Err(AvbIOError::NoSuchValue),
+            Some(&NUL_BYTE) => data.get(start..end).ok_or(avb::IoError::RangeOutsidePartition),
+            _ => Err(avb::IoError::NoSuchValue),
         }
     }
 }
diff --git a/pvmfw/avb/src/error.rs b/pvmfw/avb/src/error.rs
index 5e3822f..af38c54 100644
--- a/pvmfw/avb/src/error.rs
+++ b/pvmfw/avb/src/error.rs
@@ -15,50 +15,32 @@
 //! This module contains the error thrown by the payload verification API
 //! and other errors used in the library.
 
-use avb_bindgen::{AvbIOResult, AvbSlotVerifyResult};
-
 use core::fmt;
 
-/// This error is the error part of `AvbSlotVerifyResult`.
+/// Wrapper around `avb::SlotVerifyError` to add custom pvmfw errors.
 /// It is the error thrown by the payload verification API `verify_payload()`.
 #[derive(Clone, Debug, PartialEq, Eq)]
-pub enum AvbSlotVerifyError {
-    /// AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT
-    InvalidArgument,
-    /// AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA
-    InvalidMetadata,
-    /// AVB_SLOT_VERIFY_RESULT_ERROR_IO
-    Io,
-    /// AVB_SLOT_VERIFY_RESULT_ERROR_OOM
-    Oom,
-    /// AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED
-    PublicKeyRejected,
-    /// AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX
-    RollbackIndex,
-    /// AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION
-    UnsupportedVersion,
-    /// AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION
-    Verification,
+pub enum PvmfwVerifyError {
+    /// Passthrough avb::SlotVerifyError.
+    AvbError(avb::SlotVerifyError),
     /// VBMeta has invalid descriptors.
-    InvalidDescriptors(AvbIOError),
-    /// Unknown vbmeta property
+    InvalidDescriptors(avb::IoError),
+    /// Unknown vbmeta property.
     UnknownVbmetaProperty,
 }
 
-impl fmt::Display for AvbSlotVerifyError {
+/// It's always possible to convert from an `avb::SlotVerifyError` since we are
+/// a superset.
+impl From<avb::SlotVerifyError> for PvmfwVerifyError {
+    fn from(error: avb::SlotVerifyError) -> Self {
+        Self::AvbError(error)
+    }
+}
+
+impl fmt::Display for PvmfwVerifyError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
-            Self::InvalidArgument => write!(f, "Invalid parameters."),
-            Self::InvalidMetadata => write!(f, "Invalid metadata."),
-            Self::Io => write!(f, "I/O error while trying to load data or get a rollback index."),
-            Self::Oom => write!(f, "Unable to allocate memory."),
-            Self::PublicKeyRejected => write!(f, "Public key rejected or data not signed."),
-            Self::RollbackIndex => write!(f, "Rollback index is less than its stored value."),
-            Self::UnsupportedVersion => write!(
-                f,
-                "Some of the metadata requires a newer version of libavb than what is in use."
-            ),
-            Self::Verification => write!(f, "Data does not verify."),
+            Self::AvbError(e) => write!(f, "{}", e),
             Self::InvalidDescriptors(e) => {
                 write!(f, "VBMeta has invalid descriptors. Error: {:?}", e)
             }
@@ -66,72 +48,3 @@
         }
     }
 }
-
-pub(crate) fn slot_verify_result_to_verify_payload_result(
-    result: AvbSlotVerifyResult,
-) -> Result<(), AvbSlotVerifyError> {
-    match result {
-        AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_OK => Ok(()),
-        AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT => {
-            Err(AvbSlotVerifyError::InvalidArgument)
-        }
-        AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA => {
-            Err(AvbSlotVerifyError::InvalidMetadata)
-        }
-        AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_IO => Err(AvbSlotVerifyError::Io),
-        AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_OOM => Err(AvbSlotVerifyError::Oom),
-        AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED => {
-            Err(AvbSlotVerifyError::PublicKeyRejected)
-        }
-        AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX => {
-            Err(AvbSlotVerifyError::RollbackIndex)
-        }
-        AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION => {
-            Err(AvbSlotVerifyError::UnsupportedVersion)
-        }
-        AvbSlotVerifyResult::AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION => {
-            Err(AvbSlotVerifyError::Verification)
-        }
-    }
-}
-
-/// AVB IO Error.
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum AvbIOError {
-    /// AVB_IO_RESULT_ERROR_OOM,
-    #[allow(dead_code)]
-    Oom,
-    /// AVB_IO_RESULT_ERROR_IO,
-    Io,
-    /// AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION,
-    NoSuchPartition,
-    /// AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION,
-    RangeOutsidePartition,
-    /// AVB_IO_RESULT_ERROR_NO_SUCH_VALUE,
-    NoSuchValue,
-    /// AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE,
-    InvalidValueSize,
-    /// AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE,
-    #[allow(dead_code)]
-    InsufficientSpace,
-}
-
-impl From<AvbIOError> for AvbIOResult {
-    fn from(error: AvbIOError) -> Self {
-        match error {
-            AvbIOError::Oom => AvbIOResult::AVB_IO_RESULT_ERROR_OOM,
-            AvbIOError::Io => AvbIOResult::AVB_IO_RESULT_ERROR_IO,
-            AvbIOError::NoSuchPartition => AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION,
-            AvbIOError::RangeOutsidePartition => {
-                AvbIOResult::AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION
-            }
-            AvbIOError::NoSuchValue => AvbIOResult::AVB_IO_RESULT_ERROR_NO_SUCH_VALUE,
-            AvbIOError::InvalidValueSize => AvbIOResult::AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE,
-            AvbIOError::InsufficientSpace => AvbIOResult::AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE,
-        }
-    }
-}
-
-pub(crate) fn to_avb_io_result(result: Result<(), AvbIOError>) -> AvbIOResult {
-    result.map_or_else(|e| e.into(), |_| AvbIOResult::AVB_IO_RESULT_OK)
-}
diff --git a/pvmfw/avb/src/lib.rs b/pvmfw/avb/src/lib.rs
index fdab990..9c3fe11 100644
--- a/pvmfw/avb/src/lib.rs
+++ b/pvmfw/avb/src/lib.rs
@@ -26,5 +26,5 @@
 mod verify;
 
 pub use descriptor::Digest;
-pub use error::{AvbIOError, AvbSlotVerifyError};
+pub use error::PvmfwVerifyError;
 pub use verify::{verify_payload, Capability, DebugLevel, VerifiedBootData};
diff --git a/pvmfw/avb/src/ops.rs b/pvmfw/avb/src/ops.rs
index 8f7295c..539291b 100644
--- a/pvmfw/avb/src/ops.rs
+++ b/pvmfw/avb/src/ops.rs
@@ -14,11 +14,9 @@
 
 //! Structs and functions relating to `AvbOps`.
 
-use crate::error::{
-    slot_verify_result_to_verify_payload_result, to_avb_io_result, AvbIOError, AvbSlotVerifyError,
-};
 use crate::partition::PartitionName;
 use crate::utils::{self, as_ref, is_not_null, to_nonnull, write};
+use avb::internal::{result_to_io_enum, slot_verify_enum_to_result};
 use avb_bindgen::{
     avb_slot_verify, avb_slot_verify_data_free, AvbHashtreeErrorMode, AvbIOResult, AvbOps,
     AvbPartitionData, AvbSlotVerifyData, AvbSlotVerifyFlags, AvbVBMetaData,
@@ -55,11 +53,11 @@
         Self { kernel, initrd, trusted_public_key }
     }
 
-    fn get_partition(&self, partition_name: *const c_char) -> Result<&[u8], AvbIOError> {
+    fn get_partition(&self, partition_name: *const c_char) -> Result<&[u8], avb::IoError> {
         match partition_name.try_into()? {
             PartitionName::Kernel => Ok(self.kernel),
             PartitionName::InitrdNormal | PartitionName::InitrdDebug => {
-                self.initrd.ok_or(AvbIOError::NoSuchPartition)
+                self.initrd.ok_or(avb::IoError::NoSuchPartition)
             }
         }
     }
@@ -96,7 +94,7 @@
     pub(crate) fn verify_partition(
         &mut self,
         partition_name: &CStr,
-    ) -> Result<AvbSlotVerifyDataWrap, AvbSlotVerifyError> {
+    ) -> Result<AvbSlotVerifyDataWrap, avb::SlotVerifyError> {
         let requested_partitions = [partition_name.as_ptr(), ptr::null()];
         let ab_suffix = CStr::from_bytes_with_nul(NULL_BYTE).unwrap();
         let mut out_data = MaybeUninit::uninit();
@@ -113,7 +111,7 @@
                 out_data.as_mut_ptr(),
             )
         };
-        slot_verify_result_to_verify_payload_result(result)?;
+        slot_verify_enum_to_result(result)?;
         // SAFETY: This is safe because `out_data` has been properly initialized after
         // calling `avb_slot_verify` and it returns OK.
         let out_data = unsafe { out_data.assume_init() };
@@ -125,7 +123,7 @@
     _ops: *mut AvbOps,
     out_is_unlocked: *mut bool,
 ) -> AvbIOResult {
-    to_avb_io_result(write(out_is_unlocked, false))
+    result_to_io_enum(write(out_is_unlocked, false))
 }
 
 extern "C" fn get_preloaded_partition(
@@ -135,7 +133,7 @@
     out_pointer: *mut *mut u8,
     out_num_bytes_preloaded: *mut usize,
 ) -> AvbIOResult {
-    to_avb_io_result(try_get_preloaded_partition(
+    result_to_io_enum(try_get_preloaded_partition(
         ops,
         partition,
         num_bytes,
@@ -165,7 +163,7 @@
     buffer: *mut c_void,
     out_num_read: *mut usize,
 ) -> AvbIOResult {
-    to_avb_io_result(try_read_from_partition(
+    result_to_io_enum(try_read_from_partition(
         ops,
         partition,
         offset,
@@ -194,9 +192,9 @@
 }
 
 fn copy_data_to_dst(src: &[u8], offset: i64, dst: &mut [u8]) -> utils::Result<()> {
-    let start = to_copy_start(offset, src.len()).ok_or(AvbIOError::InvalidValueSize)?;
-    let end = start.checked_add(dst.len()).ok_or(AvbIOError::InvalidValueSize)?;
-    dst.copy_from_slice(src.get(start..end).ok_or(AvbIOError::RangeOutsidePartition)?);
+    let start = to_copy_start(offset, src.len()).ok_or(avb::IoError::InvalidValueSize)?;
+    let end = start.checked_add(dst.len()).ok_or(avb::IoError::InvalidValueSize)?;
+    dst.copy_from_slice(src.get(start..end).ok_or(avb::IoError::RangeOutsidePartition)?);
     Ok(())
 }
 
@@ -211,7 +209,7 @@
     partition: *const c_char,
     out_size_num_bytes: *mut u64,
 ) -> AvbIOResult {
-    to_avb_io_result(try_get_size_of_partition(ops, partition, out_size_num_bytes))
+    result_to_io_enum(try_get_size_of_partition(ops, partition, out_size_num_bytes))
 }
 
 fn try_get_size_of_partition(
@@ -222,7 +220,7 @@
     let ops = as_ref(ops)?;
     let partition = ops.as_ref().get_partition(partition)?;
     let partition_size =
-        u64::try_from(partition.len()).map_err(|_| AvbIOError::InvalidValueSize)?;
+        u64::try_from(partition.len()).map_err(|_| avb::IoError::InvalidValueSize)?;
     write(out_size_num_bytes, partition_size)
 }
 
@@ -235,7 +233,7 @@
     // `avb_slot_verify()`.
     // We set `out_rollback_index` to 0 to ensure that the default rollback index (0)
     // is never smaller than it, thus the rollback index check will pass.
-    to_avb_io_result(write(out_rollback_index, 0))
+    result_to_io_enum(write(out_rollback_index, 0))
 }
 
 extern "C" fn get_unique_guid_for_partition(
@@ -258,7 +256,7 @@
     public_key_metadata_length: usize,
     out_is_trusted: *mut bool,
 ) -> AvbIOResult {
-    to_avb_io_result(try_validate_vbmeta_public_key(
+    result_to_io_enum(try_validate_vbmeta_public_key(
         ops,
         public_key_data,
         public_key_length,
@@ -290,10 +288,10 @@
 pub(crate) struct AvbSlotVerifyDataWrap(*mut AvbSlotVerifyData);
 
 impl TryFrom<*mut AvbSlotVerifyData> for AvbSlotVerifyDataWrap {
-    type Error = AvbSlotVerifyError;
+    type Error = avb::SlotVerifyError;
 
     fn try_from(data: *mut AvbSlotVerifyData) -> Result<Self, Self::Error> {
-        is_not_null(data).map_err(|_| AvbSlotVerifyError::Io)?;
+        is_not_null(data).map_err(|_| avb::SlotVerifyError::Io)?;
         Ok(Self(data))
     }
 }
@@ -317,18 +315,18 @@
 }
 
 impl AvbSlotVerifyDataWrap {
-    pub(crate) fn vbmeta_images(&self) -> Result<&[AvbVBMetaData], AvbSlotVerifyError> {
+    pub(crate) fn vbmeta_images(&self) -> Result<&[AvbVBMetaData], avb::SlotVerifyError> {
         let data = self.as_ref();
-        is_not_null(data.vbmeta_images).map_err(|_| AvbSlotVerifyError::Io)?;
+        is_not_null(data.vbmeta_images).map_err(|_| avb::SlotVerifyError::Io)?;
         let vbmeta_images =
         // SAFETY: It is safe as the raw pointer `data.vbmeta_images` is a nonnull pointer.
             unsafe { slice::from_raw_parts(data.vbmeta_images, data.num_vbmeta_images) };
         Ok(vbmeta_images)
     }
 
-    pub(crate) fn loaded_partitions(&self) -> Result<&[AvbPartitionData], AvbSlotVerifyError> {
+    pub(crate) fn loaded_partitions(&self) -> Result<&[AvbPartitionData], avb::SlotVerifyError> {
         let data = self.as_ref();
-        is_not_null(data.loaded_partitions).map_err(|_| AvbSlotVerifyError::Io)?;
+        is_not_null(data.loaded_partitions).map_err(|_| avb::SlotVerifyError::Io)?;
         let loaded_partitions =
         // SAFETY: It is safe as the raw pointer `data.loaded_partitions` is a nonnull pointer and
         // is guaranteed by libavb to point to a valid `AvbPartitionData` array as part of the
diff --git a/pvmfw/avb/src/partition.rs b/pvmfw/avb/src/partition.rs
index bc63003..ca450c9 100644
--- a/pvmfw/avb/src/partition.rs
+++ b/pvmfw/avb/src/partition.rs
@@ -14,7 +14,6 @@
 
 //! Struct and functions relating to well-known partition names.
 
-use crate::error::AvbIOError;
 use crate::utils::is_not_null;
 use core::ffi::{c_char, CStr};
 
@@ -53,7 +52,7 @@
 }
 
 impl TryFrom<*const c_char> for PartitionName {
-    type Error = AvbIOError;
+    type Error = avb::IoError;
 
     fn try_from(partition_name: *const c_char) -> Result<Self, Self::Error> {
         is_not_null(partition_name)?;
@@ -64,27 +63,27 @@
 }
 
 impl TryFrom<&CStr> for PartitionName {
-    type Error = AvbIOError;
+    type Error = avb::IoError;
 
     fn try_from(partition_name: &CStr) -> Result<Self, Self::Error> {
         match partition_name.to_bytes_with_nul() {
             Self::KERNEL_PARTITION_NAME => Ok(Self::Kernel),
             Self::INITRD_NORMAL_PARTITION_NAME => Ok(Self::InitrdNormal),
             Self::INITRD_DEBUG_PARTITION_NAME => Ok(Self::InitrdDebug),
-            _ => Err(AvbIOError::NoSuchPartition),
+            _ => Err(avb::IoError::NoSuchPartition),
         }
     }
 }
 
 impl TryFrom<&[u8]> for PartitionName {
-    type Error = AvbIOError;
+    type Error = avb::IoError;
 
     fn try_from(non_null_terminated_name: &[u8]) -> Result<Self, Self::Error> {
         match non_null_terminated_name {
             x if x == Self::Kernel.as_non_null_terminated_bytes() => Ok(Self::Kernel),
             x if x == Self::InitrdNormal.as_non_null_terminated_bytes() => Ok(Self::InitrdNormal),
             x if x == Self::InitrdDebug.as_non_null_terminated_bytes() => Ok(Self::InitrdDebug),
-            _ => Err(AvbIOError::NoSuchPartition),
+            _ => Err(avb::IoError::NoSuchPartition),
         }
     }
 }
diff --git a/pvmfw/avb/src/utils.rs b/pvmfw/avb/src/utils.rs
index a24d61f..f4f15e1 100644
--- a/pvmfw/avb/src/utils.rs
+++ b/pvmfw/avb/src/utils.rs
@@ -14,11 +14,10 @@
 
 //! Common utility functions.
 
-use crate::error::AvbIOError;
 use core::ptr::NonNull;
 use core::result;
 
-pub(crate) type Result<T> = result::Result<T, AvbIOError>;
+pub(crate) type Result<T> = result::Result<T, avb::IoError>;
 
 pub(crate) fn write<T>(ptr: *mut T, value: T) -> Result<()> {
     let ptr = to_nonnull(ptr)?;
@@ -36,21 +35,21 @@
 }
 
 pub(crate) fn to_nonnull<T>(ptr: *mut T) -> Result<NonNull<T>> {
-    NonNull::new(ptr).ok_or(AvbIOError::NoSuchValue)
+    NonNull::new(ptr).ok_or(avb::IoError::NoSuchValue)
 }
 
 pub(crate) fn is_not_null<T>(ptr: *const T) -> Result<()> {
     if ptr.is_null() {
-        Err(AvbIOError::NoSuchValue)
+        Err(avb::IoError::NoSuchValue)
     } else {
         Ok(())
     }
 }
 
 pub(crate) fn to_usize<T: TryInto<usize>>(num: T) -> Result<usize> {
-    num.try_into().map_err(|_| AvbIOError::InvalidValueSize)
+    num.try_into().map_err(|_| avb::IoError::InvalidValueSize)
 }
 
 pub(crate) fn usize_checked_add(x: usize, y: usize) -> Result<usize> {
-    x.checked_add(y).ok_or(AvbIOError::InvalidValueSize)
+    x.checked_add(y).ok_or(avb::IoError::InvalidValueSize)
 }
diff --git a/pvmfw/avb/src/verify.rs b/pvmfw/avb/src/verify.rs
index c5ed8cc..ac945e2 100644
--- a/pvmfw/avb/src/verify.rs
+++ b/pvmfw/avb/src/verify.rs
@@ -15,9 +15,9 @@
 //! This module handles the pvmfw payload verification.
 
 use crate::descriptor::{Descriptors, Digest};
-use crate::error::AvbSlotVerifyError;
 use crate::ops::{Ops, Payload};
 use crate::partition::PartitionName;
+use crate::PvmfwVerifyError;
 use alloc::vec;
 use alloc::vec::Vec;
 use avb_bindgen::{AvbPartitionData, AvbVBMetaData};
@@ -59,16 +59,16 @@
     const REMOTE_ATTEST: &[u8] = b"remote_attest";
     const SEPARATOR: u8 = b'|';
 
-    fn get_capabilities(property_value: &[u8]) -> Result<Vec<Self>, AvbSlotVerifyError> {
+    fn get_capabilities(property_value: &[u8]) -> Result<Vec<Self>, PvmfwVerifyError> {
         let mut res = Vec::new();
 
         for v in property_value.split(|b| *b == Self::SEPARATOR) {
             let cap = match v {
                 Self::REMOTE_ATTEST => Self::RemoteAttest,
-                _ => return Err(AvbSlotVerifyError::UnknownVbmetaProperty),
+                _ => return Err(PvmfwVerifyError::UnknownVbmetaProperty),
             };
             if res.contains(&cap) {
-                return Err(AvbSlotVerifyError::InvalidMetadata);
+                return Err(avb::SlotVerifyError::InvalidMetadata.into());
             }
             res.push(cap);
         }
@@ -78,30 +78,30 @@
 
 fn verify_only_one_vbmeta_exists(
     vbmeta_images: &[AvbVBMetaData],
-) -> Result<(), AvbSlotVerifyError> {
+) -> Result<(), avb::SlotVerifyError> {
     if vbmeta_images.len() == 1 {
         Ok(())
     } else {
-        Err(AvbSlotVerifyError::InvalidMetadata)
+        Err(avb::SlotVerifyError::InvalidMetadata)
     }
 }
 
 fn verify_vbmeta_is_from_kernel_partition(
     vbmeta_image: &AvbVBMetaData,
-) -> Result<(), AvbSlotVerifyError> {
+) -> Result<(), avb::SlotVerifyError> {
     match (vbmeta_image.partition_name as *const c_char).try_into() {
         Ok(PartitionName::Kernel) => Ok(()),
-        _ => Err(AvbSlotVerifyError::InvalidMetadata),
+        _ => Err(avb::SlotVerifyError::InvalidMetadata),
     }
 }
 
 fn verify_vbmeta_has_only_one_hash_descriptor(
     descriptors: &Descriptors,
-) -> Result<(), AvbSlotVerifyError> {
+) -> Result<(), avb::SlotVerifyError> {
     if descriptors.num_hash_descriptor() == 1 {
         Ok(())
     } else {
-        Err(AvbSlotVerifyError::InvalidMetadata)
+        Err(avb::SlotVerifyError::InvalidMetadata)
     }
 }
 
@@ -109,22 +109,22 @@
     loaded_partitions: &[AvbPartitionData],
     partition_name: PartitionName,
     expected_len: usize,
-) -> Result<(), AvbSlotVerifyError> {
+) -> Result<(), avb::SlotVerifyError> {
     if loaded_partitions.len() != 1 {
         // Only one partition should be loaded in each verify result.
-        return Err(AvbSlotVerifyError::Io);
+        return Err(avb::SlotVerifyError::Io);
     }
     let loaded_partition = loaded_partitions[0];
     if !PartitionName::try_from(loaded_partition.partition_name as *const c_char)
         .map_or(false, |p| p == partition_name)
     {
         // Only the requested partition should be loaded.
-        return Err(AvbSlotVerifyError::Io);
+        return Err(avb::SlotVerifyError::Io);
     }
     if loaded_partition.data_size == expected_len {
         Ok(())
     } else {
-        Err(AvbSlotVerifyError::Verification)
+        Err(avb::SlotVerifyError::Verification)
     }
 }
 
@@ -132,13 +132,13 @@
 /// vm type is service VM.
 fn verify_property_and_get_capabilities(
     descriptors: &Descriptors,
-) -> Result<Vec<Capability>, AvbSlotVerifyError> {
+) -> Result<Vec<Capability>, PvmfwVerifyError> {
     if !descriptors.has_property_descriptor() {
         return Ok(vec![]);
     }
     descriptors
         .find_property_value(Capability::KEY)
-        .ok_or(AvbSlotVerifyError::UnknownVbmetaProperty)
+        .ok_or(PvmfwVerifyError::UnknownVbmetaProperty)
         .and_then(Capability::get_capabilities)
 }
 
@@ -147,7 +147,7 @@
     kernel: &[u8],
     initrd: Option<&[u8]>,
     trusted_public_key: &'a [u8],
-) -> Result<VerifiedBootData<'a>, AvbSlotVerifyError> {
+) -> Result<VerifiedBootData<'a>, PvmfwVerifyError> {
     let mut payload = Payload::new(kernel, initrd, trusted_public_key);
     let mut ops = Ops::from(&mut payload);
     let kernel_verify_result = ops.verify_partition(PartitionName::Kernel.as_cstr())?;
@@ -181,7 +181,7 @@
         } else if let Ok(result) = ops.verify_partition(PartitionName::InitrdDebug.as_cstr()) {
             (DebugLevel::Full, result, PartitionName::InitrdDebug)
         } else {
-            return Err(AvbSlotVerifyError::Verification);
+            return Err(avb::SlotVerifyError::Verification.into());
         };
     let loaded_partitions = initrd_verify_result.loaded_partitions()?;
     verify_loaded_partition_has_expected_length(
diff --git a/pvmfw/avb/tests/api_test.rs b/pvmfw/avb/tests/api_test.rs
index 2f45d77..e7e4dcc 100644
--- a/pvmfw/avb/tests/api_test.rs
+++ b/pvmfw/avb/tests/api_test.rs
@@ -18,9 +18,7 @@
 
 use anyhow::{anyhow, Result};
 use avb_bindgen::{AvbFooter, AvbVBMetaImageHeader};
-use pvmfw_avb::{
-    verify_payload, AvbIOError, AvbSlotVerifyError, Capability, DebugLevel, VerifiedBootData,
-};
+use pvmfw_avb::{verify_payload, Capability, DebugLevel, PvmfwVerifyError, VerifiedBootData};
 use std::{fs, mem::size_of, ptr};
 use utils::*;
 
@@ -86,7 +84,7 @@
         &fs::read(TEST_IMG_WITH_NON_INITRD_HASHDESC_PATH)?,
         /*initrd=*/ None,
         &load_trusted_public_key()?,
-        AvbSlotVerifyError::InvalidDescriptors(AvbIOError::NoSuchPartition),
+        PvmfwVerifyError::InvalidDescriptors(avb::IoError::NoSuchPartition),
     )
 }
 
@@ -96,7 +94,7 @@
         &fs::read(TEST_IMG_WITH_INITRD_AND_NON_INITRD_DESC_PATH)?,
         &load_latest_initrd_normal()?,
         &load_trusted_public_key()?,
-        AvbSlotVerifyError::InvalidDescriptors(AvbIOError::NoSuchPartition),
+        PvmfwVerifyError::InvalidDescriptors(avb::IoError::NoSuchPartition),
     )
 }
 
@@ -129,7 +127,7 @@
         &fs::read(TEST_IMG_WITH_UNKNOWN_VM_TYPE_PROP_PATH)?,
         /*initrd=*/ None,
         &load_trusted_public_key()?,
-        AvbSlotVerifyError::UnknownVbmetaProperty,
+        PvmfwVerifyError::UnknownVbmetaProperty,
     )
 }
 
@@ -139,7 +137,7 @@
         &fs::read(TEST_IMG_WITH_MULTIPLE_PROPS_PATH)?,
         /*initrd=*/ None,
         &load_trusted_public_key()?,
-        AvbSlotVerifyError::InvalidDescriptors(AvbIOError::Io),
+        PvmfwVerifyError::InvalidDescriptors(avb::IoError::Io),
     )
 }
 
@@ -149,7 +147,7 @@
         &fs::read(TEST_IMG_WITH_DUPLICATED_CAP_PATH)?,
         /*initrd=*/ None,
         &load_trusted_public_key()?,
-        AvbSlotVerifyError::InvalidMetadata,
+        avb::SlotVerifyError::InvalidMetadata.into(),
     )
 }
 
@@ -159,7 +157,7 @@
         &fs::read(TEST_IMG_WITH_PROP_DESC_PATH)?,
         /*initrd=*/ None,
         &load_trusted_public_key()?,
-        AvbSlotVerifyError::UnknownVbmetaProperty,
+        PvmfwVerifyError::UnknownVbmetaProperty,
     )
 }
 
@@ -169,7 +167,7 @@
         &load_latest_signed_kernel()?,
         /*initrd=*/ None,
         &load_trusted_public_key()?,
-        AvbSlotVerifyError::InvalidMetadata,
+        avb::SlotVerifyError::InvalidMetadata.into(),
     )
 }
 
@@ -179,7 +177,7 @@
         &load_latest_signed_kernel()?,
         &load_latest_initrd_normal()?,
         /*trusted_public_key=*/ &[0u8; 0],
-        AvbSlotVerifyError::PublicKeyRejected,
+        avb::SlotVerifyError::PublicKeyRejected.into(),
     )
 }
 
@@ -189,7 +187,7 @@
         &load_latest_signed_kernel()?,
         &load_latest_initrd_normal()?,
         /*trusted_public_key=*/ &[0u8; 512],
-        AvbSlotVerifyError::PublicKeyRejected,
+        avb::SlotVerifyError::PublicKeyRejected.into(),
     )
 }
 
@@ -199,7 +197,7 @@
         &load_latest_signed_kernel()?,
         &load_latest_initrd_normal()?,
         &fs::read(PUBLIC_KEY_RSA2048_PATH)?,
-        AvbSlotVerifyError::PublicKeyRejected,
+        avb::SlotVerifyError::PublicKeyRejected.into(),
     )
 }
 
@@ -209,7 +207,7 @@
         &load_latest_signed_kernel()?,
         /*initrd=*/ &fs::read(UNSIGNED_TEST_IMG_PATH)?,
         &load_trusted_public_key()?,
-        AvbSlotVerifyError::Verification,
+        avb::SlotVerifyError::Verification.into(),
     )
 }
 
@@ -219,7 +217,7 @@
         &fs::read(UNSIGNED_TEST_IMG_PATH)?,
         &load_latest_initrd_normal()?,
         &load_trusted_public_key()?,
-        AvbSlotVerifyError::Io,
+        avb::SlotVerifyError::Io.into(),
     )
 }
 
@@ -232,7 +230,7 @@
         &kernel,
         &load_latest_initrd_normal()?,
         &load_trusted_public_key()?,
-        AvbSlotVerifyError::Verification,
+        avb::SlotVerifyError::Verification.into(),
     )
 }
 
@@ -270,7 +268,7 @@
             &kernel,
             &load_latest_initrd_normal()?,
             &load_trusted_public_key()?,
-            AvbSlotVerifyError::Io,
+            avb::SlotVerifyError::Io.into(),
         )?;
     }
     Ok(())
@@ -286,7 +284,7 @@
         &kernel,
         &load_latest_initrd_normal()?,
         &load_trusted_public_key()?,
-        AvbSlotVerifyError::InvalidMetadata,
+        avb::SlotVerifyError::InvalidMetadata.into(),
     )
 }
 
@@ -299,7 +297,7 @@
         &load_latest_signed_kernel()?,
         &initrd,
         &load_trusted_public_key()?,
-        AvbSlotVerifyError::Verification,
+        avb::SlotVerifyError::Verification.into(),
     )
 }
 
@@ -315,7 +313,7 @@
         &kernel,
         &load_latest_initrd_normal()?,
         &load_trusted_public_key()?,
-        AvbSlotVerifyError::InvalidMetadata,
+        avb::SlotVerifyError::InvalidMetadata.into(),
     )
 }
 
@@ -338,13 +336,13 @@
         &kernel,
         &load_latest_initrd_normal()?,
         &empty_public_key,
-        AvbSlotVerifyError::Verification,
+        avb::SlotVerifyError::Verification.into(),
     )?;
     assert_payload_verification_with_initrd_fails(
         &kernel,
         &load_latest_initrd_normal()?,
         &load_trusted_public_key()?,
-        AvbSlotVerifyError::Verification,
+        avb::SlotVerifyError::Verification.into(),
     )
 }
 
@@ -382,6 +380,6 @@
         &kernel,
         &load_latest_initrd_normal()?,
         &load_trusted_public_key()?,
-        AvbSlotVerifyError::Verification,
+        avb::SlotVerifyError::Verification.into(),
     )
 }
diff --git a/pvmfw/avb/tests/utils.rs b/pvmfw/avb/tests/utils.rs
index 79fdfff..86d2398 100644
--- a/pvmfw/avb/tests/utils.rs
+++ b/pvmfw/avb/tests/utils.rs
@@ -22,7 +22,7 @@
     AvbVBMetaImageHeader,
 };
 use openssl::sha;
-use pvmfw_avb::{verify_payload, AvbSlotVerifyError, DebugLevel, Digest, VerifiedBootData};
+use pvmfw_avb::{verify_payload, DebugLevel, Digest, PvmfwVerifyError, VerifiedBootData};
 use std::{
     fs,
     mem::{size_of, transmute, MaybeUninit},
@@ -39,7 +39,7 @@
     kernel: &[u8],
     initrd: &[u8],
     trusted_public_key: &[u8],
-    expected_error: AvbSlotVerifyError,
+    expected_error: PvmfwVerifyError,
 ) -> Result<()> {
     assert_payload_verification_fails(kernel, Some(initrd), trusted_public_key, expected_error)
 }
@@ -48,7 +48,7 @@
     kernel: &[u8],
     initrd: Option<&[u8]>,
     trusted_public_key: &[u8],
-    expected_error: AvbSlotVerifyError,
+    expected_error: PvmfwVerifyError,
 ) -> Result<()> {
     assert_eq!(expected_error, verify_payload(kernel, initrd, trusted_public_key).unwrap_err());
     Ok(())
diff --git a/rialto/Android.bp b/rialto/Android.bp
index 55423ea..3dfcca1 100644
--- a/rialto/Android.bp
+++ b/rialto/Android.bp
@@ -95,6 +95,7 @@
 rust_test {
     name: "rialto_test",
     crate_name: "rialto_test",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["tests/test.rs"],
     prefer_rlib: true,
     edition: "2021",
diff --git a/rialto/src/main.rs b/rialto/src/main.rs
index 42d39c4..b34b9de 100644
--- a/rialto/src/main.rs
+++ b/rialto/src/main.rs
@@ -34,7 +34,7 @@
 use libfdt::FdtError;
 use log::{debug, error, info};
 use virtio_drivers::{
-    device::socket::VsockAddr,
+    device::socket::{VsockAddr, VMADDR_CID_HOST},
     transport::{pci::bus::PciRoot, DeviceType, Transport},
     Hal,
 };
@@ -52,12 +52,7 @@
 };
 
 fn host_addr() -> VsockAddr {
-    const PROTECTED_VM_PORT: u32 = 5679;
-    const NON_PROTECTED_VM_PORT: u32 = 5680;
-    const VMADDR_CID_HOST: u64 = 2;
-
-    let port = if is_protected_vm() { PROTECTED_VM_PORT } else { NON_PROTECTED_VM_PORT };
-    VsockAddr { cid: VMADDR_CID_HOST, port }
+    VsockAddr { cid: VMADDR_CID_HOST, port: service_vm_comm::host_port(is_protected_vm()) }
 }
 
 fn is_protected_vm() -> bool {
diff --git a/rialto/tests/test.rs b/rialto/tests/test.rs
index 2bd8968..e9bdab6 100644
--- a/rialto/tests/test.rs
+++ b/rialto/tests/test.rs
@@ -24,7 +24,7 @@
 };
 use anyhow::{anyhow, bail, Context, Result};
 use log::info;
-use service_vm_comm::{Request, Response};
+use service_vm_comm::{host_port, Request, Response};
 use std::fs::File;
 use std::io::{self, BufRead, BufReader, BufWriter, Write};
 use std::os::unix::io::FromRawFd;
@@ -34,11 +34,6 @@
 use vmclient::{DeathReason, VmInstance};
 use vsock::{VsockListener, VMADDR_CID_HOST};
 
-// TODO(b/291732060): Move the port numbers to the common library shared between the host
-// and rialto.
-const PROTECTED_VM_PORT: u32 = 5679;
-const NON_PROTECTED_VM_PORT: u32 = 5680;
-
 const SIGNED_RIALTO_PATH: &str = "/data/local/tmp/rialto_test/arm64/rialto.bin";
 const UNSIGNED_RIALTO_PATH: &str = "/data/local/tmp/rialto_test/arm64/rialto_unsigned.bin";
 const INSTANCE_IMG_PATH: &str = "/data/local/tmp/rialto_test/arm64/instance.img";
@@ -131,7 +126,7 @@
     )
     .context("Failed to create VM")?;
 
-    let port = if protected_vm { PROTECTED_VM_PORT } else { NON_PROTECTED_VM_PORT };
+    let port = host_port(protected_vm);
     let check_socket_handle = thread::spawn(move || try_check_socket_connection(port).unwrap());
 
     vm.start().context("Failed to start VM")?;
diff --git a/service_vm/client_apk/Android.bp b/service_vm/client_apk/Android.bp
index 415b563..d94489d 100644
--- a/service_vm/client_apk/Android.bp
+++ b/service_vm/client_apk/Android.bp
@@ -16,6 +16,7 @@
 rust_defaults {
     name: "service_vm_client_defaults",
     crate_name: "service_vm_client",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/main.rs"],
     prefer_rlib: true,
     rustlibs: [
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index 8478f98..6301a57 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -2,26 +2,6 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-genrule_defaults {
-    name: "test_avf_dts_to_dtb",
-    tools: ["dtc"],
-    cmd: "$(location dtc) -I dts -O dtb $(in) -o $(out)",
-}
-
-genrule {
-    name: "test_avf_debug_policy_with_adb",
-    defaults: ["test_avf_dts_to_dtb"],
-    srcs: ["assets/avf_debug_policy_with_adb.dts"],
-    out: ["avf_debug_policy_with_adb.dtbo"],
-}
-
-genrule {
-    name: "test_avf_debug_policy_without_adb",
-    defaults: ["test_avf_dts_to_dtb"],
-    srcs: ["assets/avf_debug_policy_without_adb.dts"],
-    out: ["avf_debug_policy_without_adb.dtbo"],
-}
-
 java_test_host {
     name: "MicrodroidHostTestCases",
     srcs: ["java/**/*.java"],
@@ -38,7 +18,6 @@
         "compatibility-host-util",
         "cts-statsd-atom-host-test-utils",
         "microdroid_payload_metadata",
-        "MicrodroidTestPreparer", // Workaround for sandboxed test environment to install this
     ],
     per_testcase_directory: true,
     data: [
@@ -46,10 +25,6 @@
         ":microdroid_general_sepolicy.conf",
         ":test.com.android.virt.pem",
         ":test2.com.android.virt.pem",
-        ":pvmfw_test",
-        ":test_avf_debug_policy_with_adb",
-        ":test_avf_debug_policy_without_adb",
-        "assets/bcc.dat",
     ],
     data_native_bins: [
         "sepolicy-analyze",
@@ -76,5 +51,4 @@
         "libsparse",
         "libz",
     ],
-    required: ["MicrodroidTestPreparer"],
 }
diff --git a/tests/pvmfw/Android.bp b/tests/pvmfw/Android.bp
new file mode 100644
index 0000000..61667f3
--- /dev/null
+++ b/tests/pvmfw/Android.bp
@@ -0,0 +1,46 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+genrule_defaults {
+    name: "test_avf_dts_to_dtb",
+    tools: ["dtc"],
+    cmd: "$(location dtc) -I dts -O dtb $(in) -o $(out)",
+}
+
+genrule {
+    name: "test_avf_debug_policy_with_adb",
+    defaults: ["test_avf_dts_to_dtb"],
+    srcs: ["assets/avf_debug_policy_with_adb.dts"],
+    out: ["avf_debug_policy_with_adb.dtbo"],
+}
+
+genrule {
+    name: "test_avf_debug_policy_without_adb",
+    defaults: ["test_avf_dts_to_dtb"],
+    srcs: ["assets/avf_debug_policy_without_adb.dts"],
+    out: ["avf_debug_policy_without_adb.dtbo"],
+}
+
+java_test_host {
+    name: "CustomPvmfwHostTestCases",
+    srcs: ["java/**/*.java"],
+    test_suites: ["general-tests"],
+    libs: [
+        "androidx.annotation_annotation",
+        "tradefed",
+    ],
+    static_libs: [
+        "MicrodroidHostTestHelper",
+        "PvmfwHostTestHelper",
+        "compatibility-host-util",
+    ],
+    per_testcase_directory: true,
+    data: [
+        ":MicrodroidTestApp",
+        ":pvmfw_test",
+        ":test_avf_debug_policy_with_adb",
+        ":test_avf_debug_policy_without_adb",
+        "assets/bcc.dat",
+    ],
+}
diff --git a/tests/pvmfw/AndroidTest.xml b/tests/pvmfw/AndroidTest.xml
new file mode 100644
index 0000000..6ff7b6f
--- /dev/null
+++ b/tests/pvmfw/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Host driven tests for pvmfw with customization">
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+        <option name="force-root" value="true"/>
+    </target_preparer>
+
+    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+        <option name="jar" value="CustomPvmfwHostTestCases.jar" />
+    </test>
+</configuration>
diff --git a/tests/hostside/assets/avf_debug_policy_with_adb.dts b/tests/pvmfw/assets/avf_debug_policy_with_adb.dts
similarity index 100%
rename from tests/hostside/assets/avf_debug_policy_with_adb.dts
rename to tests/pvmfw/assets/avf_debug_policy_with_adb.dts
diff --git a/tests/hostside/assets/avf_debug_policy_without_adb.dts b/tests/pvmfw/assets/avf_debug_policy_without_adb.dts
similarity index 100%
rename from tests/hostside/assets/avf_debug_policy_without_adb.dts
rename to tests/pvmfw/assets/avf_debug_policy_without_adb.dts
diff --git a/tests/hostside/assets/bcc.dat b/tests/pvmfw/assets/bcc.dat
similarity index 100%
rename from tests/hostside/assets/bcc.dat
rename to tests/pvmfw/assets/bcc.dat
Binary files differ
diff --git a/tests/pvmfw/helper/Android.bp b/tests/pvmfw/helper/Android.bp
new file mode 100644
index 0000000..1b96842
--- /dev/null
+++ b/tests/pvmfw/helper/Android.bp
@@ -0,0 +1,9 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library_host {
+    name: "PvmfwHostTestHelper",
+    srcs: ["java/**/*.java"],
+    libs: ["androidx.annotation_annotation"],
+}
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/Pvmfw.java b/tests/pvmfw/helper/java/com/android/pvmfw/test/host/Pvmfw.java
similarity index 98%
rename from tests/hostside/helper/java/com/android/microdroid/test/host/Pvmfw.java
rename to tests/pvmfw/helper/java/com/android/pvmfw/test/host/Pvmfw.java
index d752108..b0c1207 100644
--- a/tests/hostside/helper/java/com/android/microdroid/test/host/Pvmfw.java
+++ b/tests/pvmfw/helper/java/com/android/pvmfw/test/host/Pvmfw.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.microdroid.test.host;
+package com.android.pvmfw.test.host;
 
 import static java.nio.ByteOrder.LITTLE_ENDIAN;
 
diff --git a/tests/hostside/java/com/android/microdroid/test/DebugPolicyHostTests.java b/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java
similarity index 96%
rename from tests/hostside/java/com/android/microdroid/test/DebugPolicyHostTests.java
rename to tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java
index ffeae09..410e6e0 100644
--- a/tests/hostside/java/com/android/microdroid/test/DebugPolicyHostTests.java
+++ b/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.microdroid.test;
+package com.android.pvmfw.test;
 
 import static com.android.tradefed.device.TestDevice.MicrodroidBuilder;
 
@@ -22,7 +22,6 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assume.assumeTrue;
-import static org.junit.Assume.assumeFalse;
 import static org.junit.Assert.assertThrows;
 
 import androidx.annotation.NonNull;
@@ -30,7 +29,7 @@
 
 import com.android.microdroid.test.host.CommandRunner;
 import com.android.microdroid.test.host.MicrodroidHostTestCaseBase;
-import com.android.microdroid.test.host.Pvmfw;
+import com.android.pvmfw.test.host.Pvmfw;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.DeviceRuntimeException;
 import com.android.tradefed.device.ITestDevice;
@@ -113,9 +112,6 @@
         assumeTrue(
                 "Skip if protected VMs are not supported",
                 mAndroidDevice.supportsMicrodroid(/* protectedVm= */ true));
-        assumeFalse("Test requires setprop for using custom pvmfw and adb root", isUserBuild());
-
-        assumeTrue("Skip if adb root fails", mAndroidDevice.enableAdbRoot());
 
         // tradefed copies the test artfacts under /tmp when running tests,
         // so we should *find* the artifacts with the file name.
@@ -156,8 +152,6 @@
         FileUtil.deleteFile(mCustomPvmfwBinFileOnHost);
 
         cleanUpVirtualizationTestSetup(mAndroidDevice);
-
-        mAndroidDevice.disableAdbRoot();
     }
 
     @Test
diff --git a/tests/hostside/java/com/android/microdroid/test/PvmfwImgTest.java b/tests/pvmfw/java/com/android/pvmfw/test/PvmfwImgTest.java
similarity index 82%
rename from tests/hostside/java/com/android/microdroid/test/PvmfwImgTest.java
rename to tests/pvmfw/java/com/android/pvmfw/test/PvmfwImgTest.java
index af92674..95c1c4e 100644
--- a/tests/hostside/java/com/android/microdroid/test/PvmfwImgTest.java
+++ b/tests/pvmfw/java/com/android/pvmfw/test/PvmfwImgTest.java
@@ -14,21 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.microdroid.test;
+package com.android.pvmfw.test;
 
 import static com.android.tradefed.device.TestDevice.MicrodroidBuilder;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assume.assumeTrue;
-import static org.junit.Assume.assumeFalse;
 import static org.junit.Assert.assertThrows;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.microdroid.test.host.MicrodroidHostTestCaseBase;
-import com.android.microdroid.test.host.Pvmfw;
+import com.android.pvmfw.test.host.Pvmfw;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.DeviceRuntimeException;
 import com.android.tradefed.device.ITestDevice;
@@ -42,6 +41,8 @@
 import org.junit.runner.RunWith;
 
 import java.io.File;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
 
 /** Tests pvmfw.img and pvmfw */
@@ -77,9 +78,6 @@
         assumeTrue(
                 "Skip if protected VMs are not supported",
                 mAndroidDevice.supportsMicrodroid(/* protectedVm= */ true));
-        assumeFalse("Test requires setprop for using custom pvmfw and adb root", isUserBuild());
-
-        assumeTrue("Skip if adb root fails", mAndroidDevice.enableAdbRoot());
 
         // tradefed copies the test artfacts under /tmp when running tests,
         // so we should *find* the artifacts with the file name.
@@ -117,8 +115,6 @@
         FileUtil.deleteFile(mCustomPvmfwBinFileOnHost);
 
         cleanUpVirtualizationTestSetup(mAndroidDevice);
-
-        mAndroidDevice.disableAdbRoot();
     }
 
     @Test
@@ -141,15 +137,34 @@
 
     @Test
     public void testInvalidConfigVersion_doesNotBoot() throws Exception {
-        // Disclaimer: Update versions when it becomes valid
-        Pvmfw pvmfw =
-                new Pvmfw.Builder(mPvmfwBinFileOnHost, mBccFileOnHost).setVersion(2, 0).build();
-        pvmfw.serialize(mCustomPvmfwBinFileOnHost);
+        // Disclaimer: Update versions when they become valid
+        List<int[]> invalid_versions =
+                Arrays.asList(
+                        new int[] {0, 0},
+                        new int[] {0, 1},
+                        new int[] {0, 0xFFFF},
+                        new int[] {2, 0},
+                        new int[] {2, 1},
+                        new int[] {2, 0xFFFF},
+                        new int[] {0xFFFF, 0},
+                        new int[] {0xFFFF, 1},
+                        new int[] {0xFFFF, 0xFFFF});
 
-        assertThrows(
-                "pvmfw shouldn't boot with invalid version",
-                DeviceRuntimeException.class,
-                () -> launchProtectedVmAndWaitForBootCompleted(BOOT_FAILURE_WAIT_TIME_MS));
+        Pvmfw.Builder builder = new Pvmfw.Builder(mPvmfwBinFileOnHost, mBccFileOnHost);
+
+        for (int[] pair : invalid_versions) {
+            int major = pair[0];
+            int minor = pair[1];
+            String version = "v" + major + "." + minor;
+
+            Pvmfw pvmfw = builder.setVersion(major, minor).build();
+            pvmfw.serialize(mCustomPvmfwBinFileOnHost);
+
+            assertThrows(
+                    "pvmfw shouldn't boot with invalid version " + version,
+                    DeviceRuntimeException.class,
+                    () -> launchProtectedVmAndWaitForBootCompleted(BOOT_FAILURE_WAIT_TIME_MS));
+        }
     }
 
     private ITestDevice launchProtectedVmAndWaitForBootCompleted(long adbTimeoutMs)
diff --git a/tests/hostside/tools/Android.bp b/tests/pvmfw/tools/Android.bp
similarity index 79%
rename from tests/hostside/tools/Android.bp
rename to tests/pvmfw/tools/Android.bp
index f3cc275..7bd3ef5 100644
--- a/tests/hostside/tools/Android.bp
+++ b/tests/pvmfw/tools/Android.bp
@@ -6,5 +6,5 @@
     name: "pvmfw-tool",
     manifest: "pvmfw-tool-manifest.txt",
     srcs: ["PvmfwTool.java"],
-    static_libs: ["MicrodroidHostTestHelper"],
+    static_libs: ["PvmfwHostTestHelper"],
 }
diff --git a/tests/hostside/tools/PvmfwTool.java b/tests/pvmfw/tools/PvmfwTool.java
similarity index 95%
rename from tests/hostside/tools/PvmfwTool.java
rename to tests/pvmfw/tools/PvmfwTool.java
index 18dd6d7..e4b6020 100644
--- a/tests/hostside/tools/PvmfwTool.java
+++ b/tests/pvmfw/tools/PvmfwTool.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.microdroid;
+package com.android.pvmfw;
 
-import com.android.microdroid.test.host.Pvmfw;
+import com.android.pvmfw.test.host.Pvmfw;
 
 import java.io.File;
 import java.io.IOException;
diff --git a/tests/hostside/tools/pvmfw-tool-manifest.txt b/tests/pvmfw/tools/pvmfw-tool-manifest.txt
similarity index 100%
rename from tests/hostside/tools/pvmfw-tool-manifest.txt
rename to tests/pvmfw/tools/pvmfw-tool-manifest.txt
diff --git a/virtualizationmanager/Android.bp b/virtualizationmanager/Android.bp
index de39aa2..c660414 100644
--- a/virtualizationmanager/Android.bp
+++ b/virtualizationmanager/Android.bp
@@ -5,6 +5,7 @@
 rust_defaults {
     name: "virtualizationmanager_defaults",
     crate_name: "virtualizationmanager",
+    defaults: ["avf_build_flags_rust"],
     edition: "2021",
     // Only build on targets which crosvm builds on.
     enabled: false,
diff --git a/virtualizationmanager/src/payload.rs b/virtualizationmanager/src/payload.rs
index 343f3cf..3bfad33 100644
--- a/virtualizationmanager/src/payload.rs
+++ b/virtualizationmanager/src/payload.rs
@@ -41,11 +41,6 @@
 use std::time::SystemTime;
 use vmconfig::open_parcel_file;
 
-/// The list of APEXes which microdroid requires.
-// TODO(b/192200378) move this to microdroid.json?
-const MICRODROID_REQUIRED_APEXES: [&str; 1] = ["com.android.os.statsd"];
-const MICRODROID_REQUIRED_APEXES_DEBUG: [&str; 1] = ["com.android.adbd"];
-
 const APEX_INFO_LIST_PATH: &str = "/apex/apex-info-list.xml";
 
 const PACKAGE_MANAGER_NATIVE_SERVICE: &str = "package_native";
@@ -395,17 +390,17 @@
     apex_configs: &[ApexConfig],
     debug_config: &DebugConfig,
 ) -> Result<Vec<&'a ApexInfo>> {
-    let mut additional_apexes: Vec<&str> = MICRODROID_REQUIRED_APEXES.to_vec();
-    if debug_config.should_include_debug_apexes() {
-        additional_apexes.extend(MICRODROID_REQUIRED_APEXES_DEBUG.to_vec());
-    }
+    // APEXes which any Microdroid VM needs.
+    // TODO(b/192200378) move this to microdroid.json?
+    let required_apexes: &[_] =
+        if debug_config.should_include_debug_apexes() { &["com.android.adbd"] } else { &[] };
 
     let apex_infos = apex_list
         .list
         .iter()
         .filter(|ai| {
             apex_configs.iter().any(|cfg| ai.matches(cfg) && ai.is_active)
-                || additional_apexes.iter().any(|name| name == &ai.name && ai.is_active)
+                || required_apexes.iter().any(|name| name == &ai.name && ai.is_active)
                 || ai.provide_shared_apex_libs
         })
         .collect();
@@ -487,6 +482,7 @@
 #[cfg(test)]
 mod tests {
     use super::*;
+    use std::collections::HashMap;
     use tempfile::NamedTempFile;
 
     #[test]
@@ -505,43 +501,36 @@
 
     #[test]
     fn test_collect_apexes() -> Result<()> {
-        let apex_info_list = ApexInfoList {
-            list: vec![
+        let apex_infos_for_test = [
+            (
+                "adbd",
                 ApexInfo {
-                    // 0
                     name: "com.android.adbd".to_string(),
                     path: PathBuf::from("adbd"),
                     preinstalled_path: PathBuf::from("/system/adbd"),
                     has_classpath_jar: false,
                     last_update_seconds: 12345678,
                     is_factory: true,
-                    is_active: true,
-                    ..Default::default()
-                },
-                ApexInfo {
-                    // 1
-                    name: "com.android.os.statsd".to_string(),
-                    path: PathBuf::from("statsd"),
-                    preinstalled_path: PathBuf::from("/system/statsd"),
-                    has_classpath_jar: false,
-                    last_update_seconds: 12345678,
-                    is_factory: true,
                     is_active: false,
                     ..Default::default()
                 },
+            ),
+            (
+                "adbd_updated",
                 ApexInfo {
-                    // 2
-                    name: "com.android.os.statsd".to_string(),
-                    path: PathBuf::from("statsd/updated"),
-                    preinstalled_path: PathBuf::from("/system/statsd"),
+                    name: "com.android.adbd".to_string(),
+                    path: PathBuf::from("adbd"),
+                    preinstalled_path: PathBuf::from("/system/adbd"),
                     has_classpath_jar: false,
                     last_update_seconds: 12345678 + 1,
                     is_factory: false,
                     is_active: true,
                     ..Default::default()
                 },
+            ),
+            (
+                "no_classpath",
                 ApexInfo {
-                    // 3
                     name: "no_classpath".to_string(),
                     path: PathBuf::from("no_classpath"),
                     has_classpath_jar: false,
@@ -550,8 +539,10 @@
                     is_active: true,
                     ..Default::default()
                 },
+            ),
+            (
+                "has_classpath",
                 ApexInfo {
-                    // 4
                     name: "has_classpath".to_string(),
                     path: PathBuf::from("has_classpath"),
                     has_classpath_jar: true,
@@ -560,8 +551,10 @@
                     is_active: false,
                     ..Default::default()
                 },
+            ),
+            (
+                "has_classpath_updated",
                 ApexInfo {
-                    // 5
                     name: "has_classpath".to_string(),
                     path: PathBuf::from("has_classpath/updated"),
                     preinstalled_path: PathBuf::from("/system/has_classpath"),
@@ -571,8 +564,10 @@
                     is_active: true,
                     ..Default::default()
                 },
+            ),
+            (
+                "apex-foo",
                 ApexInfo {
-                    // 6
                     name: "apex-foo".to_string(),
                     path: PathBuf::from("apex-foo"),
                     preinstalled_path: PathBuf::from("/system/apex-foo"),
@@ -582,8 +577,10 @@
                     is_active: false,
                     ..Default::default()
                 },
+            ),
+            (
+                "apex-foo-updated",
                 ApexInfo {
-                    // 7
                     name: "apex-foo".to_string(),
                     path: PathBuf::from("apex-foo/updated"),
                     preinstalled_path: PathBuf::from("/system/apex-foo"),
@@ -593,8 +590,10 @@
                     is_active: true,
                     ..Default::default()
                 },
+            ),
+            (
+                "sharedlibs",
                 ApexInfo {
-                    // 8
                     name: "sharedlibs".to_string(),
                     path: PathBuf::from("apex-foo"),
                     preinstalled_path: PathBuf::from("/system/apex-foo"),
@@ -603,8 +602,10 @@
                     provide_shared_apex_libs: true,
                     ..Default::default()
                 },
+            ),
+            (
+                "sharedlibs-updated",
                 ApexInfo {
-                    // 9
                     name: "sharedlibs".to_string(),
                     path: PathBuf::from("apex-foo/updated"),
                     preinstalled_path: PathBuf::from("/system/apex-foo"),
@@ -613,8 +614,12 @@
                     provide_shared_apex_libs: true,
                     ..Default::default()
                 },
-            ],
+            ),
+        ];
+        let apex_info_list = ApexInfoList {
+            list: apex_infos_for_test.iter().map(|(_, info)| info).cloned().collect(),
         };
+        let apex_info_map = HashMap::from(apex_infos_for_test);
         let apex_configs = vec![
             ApexConfig { name: "apex-foo".to_string() },
             ApexConfig { name: "{CLASSPATH}".to_string() },
@@ -627,14 +632,13 @@
             )?,
             vec![
                 // Pass active/required APEXes
-                &apex_info_list.list[0],
-                &apex_info_list.list[2],
+                &apex_info_map["adbd_updated"],
                 // Pass active APEXes specified in the config
-                &apex_info_list.list[5],
-                &apex_info_list.list[7],
+                &apex_info_map["has_classpath_updated"],
+                &apex_info_map["apex-foo-updated"],
                 // Pass both preinstalled(inactive) and updated(active) for "sharedlibs" APEXes
-                &apex_info_list.list[8],
-                &apex_info_list.list[9],
+                &apex_info_map["sharedlibs"],
+                &apex_info_map["sharedlibs-updated"],
             ]
         );
         Ok(())
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 67890e2..74f88c5 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -5,6 +5,7 @@
 rust_binary {
     name: "virtualizationservice",
     crate_name: "virtualizationservice",
+    defaults: ["avf_build_flags_rust"],
     edition: "2021",
     srcs: ["src/main.rs"],
     // Only build on targets which crosvm builds on.
@@ -37,6 +38,15 @@
         "libstatslog_virtualization_rust",
         "libtombstoned_client_rust",
         "libvsock",
+        "libserde",
+        "libserde_xml_rs",
     ],
     apex_available: ["com.android.virt"],
 }
+
+xsd_config {
+    name: "assignable_devices",
+    srcs: ["assignable_devices.xsd"],
+    api_dir: "schema",
+    package_name: "android.system.virtualizationservice",
+}
diff --git a/virtualizationservice/assignable_devices.xsd b/virtualizationservice/assignable_devices.xsd
new file mode 100644
index 0000000..842542e
--- /dev/null
+++ b/virtualizationservice/assignable_devices.xsd
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+<!-- KEEP IN SYNC WITH aidl.rs -->
+<xs:schema version="2.0"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <xs:element name="devices">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="device" type="device" minOccurs="0" maxOccurs="unbounded"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:complexType name="device">
+        <xs:attribute name="kind" type="xs:string"/>
+        <xs:attribute name="sysfs_path" type="xs:string"/>
+    </xs:complexType>
+</xs:schema>
diff --git a/virtualizationservice/schema/current.txt b/virtualizationservice/schema/current.txt
new file mode 100644
index 0000000..ed0763a
--- /dev/null
+++ b/virtualizationservice/schema/current.txt
@@ -0,0 +1,25 @@
+// Signature format: 2.0
+package android.system.virtualizationservice {
+
+  public class Device {
+    ctor public Device();
+    method public String getKind();
+    method public String getSysfs_path();
+    method public void setKind(String);
+    method public void setSysfs_path(String);
+  }
+
+  public class Devices {
+    ctor public Devices();
+    method public java.util.List<android.system.virtualizationservice.Device> getDevice();
+  }
+
+  public class XmlParser {
+    ctor public XmlParser();
+    method public static android.system.virtualizationservice.Devices read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+  }
+
+}
+
diff --git a/virtualizationservice/schema/last_current.txt b/virtualizationservice/schema/last_current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/virtualizationservice/schema/last_current.txt
diff --git a/virtualizationservice/schema/last_removed.txt b/virtualizationservice/schema/last_removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/virtualizationservice/schema/last_removed.txt
diff --git a/virtualizationservice/schema/removed.txt b/virtualizationservice/schema/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/virtualizationservice/schema/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 4c97ad4..8586597 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -38,8 +38,9 @@
 use libc::VMADDR_CID_HOST;
 use log::{error, info, warn};
 use rustutils::system_properties;
-use std::collections::HashMap;
-use std::fs::{create_dir, remove_dir_all, set_permissions, Permissions};
+use serde::Deserialize;
+use std::collections::{HashMap, HashSet};
+use std::fs::{self, create_dir, remove_dir_all, set_permissions, Permissions};
 use std::io::{Read, Write};
 use std::os::unix::fs::PermissionsExt;
 use std::os::unix::raw::{pid_t, uid_t};
@@ -168,13 +169,41 @@
     fn getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>> {
         check_use_custom_virtual_machine()?;
 
-        // TODO(b/291191362): read VM DTBO to find assignable devices.
-        let mut devices = Vec::new();
-        let eh_path = "/sys/bus/platform/devices/16d00000.eh";
-        if Path::new(eh_path).exists() {
-            devices.push(AssignableDevice { kind: "eh".to_owned(), node: eh_path.to_owned() });
+        let mut ret = Vec::new();
+        let xml_path = Path::new("/vendor/etc/avf/assignable_devices.xml");
+        if !xml_path.exists() {
+            return Ok(ret);
         }
-        Ok(devices)
+
+        let xml = fs::read(xml_path)
+            .context("Failed to read assignable_devices.xml")
+            .with_log()
+            .or_service_specific_exception(-1)?;
+
+        let xml = String::from_utf8(xml)
+            .context("assignable_devices.xml is not a valid UTF-8 file")
+            .with_log()
+            .or_service_specific_exception(-1)?;
+
+        let devices: Devices = serde_xml_rs::from_str(&xml)
+            .context("can't parse assignable_devices.xml")
+            .with_log()
+            .or_service_specific_exception(-1)?;
+
+        let mut device_set = HashSet::new();
+
+        for device in devices.device.into_iter() {
+            if device_set.contains(&device.sysfs_path) {
+                warn!("duplicated assignable device {device:?}; ignoring...")
+            } else if Path::new(&device.sysfs_path).exists() {
+                device_set.insert(device.sysfs_path.clone());
+                ret.push(AssignableDevice { kind: device.kind, node: device.sysfs_path });
+            } else {
+                warn!("assignable device {device:?} doesn't exist; ignoring...");
+            }
+        }
+
+        Ok(ret)
     }
 
     fn bindDevicesToVfioDriver(
@@ -192,6 +221,18 @@
     }
 }
 
+// KEEP IN SYNC WITH assignable_devices.xsd
+#[derive(Debug, Deserialize)]
+struct Device {
+    kind: String,
+    sysfs_path: String,
+}
+
+#[derive(Debug, Deserialize)]
+struct Devices {
+    device: Vec<Device>,
+}
+
 #[derive(Debug, Default)]
 struct GlobalVmInstance {
     /// The unique CID assigned to the VM for vsock communication.
diff --git a/virtualizationservice/vfio_handler/Android.bp b/virtualizationservice/vfio_handler/Android.bp
index 66662d5..66fc2ee 100644
--- a/virtualizationservice/vfio_handler/Android.bp
+++ b/virtualizationservice/vfio_handler/Android.bp
@@ -5,6 +5,7 @@
 rust_binary {
     name: "vfio_handler",
     crate_name: "vfio_handler",
+    defaults: ["avf_build_flags_rust"],
     edition: "2021",
     srcs: ["src/main.rs"],
     // Only build on targets which crosvm builds on.
diff --git a/vm/Android.bp b/vm/Android.bp
index 50e68cc..04aff5e 100644
--- a/vm/Android.bp
+++ b/vm/Android.bp
@@ -5,6 +5,7 @@
 rust_defaults {
     name: "vm.defaults",
     crate_name: "vm",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/main.rs"],
     edition: "2021",
     prefer_rlib: true,
diff --git a/vm_payload/Android.bp b/vm_payload/Android.bp
index 49b7f5f..d2ac7ee 100644
--- a/vm_payload/Android.bp
+++ b/vm_payload/Android.bp
@@ -6,6 +6,7 @@
 rust_ffi_static {
     name: "libvm_payload_impl",
     crate_name: "vm_payload",
+    defaults: ["avf_build_flags_rust"],
     visibility: ["//visibility:private"],
     srcs: ["src/*.rs"],
     include_dirs: ["include"],
@@ -29,6 +30,7 @@
     name: "libvm_payload_bindgen",
     wrapper_src: "include-restricted/vm_payload_restricted.h",
     crate_name: "vm_payload_bindgen",
+    defaults: ["avf_build_flags_rust"],
     source_stem: "bindings",
     apex_available: ["com.android.compos"],
     visibility: [
diff --git a/vmbase/Android.bp b/vmbase/Android.bp
index 71b9e76..b2b1549 100644
--- a/vmbase/Android.bp
+++ b/vmbase/Android.bp
@@ -15,6 +15,7 @@
 // Used by intermediate rust_library_rlib for vmbase-based binaries.
 rust_defaults {
     name: "vmbase_rlib_defaults",
+    defaults: ["avf_build_flags_rust"],
     edition: "2021",
     prefer_rlib: true,
     host_supported: false,
diff --git a/vmclient/Android.bp b/vmclient/Android.bp
index 8517c88..96fe667 100644
--- a/vmclient/Android.bp
+++ b/vmclient/Android.bp
@@ -5,6 +5,7 @@
 rust_library {
     name: "libvmclient",
     crate_name: "vmclient",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/lib.rs"],
     edition: "2021",
     rustlibs: [
diff --git a/zipfuse/Android.bp b/zipfuse/Android.bp
index 1bdc5fe..974d66a 100644
--- a/zipfuse/Android.bp
+++ b/zipfuse/Android.bp
@@ -5,6 +5,7 @@
 rust_defaults {
     name: "zipfuse.defaults",
     crate_name: "zipfuse",
+    defaults: ["avf_build_flags_rust"],
     srcs: ["src/main.rs"],
     edition: "2021",
     prefer_rlib: true,