Merge "Create SDK of CompOS module"
diff --git a/authfs/fd_server/src/aidl.rs b/authfs/fd_server/src/aidl.rs
new file mode 100644
index 0000000..ed3a0ea
--- /dev/null
+++ b/authfs/fd_server/src/aidl.rs
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+use anyhow::Result;
+use log::error;
+use std::cmp::min;
+use std::collections::BTreeMap;
+use std::convert::TryInto;
+use std::fs::File;
+use std::io;
+use std::os::unix::fs::FileExt;
+use std::os::unix::io::AsRawFd;
+
+use crate::fsverity;
+use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService::{
+    BnVirtFdService, IVirtFdService, ERROR_FILE_TOO_LARGE, ERROR_IO, ERROR_UNKNOWN_FD,
+    MAX_REQUESTING_DATA,
+};
+use authfs_aidl_interface::binder::{
+    BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Status, StatusCode, Strong,
+};
+use binder_common::new_binder_exception;
+
+fn validate_and_cast_offset(offset: i64) -> Result<u64, Status> {
+    offset.try_into().map_err(|_| {
+        new_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT, format!("Invalid offset: {}", offset))
+    })
+}
+
+fn validate_and_cast_size(size: i32) -> Result<usize, Status> {
+    if size > MAX_REQUESTING_DATA {
+        Err(new_binder_exception(
+            ExceptionCode::ILLEGAL_ARGUMENT,
+            format!("Unexpectedly large size: {}", size),
+        ))
+    } else {
+        size.try_into().map_err(|_| {
+            new_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT, format!("Invalid size: {}", size))
+        })
+    }
+}
+
+/// Configuration of a file descriptor to be served/exposed/shared.
+pub enum FdConfig {
+    /// A read-only file to serve by this server. The file is supposed to be verifiable with the
+    /// associated fs-verity metadata.
+    Readonly {
+        /// The file to read from. fs-verity metadata can be retrieved from this file's FD.
+        file: File,
+
+        /// Alternative Merkle tree stored in another file.
+        alt_merkle_tree: Option<File>,
+
+        /// Alternative signature stored in another file.
+        alt_signature: Option<File>,
+    },
+
+    /// A readable/writable file to serve by this server. This backing file should just be a
+    /// regular file and does not have any specific property.
+    ReadWrite(File),
+}
+
+pub struct FdService {
+    /// A pool of opened files, may be readonly or read-writable.
+    fd_pool: BTreeMap<i32, FdConfig>,
+}
+
+impl FdService {
+    pub fn new_binder(fd_pool: BTreeMap<i32, FdConfig>) -> Strong<dyn IVirtFdService> {
+        BnVirtFdService::new_binder(FdService { fd_pool }, BinderFeatures::default())
+    }
+
+    fn get_file_config(&self, id: i32) -> BinderResult<&FdConfig> {
+        self.fd_pool.get(&id).ok_or_else(|| Status::from(ERROR_UNKNOWN_FD))
+    }
+}
+
+impl Interface for FdService {}
+
+impl IVirtFdService for FdService {
+    fn readFile(&self, id: i32, offset: i64, size: i32) -> BinderResult<Vec<u8>> {
+        let size: usize = validate_and_cast_size(size)?;
+        let offset: u64 = validate_and_cast_offset(offset)?;
+
+        match self.get_file_config(id)? {
+            FdConfig::Readonly { file, .. } | FdConfig::ReadWrite(file) => {
+                read_into_buf(file, size, offset).map_err(|e| {
+                    error!("readFile: read error: {}", e);
+                    Status::from(ERROR_IO)
+                })
+            }
+        }
+    }
+
+    fn readFsverityMerkleTree(&self, id: i32, offset: i64, size: i32) -> BinderResult<Vec<u8>> {
+        let size: usize = validate_and_cast_size(size)?;
+        let offset: u64 = validate_and_cast_offset(offset)?;
+
+        match &self.get_file_config(id)? {
+            FdConfig::Readonly { file, alt_merkle_tree, .. } => {
+                if let Some(tree_file) = &alt_merkle_tree {
+                    read_into_buf(tree_file, size, offset).map_err(|e| {
+                        error!("readFsverityMerkleTree: read error: {}", e);
+                        Status::from(ERROR_IO)
+                    })
+                } else {
+                    let mut buf = vec![0; size];
+                    let s = fsverity::read_merkle_tree(file.as_raw_fd(), offset, &mut buf)
+                        .map_err(|e| {
+                            error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e);
+                            Status::from(e.raw_os_error().unwrap_or(ERROR_IO))
+                        })?;
+                    debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked");
+                    buf.truncate(s);
+                    Ok(buf)
+                }
+            }
+            FdConfig::ReadWrite(_file) => {
+                // For a writable file, Merkle tree is not expected to be served since Auth FS
+                // doesn't trust it anyway. Auth FS may keep the Merkle tree privately for its own
+                // use.
+                Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Unsupported"))
+            }
+        }
+    }
+
+    fn readFsveritySignature(&self, id: i32) -> BinderResult<Vec<u8>> {
+        match &self.get_file_config(id)? {
+            FdConfig::Readonly { file, alt_signature, .. } => {
+                if let Some(sig_file) = &alt_signature {
+                    // Supposedly big enough buffer size to store signature.
+                    let size = MAX_REQUESTING_DATA as usize;
+                    let offset = 0;
+                    read_into_buf(sig_file, size, offset).map_err(|e| {
+                        error!("readFsveritySignature: read error: {}", e);
+                        Status::from(ERROR_IO)
+                    })
+                } else {
+                    let mut buf = vec![0; MAX_REQUESTING_DATA as usize];
+                    let s = fsverity::read_signature(file.as_raw_fd(), &mut buf).map_err(|e| {
+                        error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e);
+                        Status::from(e.raw_os_error().unwrap_or(ERROR_IO))
+                    })?;
+                    debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked");
+                    buf.truncate(s);
+                    Ok(buf)
+                }
+            }
+            FdConfig::ReadWrite(_file) => {
+                // There is no signature for a writable file.
+                Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Unsupported"))
+            }
+        }
+    }
+
+    fn writeFile(&self, id: i32, buf: &[u8], offset: i64) -> BinderResult<i32> {
+        match &self.get_file_config(id)? {
+            FdConfig::Readonly { .. } => Err(StatusCode::INVALID_OPERATION.into()),
+            FdConfig::ReadWrite(file) => {
+                let offset: u64 = offset.try_into().map_err(|_| {
+                    new_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT, "Invalid offset")
+                })?;
+                // Check buffer size just to make `as i32` safe below.
+                if buf.len() > i32::MAX as usize {
+                    return Err(new_binder_exception(
+                        ExceptionCode::ILLEGAL_ARGUMENT,
+                        "Buffer size is too big",
+                    ));
+                }
+                Ok(file.write_at(buf, offset).map_err(|e| {
+                    error!("writeFile: write error: {}", e);
+                    Status::from(ERROR_IO)
+                })? as i32)
+            }
+        }
+    }
+
+    fn resize(&self, id: i32, size: i64) -> BinderResult<()> {
+        match &self.get_file_config(id)? {
+            FdConfig::Readonly { .. } => Err(StatusCode::INVALID_OPERATION.into()),
+            FdConfig::ReadWrite(file) => {
+                if size < 0 {
+                    return Err(new_binder_exception(
+                        ExceptionCode::ILLEGAL_ARGUMENT,
+                        "Invalid size to resize to",
+                    ));
+                }
+                file.set_len(size as u64).map_err(|e| {
+                    error!("resize: set_len error: {}", e);
+                    Status::from(ERROR_IO)
+                })
+            }
+        }
+    }
+
+    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>> {
+    let remaining = file.metadata()?.len().saturating_sub(offset);
+    let buf_size = min(remaining, max_size as u64) as usize;
+    let mut buf = vec![0; buf_size];
+    file.read_exact_at(&mut buf, offset)?;
+    Ok(buf)
+}
diff --git a/authfs/fd_server/src/main.rs b/authfs/fd_server/src/main.rs
index 395e2e9..3413ce6 100644
--- a/authfs/fd_server/src/main.rs
+++ b/authfs/fd_server/src/main.rs
@@ -22,235 +22,20 @@
 //! 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.
 
+mod aidl;
 mod fsverity;
 
 use anyhow::{bail, Result};
 use binder_common::rpc_server::run_rpc_server;
-use log::{debug, error};
-use std::cmp::min;
+use log::debug;
 use std::collections::BTreeMap;
-use std::convert::TryInto;
 use std::fs::File;
-use std::io;
-use std::os::unix::fs::FileExt;
-use std::os::unix::io::{AsRawFd, FromRawFd};
+use std::os::unix::io::FromRawFd;
 
-use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService::{
-    BnVirtFdService, IVirtFdService, ERROR_FILE_TOO_LARGE, ERROR_IO, ERROR_UNKNOWN_FD,
-    MAX_REQUESTING_DATA,
-};
-use authfs_aidl_interface::binder::{
-    BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Status, StatusCode, Strong,
-};
-use binder_common::new_binder_exception;
+use aidl::{FdConfig, FdService};
 
 const RPC_SERVICE_PORT: u32 = 3264; // TODO: support dynamic port for multiple fd_server instances
 
-fn validate_and_cast_offset(offset: i64) -> Result<u64, Status> {
-    offset.try_into().map_err(|_| {
-        new_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT, format!("Invalid offset: {}", offset))
-    })
-}
-
-fn validate_and_cast_size(size: i32) -> Result<usize, Status> {
-    if size > MAX_REQUESTING_DATA {
-        Err(new_binder_exception(
-            ExceptionCode::ILLEGAL_ARGUMENT,
-            format!("Unexpectedly large size: {}", size),
-        ))
-    } else {
-        size.try_into().map_err(|_| {
-            new_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT, format!("Invalid size: {}", size))
-        })
-    }
-}
-
-/// Configuration of a file descriptor to be served/exposed/shared.
-enum FdConfig {
-    /// A read-only file to serve by this server. The file is supposed to be verifiable with the
-    /// associated fs-verity metadata.
-    Readonly {
-        /// The file to read from. fs-verity metadata can be retrieved from this file's FD.
-        file: File,
-
-        /// Alternative Merkle tree stored in another file.
-        alt_merkle_tree: Option<File>,
-
-        /// Alternative signature stored in another file.
-        alt_signature: Option<File>,
-    },
-
-    /// A readable/writable file to serve by this server. This backing file should just be a
-    /// regular file and does not have any specific property.
-    ReadWrite(File),
-}
-
-struct FdService {
-    /// A pool of opened files, may be readonly or read-writable.
-    fd_pool: BTreeMap<i32, FdConfig>,
-}
-
-impl FdService {
-    pub fn new_binder(fd_pool: BTreeMap<i32, FdConfig>) -> Strong<dyn IVirtFdService> {
-        BnVirtFdService::new_binder(FdService { fd_pool }, BinderFeatures::default())
-    }
-
-    fn get_file_config(&self, id: i32) -> BinderResult<&FdConfig> {
-        self.fd_pool.get(&id).ok_or_else(|| Status::from(ERROR_UNKNOWN_FD))
-    }
-}
-
-impl Interface for FdService {}
-
-impl IVirtFdService for FdService {
-    fn readFile(&self, id: i32, offset: i64, size: i32) -> BinderResult<Vec<u8>> {
-        let size: usize = validate_and_cast_size(size)?;
-        let offset: u64 = validate_and_cast_offset(offset)?;
-
-        match self.get_file_config(id)? {
-            FdConfig::Readonly { file, .. } | FdConfig::ReadWrite(file) => {
-                read_into_buf(file, size, offset).map_err(|e| {
-                    error!("readFile: read error: {}", e);
-                    Status::from(ERROR_IO)
-                })
-            }
-        }
-    }
-
-    fn readFsverityMerkleTree(&self, id: i32, offset: i64, size: i32) -> BinderResult<Vec<u8>> {
-        let size: usize = validate_and_cast_size(size)?;
-        let offset: u64 = validate_and_cast_offset(offset)?;
-
-        match &self.get_file_config(id)? {
-            FdConfig::Readonly { file, alt_merkle_tree, .. } => {
-                if let Some(tree_file) = &alt_merkle_tree {
-                    read_into_buf(tree_file, size, offset).map_err(|e| {
-                        error!("readFsverityMerkleTree: read error: {}", e);
-                        Status::from(ERROR_IO)
-                    })
-                } else {
-                    let mut buf = vec![0; size];
-                    let s = fsverity::read_merkle_tree(file.as_raw_fd(), offset, &mut buf)
-                        .map_err(|e| {
-                            error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e);
-                            Status::from(e.raw_os_error().unwrap_or(ERROR_IO))
-                        })?;
-                    debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked");
-                    buf.truncate(s);
-                    Ok(buf)
-                }
-            }
-            FdConfig::ReadWrite(_file) => {
-                // For a writable file, Merkle tree is not expected to be served since Auth FS
-                // doesn't trust it anyway. Auth FS may keep the Merkle tree privately for its own
-                // use.
-                Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Unsupported"))
-            }
-        }
-    }
-
-    fn readFsveritySignature(&self, id: i32) -> BinderResult<Vec<u8>> {
-        match &self.get_file_config(id)? {
-            FdConfig::Readonly { file, alt_signature, .. } => {
-                if let Some(sig_file) = &alt_signature {
-                    // Supposedly big enough buffer size to store signature.
-                    let size = MAX_REQUESTING_DATA as usize;
-                    let offset = 0;
-                    read_into_buf(sig_file, size, offset).map_err(|e| {
-                        error!("readFsveritySignature: read error: {}", e);
-                        Status::from(ERROR_IO)
-                    })
-                } else {
-                    let mut buf = vec![0; MAX_REQUESTING_DATA as usize];
-                    let s = fsverity::read_signature(file.as_raw_fd(), &mut buf).map_err(|e| {
-                        error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e);
-                        Status::from(e.raw_os_error().unwrap_or(ERROR_IO))
-                    })?;
-                    debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked");
-                    buf.truncate(s);
-                    Ok(buf)
-                }
-            }
-            FdConfig::ReadWrite(_file) => {
-                // There is no signature for a writable file.
-                Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Unsupported"))
-            }
-        }
-    }
-
-    fn writeFile(&self, id: i32, buf: &[u8], offset: i64) -> BinderResult<i32> {
-        match &self.get_file_config(id)? {
-            FdConfig::Readonly { .. } => Err(StatusCode::INVALID_OPERATION.into()),
-            FdConfig::ReadWrite(file) => {
-                let offset: u64 = offset.try_into().map_err(|_| {
-                    new_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT, "Invalid offset")
-                })?;
-                // Check buffer size just to make `as i32` safe below.
-                if buf.len() > i32::MAX as usize {
-                    return Err(new_binder_exception(
-                        ExceptionCode::ILLEGAL_ARGUMENT,
-                        "Buffer size is too big",
-                    ));
-                }
-                Ok(file.write_at(buf, offset).map_err(|e| {
-                    error!("writeFile: write error: {}", e);
-                    Status::from(ERROR_IO)
-                })? as i32)
-            }
-        }
-    }
-
-    fn resize(&self, id: i32, size: i64) -> BinderResult<()> {
-        match &self.get_file_config(id)? {
-            FdConfig::Readonly { .. } => Err(StatusCode::INVALID_OPERATION.into()),
-            FdConfig::ReadWrite(file) => {
-                if size < 0 {
-                    return Err(new_binder_exception(
-                        ExceptionCode::ILLEGAL_ARGUMENT,
-                        "Invalid size to resize to",
-                    ));
-                }
-                file.set_len(size as u64).map_err(|e| {
-                    error!("resize: set_len error: {}", e);
-                    Status::from(ERROR_IO)
-                })
-            }
-        }
-    }
-
-    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>> {
-    let remaining = file.metadata()?.len().saturating_sub(offset);
-    let buf_size = min(remaining, max_size as u64) as usize;
-    let mut buf = vec![0; buf_size];
-    file.read_exact_at(&mut buf, offset)?;
-    Ok(buf)
-}
-
 fn is_fd_valid(fd: i32) -> bool {
     // SAFETY: a query-only syscall
     let retval = unsafe { libc::fcntl(fd, libc::F_GETFD) };
diff --git a/authfs/src/file.rs b/authfs/src/file.rs
index 947b59f..404e3a5 100644
--- a/authfs/src/file.rs
+++ b/authfs/src/file.rs
@@ -1,7 +1,5 @@
-mod local_file;
 mod remote_file;
 
-pub use local_file::LocalFileReader;
 pub use remote_file::{RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader};
 
 use binder::unstable_api::{new_spibinder, AIBinder};
diff --git a/authfs/src/file/local_file.rs b/authfs/src/file/local_file.rs
deleted file mode 100644
index 13c954f..0000000
--- a/authfs/src/file/local_file.rs
+++ /dev/null
@@ -1,112 +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.
- */
-
-use std::cmp::min;
-use std::fs::File;
-use std::io;
-use std::os::unix::fs::FileExt;
-
-use super::{ChunkBuffer, ReadByChunk};
-use crate::common::CHUNK_SIZE;
-
-/// A read-only file that can be read by chunks.
-pub struct LocalFileReader {
-    file: File,
-    size: u64,
-}
-
-impl LocalFileReader {
-    /// Creates a `LocalFileReader` to read from for the specified `path`.
-    pub fn new(file: File) -> io::Result<LocalFileReader> {
-        let size = file.metadata()?.len();
-        Ok(LocalFileReader { file, size })
-    }
-
-    pub fn len(&self) -> u64 {
-        self.size
-    }
-}
-
-impl ReadByChunk for LocalFileReader {
-    fn read_chunk(&self, chunk_index: u64, buf: &mut ChunkBuffer) -> io::Result<usize> {
-        let start = chunk_index * CHUNK_SIZE;
-        if start >= self.size {
-            return Ok(0);
-        }
-        let end = min(self.size, start + CHUNK_SIZE);
-        let read_size = (end - start) as usize;
-        debug_assert!(read_size <= buf.len());
-        self.file.read_exact_at(&mut buf[..read_size], start)?;
-        Ok(read_size)
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use std::env::temp_dir;
-
-    #[test]
-    fn test_read_4k_file() -> io::Result<()> {
-        let file_reader = LocalFileReader::new(File::open("testdata/input.4k")?)?;
-        let mut buf = [0u8; 4096];
-        let size = file_reader.read_chunk(0, &mut buf)?;
-        assert_eq!(size, buf.len());
-        Ok(())
-    }
-
-    #[test]
-    fn test_read_4k1_file() -> io::Result<()> {
-        let file_reader = LocalFileReader::new(File::open("testdata/input.4k1")?)?;
-        let mut buf = [0u8; 4096];
-        let size = file_reader.read_chunk(0, &mut buf)?;
-        assert_eq!(size, buf.len());
-        let size = file_reader.read_chunk(1, &mut buf)?;
-        assert_eq!(size, 1);
-        Ok(())
-    }
-
-    #[test]
-    fn test_read_4m_file() -> io::Result<()> {
-        let file_reader = LocalFileReader::new(File::open("testdata/input.4m")?)?;
-        for index in 0..file_reader.len() / 4096 {
-            let mut buf = [0u8; 4096];
-            let size = file_reader.read_chunk(index, &mut buf)?;
-            assert_eq!(size, buf.len());
-        }
-        Ok(())
-    }
-
-    #[test]
-    fn test_read_beyond_file_size() -> io::Result<()> {
-        let file_reader = LocalFileReader::new(File::open("testdata/input.4k").unwrap()).unwrap();
-        let mut buf = [0u8; 4096];
-        let size = file_reader.read_chunk(1u64, &mut buf)?;
-        assert_eq!(size, 0);
-        Ok(())
-    }
-
-    #[test]
-    fn test_read_empty_file() -> io::Result<()> {
-        let mut temp_file = temp_dir();
-        temp_file.push("authfs_test_empty_file");
-        let file_reader = LocalFileReader::new(File::create(temp_file).unwrap()).unwrap();
-        let mut buf = [0u8; 4096];
-        let size = file_reader.read_chunk(0, &mut buf)?;
-        assert_eq!(size, 0);
-        Ok(())
-    }
-}
diff --git a/authfs/src/fsverity/verifier.rs b/authfs/src/fsverity/verifier.rs
index 1f21b13..4a18c6a 100644
--- a/authfs/src/fsverity/verifier.rs
+++ b/authfs/src/fsverity/verifier.rs
@@ -173,10 +173,42 @@
 mod tests {
     use super::*;
     use crate::auth::FakeAuthenticator;
-    use crate::file::{LocalFileReader, ReadByChunk};
+    use crate::file::ReadByChunk;
     use anyhow::Result;
+    use std::cmp::min;
     use std::fs::{self, File};
     use std::io::Read;
+    use std::os::unix::fs::FileExt;
+
+    struct LocalFileReader {
+        file: File,
+        size: u64,
+    }
+
+    impl LocalFileReader {
+        fn new(file: File) -> io::Result<LocalFileReader> {
+            let size = file.metadata()?.len();
+            Ok(LocalFileReader { file, size })
+        }
+
+        fn len(&self) -> u64 {
+            self.size
+        }
+    }
+
+    impl ReadByChunk for LocalFileReader {
+        fn read_chunk(&self, chunk_index: u64, buf: &mut ChunkBuffer) -> io::Result<usize> {
+            let start = chunk_index * CHUNK_SIZE;
+            if start >= self.size {
+                return Ok(0);
+            }
+            let end = min(self.size, start + CHUNK_SIZE);
+            let read_size = (end - start) as usize;
+            debug_assert!(read_size <= buf.len());
+            self.file.read_exact_at(&mut buf[..read_size], start)?;
+            Ok(read_size)
+        }
+    }
 
     type LocalVerifiedFileReader = VerifiedFileReader<LocalFileReader, LocalFileReader>;
 
diff --git a/authfs/src/fusefs.rs b/authfs/src/fusefs.rs
index 6bdb498..d54b5be 100644
--- a/authfs/src/fusefs.rs
+++ b/authfs/src/fusefs.rs
@@ -35,8 +35,7 @@
 
 use crate::common::{divide_roundup, ChunkedSizeIter, CHUNK_SIZE};
 use crate::file::{
-    LocalFileReader, RandomWrite, ReadByChunk, RemoteFileEditor, RemoteFileReader,
-    RemoteMerkleTreeReader,
+    RandomWrite, ReadByChunk, RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader,
 };
 use crate::fsverity::{VerifiedFileEditor, VerifiedFileReader};
 
@@ -48,24 +47,16 @@
 /// `FileConfig` defines the file type supported by AuthFS.
 pub enum FileConfig {
     /// A file type that is verified against fs-verity signature (thus read-only). The file is
-    /// backed by a local file. Debug only.
-    LocalVerifiedReadonly {
-        reader: VerifiedFileReader<LocalFileReader, LocalFileReader>,
-        file_size: u64,
-    },
-    /// A file type that is a read-only passthrough from a local file. Debug only.
-    LocalUnverifiedReadonly { reader: LocalFileReader, file_size: u64 },
-    /// A file type that is verified against fs-verity signature (thus read-only). The file is
     /// served from a remote server.
-    RemoteVerifiedReadonly {
+    VerifiedReadonly {
         reader: VerifiedFileReader<RemoteFileReader, RemoteMerkleTreeReader>,
         file_size: u64,
     },
     /// A file type that is a read-only passthrough from a file on a remote serrver.
-    RemoteUnverifiedReadonly { reader: RemoteFileReader, file_size: u64 },
+    UnverifiedReadonly { reader: RemoteFileReader, file_size: u64 },
     /// A file type that is initially empty, and the content is stored on a remote server. File
     /// integrity is guaranteed with private Merkle tree.
-    RemoteVerifiedNew { editor: VerifiedFileEditor<RemoteFileEditor> },
+    VerifiedNew { editor: VerifiedFileEditor<RemoteFileEditor> },
 }
 
 struct AuthFs {
@@ -207,13 +198,11 @@
         // be static.
         let inode = num.parse::<Inode>().map_err(|_| io::Error::from_raw_os_error(libc::ENOENT))?;
         let st = match self.get_file_config(&inode)? {
-            FileConfig::LocalVerifiedReadonly { file_size, .. }
-            | FileConfig::LocalUnverifiedReadonly { file_size, .. }
-            | FileConfig::RemoteUnverifiedReadonly { file_size, .. }
-            | FileConfig::RemoteVerifiedReadonly { file_size, .. } => {
+            FileConfig::UnverifiedReadonly { file_size, .. }
+            | FileConfig::VerifiedReadonly { file_size, .. } => {
                 create_stat(inode, *file_size, FileMode::ReadOnly)?
             }
-            FileConfig::RemoteVerifiedNew { editor } => {
+            FileConfig::VerifiedNew { editor } => {
                 create_stat(inode, editor.size(), FileMode::ReadWrite)?
             }
         };
@@ -234,13 +223,11 @@
     ) -> io::Result<(libc::stat64, Duration)> {
         Ok((
             match self.get_file_config(&inode)? {
-                FileConfig::LocalVerifiedReadonly { file_size, .. }
-                | FileConfig::LocalUnverifiedReadonly { file_size, .. }
-                | FileConfig::RemoteUnverifiedReadonly { file_size, .. }
-                | FileConfig::RemoteVerifiedReadonly { file_size, .. } => {
+                FileConfig::UnverifiedReadonly { file_size, .. }
+                | FileConfig::VerifiedReadonly { file_size, .. } => {
                     create_stat(inode, *file_size, FileMode::ReadOnly)?
                 }
-                FileConfig::RemoteVerifiedNew { editor } => {
+                FileConfig::VerifiedNew { editor } => {
                     create_stat(inode, editor.size(), FileMode::ReadWrite)?
                 }
             },
@@ -257,13 +244,10 @@
         // Since file handle is not really used in later operations (which use Inode directly),
         // return None as the handle.
         match self.get_file_config(&inode)? {
-            FileConfig::LocalVerifiedReadonly { .. }
-            | FileConfig::LocalUnverifiedReadonly { .. }
-            | FileConfig::RemoteVerifiedReadonly { .. }
-            | FileConfig::RemoteUnverifiedReadonly { .. } => {
+            FileConfig::VerifiedReadonly { .. } | FileConfig::UnverifiedReadonly { .. } => {
                 check_access_mode(flags, libc::O_RDONLY)?;
             }
-            FileConfig::RemoteVerifiedNew { .. } => {
+            FileConfig::VerifiedNew { .. } => {
                 // No need to check access modes since all the modes are allowed to the
                 // read-writable file.
             }
@@ -285,19 +269,13 @@
         _flags: u32,
     ) -> io::Result<usize> {
         match self.get_file_config(&inode)? {
-            FileConfig::LocalVerifiedReadonly { reader, file_size } => {
+            FileConfig::VerifiedReadonly { reader, file_size } => {
                 read_chunks(w, reader, *file_size, offset, size)
             }
-            FileConfig::LocalUnverifiedReadonly { reader, file_size } => {
+            FileConfig::UnverifiedReadonly { reader, file_size } => {
                 read_chunks(w, reader, *file_size, offset, size)
             }
-            FileConfig::RemoteVerifiedReadonly { reader, file_size } => {
-                read_chunks(w, reader, *file_size, offset, size)
-            }
-            FileConfig::RemoteUnverifiedReadonly { reader, file_size } => {
-                read_chunks(w, reader, *file_size, offset, size)
-            }
-            FileConfig::RemoteVerifiedNew { editor } => {
+            FileConfig::VerifiedNew { editor } => {
                 // Note that with FsOptions::WRITEBACK_CACHE, it's possible for the kernel to
                 // request a read even if the file is open with O_WRONLY.
                 read_chunks(w, editor, editor.size(), offset, size)
@@ -318,7 +296,7 @@
         _flags: u32,
     ) -> io::Result<usize> {
         match self.get_file_config(&inode)? {
-            FileConfig::RemoteVerifiedNew { editor } => {
+            FileConfig::VerifiedNew { editor } => {
                 let mut buf = vec![0; size as usize];
                 r.read_exact(&mut buf)?;
                 editor.write_at(&buf, offset)
@@ -336,7 +314,7 @@
         valid: SetattrValid,
     ) -> io::Result<(libc::stat64, Duration)> {
         match self.get_file_config(&inode)? {
-            FileConfig::RemoteVerifiedNew { editor } => {
+            FileConfig::VerifiedNew { editor } => {
                 // Initialize the default stat.
                 let mut new_attr = create_stat(inode, editor.size(), FileMode::ReadWrite)?;
                 // `valid` indicates what fields in `attr` are valid. Update to return correctly.
@@ -383,7 +361,7 @@
         size: u32,
     ) -> io::Result<GetxattrReply> {
         match self.get_file_config(&inode)? {
-            FileConfig::RemoteVerifiedNew { editor } => {
+            FileConfig::VerifiedNew { editor } => {
                 // FUSE ioctl is limited, thus we can't implement fs-verity ioctls without a kernel
                 // change (see b/196635431). Until it's possible, use xattr to expose what we need
                 // as an authfs specific API.
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index ecb0e68..a6956e2 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -22,18 +22,16 @@
 //! each read of file block can be verified individually only when needed.
 //!
 //! AuthFS only serve files that are specifically configured. A file configuration may include the
-//! source (e.g. local file or remote file server), verification method (e.g. certificate for
-//! fs-verity verification, or no verification if expected to mount over dm-verity), and file ID.
-//! Regardless of the actual file name, the exposed file names through AuthFS are currently integer,
-//! e.g. /mountpoint/42.
+//! source (e.g. remote file server), verification method (e.g. certificate for fs-verity
+//! verification, or no verification if expected to mount over dm-verity), and file ID. Regardless
+//! of the actual file name, the exposed file names through AuthFS are currently integer, e.g.
+//! /mountpoint/42.
 
 use anyhow::{bail, Context, Result};
 use log::error;
 use std::collections::BTreeMap;
 use std::convert::TryInto;
-use std::fs::File;
-use std::io::Read;
-use std::path::{Path, PathBuf};
+use std::path::PathBuf;
 use structopt::StructOpt;
 
 mod auth;
@@ -44,7 +42,7 @@
 mod fusefs;
 
 use auth::FakeAuthenticator;
-use file::{LocalFileReader, RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader};
+use file::{RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader};
 use fsverity::{VerifiedFileEditor, VerifiedFileReader};
 use fusefs::{FileConfig, Inode};
 
@@ -83,27 +81,11 @@
     #[structopt(long, parse(try_from_str = parse_remote_new_rw_file_option))]
     remote_new_rw_file: Vec<OptionRemoteRwFile>,
 
-    /// Debug only. A read-only local file with integrity check. Can be multiple.
-    #[structopt(long, parse(try_from_str = parse_local_file_ro_option))]
-    local_ro_file: Vec<OptionLocalFileRo>,
-
-    /// Debug only. A read-only local file without integrity check. Can be multiple.
-    #[structopt(long, parse(try_from_str = parse_local_ro_file_unverified_ro_option))]
-    local_ro_file_unverified: Vec<OptionLocalRoFileUnverified>,
-
     /// Enable debugging features.
     #[structopt(long)]
     debug: bool,
 }
 
-impl Args {
-    fn has_remote_files(&self) -> bool {
-        !self.remote_ro_file.is_empty()
-            || !self.remote_ro_file_unverified.is_empty()
-            || !self.remote_new_rw_file.is_empty()
-    }
-}
-
 struct OptionRemoteRoFile {
     ino: Inode,
 
@@ -129,30 +111,6 @@
     remote_id: i32,
 }
 
-struct OptionLocalFileRo {
-    ino: Inode,
-
-    /// Local path of the backing file.
-    file_path: PathBuf,
-
-    /// Local path of the backing file's fs-verity Merkle tree dump.
-    merkle_tree_dump_path: PathBuf,
-
-    /// Local path of fs-verity signature for the backing file.
-    signature_path: PathBuf,
-
-    /// Certificate to verify the authenticity of the file's fs-verity signature.
-    /// TODO(170494765): Implement PKCS#7 signature verification.
-    _certificate_path: PathBuf,
-}
-
-struct OptionLocalRoFileUnverified {
-    ino: Inode,
-
-    /// Local path of the backing file.
-    file_path: PathBuf,
-}
-
 fn parse_remote_ro_file_option(option: &str) -> Result<OptionRemoteRoFile> {
     let strs: Vec<&str> = option.split(':').collect();
     if strs.len() != 3 {
@@ -187,31 +145,6 @@
     })
 }
 
-fn parse_local_file_ro_option(option: &str) -> Result<OptionLocalFileRo> {
-    let strs: Vec<&str> = option.split(':').collect();
-    if strs.len() != 5 {
-        bail!("Invalid option: {}", option);
-    }
-    Ok(OptionLocalFileRo {
-        ino: strs[0].parse::<Inode>()?,
-        file_path: PathBuf::from(strs[1]),
-        merkle_tree_dump_path: PathBuf::from(strs[2]),
-        signature_path: PathBuf::from(strs[3]),
-        _certificate_path: PathBuf::from(strs[4]),
-    })
-}
-
-fn parse_local_ro_file_unverified_ro_option(option: &str) -> Result<OptionLocalRoFileUnverified> {
-    let strs: Vec<&str> = option.split(':').collect();
-    if strs.len() != 2 {
-        bail!("Invalid option: {}", option);
-    }
-    Ok(OptionLocalRoFileUnverified {
-        ino: strs[0].parse::<Inode>()?,
-        file_path: PathBuf::from(strs[1]),
-    })
-}
-
 fn new_config_remote_verified_file(
     service: file::VirtFdService,
     remote_id: i32,
@@ -220,7 +153,7 @@
     let signature = service.readFsveritySignature(remote_id).context("Failed to read signature")?;
 
     let authenticator = FakeAuthenticator::always_succeed();
-    Ok(FileConfig::RemoteVerifiedReadonly {
+    Ok(FileConfig::VerifiedReadonly {
         reader: VerifiedFileReader::new(
             &authenticator,
             RemoteFileReader::new(service.clone(), remote_id),
@@ -238,30 +171,7 @@
     file_size: u64,
 ) -> Result<FileConfig> {
     let reader = RemoteFileReader::new(service, remote_id);
-    Ok(FileConfig::RemoteUnverifiedReadonly { reader, file_size })
-}
-
-fn new_config_local_ro_file(
-    protected_file: &Path,
-    merkle_tree_dump: &Path,
-    signature: &Path,
-) -> Result<FileConfig> {
-    let file = File::open(&protected_file)?;
-    let file_size = file.metadata()?.len();
-    let file_reader = LocalFileReader::new(file)?;
-    let merkle_tree_reader = LocalFileReader::new(File::open(merkle_tree_dump)?)?;
-    let authenticator = FakeAuthenticator::always_succeed();
-    let mut sig = Vec::new();
-    let _ = File::open(signature)?.read_to_end(&mut sig)?;
-    let reader =
-        VerifiedFileReader::new(&authenticator, file_reader, file_size, sig, merkle_tree_reader)?;
-    Ok(FileConfig::LocalVerifiedReadonly { reader, file_size })
-}
-
-fn new_config_local_ro_file_unverified(file_path: &Path) -> Result<FileConfig> {
-    let reader = LocalFileReader::new(File::open(file_path)?)?;
-    let file_size = reader.len();
-    Ok(FileConfig::LocalUnverifiedReadonly { reader, file_size })
+    Ok(FileConfig::UnverifiedReadonly { reader, file_size })
 }
 
 fn new_config_remote_new_verified_file(
@@ -269,58 +179,41 @@
     remote_id: i32,
 ) -> Result<FileConfig> {
     let remote_file = RemoteFileEditor::new(service, remote_id);
-    Ok(FileConfig::RemoteVerifiedNew { editor: VerifiedFileEditor::new(remote_file) })
+    Ok(FileConfig::VerifiedNew { editor: VerifiedFileEditor::new(remote_file) })
 }
 
 fn prepare_file_pool(args: &Args) -> Result<BTreeMap<Inode, FileConfig>> {
     let mut file_pool = BTreeMap::new();
 
-    if args.has_remote_files() {
-        let service = file::get_rpc_binder_service(args.cid)?;
+    let service = file::get_rpc_binder_service(args.cid)?;
 
-        for config in &args.remote_ro_file {
-            file_pool.insert(
-                config.ino,
-                new_config_remote_verified_file(
-                    service.clone(),
-                    config.remote_id,
-                    service.getFileSize(config.remote_id)?.try_into()?,
-                )?,
-            );
-        }
-
-        for config in &args.remote_ro_file_unverified {
-            file_pool.insert(
-                config.ino,
-                new_config_remote_unverified_file(
-                    service.clone(),
-                    config.remote_id,
-                    service.getFileSize(config.remote_id)?.try_into()?,
-                )?,
-            );
-        }
-
-        for config in &args.remote_new_rw_file {
-            file_pool.insert(
-                config.ino,
-                new_config_remote_new_verified_file(service.clone(), config.remote_id)?,
-            );
-        }
-    }
-
-    for config in &args.local_ro_file {
+    for config in &args.remote_ro_file {
         file_pool.insert(
             config.ino,
-            new_config_local_ro_file(
-                &config.file_path,
-                &config.merkle_tree_dump_path,
-                &config.signature_path,
+            new_config_remote_verified_file(
+                service.clone(),
+                config.remote_id,
+                service.getFileSize(config.remote_id)?.try_into()?,
             )?,
         );
     }
 
-    for config in &args.local_ro_file_unverified {
-        file_pool.insert(config.ino, new_config_local_ro_file_unverified(&config.file_path)?);
+    for config in &args.remote_ro_file_unverified {
+        file_pool.insert(
+            config.ino,
+            new_config_remote_unverified_file(
+                service.clone(),
+                config.remote_id,
+                service.getFileSize(config.remote_id)?.try_into()?,
+            )?,
+        );
+    }
+
+    for config in &args.remote_new_rw_file {
+        file_pool.insert(
+            config.ino,
+            new_config_remote_new_verified_file(service.clone(), config.remote_id)?,
+        );
     }
 
     Ok(file_pool)
diff --git a/demo/Android.bp b/demo/Android.bp
index 749ca90..1342a26 100644
--- a/demo/Android.bp
+++ b/demo/Android.bp
@@ -19,5 +19,4 @@
     platform_apis: true,
     use_embedded_native_libs: true,
     v4_signature: true,
-    certificate: "platform",
 }
diff --git a/demo/AndroidManifest.xml b/demo/AndroidManifest.xml
index 7e1a58d..74ec210 100644
--- a/demo/AndroidManifest.xml
+++ b/demo/AndroidManifest.xml
@@ -6,7 +6,8 @@
 
     <application
         android:label="MicrodroidDemo"
-        android:theme="@style/Theme.MicrodroidDemo">
+        android:theme="@style/Theme.MicrodroidDemo"
+        android:testOnly="true">
         <uses-library android:name="android.system.virtualmachine" android:required="true" />
         <activity android:name=".MainActivity" android:exported="true">
             <intent-filter>
diff --git a/javalib/AndroidManifest.xml b/javalib/AndroidManifest.xml
index f96b39f..2a0b903 100644
--- a/javalib/AndroidManifest.xml
+++ b/javalib/AndroidManifest.xml
@@ -18,7 +18,7 @@
   package="com.android.virtualmachine.res">
 
   <permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE"
-      android:protectionLevel="signature" />
+      android:protectionLevel="signature|development" />
 
   <permission android:name="android.permission.DEBUG_VIRTUAL_MACHINE"
       android:protectionLevel="signature" />
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 389ebb0..425657f 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -354,7 +354,7 @@
             // For unknown reason, the signed bootloader doesn't work on x86_64. Until the problem
             // is fixed, let's use the unsigned bootloader for the architecture.
             // TODO(b/185115783): remove this
-            src: ":cuttlefish_crosvm_bootloader",
+            src: ":microdroid_crosvm_bootloader",
         },
     },
     filename: "microdroid_bootloader",
@@ -368,7 +368,7 @@
     name: "microdroid_bootloader_gen",
     tools: ["avbtool"],
     srcs: [
-        ":cuttlefish_crosvm_bootloader",
+        ":microdroid_crosvm_bootloader",
         ":avb_testkey_rsa4096",
     ],
     out: ["bootloader-signed"],
@@ -378,7 +378,7 @@
     // bootloader file whose size is 1. It can't pass avbtool.
     // 3. Add the hash footer. The partition size is set to (image size + 68KB)
     // rounded up to 4KB boundary.
-    cmd: "cp $(location :cuttlefish_crosvm_bootloader) $(out) && " +
+    cmd: "cp $(location :microdroid_crosvm_bootloader) $(out) && " +
         "if [ $$(stat --format=%s $(out)) -gt 4096 ]; then " +
         "$(location avbtool) add_hash_footer " +
         "--algorithm SHA256_RSA4096 " +
diff --git a/microdroid/bootconfig.app_debuggable b/microdroid/bootconfig.app_debuggable
index 79e2b08..f65d4cd 100644
--- a/microdroid/bootconfig.app_debuggable
+++ b/microdroid/bootconfig.app_debuggable
@@ -1 +1,10 @@
+# TODO(b/203369076) This should be 0 to disable adb rooting. For now, we can't do that because
+# if this is set to 0, adbd enforces the host authentication but we don't put the adb
+# public key (which represents the owner) in the VM yet.
+androidboot.microdroid.debuggable=0
+
+# Console output is not redirect to the host-side.
 kernel.console = null
+
+# ADB is supported but rooting is prohibited.
+androidboot.adb.enabled=1
diff --git a/microdroid/bootconfig.full_debuggable b/microdroid/bootconfig.full_debuggable
index d67cd76..f6afdcf 100644
--- a/microdroid/bootconfig.full_debuggable
+++ b/microdroid/bootconfig.full_debuggable
@@ -1 +1,10 @@
+# ro.debuggable is set.
+androidboot.microdroid.debuggable=1
+
+# Kernel message is exported.
 kernel.printk.devkmsg=on
+
+# ADB is supported and rooting is possible. Note that
+# ro.adb.secure is still 0 (see build.prop) which means that adbd is started
+# unrooted by default. To root, developer should explicitly execute `adb root`.
+androidboot.adb.enabled=1
diff --git a/microdroid/bootconfig.normal b/microdroid/bootconfig.normal
index 79e2b08..f7cdfc7 100644
--- a/microdroid/bootconfig.normal
+++ b/microdroid/bootconfig.normal
@@ -1 +1,8 @@
+# ro.debuggable is off
+androidboot.microdroid.debuggable=0
+
+# Console output is not redirect to the host-side.
 kernel.console = null
+
+# ADB is not enabled.
+androidboot.adb.enabled=0
diff --git a/microdroid/build.prop b/microdroid/build.prop
index eaca63d..ada945d 100644
--- a/microdroid/build.prop
+++ b/microdroid/build.prop
@@ -1,6 +1,5 @@
 # build.prop for microdroid
 ro.apex.updatable=true
-ro.debuggable=1
 ro.adb.secure=0
 service.adb.listen_addrs=vsock:5555
 
diff --git a/microdroid/init.rc b/microdroid/init.rc
index 23434bb..078b51d 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -30,6 +30,8 @@
     # payloads are not designed to run with bootstrap bionic
     setprop apex_config.done true
 
+    setprop ro.debuggable ${ro.boot.microdroid.debuggable:-0}
+
 on init
     # Mount binderfs
     mkdir /dev/binderfs
@@ -80,6 +82,7 @@
     # TODO(b/185767624): remove hidl after full keymint support
     start hwservicemanager
 
+on init && property:ro.boot.adb.enabled=1
     start adbd
 
 on load_persist_props_action