Merge "Data written to instance.img has schema"
diff --git a/authfs/aidl/com/android/virt/fs/IVirtFdService.aidl b/authfs/aidl/com/android/virt/fs/IVirtFdService.aidl
index d3c0979..a565a6f 100644
--- a/authfs/aidl/com/android/virt/fs/IVirtFdService.aidl
+++ b/authfs/aidl/com/android/virt/fs/IVirtFdService.aidl
@@ -28,6 +28,9 @@
      */
     const int ERROR_IO = 2;
 
+    /** Error when the file is too large to handle correctly. */
+    const int ERROR_FILE_TOO_LARGE = 3;
+
     /** Maximum content size that the service allows the client to request. */
     const int MAX_REQUESTING_DATA = 16384;
 
@@ -54,4 +57,6 @@
 
     /** Resizes the file backed by the given file ID to the new size. */
     void resize(int id, long size);
+
+    long getFileSize(int id);
 }
diff --git a/authfs/aidl/com/android/virt/fs/InputFdAnnotation.aidl b/authfs/aidl/com/android/virt/fs/InputFdAnnotation.aidl
index dafb137..3534a77 100644
--- a/authfs/aidl/com/android/virt/fs/InputFdAnnotation.aidl
+++ b/authfs/aidl/com/android/virt/fs/InputFdAnnotation.aidl
@@ -23,7 +23,4 @@
      * number used in the backend server.
      */
     int fd;
-
-    /** The actual file size in bytes of the backing file to be read. */
-    long fileSize;
 }
diff --git a/authfs/fd_server/src/main.rs b/authfs/fd_server/src/main.rs
index d63fe93..7e551a3 100644
--- a/authfs/fd_server/src/main.rs
+++ b/authfs/fd_server/src/main.rs
@@ -14,19 +14,19 @@
  * limitations under the License.
  */
 
-//! This program is a constrained file/FD server to serve file requests through a remote[1] binder
+//! This program is a constrained file/FD server to serve file requests through a remote binder
 //! service. The file server is not designed to serve arbitrary file paths in the filesystem. On
 //! the contrary, the server should be configured to start with already opened FDs, and serve the
 //! client's request against the FDs
 //!
 //! For example, `exec 9</path/to/file fd_server --ro-fds 9` starts the binder service. A client
 //! client can then request the content of file 9 by offset and size.
-//!
-//! [1] Since the remote binder is not ready, this currently implementation uses local binder
-//!     first.
 
 mod fsverity;
 
+use anyhow::{bail, Result};
+use binder::unstable_api::AsNative;
+use log::{debug, error};
 use std::cmp::min;
 use std::collections::BTreeMap;
 use std::convert::TryInto;
@@ -36,19 +36,14 @@
 use std::os::unix::fs::FileExt;
 use std::os::unix::io::{AsRawFd, FromRawFd};
 
-use anyhow::{bail, Context, Result};
-use binder::unstable_api::AsNative;
-use log::{debug, error};
-
 use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService::{
-    BnVirtFdService, IVirtFdService, ERROR_IO, ERROR_UNKNOWN_FD, MAX_REQUESTING_DATA,
+    BnVirtFdService, IVirtFdService, ERROR_FILE_TOO_LARGE, ERROR_IO, ERROR_UNKNOWN_FD,
+    MAX_REQUESTING_DATA,
 };
 use authfs_aidl_interface::binder::{
-    add_service, BinderFeatures, ExceptionCode, Interface, ProcessState, Result as BinderResult,
-    Status, StatusCode, Strong,
+    BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Status, StatusCode, Strong,
 };
 
-const SERVICE_NAME: &str = "authfs_fd_server";
 const RPC_SERVICE_PORT: u32 = 3264; // TODO: support dynamic port for multiple fd_server instances
 
 fn new_binder_exception<T: AsRef<str>>(exception: ExceptionCode, message: T) -> Status {
@@ -226,6 +221,30 @@
             }
         }
     }
+
+    fn getFileSize(&self, id: i32) -> BinderResult<i64> {
+        match &self.get_file_config(id)? {
+            FdConfig::Readonly { file, .. } => {
+                let size = file
+                    .metadata()
+                    .map_err(|e| {
+                        error!("getFileSize error: {}", e);
+                        Status::from(ERROR_IO)
+                    })?
+                    .len();
+                Ok(size.try_into().map_err(|e| {
+                    error!("getFileSize: File too large: {}", e);
+                    Status::from(ERROR_FILE_TOO_LARGE)
+                })?)
+            }
+            FdConfig::ReadWrite(_file) => {
+                // Content and metadata of a writable file needs to be tracked by authfs, since
+                // fd_server isn't considered trusted. So there is no point to support getFileSize
+                // for a writable file.
+                Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Unsupported"))
+            }
+        }
+    }
 }
 
 fn read_into_buf(file: &File, max_size: usize, offset: u64) -> io::Result<Vec<u8>> {
@@ -277,7 +296,7 @@
     Ok((fd, FdConfig::ReadWrite(file)))
 }
 
-fn parse_args() -> Result<(bool, BTreeMap<i32, FdConfig>)> {
+fn parse_args() -> Result<BTreeMap<i32, FdConfig>> {
     #[rustfmt::skip]
     let matches = clap::App::new("fd_server")
         .arg(clap::Arg::with_name("ro-fds")
@@ -288,8 +307,6 @@
              .long("rw-fds")
              .multiple(true)
              .number_of_values(1))
-        .arg(clap::Arg::with_name("rpc-binder")
-             .long("rpc-binder"))
         .get_matches();
 
     let mut fd_pool = BTreeMap::new();
@@ -306,8 +323,7 @@
         }
     }
 
-    let rpc_binder = matches.is_present("rpc-binder");
-    Ok((rpc_binder, fd_pool))
+    Ok(fd_pool)
 }
 
 fn main() -> Result<()> {
@@ -315,32 +331,22 @@
         android_logger::Config::default().with_tag("fd_server").with_min_level(log::Level::Debug),
     );
 
-    let (rpc_binder, fd_pool) = parse_args()?;
+    let fd_pool = parse_args()?;
 
-    if rpc_binder {
-        let mut service = FdService::new_binder(fd_pool).as_binder();
-        debug!("fd_server is starting as a rpc service.");
-        // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
-        // Plus the binder objects are threadsafe.
-        let retval = unsafe {
-            binder_rpc_unstable_bindgen::RunRpcServer(
-                service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder,
-                RPC_SERVICE_PORT,
-            )
-        };
-        if retval {
-            debug!("RPC server has shut down gracefully");
-            Ok(())
-        } else {
-            bail!("Premature termination of RPC server");
-        }
+    let mut service = FdService::new_binder(fd_pool).as_binder();
+    debug!("fd_server is starting as a rpc service.");
+    // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
+    // Plus the binder objects are threadsafe.
+    let retval = unsafe {
+        binder_rpc_unstable_bindgen::RunRpcServer(
+            service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder,
+            RPC_SERVICE_PORT,
+        )
+    };
+    if retval {
+        debug!("RPC server has shut down gracefully");
+        Ok(())
     } else {
-        ProcessState::start_thread_pool();
-        let service = FdService::new_binder(fd_pool).as_binder();
-        add_service(SERVICE_NAME, service)
-            .with_context(|| format!("Failed to register service {}", SERVICE_NAME))?;
-        debug!("fd_server is running as a local service.");
-        ProcessState::join_thread_pool();
-        bail!("Unexpected exit after join_thread_pool")
+        bail!("Premature termination of RPC server");
     }
 }
diff --git a/authfs/service/src/authfs.rs b/authfs/service/src/authfs.rs
index 7a466d3..f41a3a6 100644
--- a/authfs/service/src/authfs.rs
+++ b/authfs/service/src/authfs.rs
@@ -129,7 +129,7 @@
         // TODO(b/185178698): Many input files need to be signed and verified.
         // or can we use debug cert for now, which is better than nothing?
         args.push(OsString::from("--remote-ro-file-unverified"));
-        args.push(OsString::from(format!("{}:{}:{}", conf.fd, conf.fd, conf.fileSize)));
+        args.push(OsString::from(format!("{}:{}", conf.fd, conf.fd)));
     }
     for conf in out_fds {
         args.push(OsString::from("--remote-new-rw-file"));
diff --git a/authfs/src/file.rs b/authfs/src/file.rs
index 703eddb..947b59f 100644
--- a/authfs/src/file.rs
+++ b/authfs/src/file.rs
@@ -10,7 +10,7 @@
 
 use crate::common::CHUNK_SIZE;
 use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService::IVirtFdService;
-use authfs_aidl_interface::binder::{get_interface, Strong};
+use authfs_aidl_interface::binder::Strong;
 
 pub type VirtFdService = Strong<dyn IVirtFdService>;
 
@@ -18,17 +18,7 @@
 
 pub const RPC_SERVICE_PORT: u32 = 3264;
 
-fn get_local_binder() -> io::Result<VirtFdService> {
-    let service_name = "authfs_fd_server";
-    get_interface(service_name).map_err(|e| {
-        io::Error::new(
-            io::ErrorKind::AddrNotAvailable,
-            format!("Cannot reach authfs_fd_server binder service: {}", e),
-        )
-    })
-}
-
-fn get_rpc_binder(cid: u32) -> io::Result<VirtFdService> {
+pub fn get_rpc_binder_service(cid: u32) -> io::Result<VirtFdService> {
     // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership can be
     // safely taken by new_spibinder.
     let ibinder = unsafe {
@@ -46,14 +36,6 @@
     }
 }
 
-pub fn get_binder_service(cid: Option<u32>) -> io::Result<VirtFdService> {
-    if let Some(cid) = cid {
-        get_rpc_binder(cid)
-    } else {
-        get_local_binder()
-    }
-}
-
 /// A trait for reading data by chunks. Chunks can be read by specifying the chunk index. Only the
 /// last chunk may have incomplete chunk size.
 pub trait ReadByChunk {
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index 32ea3de..c85d801 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -29,6 +29,7 @@
 
 use anyhow::{bail, Context, Result};
 use std::collections::BTreeMap;
+use std::convert::TryInto;
 use std::fs::File;
 use std::io::Read;
 use std::path::{Path, PathBuf};
@@ -54,7 +55,7 @@
 
     /// CID of the VM where the service runs.
     #[structopt(long)]
-    cid: Option<u32>,
+    cid: u32,
 
     /// Extra options to FUSE
     #[structopt(short = "o")]
@@ -62,16 +63,15 @@
 
     /// A read-only remote file with integrity check. Can be multiple.
     ///
-    /// For example, `--remote-verified-file 5:10:1234:/path/to/cert` tells the filesystem to
-    /// associate entry 5 with a remote file 10 of size 1234 bytes, and need to be verified against
-    /// the /path/to/cert.
+    /// For example, `--remote-verified-file 5:10:/path/to/cert` tells the filesystem to associate
+    /// entry 5 with a remote file 10, and need to be verified against the /path/to/cert.
     #[structopt(long, parse(try_from_str = parse_remote_ro_file_option))]
     remote_ro_file: Vec<OptionRemoteRoFile>,
 
     /// A read-only remote file without integrity check. Can be multiple.
     ///
-    /// For example, `--remote-unverified-file 5:10:1234` tells the filesystem to associate entry 5
-    /// with a remote file 10 of size 1234 bytes.
+    /// For example, `--remote-unverified-file 5:10` tells the filesystem to associate entry 5
+    /// with a remote file 10.
     #[structopt(long, parse(try_from_str = parse_remote_ro_file_unverified_option))]
     remote_ro_file_unverified: Vec<OptionRemoteRoFileUnverified>,
 
@@ -109,10 +109,6 @@
     /// ID to refer to the remote file.
     remote_id: i32,
 
-    /// Expected size of the remote file. Necessary for signature check and Merkle tree
-    /// verification.
-    file_size: u64,
-
     /// Certificate to verify the authenticity of the file's fs-verity signature.
     /// TODO(170494765): Implement PKCS#7 signature verification.
     _certificate_path: PathBuf,
@@ -123,9 +119,6 @@
 
     /// ID to refer to the remote file.
     remote_id: i32,
-
-    /// Expected size of the remote file.
-    file_size: u64,
 }
 
 struct OptionRemoteRwFile {
@@ -161,26 +154,24 @@
 
 fn parse_remote_ro_file_option(option: &str) -> Result<OptionRemoteRoFile> {
     let strs: Vec<&str> = option.split(':').collect();
-    if strs.len() != 4 {
+    if strs.len() != 3 {
         bail!("Invalid option: {}", option);
     }
     Ok(OptionRemoteRoFile {
         ino: strs[0].parse::<Inode>()?,
         remote_id: strs[1].parse::<i32>()?,
-        file_size: strs[2].parse::<u64>()?,
-        _certificate_path: PathBuf::from(strs[3]),
+        _certificate_path: PathBuf::from(strs[2]),
     })
 }
 
 fn parse_remote_ro_file_unverified_option(option: &str) -> Result<OptionRemoteRoFileUnverified> {
     let strs: Vec<&str> = option.split(':').collect();
-    if strs.len() != 3 {
+    if strs.len() != 2 {
         bail!("Invalid option: {}", option);
     }
     Ok(OptionRemoteRoFileUnverified {
         ino: strs[0].parse::<Inode>()?,
         remote_id: strs[1].parse::<i32>()?,
-        file_size: strs[2].parse::<u64>()?,
     })
 }
 
@@ -284,7 +275,7 @@
     let mut file_pool = BTreeMap::new();
 
     if args.has_remote_files() {
-        let service = file::get_binder_service(args.cid)?;
+        let service = file::get_rpc_binder_service(args.cid)?;
 
         for config in &args.remote_ro_file {
             file_pool.insert(
@@ -292,7 +283,7 @@
                 new_config_remote_verified_file(
                     service.clone(),
                     config.remote_id,
-                    config.file_size,
+                    service.getFileSize(config.remote_id)?.try_into()?,
                 )?,
             );
         }
@@ -303,7 +294,7 @@
                 new_config_remote_unverified_file(
                     service.clone(),
                     config.remote_id,
-                    config.file_size,
+                    service.getFileSize(config.remote_id)?.try_into()?,
                 )?,
             );
         }
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
index 6e1c890..1b4fa4a 100644
--- a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
+++ b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
@@ -151,11 +151,10 @@
         // Setup
         runFdServerOnAndroid(
                 "3<input.4m 4<input.4m.merkle_dump 5<input.4m.fsv_sig 6<input.4m",
-                "--ro-fds 3:4:5 --ro-fds 6 --rpc-binder");
+                "--ro-fds 3:4:5 --ro-fds 6");
 
         runAuthFsOnMicrodroid(
-                "--remote-ro-file-unverified 10:6:4194304 --remote-ro-file 11:3:4194304:cert.der"
-                        + " --cid 2");
+                "--remote-ro-file-unverified 10:6 --remote-ro-file 11:3:cert.der --cid 2");
 
         // Action
         String actualHashUnverified4m = computeFileHashOnMicrodroid(MOUNT_DIR + "/10");
@@ -177,9 +176,9 @@
         runFdServerOnAndroid(
                 "3<input.4k 4<input.4k.merkle_dump 5<input.4k.fsv_sig"
                         + " 6<input.4k1 7<input.4k1.merkle_dump 8<input.4k1.fsv_sig",
-                "--ro-fds 3:4:5 --ro-fds 6:7:8 --rpc-binder");
+                "--ro-fds 3:4:5 --ro-fds 6:7:8");
         runAuthFsOnMicrodroid(
-                "--remote-ro-file 10:3:4096:cert.der --remote-ro-file 11:6:4097:cert.der --cid 2");
+                "--remote-ro-file 10:3:cert.der --remote-ro-file 11:6:cert.der --cid 2");
 
         // Action
         String actualHash4k = computeFileHashOnMicrodroid(MOUNT_DIR + "/10");
@@ -198,9 +197,8 @@
             throws DeviceNotAvailableException, InterruptedException {
         // Setup
         runFdServerOnAndroid(
-                "3<input.4m 4<input.4m.merkle_dump.bad 5<input.4m.fsv_sig",
-                "--ro-fds 3:4:5 --rpc-binder");
-        runAuthFsOnMicrodroid("--remote-ro-file 10:3:4096:cert.der --cid 2");
+                "3<input.4m 4<input.4m.merkle_dump.bad 5<input.4m.fsv_sig", "--ro-fds 3:4:5");
+        runAuthFsOnMicrodroid("--remote-ro-file 10:3:cert.der --cid 2");
 
         // Verify
         assertFalse(copyFileOnMicrodroid(MOUNT_DIR + "/10", "/dev/null"));
@@ -210,7 +208,7 @@
     public void testWriteThroughCorrectly()
             throws DeviceNotAvailableException, InterruptedException {
         // Setup
-        runFdServerOnAndroid("3<>output", "--rw-fds 3 --rpc-binder");
+        runFdServerOnAndroid("3<>output", "--rw-fds 3");
         runAuthFsOnMicrodroid("--remote-new-rw-file 20:3 --cid 2");
 
         // Action
@@ -228,7 +226,7 @@
     public void testWriteFailedIfDetectsTampering()
             throws DeviceNotAvailableException, InterruptedException {
         // Setup
-        runFdServerOnAndroid("3<>output", "--rw-fds 3 --rpc-binder");
+        runFdServerOnAndroid("3<>output", "--rw-fds 3");
         runAuthFsOnMicrodroid("--remote-new-rw-file 20:3 --cid 2");
 
         String srcPath = "/system/bin/linker64";
@@ -259,7 +257,7 @@
     @Test
     public void testFileResize() throws DeviceNotAvailableException, InterruptedException {
         // Setup
-        runFdServerOnAndroid("3<>output", "--rw-fds 3 --rpc-binder");
+        runFdServerOnAndroid("3<>output", "--rw-fds 3");
         runAuthFsOnMicrodroid("--remote-new-rw-file 20:3 --cid 2");
         String outputPath = MOUNT_DIR + "/20";
         String backendPath = TEST_DIR + "/output";
diff --git a/compos/aidl/com/android/compos/InputFdAnnotation.aidl b/compos/aidl/com/android/compos/FdAnnotation.aidl
similarity index 61%
rename from compos/aidl/com/android/compos/InputFdAnnotation.aidl
rename to compos/aidl/com/android/compos/FdAnnotation.aidl
index 44a5591..b910391 100644
--- a/compos/aidl/com/android/compos/InputFdAnnotation.aidl
+++ b/compos/aidl/com/android/compos/FdAnnotation.aidl
@@ -17,13 +17,16 @@
 package com.android.compos;
 
 /** {@hide} */
-parcelable InputFdAnnotation {
+parcelable FdAnnotation {
     /**
-     * File descriptor number to be passed to the program.  This is also the same file descriptor
-     * number used in the backend server.
+     * Input file descriptor numbers to be passed to the program.  This is currently assumed to be
+     * same as the file descriptor number used in the backend server.
      */
-    int fd;
+    int[] input_fds;
 
-    /** The actual file size in bytes of the backing file to be read. */
-    long file_size;
+    /**
+     * Output file descriptor numbers to be passed to the program.  This is currently assumed to be
+     * same as the file descriptor number used in the backend server.
+     */
+    int[] output_fds;
 }
diff --git a/compos/aidl/com/android/compos/ICompOsService.aidl b/compos/aidl/com/android/compos/ICompOsService.aidl
index 3a74940..7904130 100644
--- a/compos/aidl/com/android/compos/ICompOsService.aidl
+++ b/compos/aidl/com/android/compos/ICompOsService.aidl
@@ -18,7 +18,7 @@
 
 import com.android.compos.CompOsKeyData;
 import com.android.compos.CompilationResult;
-import com.android.compos.Metadata;
+import com.android.compos.FdAnnotation;
 
 /** {@hide} */
 interface ICompOsService {
@@ -33,17 +33,17 @@
     void initializeSigningKey(in byte[] keyBlob);
 
     /**
-     * Run dex2oat command with provided args, in a context that may be specified in the Metadata,
+     * Run dex2oat command with provided args, in a context that may be specified in FdAnnotation,
      * e.g. with file descriptors pre-opened. The service is responsible to decide what executables
      * it may run.
      *
      * @param args The command line arguments to run. The 0-th args is normally the program name,
      *             which may not be used by the service. The service may be configured to always use
      *             a fixed executable, or possibly use the 0-th args are the executable lookup hint.
-     * @param metadata Additional information of the execution
+     * @param fd_annotation Additional file descriptor information of the execution
      * @return a CompilationResult
      */
-    CompilationResult compile(in String[] args, in Metadata metadata);
+    CompilationResult compile(in String[] args, in FdAnnotation fd_annotation);
 
     /**
      * Generate a new public/private key pair suitable for signing CompOs output files.
diff --git a/compos/aidl/com/android/compos/Metadata.aidl b/compos/aidl/com/android/compos/Metadata.aidl
deleted file mode 100644
index a15214d..0000000
--- a/compos/aidl/com/android/compos/Metadata.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 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 com.android.compos;
-
-import com.android.compos.InputFdAnnotation;
-import com.android.compos.OutputFdAnnotation;
-
-/** {@hide} */
-parcelable Metadata {
-    InputFdAnnotation[] input_fd_annotations;
-    OutputFdAnnotation[] output_fd_annotations;
-}
diff --git a/compos/aidl/com/android/compos/OutputFdAnnotation.aidl b/compos/aidl/com/android/compos/OutputFdAnnotation.aidl
deleted file mode 100644
index 95ce425..0000000
--- a/compos/aidl/com/android/compos/OutputFdAnnotation.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 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 com.android.compos;
-
-/** {@hide} */
-parcelable OutputFdAnnotation {
-    /**
-     * File descriptor number to be passed to the program.  This is currently assumed to be same as
-     * the file descriptor number used in the backend server.
-     */
-    int fd;
-}
diff --git a/compos/apk/assets/vm_config.json b/compos/apk/assets/vm_config.json
index 3be8a8a..9be60d0 100644
--- a/compos/apk/assets/vm_config.json
+++ b/compos/apk/assets/vm_config.json
@@ -5,10 +5,7 @@
   },
   "task": {
     "type": "executable",
-    "command": "/apex/com.android.compos/bin/compsvc",
-    "args": [
-      "--rpc-binder"
-    ]
+    "command": "/apex/com.android.compos/bin/compsvc"
   },
   "apexes": [
     {
diff --git a/compos/src/common.rs b/compos/src/common.rs
index 6cad63a..ca831bb 100644
--- a/compos/src/common.rs
+++ b/compos/src/common.rs
@@ -17,6 +17,3 @@
 /// Port to listen. This should be out of future port range (if happens) that microdroid may
 /// reserve for system components.
 pub const VSOCK_PORT: u32 = 6432;
-
-/// Service name of local binder. Used only for debugging purpose.
-pub const SERVICE_NAME: &str = "compsvc";
diff --git a/compos/src/compilation.rs b/compos/src/compilation.rs
index 0199eb5..fec82a6 100644
--- a/compos/src/compilation.rs
+++ b/compos/src/compilation.rs
@@ -27,7 +27,7 @@
     InputFdAnnotation::InputFdAnnotation, OutputFdAnnotation::OutputFdAnnotation,
 };
 use authfs_aidl_interface::binder::{ParcelFileDescriptor, Strong};
-use compos_aidl_interface::aidl::com::android::compos::Metadata::Metadata;
+use compos_aidl_interface::aidl::com::android::compos::FdAnnotation::FdAnnotation;
 
 /// The number that represents the file descriptor number expecting by the task. The number may be
 /// meaningless in the current process.
@@ -50,17 +50,17 @@
     image: ParcelFileDescriptor,
 }
 
-/// Runs the compiler with given flags with file descriptors described in `metadata` retrieved via
-/// `authfs_service`. Returns exit code of the compiler process.
+/// Runs the compiler with given flags with file descriptors described in `fd_annotation` retrieved
+/// via `authfs_service`. Returns exit code of the compiler process.
 pub fn compile(
     compiler_path: &Path,
     compiler_args: &[String],
     authfs_service: Strong<dyn IAuthFsService>,
-    metadata: &Metadata,
+    fd_annotation: &FdAnnotation,
 ) -> Result<CompilerOutput> {
     // Mount authfs (via authfs_service). The authfs instance unmounts once the `authfs` variable
     // is out of scope.
-    let authfs_config = build_authfs_config(metadata);
+    let authfs_config = build_authfs_config(fd_annotation);
     let authfs = authfs_service.mount(&authfs_config)?;
 
     // The task expects to receive FD numbers that match its flags (e.g. --zip-fd=42) prepared
@@ -135,18 +135,18 @@
     })
 }
 
-fn build_authfs_config(metadata: &Metadata) -> AuthFsConfig {
+fn build_authfs_config(fd_annotation: &FdAnnotation) -> AuthFsConfig {
     AuthFsConfig {
         port: 3264, // TODO: support dynamic port
-        inputFdAnnotations: metadata
-            .input_fd_annotations
+        inputFdAnnotations: fd_annotation
+            .input_fds
             .iter()
-            .map(|x| InputFdAnnotation { fd: x.fd, fileSize: x.file_size })
+            .map(|fd| InputFdAnnotation { fd: *fd })
             .collect(),
-        outputFdAnnotations: metadata
-            .output_fd_annotations
+        outputFdAnnotations: fd_annotation
+            .output_fds
             .iter()
-            .map(|x| OutputFdAnnotation { fd: x.fd })
+            .map(|fd| OutputFdAnnotation { fd: *fd })
             .collect(),
     }
 }
diff --git a/compos/src/compos_key_service.rs b/compos/src/compos_key_service.rs
index 92b04f2..4a1566d 100644
--- a/compos/src/compos_key_service.rs
+++ b/compos/src/compos_key_service.rs
@@ -32,15 +32,9 @@
 use ring::signature;
 use scopeguard::ScopeGuard;
 
-/// Keystore2 namespace IDs, used for access control to keys.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum KeystoreNamespace {
-    /// In the host we re-use the ID assigned to odsign. See system/sepolicy/private/keystore2_key_contexts.
-    // TODO(alanstokes): Remove this.
-    Odsign = 101,
-    /// In a VM we can use the generic ID allocated for payloads. See microdroid's keystore2_key_contexts.
-    VmPayload = 140,
-}
+/// Keystore2 namespace ID, used for access control to keys. In a VM we can use the generic ID
+/// allocated for payloads. See microdroid's keystore2_key_contexts.
+const KEYSTORE_NAMESPACE: i64 = 140;
 
 const KEYSTORE_SERVICE_NAME: &str = "android.system.keystore2.IKeystoreService/default";
 const PURPOSE_SIGN: KeyParameter =
@@ -61,25 +55,21 @@
     KeyParameter { tag: Tag::NO_AUTH_REQUIRED, value: KeyParameterValue::BoolValue(true) };
 
 const BLOB_KEY_DESCRIPTOR: KeyDescriptor =
-    KeyDescriptor { domain: Domain::BLOB, nspace: 0, alias: None, blob: None };
+    KeyDescriptor { domain: Domain::BLOB, nspace: KEYSTORE_NAMESPACE, alias: None, blob: None };
 
 /// An internal service for CompOS key management.
 #[derive(Clone)]
 pub struct CompOsKeyService {
-    namespace: KeystoreNamespace,
     random: SystemRandom,
     security_level: Strong<dyn IKeystoreSecurityLevel>,
 }
 
 impl CompOsKeyService {
-    pub fn new(rpc_binder: bool) -> Result<Self> {
+    pub fn new() -> Result<Self> {
         let keystore_service = wait_for_interface::<dyn IKeystoreService>(KEYSTORE_SERVICE_NAME)
             .context("No Keystore service")?;
 
-        let namespace =
-            if rpc_binder { KeystoreNamespace::VmPayload } else { KeystoreNamespace::Odsign };
         Ok(CompOsKeyService {
-            namespace,
             random: SystemRandom::new(),
             security_level: keystore_service
                 .getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT)
@@ -88,7 +78,7 @@
     }
 
     pub fn do_generate(&self) -> Result<CompOsKeyData> {
-        let key_descriptor = KeyDescriptor { nspace: self.namespace as i64, ..BLOB_KEY_DESCRIPTOR };
+        let key_descriptor = BLOB_KEY_DESCRIPTOR;
         let key_parameters =
             [PURPOSE_SIGN, ALGORITHM, PADDING, DIGEST, KEY_SIZE, EXPONENT, NO_AUTH_REQUIRED];
         let attestation_key = None;
@@ -121,11 +111,7 @@
     }
 
     pub fn do_sign(&self, key_blob: &[u8], data: &[u8]) -> Result<Vec<u8>> {
-        let key_descriptor = KeyDescriptor {
-            nspace: self.namespace as i64,
-            blob: Some(key_blob.to_vec()),
-            ..BLOB_KEY_DESCRIPTOR
-        };
+        let key_descriptor = KeyDescriptor { blob: Some(key_blob.to_vec()), ..BLOB_KEY_DESCRIPTOR };
         let operation_parameters = [PURPOSE_SIGN, ALGORITHM, PADDING, DIGEST];
         let forced = false;
 
diff --git a/compos/src/compsvc.rs b/compos/src/compsvc.rs
index 4a19030..55d9d64 100644
--- a/compos/src/compsvc.rs
+++ b/compos/src/compsvc.rs
@@ -32,8 +32,8 @@
 use compos_aidl_interface::aidl::com::android::compos::{
     CompOsKeyData::CompOsKeyData,
     CompilationResult::CompilationResult,
+    FdAnnotation::FdAnnotation,
     ICompOsService::{BnCompOsService, ICompOsService},
-    Metadata::Metadata,
 };
 use compos_aidl_interface::binder::{
     BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Status, Strong,
@@ -43,10 +43,10 @@
 const DEX2OAT_PATH: &str = "/apex/com.android.art/bin/dex2oat64";
 
 /// Constructs a binder object that implements ICompOsService.
-pub fn new_binder(rpc_binder: bool) -> Result<Strong<dyn ICompOsService>> {
+pub fn new_binder() -> Result<Strong<dyn ICompOsService>> {
     let service = CompOsService {
         dex2oat_path: PathBuf::from(DEX2OAT_PATH),
-        key_service: CompOsKeyService::new(rpc_binder)?,
+        key_service: CompOsKeyService::new()?,
         key_blob: Arc::new(RwLock::new(Vec::new())),
     };
     Ok(BnCompOsService::new_binder(service, BinderFeatures::default()))
@@ -85,14 +85,19 @@
         }
     }
 
-    fn compile(&self, args: &[String], metadata: &Metadata) -> BinderResult<CompilationResult> {
+    fn compile(
+        &self,
+        args: &[String],
+        fd_annotation: &FdAnnotation,
+    ) -> BinderResult<CompilationResult> {
         let authfs_service = get_authfs_service()?;
-        let output = compile(&self.dex2oat_path, args, authfs_service, metadata).map_err(|e| {
-            new_binder_exception(
-                ExceptionCode::SERVICE_SPECIFIC,
-                format!("Compilation failed: {}", e),
-            )
-        })?;
+        let output =
+            compile(&self.dex2oat_path, args, authfs_service, fd_annotation).map_err(|e| {
+                new_binder_exception(
+                    ExceptionCode::SERVICE_SPECIFIC,
+                    format!("Compilation failed: {}", e),
+                )
+            })?;
         match output {
             CompilerOutput::Digests { oat, vdex, image } => {
                 let key = &*self.key_blob.read().unwrap();
diff --git a/compos/src/compsvc_main.rs b/compos/src/compsvc_main.rs
index 6396556..46c8f8c 100644
--- a/compos/src/compsvc_main.rs
+++ b/compos/src/compsvc_main.rs
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-//! A tool to start a standalone compsvc server, either in the host using Binder or in a VM using
-//! RPC binder over vsock.
+//! A tool to start a standalone compsvc server that serves over RPC binder.
 
 mod common;
 mod compilation;
@@ -24,55 +23,30 @@
 mod fsverity;
 mod signer;
 
-use crate::common::{SERVICE_NAME, VSOCK_PORT};
-use anyhow::{bail, Context, Result};
+use crate::common::VSOCK_PORT;
+use anyhow::{bail, Result};
 use binder::unstable_api::AsNative;
-use compos_aidl_interface::binder::{add_service, ProcessState};
 use log::debug;
 
-struct Config {
-    rpc_binder: bool,
-}
-
-fn parse_args() -> Result<Config> {
-    #[rustfmt::skip]
-    let matches = clap::App::new("compsvc")
-        .arg(clap::Arg::with_name("rpc_binder")
-             .long("rpc-binder"))
-        .get_matches();
-
-    Ok(Config { rpc_binder: matches.is_present("rpc_binder") })
-}
-
 fn main() -> Result<()> {
     android_logger::init_once(
         android_logger::Config::default().with_tag("compsvc").with_min_level(log::Level::Debug),
     );
 
-    let config = parse_args()?;
-    let mut service = compsvc::new_binder(config.rpc_binder)?.as_binder();
-    if config.rpc_binder {
-        debug!("compsvc is starting as a rpc service.");
-        // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
-        // Plus the binder objects are threadsafe.
-        let retval = unsafe {
-            binder_rpc_unstable_bindgen::RunRpcServer(
-                service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder,
-                VSOCK_PORT,
-            )
-        };
-        if retval {
-            debug!("RPC server has shut down gracefully");
-            Ok(())
-        } else {
-            bail!("Premature termination of RPC server");
-        }
+    let mut service = compsvc::new_binder()?.as_binder();
+    debug!("compsvc is starting as a rpc service.");
+    // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
+    // Plus the binder objects are threadsafe.
+    let retval = unsafe {
+        binder_rpc_unstable_bindgen::RunRpcServer(
+            service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder,
+            VSOCK_PORT,
+        )
+    };
+    if retval {
+        debug!("RPC server has shut down gracefully");
+        Ok(())
     } else {
-        ProcessState::start_thread_pool();
-        debug!("compsvc is starting as a local service.");
-        add_service(SERVICE_NAME, service)
-            .with_context(|| format!("Failed to register service {}", SERVICE_NAME))?;
-        ProcessState::join_thread_pool();
-        bail!("Unexpected exit after join_thread_pool")
+        bail!("Premature termination of RPC server");
     }
 }
diff --git a/compos/src/pvm_exec.rs b/compos/src/pvm_exec.rs
index 99eddfc..6938370 100644
--- a/compos/src/pvm_exec.rs
+++ b/compos/src/pvm_exec.rs
@@ -29,29 +29,24 @@
 use anyhow::{bail, Context, Result};
 use binder::unstable_api::{new_spibinder, AIBinder};
 use binder::FromIBinder;
+use clap::{value_t, App, Arg};
 use log::{debug, error, warn};
 use minijail::Minijail;
 use nix::fcntl::{fcntl, FcntlArg::F_GETFD};
-use nix::sys::stat::fstat;
 use std::os::unix::io::RawFd;
 use std::path::Path;
 use std::process::exit;
 
 use compos_aidl_interface::aidl::com::android::compos::{
-    ICompOsService::ICompOsService, InputFdAnnotation::InputFdAnnotation, Metadata::Metadata,
-    OutputFdAnnotation::OutputFdAnnotation,
+    FdAnnotation::FdAnnotation, ICompOsService::ICompOsService,
 };
 use compos_aidl_interface::binder::Strong;
 
 mod common;
-use common::{SERVICE_NAME, VSOCK_PORT};
+use common::VSOCK_PORT;
 
 const FD_SERVER_BIN: &str = "/apex/com.android.virt/bin/fd_server";
 
-fn get_local_service() -> Result<Strong<dyn ICompOsService>> {
-    compos_aidl_interface::binder::get_interface(SERVICE_NAME).context("get local binder")
-}
-
 fn get_rpc_binder(cid: u32) -> Result<Strong<dyn ICompOsService>> {
     // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership can be
     // safely taken by new_spibinder.
@@ -65,23 +60,23 @@
     }
 }
 
-fn spawn_fd_server(metadata: &Metadata, debuggable: bool) -> Result<Minijail> {
+fn spawn_fd_server(fd_annotation: &FdAnnotation, debuggable: bool) -> Result<Minijail> {
     let mut inheritable_fds = if debuggable {
         vec![1, 2] // inherit/redirect stdout/stderr for debugging
     } else {
         vec![]
     };
 
-    let mut args = vec![FD_SERVER_BIN.to_string(), "--rpc-binder".to_string()];
-    for metadata in &metadata.input_fd_annotations {
+    let mut args = vec![FD_SERVER_BIN.to_string()];
+    for fd in &fd_annotation.input_fds {
         args.push("--ro-fds".to_string());
-        args.push(metadata.fd.to_string());
-        inheritable_fds.push(metadata.fd);
+        args.push(fd.to_string());
+        inheritable_fds.push(*fd);
     }
-    for metadata in &metadata.output_fd_annotations {
+    for fd in &fd_annotation.output_fds {
         args.push("--rw-fds".to_string());
-        args.push(metadata.fd.to_string());
-        inheritable_fds.push(metadata.fd);
+        args.push(fd.to_string());
+        inheritable_fds.push(*fd);
     }
 
     let jail = Minijail::new()?;
@@ -104,67 +99,49 @@
 
 struct Config {
     args: Vec<String>,
-    metadata: Metadata,
-    cid: Option<u32>,
+    fd_annotation: FdAnnotation,
+    cid: u32,
     debuggable: bool,
 }
 
 fn parse_args() -> Result<Config> {
     #[rustfmt::skip]
-    let matches = clap::App::new("pvm_exec")
-        .arg(clap::Arg::with_name("in-fd")
+    let matches = App::new("pvm_exec")
+        .arg(Arg::with_name("in-fd")
              .long("in-fd")
              .takes_value(true)
              .multiple(true)
              .use_delimiter(true))
-        .arg(clap::Arg::with_name("out-fd")
+        .arg(Arg::with_name("out-fd")
              .long("out-fd")
              .takes_value(true)
              .multiple(true)
              .use_delimiter(true))
-        .arg(clap::Arg::with_name("cid")
+        .arg(Arg::with_name("cid")
              .takes_value(true)
+             .required(true)
              .long("cid"))
-        .arg(clap::Arg::with_name("debug")
+        .arg(Arg::with_name("debug")
              .long("debug"))
-        .arg(clap::Arg::with_name("args")
+        .arg(Arg::with_name("args")
              .last(true)
              .required(true)
              .multiple(true))
         .get_matches();
 
-    let results: Result<Vec<_>> = matches
-        .values_of("in-fd")
-        .unwrap_or_default()
-        .map(|arg| {
-            let fd = parse_arg_fd(arg)?;
-            let file_size = fstat(fd)?.st_size;
-            Ok(InputFdAnnotation { fd, file_size })
-        })
-        .collect();
-    let input_fd_annotations = results?;
+    let results: Result<Vec<_>> =
+        matches.values_of("in-fd").unwrap_or_default().map(parse_arg_fd).collect();
+    let input_fds = results?;
 
-    let results: Result<Vec<_>> = matches
-        .values_of("out-fd")
-        .unwrap_or_default()
-        .map(|arg| {
-            let fd = parse_arg_fd(arg)?;
-            Ok(OutputFdAnnotation { fd })
-        })
-        .collect();
-    let output_fd_annotations = results?;
+    let results: Result<Vec<_>> =
+        matches.values_of("out-fd").unwrap_or_default().map(parse_arg_fd).collect();
+    let output_fds = results?;
 
     let args: Vec<_> = matches.values_of("args").unwrap().map(|s| s.to_string()).collect();
-    let cid =
-        if let Some(arg) = matches.value_of("cid") { Some(arg.parse::<u32>()?) } else { None };
+    let cid = value_t!(matches, "cid", u32)?;
     let debuggable = matches.is_present("debug");
 
-    Ok(Config {
-        args,
-        metadata: Metadata { input_fd_annotations, output_fd_annotations },
-        cid,
-        debuggable,
-    })
+    Ok(Config { args, fd_annotation: FdAnnotation { input_fds, output_fds }, cid, debuggable })
 }
 
 fn main() -> Result<()> {
@@ -175,10 +152,10 @@
     );
 
     // 1. Parse the command line arguments for collect execution data.
-    let Config { args, metadata, cid, debuggable } = parse_args()?;
+    let Config { args, fd_annotation, cid, debuggable } = parse_args()?;
 
     // 2. Spawn and configure a fd_server to serve remote read/write requests.
-    let fd_server_jail = spawn_fd_server(&metadata, debuggable)?;
+    let fd_server_jail = spawn_fd_server(&fd_annotation, debuggable)?;
     let fd_server_lifetime = scopeguard::guard(fd_server_jail, |fd_server_jail| {
         if let Err(e) = fd_server_jail.kill() {
             if !matches!(e, minijail::Error::Killed(_)) {
@@ -188,8 +165,8 @@
     });
 
     // 3. Send the command line args to the remote to execute.
-    let service = if let Some(cid) = cid { get_rpc_binder(cid) } else { get_local_service() }?;
-    let result = service.compile(&args, &metadata).context("Binder call failed")?;
+    let service = get_rpc_binder(cid)?;
+    let result = service.compile(&args, &fd_annotation).context("Binder call failed")?;
 
     // TODO: store/use the signature
     debug!(
diff --git a/idsig/Android.bp b/idsig/Android.bp
index c5fc161..3f70a64 100644
--- a/idsig/Android.bp
+++ b/idsig/Android.bp
@@ -15,11 +15,6 @@
         "libnum_traits",
     ],
     proc_macros: ["libnum_derive"],
-    multilib: {
-        lib32: {
-            enabled: false,
-        },
-    },
 }
 
 rust_library {
diff --git a/virtualizationservice/src/main.rs b/virtualizationservice/src/main.rs
index 018be7b..8628c01 100644
--- a/virtualizationservice/src/main.rs
+++ b/virtualizationservice/src/main.rs
@@ -65,3 +65,12 @@
     }
     Ok(())
 }
+
+#[cfg(test)]
+mod tests {
+    /// We need to have at least one test to avoid errors running the test suite, so this is a
+    /// placeholder until we have real tests.
+    #[test]
+    #[ignore]
+    fn placeholder() {}
+}