Mark ab/7061308 as merged in stage.
Bug: 180401296
Merged-In: I3022e4f147689af37c157abfa268293a5d4a5e10
Change-Id: Iea47e3cc56a4991cd0447fe8d0dc1bab1148ed1d
diff --git a/apex/Android.bp b/apex/Android.bp
index 7baf966..4f7201a 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -1,15 +1,33 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
apex {
name: "com.android.virt",
// TODO(jiyong): make it updatable
- // updatable: true,
+ updatable: false,
manifest: "manifest.json",
key: "com.android.virt.key",
certificate: ":com.android.virt.certificate",
- min_sdk_version: "S",
+ // crosvm is enabled for only 64-bit targets on device
+ arch: {
+ arm64: {
+ binaries: [
+ "crosvm",
+ ],
+ },
+ x86_64: {
+ binaries: [
+ "crosvm",
+ ],
+ },
+ },
+ binaries: ["assemble_cvd"],
+ filesystems: ["microdroid"],
}
apex_key {
diff --git a/apex/product_packages.mk b/apex/product_packages.mk
new file mode 100644
index 0000000..ef99f0f
--- /dev/null
+++ b/apex/product_packages.mk
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2021 Google Inc.
+#
+# 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.
+#
+
+# TODO: Remove this once the APEX is included in base system.
+
+# To include the APEX in your build, insert this in your device.mk:
+# $(call inherit-product, packages/modules/Virtualization/apex/product_packages.mk)
+
+PRODUCT_PACKAGES += com.android.virt
+PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
+ system/apex/com.android.virt.apex \
+ system/bin/crosvm \
+ system/lib64/%.dylib.so \
+ system/lib64/libfdt.so \
+ system/lib64/libgfxstream_backend.so \
+ system/lib64/libcuttlefish_allocd_utils.so \
+ system/lib64/libcuttlefish_fs.so \
+ system/lib64/libcuttlefish_utils.so
+
+$(call inherit-product, external/crosvm/seccomp/crosvm_seccomp_policy_product_packages.mk)
diff --git a/authfs/Android.bp b/authfs/Android.bp
index 3c5849b..3fc4504 100644
--- a/authfs/Android.bp
+++ b/authfs/Android.bp
@@ -1,19 +1,30 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
rust_defaults {
name: "authfs_defaults",
crate_name: "authfs",
srcs: [
- "src/lib.rs",
+ "src/main.rs",
],
edition: "2018",
rustlibs: [
"libanyhow",
"libauthfs_crypto_bindgen",
+ "libcfg_if",
+ "libfuse_rust",
"liblibc",
+ "libstructopt",
"libthiserror",
],
- host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
shared_libs: ["libcrypto"],
- clippy_lints: "android",
+ defaults: ["crosvm_defaults"],
}
// TODO(b/172687320): remove once there is a canonical bindgen.
@@ -27,23 +38,27 @@
],
bindgen_flags: ["--size_t-is-usize"],
cflags: ["-D BORINGSSL_NO_CXX"],
- host_supported: true,
}
-rust_library {
- name: "libauthfs",
+rust_binary {
+ name: "authfs",
defaults: ["authfs_defaults"],
}
-rust_test_host {
- name: "authfs_host_test_src_lib",
- test_suites: ["general-tests"],
+rust_test {
+ name: "authfs_device_test_src_lib",
defaults: ["authfs_defaults"],
+ test_suites: ["device-tests"],
+ data: [
+ "testdata/input.4k",
+ "testdata/input.4k.fsv_sig",
+ "testdata/input.4k.merkle_dump",
+ "testdata/input.4k1",
+ "testdata/input.4k1.fsv_sig",
+ "testdata/input.4k1.merkle_dump",
+ "testdata/input.4m",
+ "testdata/input.4m.fsv_sig",
+ "testdata/input.4m.merkle_dump",
+ "testdata/input.4m.merkle_dump.bad",
+ ],
}
-
-// TODO(victorhsieh): Enable the test once "undefined symbol: _Unwind_Resume" is fixed, then add to
-// TEST_MAPPING.
-//rust_test {
-// name: "authfs_device_test_src_lib",
-// defaults: ["authfs_defaults"],
-//}
diff --git a/authfs/TEST_MAPPING b/authfs/TEST_MAPPING
new file mode 100644
index 0000000..d0c0b09
--- /dev/null
+++ b/authfs/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "authfs_device_test_src_lib"
+ }
+ ]
+}
diff --git a/authfs/src/common.rs b/authfs/src/common.rs
new file mode 100644
index 0000000..2220ae7
--- /dev/null
+++ b/authfs/src/common.rs
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+pub const COMMON_PAGE_SIZE: u64 = 4096;
+
+pub fn divide_roundup(dividend: u64, divisor: u64) -> u64 {
+ (dividend + divisor - 1) / divisor
+}
diff --git a/authfs/src/fsverity.rs b/authfs/src/fsverity.rs
index c9070ba..306c9d9 100644
--- a/authfs/src/fsverity.rs
+++ b/authfs/src/fsverity.rs
@@ -19,6 +19,7 @@
use thiserror::Error;
use crate::auth::Authenticator;
+use crate::common::divide_roundup;
use crate::crypto::{CryptoError, Sha256Hasher};
use crate::reader::ReadOnlyDataByChunk;
@@ -43,10 +44,6 @@
type HashBuffer = [u8; Sha256Hasher::HASH_SIZE];
-fn divide_roundup(dividend: u64, divisor: u64) -> u64 {
- (dividend + divisor - 1) / divisor
-}
-
fn hash_with_padding(chunk: &[u8], pad_to: usize) -> Result<HashBuffer, CryptoError> {
let padding_size = pad_to - chunk.len();
Sha256Hasher::new()?.update(&chunk)?.update(&ZEROS[..padding_size])?.finalize()
@@ -91,11 +88,11 @@
/// offset of the child node's hash. It is up to the iterator user to use the node and hash,
/// e.g. for the actual verification.
#[allow(clippy::needless_collect)]
-fn fsverity_walk<'a, T: ReadOnlyDataByChunk>(
+fn fsverity_walk<T: ReadOnlyDataByChunk>(
chunk_index: u64,
file_size: u64,
- merkle_tree: &'a T,
-) -> Result<impl Iterator<Item = Result<([u8; 4096], usize), FsverityError>> + 'a, FsverityError> {
+ merkle_tree: &T,
+) -> Result<impl Iterator<Item = Result<([u8; 4096], usize), FsverityError>> + '_, FsverityError> {
let hashes_per_node = T::CHUNK_SIZE / Sha256Hasher::HASH_SIZE as u64;
let hash_pages = divide_roundup(file_size, hashes_per_node * T::CHUNK_SIZE);
debug_assert_eq!(hashes_per_node, 128u64);
@@ -168,7 +165,6 @@
}
impl<F: ReadOnlyDataByChunk, M: ReadOnlyDataByChunk> FsverityChunkedFileReader<F, M> {
- #[allow(dead_code)]
pub fn new<A: Authenticator>(
authenticator: &A,
chunked_file: F,
@@ -215,89 +211,93 @@
mod tests {
use super::*;
use crate::auth::FakeAuthenticator;
- use crate::reader::ReadOnlyDataByChunk;
+ use crate::reader::{ChunkedFileReader, ReadOnlyDataByChunk};
use anyhow::Result;
+ use std::fs::File;
+ use std::io::Read;
+
+ type LocalFsverityChunkedFileReader =
+ FsverityChunkedFileReader<ChunkedFileReader, ChunkedFileReader>;
fn total_chunk_number(file_size: u64) -> u64 {
(file_size + 4095) / 4096
}
+ // Returns a reader with fs-verity verification and the file size.
+ fn new_reader_with_fsverity(
+ content_path: &str,
+ merkle_tree_path: &str,
+ signature_path: &str,
+ ) -> Result<(LocalFsverityChunkedFileReader, u64)> {
+ let file_reader = ChunkedFileReader::new(File::open(content_path)?)?;
+ let file_size = file_reader.len();
+ let merkle_tree = ChunkedFileReader::new(File::open(merkle_tree_path)?)?;
+ let mut sig = Vec::new();
+ let _ = File::open(signature_path)?.read_to_end(&mut sig)?;
+ let authenticator = FakeAuthenticator::always_succeed();
+ Ok((
+ FsverityChunkedFileReader::new(
+ &authenticator,
+ file_reader,
+ file_size,
+ sig,
+ merkle_tree,
+ )?,
+ file_size,
+ ))
+ }
+
#[test]
fn fsverity_verify_full_read_4k() -> Result<()> {
- let file = &include_bytes!("../testdata/input.4k")[..];
- let merkle_tree = &include_bytes!("../testdata/input.4k.merkle_dump")[..];
- let sig = include_bytes!("../testdata/input.4k.fsv_sig").to_vec();
- let authenticator = FakeAuthenticator::always_succeed();
- let verified_file = FsverityChunkedFileReader::new(
- &authenticator,
- file,
- file.len() as u64,
- sig,
- merkle_tree,
+ let (file_reader, file_size) = new_reader_with_fsverity(
+ "testdata/input.4k",
+ "testdata/input.4k.merkle_dump",
+ "testdata/input.4k.fsv_sig",
)?;
- for i in 0..total_chunk_number(file.len() as u64) {
+ for i in 0..total_chunk_number(file_size) {
let mut buf = [0u8; 4096];
- assert!(verified_file.read_chunk(i, &mut buf[..]).is_ok());
+ assert!(file_reader.read_chunk(i, &mut buf[..]).is_ok());
}
Ok(())
}
#[test]
fn fsverity_verify_full_read_4k1() -> Result<()> {
- let file = &include_bytes!("../testdata/input.4k1")[..];
- let merkle_tree = &include_bytes!("../testdata/input.4k1.merkle_dump")[..];
- let sig = include_bytes!("../testdata/input.4k1.fsv_sig").to_vec();
- let authenticator = FakeAuthenticator::always_succeed();
- let verified_file = FsverityChunkedFileReader::new(
- &authenticator,
- file,
- file.len() as u64,
- sig,
- merkle_tree,
+ let (file_reader, file_size) = new_reader_with_fsverity(
+ "testdata/input.4k1",
+ "testdata/input.4k1.merkle_dump",
+ "testdata/input.4k1.fsv_sig",
)?;
- for i in 0..total_chunk_number(file.len() as u64) {
+ for i in 0..total_chunk_number(file_size) {
let mut buf = [0u8; 4096];
- assert!(verified_file.read_chunk(i, &mut buf[..]).is_ok());
+ assert!(file_reader.read_chunk(i, &mut buf[..]).is_ok());
}
Ok(())
}
#[test]
fn fsverity_verify_full_read_4m() -> Result<()> {
- let file = &include_bytes!("../testdata/input.4m")[..];
- let merkle_tree = &include_bytes!("../testdata/input.4m.merkle_dump")[..];
- let sig = include_bytes!("../testdata/input.4m.fsv_sig").to_vec();
- let authenticator = FakeAuthenticator::always_succeed();
- let verified_file = FsverityChunkedFileReader::new(
- &authenticator,
- file,
- file.len() as u64,
- sig,
- merkle_tree,
+ let (file_reader, file_size) = new_reader_with_fsverity(
+ "testdata/input.4m",
+ "testdata/input.4m.merkle_dump",
+ "testdata/input.4m.fsv_sig",
)?;
- for i in 0..total_chunk_number(file.len() as u64) {
+ for i in 0..total_chunk_number(file_size) {
let mut buf = [0u8; 4096];
- assert!(verified_file.read_chunk(i, &mut buf[..]).is_ok());
+ assert!(file_reader.read_chunk(i, &mut buf[..]).is_ok());
}
Ok(())
}
#[test]
fn fsverity_verify_bad_merkle_tree() -> Result<()> {
- let file = &include_bytes!("../testdata/input.4m")[..];
- // First leaf node is corrupted.
- let merkle_tree = &include_bytes!("../testdata/input.4m.merkle_dump.bad")[..];
- let sig = include_bytes!("../testdata/input.4m.fsv_sig").to_vec();
- let authenticator = FakeAuthenticator::always_succeed();
- let verified_file = FsverityChunkedFileReader::new(
- &authenticator,
- file,
- file.len() as u64,
- sig,
- merkle_tree,
+ let (file_reader, _) = new_reader_with_fsverity(
+ "testdata/input.4m",
+ "testdata/input.4m.merkle_dump.bad", // First leaf node is corrupted.
+ "testdata/input.4m.fsv_sig",
)?;
// A lowest broken node (a 4K chunk that contains 128 sha256 hashes) will fail the read
@@ -306,22 +306,23 @@
let num_hashes = 4096 / 32;
let last_index = num_hashes;
for i in 0..last_index {
- assert!(verified_file.read_chunk(i, &mut buf[..]).is_err());
+ assert!(file_reader.read_chunk(i, &mut buf[..]).is_err());
}
- assert!(verified_file.read_chunk(last_index, &mut buf[..]).is_ok());
+ assert!(file_reader.read_chunk(last_index, &mut buf[..]).is_ok());
Ok(())
}
#[test]
fn invalid_signature() -> Result<()> {
let authenticator = FakeAuthenticator::always_fail();
- let file = &include_bytes!("../testdata/input.4m")[..];
- let merkle_tree = &include_bytes!("../testdata/input.4m.merkle_dump")[..];
+ let file_reader = ChunkedFileReader::new(File::open("testdata/input.4m")?)?;
+ let file_size = file_reader.len();
+ let merkle_tree = ChunkedFileReader::new(File::open("testdata/input.4m.merkle_dump")?)?;
let sig = include_bytes!("../testdata/input.4m.fsv_sig").to_vec();
assert!(FsverityChunkedFileReader::new(
&authenticator,
- file,
- file.len() as u64,
+ file_reader,
+ file_size,
sig,
merkle_tree
)
diff --git a/authfs/src/fusefs.rs b/authfs/src/fusefs.rs
new file mode 100644
index 0000000..484aad4
--- /dev/null
+++ b/authfs/src/fusefs.rs
@@ -0,0 +1,338 @@
+/*
+ * 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 std::collections::BTreeMap;
+use std::convert::TryFrom;
+use std::ffi::CStr;
+use std::fs::OpenOptions;
+use std::io;
+use std::mem::MaybeUninit;
+use std::option::Option;
+use std::os::unix::io::AsRawFd;
+use std::path::Path;
+use std::time::Duration;
+
+use fuse::filesystem::{Context, DirEntry, DirectoryIterator, Entry, FileSystem, ZeroCopyWriter};
+use fuse::mount::MountOption;
+
+use crate::common::{divide_roundup, COMMON_PAGE_SIZE};
+use crate::fsverity::FsverityChunkedFileReader;
+use crate::reader::{ChunkedFileReader, ReadOnlyDataByChunk};
+
+// We're reading the backing file by chunk, so setting the block size to be the same.
+const BLOCK_SIZE: usize = COMMON_PAGE_SIZE as usize;
+
+const DEFAULT_METADATA_TIMEOUT: std::time::Duration = Duration::from_secs(5);
+
+pub type Inode = u64;
+type Handle = u64;
+
+// A debug only type where everything are stored as local files.
+type FileBackedFsverityChunkedFileReader =
+ FsverityChunkedFileReader<ChunkedFileReader, ChunkedFileReader>;
+
+pub enum FileConfig {
+ LocalVerifiedFile(FileBackedFsverityChunkedFileReader, u64),
+ LocalUnverifiedFile(ChunkedFileReader, u64),
+}
+
+struct AuthFs {
+ /// Store `FileConfig`s using the `Inode` number as the search index.
+ ///
+ /// For further optimization to minimize the search cost, since Inode is integer, we may
+ /// consider storing them in a Vec if we can guarantee that the numbers are small and
+ /// consecutive.
+ file_pool: BTreeMap<Inode, FileConfig>,
+
+ /// Maximum bytes in the write transaction to the FUSE device. This limits the maximum size to
+ /// a read request (including FUSE protocol overhead).
+ max_write: u32,
+}
+
+impl AuthFs {
+ pub fn new(file_pool: BTreeMap<Inode, FileConfig>, max_write: u32) -> AuthFs {
+ AuthFs { file_pool, max_write }
+ }
+
+ fn get_file_config(&self, inode: &Inode) -> io::Result<&FileConfig> {
+ self.file_pool.get(&inode).ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))
+ }
+}
+
+fn check_access_mode(flags: u32, mode: libc::c_int) -> io::Result<()> {
+ if (flags & libc::O_ACCMODE as u32) == mode as u32 {
+ Ok(())
+ } else {
+ Err(io::Error::from_raw_os_error(libc::EACCES))
+ }
+}
+
+cfg_if::cfg_if! {
+ if #[cfg(all(target_arch = "aarch64", target_pointer_width = "64"))] {
+ fn blk_size() -> libc::c_int { BLOCK_SIZE as libc::c_int }
+ } else {
+ fn blk_size() -> libc::c_long { BLOCK_SIZE as libc::c_long }
+ }
+}
+
+fn create_stat(ino: libc::ino_t, file_size: u64) -> io::Result<libc::stat64> {
+ let mut st = unsafe { MaybeUninit::<libc::stat64>::zeroed().assume_init() };
+
+ st.st_ino = ino;
+ st.st_mode = libc::S_IFREG | libc::S_IRUSR | libc::S_IRGRP | libc::S_IROTH;
+ st.st_dev = 0;
+ st.st_nlink = 1;
+ st.st_uid = 0;
+ st.st_gid = 0;
+ st.st_rdev = 0;
+ st.st_size = libc::off64_t::try_from(file_size)
+ .map_err(|_| io::Error::from_raw_os_error(libc::EFBIG))?;
+ st.st_blksize = blk_size();
+ // Per man stat(2), st_blocks is "Number of 512B blocks allocated".
+ st.st_blocks = libc::c_longlong::try_from(divide_roundup(file_size, 512))
+ .map_err(|_| io::Error::from_raw_os_error(libc::EFBIG))?;
+ Ok(st)
+}
+
+/// An iterator that generates (offset, size) for a chunked read operation, where offset is the
+/// global file offset, and size is the amount of read from the offset.
+struct ChunkReadIter {
+ remaining: usize,
+ offset: u64,
+}
+
+impl ChunkReadIter {
+ pub fn new(remaining: usize, offset: u64) -> Self {
+ ChunkReadIter { remaining, offset }
+ }
+}
+
+impl Iterator for ChunkReadIter {
+ type Item = (u64, usize);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.remaining == 0 {
+ return None;
+ }
+ let chunk_data_size =
+ std::cmp::min(self.remaining, BLOCK_SIZE - (self.offset % BLOCK_SIZE as u64) as usize);
+ let retval = (self.offset, chunk_data_size);
+ self.offset += chunk_data_size as u64;
+ self.remaining = self.remaining.saturating_sub(chunk_data_size);
+ Some(retval)
+ }
+}
+
+fn offset_to_chunk_index(offset: u64) -> u64 {
+ offset / BLOCK_SIZE as u64
+}
+
+fn read_chunks<W: io::Write, T: ReadOnlyDataByChunk>(
+ mut w: W,
+ file: &T,
+ file_size: u64,
+ offset: u64,
+ size: u32,
+) -> io::Result<usize> {
+ let remaining = file_size.saturating_sub(offset);
+ let size_to_read = std::cmp::min(size as usize, remaining as usize);
+ let total = ChunkReadIter::new(size_to_read, offset).try_fold(
+ 0,
+ |total, (current_offset, planned_data_size)| {
+ // TODO(victorhsieh): There might be a non-trivial way to avoid this copy. For example,
+ // instead of accepting a buffer, the writer could expose the final destination buffer
+ // for the reader to write to. It might not be generally applicable though, e.g. with
+ // virtio transport, the buffer may not be continuous.
+ let mut buf = [0u8; BLOCK_SIZE];
+ let read_size = file.read_chunk(offset_to_chunk_index(current_offset), &mut buf)?;
+ if read_size < planned_data_size {
+ return Err(io::Error::from_raw_os_error(libc::ENODATA));
+ }
+
+ let begin = (current_offset % BLOCK_SIZE as u64) as usize;
+ let end = begin + planned_data_size;
+ let s = w.write(&buf[begin..end])?;
+ if s != planned_data_size {
+ return Err(io::Error::from_raw_os_error(libc::EIO));
+ }
+ Ok(total + s)
+ },
+ )?;
+
+ Ok(total)
+}
+
+// No need to support enumerating directory entries.
+struct EmptyDirectoryIterator {}
+
+impl DirectoryIterator for EmptyDirectoryIterator {
+ fn next(&mut self) -> Option<DirEntry> {
+ None
+ }
+}
+
+impl FileSystem for AuthFs {
+ type Inode = Inode;
+ type Handle = Handle;
+ type DirIter = EmptyDirectoryIterator;
+
+ fn max_buffer_size(&self) -> u32 {
+ self.max_write
+ }
+
+ fn lookup(&self, _ctx: Context, _parent: Inode, name: &CStr) -> io::Result<Entry> {
+ // Only accept file name that looks like an integrer. Files in the pool are simply exposed
+ // by their inode number. Also, there is currently no directory structure.
+ let num = name.to_str().map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?;
+ // Normally, `lookup` is required to increase a reference count for the inode (while
+ // `forget` will decrease it). It is not necessary here since the files are configured to
+ // 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::LocalVerifiedFile(_, file_size)
+ | FileConfig::LocalUnverifiedFile(_, file_size) => create_stat(inode, *file_size)?,
+ };
+ Ok(Entry {
+ inode,
+ generation: 0,
+ attr: st,
+ entry_timeout: DEFAULT_METADATA_TIMEOUT,
+ attr_timeout: DEFAULT_METADATA_TIMEOUT,
+ })
+ }
+
+ fn getattr(
+ &self,
+ _ctx: Context,
+ inode: Inode,
+ _handle: Option<Handle>,
+ ) -> io::Result<(libc::stat64, Duration)> {
+ Ok((
+ match self.get_file_config(&inode)? {
+ FileConfig::LocalVerifiedFile(_, file_size)
+ | FileConfig::LocalUnverifiedFile(_, file_size) => create_stat(inode, *file_size)?,
+ },
+ DEFAULT_METADATA_TIMEOUT,
+ ))
+ }
+
+ fn open(
+ &self,
+ _ctx: Context,
+ inode: Self::Inode,
+ flags: u32,
+ ) -> io::Result<(Option<Self::Handle>, fuse::sys::OpenOptions)> {
+ // 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::LocalVerifiedFile(_, _) => {
+ check_access_mode(flags, libc::O_RDONLY)?;
+ // Once verified, and only if verified, the file content can be cached. This is not
+ // really needed for a local file, but is the behavior of RemoteVerifiedFile later.
+ Ok((None, fuse::sys::OpenOptions::KEEP_CACHE))
+ }
+ FileConfig::LocalUnverifiedFile(_, _) => {
+ check_access_mode(flags, libc::O_RDONLY)?;
+ // Do not cache the content. This type of file is supposed to be verified using
+ // dm-verity. The filesystem mount over dm-verity already is already cached, so use
+ // direct I/O here to avoid double cache.
+ Ok((None, fuse::sys::OpenOptions::DIRECT_IO))
+ }
+ }
+ }
+
+ fn read<W: io::Write + ZeroCopyWriter>(
+ &self,
+ _ctx: Context,
+ inode: Inode,
+ _handle: Handle,
+ w: W,
+ size: u32,
+ offset: u64,
+ _lock_owner: Option<u64>,
+ _flags: u32,
+ ) -> io::Result<usize> {
+ match self.get_file_config(&inode)? {
+ FileConfig::LocalVerifiedFile(file, file_size) => {
+ read_chunks(w, file, *file_size, offset, size)
+ }
+ FileConfig::LocalUnverifiedFile(file, file_size) => {
+ read_chunks(w, file, *file_size, offset, size)
+ }
+ }
+ }
+}
+
+/// Mount and start the FUSE instance. This requires CAP_SYS_ADMIN.
+pub fn loop_forever(
+ file_pool: BTreeMap<Inode, FileConfig>,
+ mountpoint: &Path,
+) -> Result<(), fuse::Error> {
+ let max_read: u32 = 65536;
+ let max_write: u32 = 65536;
+ let dev_fuse = OpenOptions::new()
+ .read(true)
+ .write(true)
+ .open("/dev/fuse")
+ .expect("Failed to open /dev/fuse");
+
+ fuse::mount(
+ mountpoint,
+ "authfs",
+ libc::MS_NOSUID | libc::MS_NODEV,
+ &[
+ MountOption::FD(dev_fuse.as_raw_fd()),
+ MountOption::RootMode(libc::S_IFDIR | libc::S_IXUSR | libc::S_IXGRP | libc::S_IXOTH),
+ MountOption::AllowOther,
+ MountOption::UserId(0),
+ MountOption::GroupId(0),
+ MountOption::MaxRead(max_read),
+ ],
+ )
+ .expect("Failed to mount fuse");
+
+ fuse::worker::start_message_loop(
+ dev_fuse,
+ max_write,
+ max_read,
+ AuthFs::new(file_pool, max_write),
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ fn collect_chunk_read_iter(remaining: usize, offset: u64) -> Vec<(u64, usize)> {
+ ChunkReadIter::new(remaining, offset).collect::<Vec<_>>()
+ }
+
+ #[test]
+ fn test_chunk_read_iter() {
+ assert_eq!(collect_chunk_read_iter(4096, 0), [(0, 4096)]);
+ assert_eq!(collect_chunk_read_iter(8192, 0), [(0, 4096), (4096, 4096)]);
+ assert_eq!(collect_chunk_read_iter(8192, 4096), [(4096, 4096), (8192, 4096)]);
+
+ assert_eq!(
+ collect_chunk_read_iter(16384, 1),
+ [(1, 4095), (4096, 4096), (8192, 4096), (12288, 4096), (16384, 1)]
+ );
+
+ assert_eq!(collect_chunk_read_iter(0, 0), []);
+ assert_eq!(collect_chunk_read_iter(0, 100), []);
+ }
+}
diff --git a/authfs/src/lib.rs b/authfs/src/lib.rs
deleted file mode 100644
index 05070d6..0000000
--- a/authfs/src/lib.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-//! This crate provides a FUSE-based, non-generic filesystem that I/O is authenticated. This
-//! filesystem assumes the storage layer is not trusted, e.g. file is provided by an untrusted VM,
-//! and the content can't be simply trusted. The filesystem can use its public key to verify a
-//! (read-only) file against its associated fs-verity signature by a trusted party. With the Merkle
-//! tree, each read of file block can be verified individually.
-//!
-//! The implementation is not finished.
-
-mod auth;
-mod crypto;
-mod fsverity;
-mod reader;
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
new file mode 100644
index 0000000..46e6fd8
--- /dev/null
+++ b/authfs/src/main.rs
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+//! This crate implements AuthFS, a FUSE-based, non-generic filesystem where file access is
+//! authenticated. This filesystem assumes the underlying layer is not trusted, e.g. file may be
+//! provided by an untrusted host/VM, so that the content can't be simply trusted. However, with a
+//! public key from a trusted party, this filesystem can still verify a (read-only) file signed by
+//! the trusted party even if the host/VM as the blob provider is malicious. With the Merkle tree,
+//! 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.
+
+use anyhow::{bail, Result};
+use std::collections::BTreeMap;
+use std::fs::File;
+use std::io::Read;
+use std::path::PathBuf;
+use structopt::StructOpt;
+
+mod auth;
+mod common;
+mod crypto;
+mod fsverity;
+mod fusefs;
+mod reader;
+
+use auth::FakeAuthenticator;
+use fsverity::FsverityChunkedFileReader;
+use fusefs::{FileConfig, Inode};
+use reader::ChunkedFileReader;
+
+#[derive(StructOpt)]
+struct Options {
+ /// Mount point of AuthFS.
+ #[structopt(parse(from_os_str))]
+ mount_point: PathBuf,
+
+ /// Debug only. A readonly file to be protected by fs-verity. Can be multiple.
+ #[structopt(long, parse(try_from_str = parse_local_verified_file_option))]
+ local_verified_file: Vec<LocalVerifiedFileConfig>,
+
+ /// Debug only. An unverified read-only file. Can be multiple.
+ #[structopt(long, parse(try_from_str = parse_local_unverified_file_option))]
+ local_unverified_file: Vec<LocalUnverifiedFileConfig>,
+}
+
+struct LocalVerifiedFileConfig {
+ ino: Inode,
+ file_path: PathBuf,
+ merkle_tree_dump_path: PathBuf,
+ signature_path: PathBuf,
+}
+
+struct LocalUnverifiedFileConfig {
+ ino: Inode,
+ file_path: PathBuf,
+}
+
+fn parse_local_verified_file_option(option: &str) -> Result<LocalVerifiedFileConfig> {
+ let strs: Vec<&str> = option.split(':').collect();
+ if strs.len() != 4 {
+ bail!("Invalid option: {}", option);
+ }
+ Ok(LocalVerifiedFileConfig {
+ ino: strs[0].parse::<Inode>().unwrap(),
+ file_path: PathBuf::from(strs[1]),
+ merkle_tree_dump_path: PathBuf::from(strs[2]),
+ signature_path: PathBuf::from(strs[3]),
+ })
+}
+
+fn parse_local_unverified_file_option(option: &str) -> Result<LocalUnverifiedFileConfig> {
+ let strs: Vec<&str> = option.split(':').collect();
+ if strs.len() != 2 {
+ bail!("Invalid option: {}", option);
+ }
+ Ok(LocalUnverifiedFileConfig {
+ ino: strs[0].parse::<Inode>().unwrap(),
+ file_path: PathBuf::from(strs[1]),
+ })
+}
+
+fn new_config_local_verified_file(
+ protected_file: &PathBuf,
+ merkle_tree_dump: &PathBuf,
+ signature: &PathBuf,
+) -> Result<FileConfig> {
+ let file = File::open(&protected_file)?;
+ let file_size = file.metadata()?.len();
+ let file_reader = ChunkedFileReader::new(file)?;
+ let merkle_tree_reader = ChunkedFileReader::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 file_reader = FsverityChunkedFileReader::new(
+ &authenticator,
+ file_reader,
+ file_size,
+ sig,
+ merkle_tree_reader,
+ )?;
+ Ok(FileConfig::LocalVerifiedFile(file_reader, file_size))
+}
+
+fn new_config_local_unverified_file(file_path: &PathBuf) -> Result<FileConfig> {
+ let file_reader = ChunkedFileReader::new(File::open(file_path)?)?;
+ let file_size = file_reader.len();
+ Ok(FileConfig::LocalUnverifiedFile(file_reader, file_size))
+}
+
+fn prepare_file_pool(args: &Options) -> Result<BTreeMap<Inode, FileConfig>> {
+ let mut file_pool = BTreeMap::new();
+
+ for config in &args.local_verified_file {
+ file_pool.insert(
+ config.ino,
+ new_config_local_verified_file(
+ &config.file_path,
+ &config.merkle_tree_dump_path,
+ &config.signature_path,
+ )?,
+ );
+ }
+
+ for config in &args.local_unverified_file {
+ file_pool.insert(config.ino, new_config_local_unverified_file(&config.file_path)?);
+ }
+
+ Ok(file_pool)
+}
+
+fn main() -> Result<()> {
+ let args = Options::from_args();
+ let file_pool = prepare_file_pool(&args)?;
+ fusefs::loop_forever(file_pool, &args.mount_point)?;
+ Ok(())
+}
diff --git a/authfs/src/reader.rs b/authfs/src/reader.rs
index 135a793..d365a41 100644
--- a/authfs/src/reader.rs
+++ b/authfs/src/reader.rs
@@ -19,13 +19,14 @@
use std::fs::File;
use std::io::Result;
use std::os::unix::fs::FileExt;
-use std::path::Path;
+
+use crate::common::COMMON_PAGE_SIZE;
/// A trait for reading data by chunks. The data is assumed readonly and has fixed length. Chunks
/// can be read by specifying the chunk index. Only the last chunk may have incomplete chunk size.
pub trait ReadOnlyDataByChunk {
/// Default chunk size.
- const CHUNK_SIZE: u64 = 4096;
+ const CHUNK_SIZE: u64 = COMMON_PAGE_SIZE;
/// Read the `chunk_index`-th chunk to `buf`. Each slice/chunk has size `CHUNK_SIZE` except for
/// the last one, which can be an incomplete chunk. `buf` is currently required to be large
@@ -49,12 +50,14 @@
impl ChunkedFileReader {
/// Creates a `ChunkedFileReader` to read from for the specified `path`.
- #[allow(dead_code)]
- pub fn new<P: AsRef<Path>>(path: P) -> Result<ChunkedFileReader> {
- let file = File::open(path)?;
+ pub fn new(file: File) -> Result<ChunkedFileReader> {
let size = file.metadata()?.len();
Ok(ChunkedFileReader { file, size })
}
+
+ pub fn len(&self) -> u64 {
+ self.size
+ }
}
impl ReadOnlyDataByChunk for ChunkedFileReader {
@@ -66,55 +69,57 @@
}
}
-impl ReadOnlyDataByChunk for &[u8] {
- fn read_chunk(&self, chunk_index: u64, buf: &mut [u8]) -> Result<usize> {
- debug_assert!(buf.len() as u64 >= Self::CHUNK_SIZE);
- let chunk = &self.chunks(Self::CHUNK_SIZE as usize).nth(chunk_index as usize).unwrap();
- buf[..chunk.len()].copy_from_slice(&chunk);
- Ok(chunk.len())
- }
-}
-
#[cfg(test)]
mod tests {
use super::*;
+ use std::env::temp_dir;
- fn test_reading_more_than_4kb_data<T: ReadOnlyDataByChunk>(
- reader: T,
- data_size: u64,
- ) -> Result<()> {
+ #[test]
+ fn test_read_4k_file() -> Result<()> {
+ let file_reader = ChunkedFileReader::new(File::open("testdata/input.4k")?)?;
let mut buf = [0u8; 4096];
- assert_eq!(reader.read_chunk(0, &mut buf)?, 4096);
- let last_index = (data_size + 4095) / 4096 - 1;
- assert_eq!(reader.read_chunk(last_index, &mut buf)?, (data_size % 4096) as usize);
+ let size = file_reader.read_chunk(0, &mut buf)?;
+ assert_eq!(size, buf.len());
Ok(())
}
- // TODO(victorhsieh): test ChunkedFileReader once there is a way to access testdata in the test
- // environement.
-
#[test]
- fn test_read_in_memory_data() -> Result<()> {
- let data = &[1u8; 5000][..];
- test_reading_more_than_4kb_data(data, data.len() as u64)
- }
-
- #[test]
- #[should_panic]
- #[allow(unused_must_use)]
- fn test_read_in_memory_empty_data() {
- let data = &[][..]; // zero length slice
+ fn test_read_4k1_file() -> Result<()> {
+ let file_reader = ChunkedFileReader::new(File::open("testdata/input.4k1")?)?;
let mut buf = [0u8; 4096];
- data.read_chunk(0, &mut buf); // should panic
+ 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() -> Result<()> {
+ let file_reader = ChunkedFileReader::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]
#[should_panic]
- #[allow(unused_must_use)]
fn test_read_beyond_file_size() {
- let data = &[1u8; 5000][..];
+ let file_reader = ChunkedFileReader::new(File::open("testdata/input.4k").unwrap()).unwrap();
let mut buf = [0u8; 4096];
- let last_index_plus_1 = (data.len() + 4095) / 4096;
- data.read_chunk(last_index_plus_1 as u64, &mut buf); // should panic
+ let _ = file_reader.read_chunk(1u64, &mut buf); // should panic
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_read_empty_file() {
+ let mut temp_file = temp_dir();
+ temp_file.push("authfs_test_empty_file");
+ let file_reader = ChunkedFileReader::new(File::create(temp_file).unwrap()).unwrap();
+ let mut buf = [0u8; 4096];
+ let _ = file_reader.read_chunk(0, &mut buf); // should panic
}
}
diff --git a/authfs/tools/test.sh b/authfs/tools/test.sh
new file mode 100755
index 0000000..9ed3a99
--- /dev/null
+++ b/authfs/tools/test.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+# Run with -u to enter new namespace.
+if [[ $1 == "-u" ]]; then
+ exec unshare -m -U -r $0
+fi
+
+trap "umount /tmp/mnt" EXIT;
+mkdir -p /tmp/mnt
+
+echo "Mounting authfs in background ..."
+strace -o authfs.strace target/debug/authfs \
+ /tmp/mnt \
+ --local-verified-file 2:testdata/input.4m:testdata/input.4m.merkle_dump:testdata/input.4m.fsv_sig \
+ --local-verified-file 3:testdata/input.4k1:testdata/input.4k1.merkle_dump:testdata/input.4k1.fsv_sig \
+ --local-verified-file 4:testdata/input.4k:testdata/input.4k.merkle_dump:testdata/input.4k.fsv_sig \
+ --local-unverified-file 5:testdata/input.4k \
+ &
+sleep 0.1
+
+echo "Accessing files in authfs ..."
+echo
+md5sum /tmp/mnt/2 testdata/input.4m
+echo
+md5sum /tmp/mnt/3 testdata/input.4k1
+echo
+md5sum /tmp/mnt/4 /tmp/mnt/5 testdata/input.4k
+echo
+dd if=/tmp/mnt/2 bs=1000 skip=100 count=50 status=none |md5sum
+dd if=testdata/input.4m bs=1000 skip=100 count=50 status=none |md5sum
+echo
+tac /tmp/mnt/4 |md5sum
+tac /tmp/mnt/5 |md5sum
+tac testdata/input.4k |md5sum
+echo
+test -f /tmp/mnt/2 || echo 'FAIL: an expected file is missing'
+test -f /tmp/mnt/0 && echo 'FAIL: unexpected file presents'
+test -f /tmp/mnt/1 && echo 'FAIL: unexpected file presents, 1 is root dir'
+test -f /tmp/mnt/100 && echo 'FAIL: unexpected file presents'
+test -f /tmp/mnt/foo && echo 'FAIL: unexpected file presents'
+test -f /tmp/mnt/dir/3 && echo 'FAIL: unexpected file presents'
+echo "Done!"
diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md
index 022831b..e43124c 100644
--- a/docs/getting_started/index.md
+++ b/docs/getting_started/index.md
@@ -35,7 +35,8 @@
[CrosVM](https://android.googlesource.com/platform/external/crosvm/) is a Rust-based Virtual Machine
Monitor (VMM) originally built for ChromeOS and ported to Android.
-It is not installed in `release` builds of Android but you will find it in `userdebug` and `eng`
+It is not installed in regular Android builds (yet!), but it's installed in the
+VIM3L (yukawa) build, as part of the `com.android.virt` APEX.
builds.
### Spawning your own VMs
@@ -46,31 +47,33 @@
$ adb root
$ adb push <kernel> /data/local/tmp/kernel
$ adb push <ramdisk> /data/local/tmp/ramdisk
-$ adb shell crosvm run --initrd /data/local/tmp/ramdisk /data/local/tmp/kernel
+$ adb shell /apex/com.android.virt/bin/crosvm run --initrd /data/local/tmp/ramdisk /data/local/tmp/kernel
```
-### Syncing system files
+### Building and updating CrosVM
-When you're developing CrosVM, it is handy to update the system files using `adb sync` instead of
-flashing the device each time. It requires root and mounting the system image as writable.
+You can update CrosVM by updating the `com.android.virt` APEX where CrosVM is
+in. If your device already has `com.android.virt` (e.g. VIM3L),
-If you're using the emulator (goldfish), the following instructions will only work if it was
-started with `-writable-system`.
+``` shell
+$ m com.android.virt
+$ adb install out/target/product/<device_name>/system/apex/com.android.virt.apex
+$ adb reboot
+```
-First, disable verity checks on your device, reboot and remount the system partition.
-This is only needed once:
+If it doesn't have the APEX yet, you first need to place it manually to the
+system partition.
+
``` shell
$ adb root
$ adb disable-verity
$ adb reboot
$ adb wait-for-device root
$ adb remount
+$ m com.android.virt
+$ adb sync
+$ adb reboot
```
-Now (re-)build CrosVM and sync the system files:
-``` shell
-$ m crosvm
-$ adb shell stop
-$ adb sync
-$ adb shell start
-```
+Once the APEX is in `/system/apex`, you can use `adb install` to update it
+further.
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
new file mode 100644
index 0000000..fc26f89
--- /dev/null
+++ b/microdroid/Android.bp
@@ -0,0 +1,104 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+microdroid_shell_and_utilities = [
+ "reboot",
+ "sh",
+ "toolbox",
+ "toybox",
+]
+
+microdroid_rootdirs = [
+ "dev",
+ "proc",
+ "sys",
+
+ // TODO(b/180267599): clean up unnecessary partition mount points
+ "system",
+ "system_ext",
+ "vendor",
+ "vendor_dlkm",
+ "product",
+ "odm",
+ "odm_dlkm",
+ "debug_ramdisk",
+ "mnt",
+
+ "apex",
+ "linkerconfig",
+ "second_stage_resources",
+ "postinstall",
+]
+
+microdroid_symlinks = [
+ {
+ target: "/sys/kernel/debug",
+ name: "d",
+ },
+]
+
+android_filesystem {
+ name: "microdroid",
+ use_avb: true,
+ avb_private_key: "microdroid.pem",
+ avb_algorithm: "SHA256_RSA4096",
+ deps: [
+ "init_second_stage",
+ "microdroid_init_rc",
+ "libbinder",
+ "libstdc++",
+ "logcat",
+ "logd",
+ "run-as",
+ "secilc",
+ "adbd",
+ "apexd",
+ "debuggerd",
+ "linker",
+ "servicemanager",
+ "tombstoned",
+ "cgroups.json",
+ ] + microdroid_shell_and_utilities,
+ multilib: {
+ common: {
+ deps: [
+ "com.android.runtime",
+ ],
+ },
+ },
+ base_dir: "system",
+ dirs: microdroid_rootdirs,
+ symlinks: microdroid_symlinks,
+ file_contexts: "microdroid_file_contexts",
+}
+
+prebuilt_etc {
+ name: "microdroid_init_rc",
+ filename: "init.rc",
+ src: "init.rc",
+ relative_install_path: "init/hw",
+ installable: false, // avoid collision with system partition's init.rc
+}
+
+bootimg {
+ name: "microdroid_vendor_boot-5.10",
+ ramdisk_module: "microdroid_ramdisk-5.10",
+ dtb_prebuilt: "dummy_dtb.img",
+ header_version: "3",
+ vendor_boot: true,
+ partition_name: "vendor_boot",
+}
+
+android_filesystem {
+ name: "microdroid_ramdisk-5.10",
+ arch: {
+ arm64: {
+ deps: ["virt_device_prebuilts_kernel_modules-5.10-arm64"],
+ },
+ x86_64: {
+ deps: ["virt_device_prebuilts_kernel_modules-5.10-x86_64"],
+ },
+ },
+ type: "compressed_cpio",
+}
diff --git a/microdroid/dummy_dtb.img b/microdroid/dummy_dtb.img
new file mode 100644
index 0000000..9d6cec7
--- /dev/null
+++ b/microdroid/dummy_dtb.img
@@ -0,0 +1 @@
+workaround
diff --git a/microdroid/init.rc b/microdroid/init.rc
new file mode 100644
index 0000000..3ad22f8
--- /dev/null
+++ b/microdroid/init.rc
@@ -0,0 +1,172 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# init.rc for microdroid. This contains a minimal script plus basic service definitions (e.g. apexd)
+# needed for microdroid to run.
+# TODO(b/179340780): support APEX init scripts
+#
+# IMPORTANT: Do not create world writable files or directories.
+# This is a common source of Android security bugs.
+#
+
+import /init.environ.rc
+
+# Cgroups are mounted right before early-init using list from /etc/cgroups.json
+on early-init
+ start ueventd
+
+ # Run apexd-bootstrap so that APEXes that provide critical libraries
+ # become available. Note that this is executed as exec_start to ensure that
+ # the libraries are available to the processes started after this statement.
+ exec_start apexd-bootstrap
+
+on init
+ # Mount binderfs
+ mkdir /dev/binderfs
+ mount binder binder /dev/binderfs stats=global
+ chmod 0755 /dev/binderfs
+
+ symlink /dev/binderfs/binder /dev/binder
+ symlink /dev/binderfs/hwbinder /dev/hwbinder
+ symlink /dev/binderfs/vndbinder /dev/vndbinder
+
+ chmod 0666 /dev/binderfs/hwbinder
+ chmod 0666 /dev/binderfs/binder
+ chmod 0666 /dev/binderfs/vndbinder
+
+ # Start logd before any other services run to ensure we capture all of their logs.
+ start logd
+
+ start servicemanager
+
+on load_persist_props_action
+ start logd
+ start logd-reinit
+
+# Mount filesystems and start core system services.
+on late-init
+ trigger early-fs
+
+ # Mount fstab in init.{$device}.rc by mount_all command. Optional parameter
+ # '--early' can be specified to skip entries with 'latemount'.
+ # /system and /vendor must be mounted by the end of the fs stage,
+ # while /data is optional.
+ trigger fs
+ trigger post-fs
+
+ # Mount fstab in init.{$device}.rc by mount_all with '--late' parameter
+ # to only mount entries with 'latemount'. This is needed if '--early' is
+ # specified in the previous mount_all command on the fs stage.
+ # With /system mounted and properties form /system + /factory available,
+ # some services can be started.
+ trigger late-fs
+
+ # Load persist properties and override properties (if enabled) from /data.
+ trigger load_persist_props_action
+
+ # Should be before netd, but after apex, properties and logging is available.
+ trigger load_bpf_programs
+
+ # Now we can start zygote for devices with file based encryption
+ trigger zygote-start
+
+ # Remove a file to wake up anything waiting for firmware.
+ trigger firmware_mounts_complete
+
+ trigger early-boot
+ trigger boot
+
+on post-fs
+ # Once everything is setup, no need to modify /.
+ # The bind+remount combination allows this to work in containers.
+ mount rootfs rootfs / remount bind ro nodev
+
+ # Currently, exec_start apexd-bootstrap is enough to run adb.
+ # TODO(b/179342589): uncomment after turning off APEX session on microdroid
+ # start apexd
+ # Wait for apexd to finish activating APEXes before starting more processes.
+ # wait_for_prop apexd.status activated
+
+ start adbd
+
+service ueventd /system/bin/ueventd
+ class core
+ critical
+ seclabel u:r:ueventd:s0
+ shutdown critical
+
+service console /system/bin/sh
+ class core
+ console
+ disabled
+ user shell
+ group shell log readproc
+ seclabel u:r:shell:s0
+ setenv HOSTNAME console
+
+service servicemanager /system/bin/servicemanager
+ class core animation
+ user system
+ group system readproc
+ critical
+ # TODO(b/179342589): uncomment after turning off APEX session on microdroid
+ # onrestart restart apexd
+ onrestart class_restart main
+ shutdown critical
+
+service logd /system/bin/logd
+ socket logd stream 0666 logd logd
+ socket logdr seqpacket 0666 logd logd
+ socket logdw dgram+passcred 0222 logd logd
+ file /proc/kmsg r
+ file /dev/kmsg w
+ user logd
+ group logd system package_info readproc
+ capabilities SYSLOG AUDIT_CONTROL
+ priority 10
+
+service logd-reinit /system/bin/logd --reinit
+ oneshot
+ disabled
+ user logd
+ group logd
+
+# Limit SELinux denial generation to 5/second
+service logd-auditctl /system/bin/auditctl -r 5
+ oneshot
+ disabled
+ user logd
+ group logd
+ capabilities AUDIT_CONTROL
+
+on fs
+ write /dev/event-log-tags "# content owned by logd
+"
+ chown logd logd /dev/event-log-tags
+ chmod 0644 /dev/event-log-tags
+
+on property:sys.boot_completed=1
+ start logd-auditctl
+
+service adbd /system/bin/adbd --root_seclabel=u:r:su:s0
+ class core
+ socket adbd seqpacket 660 system system
+ disabled
+ seclabel u:r:adbd:s0
+
+#TODO(b/179342589): uncomment after turning off APEX session on microdroid
+#service apexd /system/bin/apexd
+# interface aidl apexservice
+# class core
+# user root
+# group system
+# oneshot
+# disabled # does not start with the core class
+# reboot_on_failure reboot,apexd-failed
+
+service apexd-bootstrap /system/bin/apexd --bootstrap
+ user root
+ group system
+ oneshot
+ disabled
+ reboot_on_failure reboot,bootloader,bootstrap-apexd-failed
+
diff --git a/microdroid/microdroid.pem b/microdroid/microdroid.pem
new file mode 100644
index 0000000..2c9de45
--- /dev/null
+++ b/microdroid/microdroid.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAwOn8PTeXaSbNtTx0iJspX7ZJn1dUWHeDtq/r6HPTCEnFuYqV
+c4foSjbF3nIR5T5ZxLeAcTYUl2A/mieUYKG7DkySf5LGlAgWOn4/UXSl14fvESBK
+XA51w9Stg3GyWycYYVkz52mDWfkfoic2UVQJXpsTTcAFDR+u8e5oDxscgLbU7/g2
+mbOv7E4B5n6ItscmjxzRh1IvFVRlPzzyDYQ8UF3JDk0Ndoz7Pze5hF72zbkH6Dmh
+zS91xZFuIJNPjEEjr4Nli9xADhEdsRPeJoFQa+XafGabR2Z2jbVk1UqkD0yxvDhw
+jQaSQ2P6W/flzJnQgRr21FHONU9B6sh+p2ulK3Huda7JIdCExA2SsP+2PVE1Kw9G
+s+85o1/8rfa576qwSLR++ztvZwg3DF/4UQME2oqGLM9TLyj8bdN3Co4XSwsJ7pru
+1WgyQ6S8qLrGld4oxxTbQYoVIhEjUjiOHSKggflZ76Mscnh5hv6ZnuhpbV0b0zSB
+95hP/fbc7VP3ru6ArHA3HF84MQ6i60YPrB5ZpB39wbex1AynbGG0bHw9kgo7L4bF
+rE6p7T6cVazWd0LcWE+WgBHXU2dcVtMPmxUTyzhoeTSVv6eDqagTCe0q3IBZ9h4c
+5q7O6Ipbu/3ckEeRlzBFp7ymjGosqZL0siYtNSKo+FJxvEtbcEiz8xKU6ksCAwEA
+AQKCAgABk+hUN9gtK/wSt2PcmihEeCEyBGzULGYPYHD1fb0EHFZZpiCCyWHE+Z20
+rAopbdEeIsISKvcQJeFfSWOVRr1Y6JdqI7EsFMTZxZ6O8/1HPf1ejn0NM7ES4PJq
+WMu0oyU35preEabZXYg8s0VmdOF+vJXz1NY1vUSZBfIj7XzWMKidLdKEf0u5CwpI
+8E/97rMKCJpOGQX5jq8lTUJNif9USPAUBd1OUujbI4GI653pINJHu/Du1KyJ6R6V
+dZ747q90CAcUlcCQXON5R0hR2EzoFQEsc0ph0xy1G/6vH+UnmuD5QRzqils34eFy
+Txy6B6gE5kuOgPzGwtcvLGScyeyX4rjA1RS+Zst8PQG35D2WSLENbITDZ2u2n146
+C/TWWd6F/qE8cSCI8/ZQ5iEarw8Hp3sVe7KRqwG0n2ESlW/px6E9DmzbjgW/BLwB
+diBLmHVmOT5BZPqbjNiwsX/aLmf7cjtnttFuadf2roaa6jlyQzsqRyVzJKH2H9iD
+DFeakMAjngVW0XnWrpgehoG5JfWDbXWcg8Kt5ZlegSOs1KGBAj69RkotJWjQ2rlP
+TijMuHgXVUIvqQp5BIc8UnVqX3kzljLd2eVM0K6ZcPnh+yBhk/Jd88whxkONgbs4
+g8EQvov4c8UL1Sx2ZTDDCN0WK0dHG1IhtB/E014yFIq/qqQMQQKCAQEA8c6tw7o+
+36Uh1uB5oEw6zSsxY7x13cW9tis9slD4/bXiXJwTOjs1/+S+wrmJ4UoOarygFgh0
+xWc1YGs20UWBkO4T9jaZJBqESH96RvQn7ubBEHYECClPhRuwbWWc+UzHjgusmuYl
+xPtY9MgtI5AF6O2MEqzCWbLZzFhhe/V6FKKcyV3aq6vOEjCeTz0/ntfcfS08T/5l
+kayny1nlVju3eig5WK30+X66rN0+eYS1PqLInlzSpFGWMJA6s+uI+1ze506QWKFI
+vNPf4Wtm8VFksRSbrYBriKg63UsgOGfaq5zmgJ5Yst8tIheKLJE5meLjGcea8Dhe
+gz+Jtj+Es9WdVQKCAQEAzDymkIOrwh8j45zFuH+6vataWkGusloV3P6dE0V5NDPH
+Oa/EfOqZ3qQKiS8bHRcdEwRQK+TSeOWi6phDQVu2lzwBA1PySxz7T1T38RKP9myu
+yPFah6bLKntvE36fj3U7RaU2gf8GXGMuwoRWoGDQNHQ+YusKOFreht7dlclqLKKp
+Xct7lKrg3nOtJI3P/jxXZwE/+UenqS5M8QOd7X0/77d9VJBYAhUZ9A5cOuk327e+
+zxEeg23UndcqmJRj0zsxYR+d/NaDV9yf8usAHRv/S3nwVN6YkmmnVNY46ukCNlxV
+G9VZcM1PNWFu3MdGs7f8u973DXYMC3Et3JD8/XNpHwKCAQEAoN8g6dOx2raRAdUL
+9eJhSHhQ/oq2W9ofsd+ox0ZpsNleBMDtZdNYxKsZYpFvssvDNa3ST3ZGwcI9V61c
+pfO5FIPgEfEXB1cA5P6yihwLqTwp+9qYKhnZxrO6N73fplg3d0A7ED7Hp/qUnx3C
+MiOyWtoXU6FOF8EOd43hJUgWdT4OU0W2onnFuoJQAGW+Ot5f2YPL2WhliHo2k+p1
+0l4eXg3+wQnSrl5qsXDsDIqGzmocUIduuFVzN1HtTCAKXTjXL0osbFq4+q93BhCf
+RzMga1NLky2Z3SwXJXzCreQiLgRPsggm+LHT5BeHHMltafzxnAgZA3JtJqKR6wbt
+MXuPlQKCAQBRaWvL93Cj9N3NgVSfcurxaDif+ca1eYt1iFj8sZja4jjsGdBtKucj
+BKjeflSEiA4i4nlCxffQEVmbwg4tI23pYxXUScYvguH6gZhEFG4mgLQ7jVnU8PvT
+d1iToMnh2Y1C+dR8hTf34+Fj0HTngS/9eMjVjXOJe09w263gY2VbXYu9w7sDTApP
+IjVGePEMGumMOcdSiumeWsJ44EQ3wtGVsMLYzqEGU3sA+ihxoz2M6/W3fM94/HLg
+aECmiaAxN0s7t+q2Opwojd2Ea4CkGeDuKTFP41tWIZ9+BdaybSdgm/WhHq6EdJK6
+aJhUkWrxgMnR6dL5HrE2bmslnPX74eFpAoIBAE4epxTb9SstOxhwFA1Vvrz5G64k
+n61OgXj49QPpBcBOGXVH1l8L6YQJO+bCVESiACn2Q/lJ8mp6lRI2KtsemUfsa5X7
+XV1qldtTFobMmB4JZDTEM882TWainK2+yhGfEGWrdeCWDBSWqQj/VLpAUqQqdJFU
++GRvPQV+DSk4Az7zGpyrtjpiZYbRjWHBT2FAwSRS/iqEHjKrvrCqlaq16Un0JJFb
+0ztbCC2tDaVlwpojPnPE2sgojYeGQ0ghB1os9K4RFaoW9l+Pfye5vJ9G0/wmjVhY
+HQaN/WJp8k79mXacCQAD51xtWnJePGd4D5WbA855jo0Dov/mOtwf0Ahbl6w=
+-----END RSA PRIVATE KEY-----
diff --git a/microdroid/microdroid_file_contexts b/microdroid/microdroid_file_contexts
new file mode 100644
index 0000000..6d36e7c
--- /dev/null
+++ b/microdroid/microdroid_file_contexts
@@ -0,0 +1,789 @@
+# This is a copy of system/sepolicy/private/file_contexts to build microdroid.img.
+# TODO(inseob): Remove unnecessary entries after experiment
+
+###########################################
+# Root
+/ u:object_r:rootfs:s0
+
+# Data files
+/adb_keys u:object_r:adb_keys_file:s0
+/build\.prop u:object_r:rootfs:s0
+/default\.prop u:object_r:rootfs:s0
+/fstab\..* u:object_r:rootfs:s0
+/init\..* u:object_r:rootfs:s0
+/res(/.*)? u:object_r:rootfs:s0
+/selinux_version u:object_r:rootfs:s0
+/ueventd\..* u:object_r:rootfs:s0
+/verity_key u:object_r:rootfs:s0
+
+# Executables
+/init u:object_r:init_exec:s0
+/sbin(/.*)? u:object_r:rootfs:s0
+
+# For kernel modules
+/lib(/.*)? u:object_r:rootfs:s0
+
+# Empty directories
+/lost\+found u:object_r:rootfs:s0
+/acct u:object_r:cgroup:s0
+/config u:object_r:rootfs:s0
+/data_mirror u:object_r:mirror_data_file:s0
+/debug_ramdisk u:object_r:tmpfs:s0
+/mnt u:object_r:tmpfs:s0
+/postinstall u:object_r:postinstall_mnt_dir:s0
+/postinstall/apex u:object_r:postinstall_apex_mnt_dir:s0
+/proc u:object_r:rootfs:s0
+/second_stage_resources u:object_r:tmpfs:s0
+/sys u:object_r:sysfs:s0
+/apex u:object_r:apex_mnt_dir:s0
+
+/apex/(\.(bootstrap|default)-)?apex-info-list.xml u:object_r:apex_info_file:s0
+
+# Symlinks
+/bin u:object_r:rootfs:s0
+/bugreports u:object_r:rootfs:s0
+/charger u:object_r:rootfs:s0
+/d u:object_r:rootfs:s0
+/etc u:object_r:rootfs:s0
+/sdcard u:object_r:rootfs:s0
+
+# SELinux policy files
+/vendor_file_contexts u:object_r:file_contexts_file:s0
+/nonplat_file_contexts u:object_r:file_contexts_file:s0
+/plat_file_contexts u:object_r:file_contexts_file:s0
+/product_file_contexts u:object_r:file_contexts_file:s0
+/mapping_sepolicy\.cil u:object_r:sepolicy_file:s0
+/nonplat_sepolicy\.cil u:object_r:sepolicy_file:s0
+/plat_sepolicy\.cil u:object_r:sepolicy_file:s0
+/plat_property_contexts u:object_r:property_contexts_file:s0
+/product_property_contexts u:object_r:property_contexts_file:s0
+/nonplat_property_contexts u:object_r:property_contexts_file:s0
+/vendor_property_contexts u:object_r:property_contexts_file:s0
+/seapp_contexts u:object_r:seapp_contexts_file:s0
+/nonplat_seapp_contexts u:object_r:seapp_contexts_file:s0
+/vendor_seapp_contexts u:object_r:seapp_contexts_file:s0
+/plat_seapp_contexts u:object_r:seapp_contexts_file:s0
+/sepolicy u:object_r:sepolicy_file:s0
+/plat_service_contexts u:object_r:service_contexts_file:s0
+/plat_hwservice_contexts u:object_r:hwservice_contexts_file:s0
+/plat_keystore2_key_contexts u:object_r:keystore2_key_contexts_file:s0
+/nonplat_service_contexts u:object_r:nonplat_service_contexts_file:s0
+# Use nonplat_service_contexts_file to allow servicemanager to read it
+# on non full-treble devices.
+/vendor_service_contexts u:object_r:nonplat_service_contexts_file:s0
+/nonplat_hwservice_contexts u:object_r:hwservice_contexts_file:s0
+/vendor_hwservice_contexts u:object_r:hwservice_contexts_file:s0
+/vndservice_contexts u:object_r:vndservice_contexts_file:s0
+
+##########################
+# Devices
+#
+/dev(/.*)? u:object_r:device:s0
+/dev/adf[0-9]* u:object_r:graphics_device:s0
+/dev/adf-interface[0-9]*\.[0-9]* u:object_r:graphics_device:s0
+/dev/adf-overlay-engine[0-9]*\.[0-9]* u:object_r:graphics_device:s0
+/dev/ashmem u:object_r:ashmem_device:s0
+/dev/ashmem(.*)? u:object_r:ashmem_libcutils_device:s0
+/dev/audio.* u:object_r:audio_device:s0
+/dev/binder u:object_r:binder_device:s0
+/dev/block(/.*)? u:object_r:block_device:s0
+/dev/block/dm-[0-9]+ u:object_r:dm_device:s0
+/dev/block/loop[0-9]* u:object_r:loop_device:s0
+/dev/block/vold/.+ u:object_r:vold_device:s0
+/dev/block/ram[0-9]* u:object_r:ram_device:s0
+/dev/block/zram[0-9]* u:object_r:ram_device:s0
+/dev/boringssl/selftest(/.*)? u:object_r:boringssl_self_test_marker:s0
+/dev/bus/usb(.*)? u:object_r:usb_device:s0
+/dev/console u:object_r:console_device:s0
+/dev/cpu_variant:.* u:object_r:dev_cpu_variant:s0
+/dev/dma_heap(/.*)? u:object_r:dmabuf_heap_device:s0
+/dev/dma_heap/system u:object_r:dmabuf_system_heap_device:s0
+/dev/dma_heap/system-uncached u:object_r:dmabuf_system_heap_device:s0
+/dev/dma_heap/system-secure u:object_r:dmabuf_system_secure_heap_device:s0
+/dev/dm-user(/.*)? u:object_r:dm_user_device:s0
+/dev/device-mapper u:object_r:dm_device:s0
+/dev/eac u:object_r:audio_device:s0
+/dev/event-log-tags u:object_r:runtime_event_log_tags_file:s0
+/dev/cgroup_info(/.*)? u:object_r:cgroup_rc_file:s0
+/dev/fscklogs(/.*)? u:object_r:fscklogs:s0
+/dev/fuse u:object_r:fuse_device:s0
+/dev/gnss[0-9]+ u:object_r:gnss_device:s0
+/dev/graphics(/.*)? u:object_r:graphics_device:s0
+/dev/hw_random u:object_r:hw_random_device:s0
+/dev/hwbinder u:object_r:hwbinder_device:s0
+/dev/input(/.*)? u:object_r:input_device:s0
+/dev/iio:device[0-9]+ u:object_r:iio_device:s0
+/dev/ion u:object_r:ion_device:s0
+/dev/keychord u:object_r:keychord_device:s0
+/dev/loop-control u:object_r:loop_control_device:s0
+/dev/modem.* u:object_r:radio_device:s0
+/dev/mtp_usb u:object_r:mtp_device:s0
+/dev/pmsg0 u:object_r:pmsg_device:s0
+/dev/pn544 u:object_r:nfc_device:s0
+/dev/port u:object_r:port_device:s0
+/dev/ppp u:object_r:ppp_device:s0
+/dev/ptmx u:object_r:ptmx_device:s0
+/dev/pvrsrvkm u:object_r:gpu_device:s0
+/dev/kmsg u:object_r:kmsg_device:s0
+/dev/kmsg_debug u:object_r:kmsg_debug_device:s0
+/dev/null u:object_r:null_device:s0
+/dev/nvhdcp1 u:object_r:video_device:s0
+/dev/random u:object_r:random_device:s0
+/dev/rpmsg-omx[0-9] u:object_r:rpmsg_device:s0
+/dev/rproc_user u:object_r:rpmsg_device:s0
+/dev/rtc[0-9] u:object_r:rtc_device:s0
+/dev/snd(/.*)? u:object_r:audio_device:s0
+/dev/socket(/.*)? u:object_r:socket_device:s0
+/dev/socket/adbd u:object_r:adbd_socket:s0
+/dev/socket/dnsproxyd u:object_r:dnsproxyd_socket:s0
+/dev/socket/dumpstate u:object_r:dumpstate_socket:s0
+/dev/socket/fwmarkd u:object_r:fwmarkd_socket:s0
+/dev/socket/lmkd u:object_r:lmkd_socket:s0
+/dev/socket/logd u:object_r:logd_socket:s0
+/dev/socket/logdr u:object_r:logdr_socket:s0
+/dev/socket/logdw u:object_r:logdw_socket:s0
+/dev/socket/statsdw u:object_r:statsdw_socket:s0
+/dev/socket/mdns u:object_r:mdns_socket:s0
+/dev/socket/mdnsd u:object_r:mdnsd_socket:s0
+/dev/socket/mtpd u:object_r:mtpd_socket:s0
+/dev/socket/pdx/system/buffer_hub u:object_r:pdx_bufferhub_dir:s0
+/dev/socket/pdx/system/buffer_hub/client u:object_r:pdx_bufferhub_client_endpoint_socket:s0
+/dev/socket/pdx/system/performance u:object_r:pdx_performance_dir:s0
+/dev/socket/pdx/system/performance/client u:object_r:pdx_performance_client_endpoint_socket:s0
+/dev/socket/pdx/system/vr/display u:object_r:pdx_display_dir:s0
+/dev/socket/pdx/system/vr/display/client u:object_r:pdx_display_client_endpoint_socket:s0
+/dev/socket/pdx/system/vr/display/manager u:object_r:pdx_display_manager_endpoint_socket:s0
+/dev/socket/pdx/system/vr/display/screenshot u:object_r:pdx_display_screenshot_endpoint_socket:s0
+/dev/socket/pdx/system/vr/display/vsync u:object_r:pdx_display_vsync_endpoint_socket:s0
+/dev/socket/property_service u:object_r:property_socket:s0
+/dev/socket/racoon u:object_r:racoon_socket:s0
+/dev/socket/recovery u:object_r:recovery_socket:s0
+/dev/socket/rild u:object_r:rild_socket:s0
+/dev/socket/rild-debug u:object_r:rild_debug_socket:s0
+/dev/socket/snapuserd u:object_r:snapuserd_socket:s0
+/dev/socket/tombstoned_crash u:object_r:tombstoned_crash_socket:s0
+/dev/socket/tombstoned_java_trace u:object_r:tombstoned_java_trace_socket:s0
+/dev/socket/tombstoned_intercept u:object_r:tombstoned_intercept_socket:s0
+/dev/socket/traced_consumer u:object_r:traced_consumer_socket:s0
+/dev/socket/traced_perf u:object_r:traced_perf_socket:s0
+/dev/socket/traced_producer u:object_r:traced_producer_socket:s0
+/dev/socket/heapprofd u:object_r:heapprofd_socket:s0
+/dev/socket/uncrypt u:object_r:uncrypt_socket:s0
+/dev/socket/wpa_eth[0-9] u:object_r:wpa_socket:s0
+/dev/socket/wpa_wlan[0-9] u:object_r:wpa_socket:s0
+/dev/socket/zygote u:object_r:zygote_socket:s0
+/dev/socket/zygote_secondary u:object_r:zygote_socket:s0
+/dev/socket/usap_pool_primary u:object_r:zygote_socket:s0
+/dev/socket/usap_pool_secondary u:object_r:zygote_socket:s0
+/dev/spdif_out.* u:object_r:audio_device:s0
+/dev/tty u:object_r:owntty_device:s0
+/dev/tty[0-9]* u:object_r:tty_device:s0
+/dev/ttyS[0-9]* u:object_r:serial_device:s0
+/dev/ttyUSB[0-9]* u:object_r:usb_serial_device:s0
+/dev/ttyACM[0-9]* u:object_r:usb_serial_device:s0
+/dev/tun u:object_r:tun_device:s0
+/dev/uhid u:object_r:uhid_device:s0
+/dev/uinput u:object_r:uhid_device:s0
+/dev/uio[0-9]* u:object_r:uio_device:s0
+/dev/urandom u:object_r:random_device:s0
+/dev/usb_accessory u:object_r:usbaccessory_device:s0
+/dev/v4l-touch[0-9]* u:object_r:input_device:s0
+/dev/video[0-9]* u:object_r:video_device:s0
+/dev/vndbinder u:object_r:vndbinder_device:s0
+/dev/watchdog u:object_r:watchdog_device:s0
+/dev/xt_qtaguid u:object_r:qtaguid_device:s0
+/dev/zero u:object_r:zero_device:s0
+/dev/__properties__ u:object_r:properties_device:s0
+/dev/__properties__/property_info u:object_r:property_info:s0
+#############################
+# Linker configuration
+#
+/linkerconfig(/.*)? u:object_r:linkerconfig_file:s0
+#############################
+# System files
+#
+/system(/.*)? u:object_r:system_file:s0
+/system/apex/com.android.art u:object_r:art_apex_dir:s0
+/system/lib(64)?(/.*)? u:object_r:system_lib_file:s0
+/system/lib(64)?/bootstrap(/.*)? u:object_r:system_bootstrap_lib_file:s0
+/system/bin/atrace u:object_r:atrace_exec:s0
+/system/bin/auditctl u:object_r:auditctl_exec:s0
+/system/bin/bcc u:object_r:rs_exec:s0
+/system/bin/blank_screen u:object_r:blank_screen_exec:s0
+/system/bin/boringssl_self_test(32|64) u:object_r:boringssl_self_test_exec:s0
+/system/bin/charger u:object_r:charger_exec:s0
+/system/bin/canhalconfigurator u:object_r:canhalconfigurator_exec:s0
+/system/bin/e2fsdroid u:object_r:e2fs_exec:s0
+/system/bin/mke2fs u:object_r:e2fs_exec:s0
+/system/bin/e2fsck -- u:object_r:fsck_exec:s0
+/system/bin/fsck\.exfat -- u:object_r:fsck_exec:s0
+/system/bin/fsck\.f2fs -- u:object_r:fsck_exec:s0
+/system/bin/init u:object_r:init_exec:s0
+# TODO(/123600489): merge mini-keyctl into toybox
+/system/bin/mini-keyctl -- u:object_r:toolbox_exec:s0
+/system/bin/fsverity_init u:object_r:fsverity_init_exec:s0
+/system/bin/sload_f2fs -- u:object_r:e2fs_exec:s0
+/system/bin/make_f2fs -- u:object_r:e2fs_exec:s0
+/system/bin/fsck_msdos -- u:object_r:fsck_exec:s0
+/system/bin/tcpdump -- u:object_r:tcpdump_exec:s0
+/system/bin/tune2fs -- u:object_r:fsck_exec:s0
+/system/bin/resize2fs -- u:object_r:fsck_exec:s0
+/system/bin/toolbox -- u:object_r:toolbox_exec:s0
+/system/bin/toybox -- u:object_r:toolbox_exec:s0
+/system/bin/ld\.mc u:object_r:rs_exec:s0
+/system/bin/logcat -- u:object_r:logcat_exec:s0
+/system/bin/logcatd -- u:object_r:logcat_exec:s0
+/system/bin/sh -- u:object_r:shell_exec:s0
+/system/bin/run-as -- u:object_r:runas_exec:s0
+/system/bin/bootanimation u:object_r:bootanim_exec:s0
+/system/bin/bootstat u:object_r:bootstat_exec:s0
+/system/bin/app_process32 u:object_r:zygote_exec:s0
+/system/bin/app_process64 u:object_r:zygote_exec:s0
+/system/bin/servicemanager u:object_r:servicemanager_exec:s0
+/system/bin/hwservicemanager u:object_r:hwservicemanager_exec:s0
+/system/bin/surfaceflinger u:object_r:surfaceflinger_exec:s0
+/system/bin/gpuservice u:object_r:gpuservice_exec:s0
+/system/bin/bufferhubd u:object_r:bufferhubd_exec:s0
+/system/bin/performanced u:object_r:performanced_exec:s0
+/system/bin/drmserver u:object_r:drmserver_exec:s0
+/system/bin/dumpstate u:object_r:dumpstate_exec:s0
+/system/bin/incident u:object_r:incident_exec:s0
+/system/bin/incidentd u:object_r:incidentd_exec:s0
+/system/bin/incident_helper u:object_r:incident_helper_exec:s0
+/system/bin/iw u:object_r:iw_exec:s0
+/system/bin/netutils-wrapper-1\.0 u:object_r:netutils_wrapper_exec:s0
+/system/bin/vold u:object_r:vold_exec:s0
+/system/bin/netd u:object_r:netd_exec:s0
+/system/bin/wificond u:object_r:wificond_exec:s0
+/system/bin/audioserver u:object_r:audioserver_exec:s0
+/system/bin/mediadrmserver u:object_r:mediadrmserver_exec:s0
+/system/bin/mediaserver u:object_r:mediaserver_exec:s0
+/system/bin/mediametrics u:object_r:mediametrics_exec:s0
+/system/bin/cameraserver u:object_r:cameraserver_exec:s0
+/system/bin/mediaextractor u:object_r:mediaextractor_exec:s0
+/system/bin/mediaswcodec u:object_r:mediaswcodec_exec:s0
+/system/bin/mediatranscoding u:object_r:mediatranscoding_exec:s0
+/system/bin/mediatuner u:object_r:mediatuner_exec:s0
+/system/bin/mdnsd u:object_r:mdnsd_exec:s0
+/system/bin/installd u:object_r:installd_exec:s0
+/system/bin/otapreopt_chroot u:object_r:otapreopt_chroot_exec:s0
+/system/bin/otapreopt_slot u:object_r:otapreopt_slot_exec:s0
+/system/bin/credstore u:object_r:credstore_exec:s0
+/system/bin/keystore u:object_r:keystore_exec:s0
+/system/bin/keystore2 u:object_r:keystore_exec:s0
+/system/bin/fingerprintd u:object_r:fingerprintd_exec:s0
+/system/bin/gatekeeperd u:object_r:gatekeeperd_exec:s0
+/system/bin/tombstoned u:object_r:tombstoned_exec:s0
+/system/bin/recovery-persist u:object_r:recovery_persist_exec:s0
+/system/bin/recovery-refresh u:object_r:recovery_refresh_exec:s0
+/system/bin/sdcard u:object_r:sdcardd_exec:s0
+/system/bin/snapshotctl u:object_r:snapshotctl_exec:s0
+/system/bin/dhcpcd u:object_r:dhcp_exec:s0
+/system/bin/dhcpcd-6\.8\.2 u:object_r:dhcp_exec:s0
+/system/bin/mtpd u:object_r:mtp_exec:s0
+/system/bin/pppd u:object_r:ppp_exec:s0
+/system/bin/racoon u:object_r:racoon_exec:s0
+/system/xbin/su u:object_r:su_exec:s0
+/system/bin/dnsmasq u:object_r:dnsmasq_exec:s0
+/system/bin/healthd u:object_r:healthd_exec:s0
+/system/bin/clatd u:object_r:clatd_exec:s0
+/system/bin/linker(64)? u:object_r:system_linker_exec:s0
+/system/bin/linkerconfig u:object_r:linkerconfig_exec:s0
+/system/bin/bootstrap/linker(64)? u:object_r:system_linker_exec:s0
+/system/bin/bootstrap/linkerconfig u:object_r:linkerconfig_exec:s0
+/system/bin/llkd u:object_r:llkd_exec:s0
+/system/bin/lmkd u:object_r:lmkd_exec:s0
+/system/bin/usbd u:object_r:usbd_exec:s0
+/system/bin/inputflinger u:object_r:inputflinger_exec:s0
+/system/bin/logd u:object_r:logd_exec:s0
+/system/bin/lpdumpd u:object_r:lpdumpd_exec:s0
+/system/bin/rss_hwm_reset u:object_r:rss_hwm_reset_exec:s0
+/system/bin/perfetto u:object_r:perfetto_exec:s0
+/system/bin/traced u:object_r:traced_exec:s0
+/system/bin/traced_perf u:object_r:traced_perf_exec:s0
+/system/bin/traced_probes u:object_r:traced_probes_exec:s0
+/system/bin/heapprofd u:object_r:heapprofd_exec:s0
+/system/bin/uncrypt u:object_r:uncrypt_exec:s0
+/system/bin/update_verifier u:object_r:update_verifier_exec:s0
+/system/bin/logwrapper u:object_r:system_file:s0
+/system/bin/vdc u:object_r:vdc_exec:s0
+/system/bin/cppreopts\.sh u:object_r:cppreopts_exec:s0
+/system/bin/preloads_copy\.sh u:object_r:preloads_copy_exec:s0
+/system/bin/preopt2cachename u:object_r:preopt2cachename_exec:s0
+/system/bin/viewcompiler u:object_r:viewcompiler_exec:s0
+/system/bin/iorapd u:object_r:iorapd_exec:s0
+/system/bin/iorap\.inode2filename u:object_r:iorap_inode2filename_exec:s0
+/system/bin/iorap\.prefetcherd u:object_r:iorap_prefetcherd_exec:s0
+/system/bin/sgdisk u:object_r:sgdisk_exec:s0
+/system/bin/blkid u:object_r:blkid_exec:s0
+/system/bin/tzdatacheck u:object_r:tzdatacheck_exec:s0
+/system/bin/flags_health_check -- u:object_r:flags_health_check_exec:s0
+/system/bin/idmap u:object_r:idmap_exec:s0
+/system/bin/idmap2(d)? u:object_r:idmap_exec:s0
+/system/bin/update_engine u:object_r:update_engine_exec:s0
+/system/bin/profcollectd u:object_r:profcollectd_exec:s0
+/system/bin/profcollectctl u:object_r:profcollectd_exec:s0
+/system/bin/storaged u:object_r:storaged_exec:s0
+/system/bin/wpantund u:object_r:wpantund_exec:s0
+/system/bin/virtual_touchpad u:object_r:virtual_touchpad_exec:s0
+/system/bin/hw/android\.frameworks\.bufferhub@1\.0-service u:object_r:fwk_bufferhub_exec:s0
+/system/bin/hw/android\.hidl\.allocator@1\.0-service u:object_r:hal_allocator_default_exec:s0
+/system/bin/hw/android\.system\.suspend@1\.0-service u:object_r:system_suspend_exec:s0
+/system/etc/cgroups\.json u:object_r:cgroup_desc_file:s0
+/system/etc/task_profiles/cgroups_[0-9]+\.json u:object_r:cgroup_desc_api_file:s0
+/system/etc/event-log-tags u:object_r:system_event_log_tags_file:s0
+/system/etc/group u:object_r:system_group_file:s0
+/system/etc/ld\.config.* u:object_r:system_linker_config_file:s0
+/system/etc/passwd u:object_r:system_passwd_file:s0
+/system/etc/seccomp_policy(/.*)? u:object_r:system_seccomp_policy_file:s0
+/system/etc/security/cacerts(/.*)? u:object_r:system_security_cacerts_file:s0
+/system/etc/selinux/mapping/[0-9]+\.[0-9]+\.cil u:object_r:sepolicy_file:s0
+/system/etc/selinux/plat_mac_permissions\.xml u:object_r:mac_perms_file:s0
+/system/etc/selinux/plat_property_contexts u:object_r:property_contexts_file:s0
+/system/etc/selinux/plat_service_contexts u:object_r:service_contexts_file:s0
+/system/etc/selinux/plat_hwservice_contexts u:object_r:hwservice_contexts_file:s0
+/system/etc/selinux/plat_keystore2_key_contexts u:object_r:keystore2_key_contexts_file:s0
+/system/etc/selinux/plat_file_contexts u:object_r:file_contexts_file:s0
+/system/etc/selinux/plat_seapp_contexts u:object_r:seapp_contexts_file:s0
+/system/etc/selinux/plat_sepolicy\.cil u:object_r:sepolicy_file:s0
+/system/etc/selinux/plat_and_mapping_sepolicy\.cil\.sha256 u:object_r:sepolicy_file:s0
+/system/etc/task_profiles\.json u:object_r:task_profiles_file:s0
+/system/etc/task_profiles/task_profiles_[0-9]+\.json u:object_r:task_profiles_api_file:s0
+/system/usr/share/zoneinfo(/.*)? u:object_r:system_zoneinfo_file:s0
+/system/bin/vr_hwc u:object_r:vr_hwc_exec:s0
+/system/bin/adbd u:object_r:adbd_exec:s0
+/system/bin/vold_prepare_subdirs u:object_r:vold_prepare_subdirs_exec:s0
+/system/bin/stats u:object_r:stats_exec:s0
+/system/bin/statsd u:object_r:statsd_exec:s0
+/system/bin/bpfloader u:object_r:bpfloader_exec:s0
+/system/bin/wait_for_keymaster u:object_r:wait_for_keymaster_exec:s0
+/system/bin/watchdogd u:object_r:watchdogd_exec:s0
+/system/bin/apexd u:object_r:apexd_exec:s0
+/system/bin/gsid u:object_r:gsid_exec:s0
+/system/bin/simpleperf u:object_r:simpleperf_exec:s0
+/system/bin/simpleperf_app_runner u:object_r:simpleperf_app_runner_exec:s0
+/system/bin/notify_traceur\.sh u:object_r:notify_traceur_exec:s0
+/system/bin/migrate_legacy_obb_data\.sh u:object_r:migrate_legacy_obb_data_exec:s0
+/system/bin/android\.frameworks\.automotive\.display@1\.0-service u:object_r:automotive_display_service_exec:s0
+/system/bin/snapuserd u:object_r:snapuserd_exec:s0
+
+#############################
+# Vendor files
+#
+/(vendor|system/vendor)(/.*)? u:object_r:vendor_file:s0
+/(vendor|system/vendor)/bin/sh u:object_r:vendor_shell_exec:s0
+/(vendor|system/vendor)/bin/toybox_vendor u:object_r:vendor_toolbox_exec:s0
+/(vendor|system/vendor)/bin/toolbox u:object_r:vendor_toolbox_exec:s0
+/(vendor|system/vendor)/etc(/.*)? u:object_r:vendor_configs_file:s0
+/(vendor|system/vendor)/etc/cgroups\.json u:object_r:vendor_cgroup_desc_file:s0
+/(vendor|system/vendor)/etc/task_profiles\.json u:object_r:vendor_task_profiles_file:s0
+
+/(vendor|system/vendor)/lib(64)?/egl(/.*)? u:object_r:same_process_hal_file:s0
+
+/(vendor|system/vendor)/lib(64)?/vndk-sp(/.*)? u:object_r:vndk_sp_file:s0
+
+/(vendor|system/vendor)/manifest\.xml u:object_r:vendor_configs_file:s0
+/(vendor|system/vendor)/compatibility_matrix\.xml u:object_r:vendor_configs_file:s0
+/(vendor|system/vendor)/etc/vintf(/.*)? u:object_r:vendor_configs_file:s0
+/(vendor|system/vendor)/app(/.*)? u:object_r:vendor_app_file:s0
+/(vendor|system/vendor)/priv-app(/.*)? u:object_r:vendor_app_file:s0
+/(vendor|system/vendor)/overlay(/.*)? u:object_r:vendor_overlay_file:s0
+/(vendor|system/vendor)/framework(/.*)? u:object_r:vendor_framework_file:s0
+
+/(vendor|system/vendor)/apex(/[^/]+){0,2} u:object_r:vendor_apex_file:s0
+/(vendor|system/vendor)/bin/misc_writer u:object_r:vendor_misc_writer_exec:s0
+/(vendor|system/vendor)/bin/boringssl_self_test(32|64) u:object_r:vendor_boringssl_self_test_exec:s0
+
+# HAL location
+/(vendor|system/vendor)/lib(64)?/hw u:object_r:vendor_hal_file:s0
+
+/(vendor|system/vendor)/etc/selinux/nonplat_service_contexts u:object_r:nonplat_service_contexts_file:s0
+
+/(vendor|system/vendor)/etc/selinux/vendor_service_contexts u:object_r:vendor_service_contexts_file:s0
+
+/(vendor|system/vendor)/bin/install-recovery\.sh u:object_r:vendor_install_recovery_exec:s0
+
+#############################
+# OEM and ODM files
+#
+/(odm|vendor/odm)(/.*)? u:object_r:vendor_file:s0
+/(odm|vendor/odm)/lib(64)?/egl(/.*)? u:object_r:same_process_hal_file:s0
+/(odm|vendor/odm)/lib(64)?/hw u:object_r:vendor_hal_file:s0
+/(odm|vendor/odm)/lib(64)?/vndk-sp(/.*)? u:object_r:vndk_sp_file:s0
+/(odm|vendor/odm)/bin/sh u:object_r:vendor_shell_exec:s0
+/(odm|vendor/odm)/etc(/.*)? u:object_r:vendor_configs_file:s0
+/(odm|vendor/odm)/app(/.*)? u:object_r:vendor_app_file:s0
+/(odm|vendor/odm)/priv-app(/.*)? u:object_r:vendor_app_file:s0
+/(odm|vendor/odm)/overlay(/.*)? u:object_r:vendor_overlay_file:s0
+/(odm|vendor/odm)/framework(/.*)? u:object_r:vendor_framework_file:s0
+
+# Input configuration
+/(odm|vendor/odm|vendor|system/vendor)/usr/keylayout(/.*)?\.kl u:object_r:vendor_keylayout_file:s0
+/(odm|vendor/odm|vendor|system/vendor)/usr/keychars(/.*)?\.kcm u:object_r:vendor_keychars_file:s0
+/(odm|vendor/odm|vendor|system/vendor)/usr/idc(/.*)?\.idc u:object_r:vendor_idc_file:s0
+
+/oem(/.*)? u:object_r:oemfs:s0
+/oem/overlay(/.*)? u:object_r:vendor_overlay_file:s0
+
+# The precompiled monolithic sepolicy will be under /odm only when
+# BOARD_USES_ODMIMAGE is true: a separate odm.img is built.
+/odm/etc/selinux/precompiled_sepolicy u:object_r:sepolicy_file:s0
+/odm/etc/selinux/precompiled_sepolicy\.plat_and_mapping\.sha256 u:object_r:sepolicy_file:s0
+
+/(odm|vendor/odm)/etc/selinux/odm_sepolicy\.cil u:object_r:sepolicy_file:s0
+/(odm|vendor/odm)/etc/selinux/odm_file_contexts u:object_r:file_contexts_file:s0
+/(odm|vendor/odm)/etc/selinux/odm_seapp_contexts u:object_r:seapp_contexts_file:s0
+/(odm|vendor/odm)/etc/selinux/odm_property_contexts u:object_r:property_contexts_file:s0
+/(odm|vendor/odm)/etc/selinux/odm_hwservice_contexts u:object_r:hwservice_contexts_file:s0
+/(odm|vendor/odm)/etc/selinux/odm_keystore2_key_contexts u:object_r:keystore2_key_contexts_file:s0
+/(odm|vendor/odm)/etc/selinux/odm_mac_permissions\.xml u:object_r:mac_perms_file:s0
+
+#############################
+# Product files
+#
+/(product|system/product)(/.*)? u:object_r:system_file:s0
+/(product|system/product)/etc/group u:object_r:system_group_file:s0
+/(product|system/product)/etc/passwd u:object_r:system_passwd_file:s0
+/(product|system/product)/overlay(/.*)? u:object_r:vendor_overlay_file:s0
+
+/(product|system/product)/etc/selinux/product_file_contexts u:object_r:file_contexts_file:s0
+/(product|system/product)/etc/selinux/product_hwservice_contexts u:object_r:hwservice_contexts_file:s0
+/(product|system/product)/etc/selinux/product_keystore2_key_contexts u:object_r:keystore2_key_contexts_file:s0
+/(product|system/product)/etc/selinux/product_property_contexts u:object_r:property_contexts_file:s0
+/(product|system/product)/etc/selinux/product_seapp_contexts u:object_r:seapp_contexts_file:s0
+/(product|system/product)/etc/selinux/product_service_contexts u:object_r:service_contexts_file:s0
+/(product|system/product)/etc/selinux/product_mac_permissions\.xml u:object_r:mac_perms_file:s0
+
+/(product|system/product)/lib(64)?(/.*)? u:object_r:system_lib_file:s0
+
+#############################
+# SystemExt files
+#
+/(system_ext|system/system_ext)(/.*)? u:object_r:system_file:s0
+/(system_ext|system/system_ext)/etc/group u:object_r:system_group_file:s0
+/(system_ext|system/system_ext)/etc/passwd u:object_r:system_passwd_file:s0
+/(system_ext|system/system_ext)/overlay(/.*)? u:object_r:vendor_overlay_file:s0
+
+/(system_ext|system/system_ext)/etc/selinux/system_ext_file_contexts u:object_r:file_contexts_file:s0
+/(system_ext|system/system_ext)/etc/selinux/system_ext_hwservice_contexts u:object_r:hwservice_contexts_file:s0
+/(system_ext|system/system_ext)/etc/selinux/system_ext_keystore2_key_contexts u:object_r:keystore2_key_contexts_file:s0
+/(system_ext|system/system_ext)/etc/selinux/system_ext_property_contexts u:object_r:property_contexts_file:s0
+/(system_ext|system/system_ext)/etc/selinux/system_ext_seapp_contexts u:object_r:seapp_contexts_file:s0
+/(system_ext|system/system_ext)/etc/selinux/system_ext_service_contexts u:object_r:service_contexts_file:s0
+/(system_ext|system/system_ext)/etc/selinux/system_ext_mac_permissions\.xml u:object_r:mac_perms_file:s0
+
+/(system_ext|system/system_ext)/bin/aidl_lazy_test_server u:object_r:aidl_lazy_test_server_exec:s0
+/(system_ext|system/system_ext)/bin/hidl_lazy_test_server u:object_r:hidl_lazy_test_server_exec:s0
+
+/(system_ext|system/system_ext)/lib(64)?(/.*)? u:object_r:system_lib_file:s0
+
+#############################
+# VendorDlkm files
+# This includes VENDOR Dynamically Loadable Kernel Modules and other misc files.
+#
+/(vendor_dlkm|vendor/vendor_dlkm|system/vendor/vendor_dlkm)(/.*)? u:object_r:vendor_file:s0
+
+#############################
+# OdmDlkm files
+# This includes ODM Dynamically Loadable Kernel Modules and other misc files.
+#
+/(odm_dlkm|vendor/odm_dlkm|system/vendor/odm_dlkm)(/.*)? u:object_r:vendor_file:s0
+
+#############################
+# Vendor files from /(product|system/product)/vendor_overlay
+#
+# NOTE: For additional vendor file contexts for vendor overlay files,
+# use device specific file_contexts.
+#
+/(product|system/product)/vendor_overlay/[0-9]+/.* u:object_r:vendor_file:s0
+
+#############################
+# Data files
+#
+# NOTE: When modifying existing label rules, changes may also need to
+# propagate to the "Expanded data files" section.
+#
+/data u:object_r:system_data_root_file:s0
+/data/(.*)? u:object_r:system_data_file:s0
+/data/system/packages\.list u:object_r:packages_list_file:s0
+/data/unencrypted(/.*)? u:object_r:unencrypted_data_file:s0
+/data/backup(/.*)? u:object_r:backup_data_file:s0
+/data/secure/backup(/.*)? u:object_r:backup_data_file:s0
+/data/system/ndebugsocket u:object_r:system_ndebug_socket:s0
+/data/system/unsolzygotesocket u:object_r:system_unsolzygote_socket:s0
+/data/drm(/.*)? u:object_r:drm_data_file:s0
+/data/resource-cache(/.*)? u:object_r:resourcecache_data_file:s0
+/data/dalvik-cache(/.*)? u:object_r:dalvikcache_data_file:s0
+/data/ota(/.*)? u:object_r:ota_data_file:s0
+/data/ota_package(/.*)? u:object_r:ota_package_file:s0
+/data/adb(/.*)? u:object_r:adb_data_file:s0
+/data/anr(/.*)? u:object_r:anr_data_file:s0
+/data/apex(/.*)? u:object_r:apex_data_file:s0
+/data/apex/active/(.*)? u:object_r:staging_data_file:s0
+/data/apex/backup/(.*)? u:object_r:staging_data_file:s0
+/data/app(/.*)? u:object_r:apk_data_file:s0
+# Traditional /data/app/[packageName]-[randomString]/base.apk location
+/data/app/[^/]+/oat(/.*)? u:object_r:dalvikcache_data_file:s0
+# /data/app/[randomStringA]/[packageName]-[randomStringB]/base.apk layout
+/data/app/[^/]+/[^/]+/oat(/.*)? u:object_r:dalvikcache_data_file:s0
+/data/app/vmdl[^/]+\.tmp(/.*)? u:object_r:apk_tmp_file:s0
+/data/app/vmdl[^/]+\.tmp/oat(/.*)? u:object_r:dalvikcache_data_file:s0
+/data/app-private(/.*)? u:object_r:apk_private_data_file:s0
+/data/app-private/vmdl.*\.tmp(/.*)? u:object_r:apk_private_tmp_file:s0
+/data/gsi(/.*)? u:object_r:gsi_data_file:s0
+/data/gsi/ota(/.*)? u:object_r:ota_image_data_file:s0
+/data/tombstones(/.*)? u:object_r:tombstone_data_file:s0
+/data/vendor/tombstones/wifi(/.*)? u:object_r:tombstone_wifi_data_file:s0
+/data/local/tests(/.*)? u:object_r:shell_test_data_file:s0
+/data/local/tmp(/.*)? u:object_r:shell_data_file:s0
+/data/local/tmp/ltp(/.*)? u:object_r:nativetest_data_file:s0
+/data/local/traces(/.*)? u:object_r:trace_data_file:s0
+/data/media(/.*)? u:object_r:media_rw_data_file:s0
+/data/mediadrm(/.*)? u:object_r:media_data_file:s0
+/data/nativetest(/.*)? u:object_r:nativetest_data_file:s0
+/data/nativetest64(/.*)? u:object_r:nativetest_data_file:s0
+# This directory was removed after Q Beta 2, but we need to preserve labels for upgrading devices.
+/data/pkg_staging(/.*)? u:object_r:staging_data_file:s0
+/data/property(/.*)? u:object_r:property_data_file:s0
+/data/preloads(/.*)? u:object_r:preloads_data_file:s0
+/data/preloads/media(/.*)? u:object_r:preloads_media_file:s0
+/data/preloads/demo(/.*)? u:object_r:preloads_media_file:s0
+/data/server_configurable_flags(/.*)? u:object_r:server_configurable_flags_data_file:s0
+/data/app-staging(/.*)? u:object_r:staging_data_file:s0
+# Ensure we have the same labels as /data/app or /data/apex/active
+# to avoid restorecon conflicts
+/data/rollback/\d+/[^/]+/.*\.apk u:object_r:apk_data_file:s0
+/data/rollback/\d+/[^/]+/.*\.apex u:object_r:staging_data_file:s0
+/data/fonts/files(/.*)? u:object_r:font_data_file:s0
+
+# Misc data
+/data/misc/adb(/.*)? u:object_r:adb_keys_file:s0
+/data/misc/apexdata(/.*)? u:object_r:apex_module_data_file:s0
+/data/misc/apexdata/com\.android\.art(/.*)? u:object_r:apex_art_data_file:s0
+/data/misc/apexdata/com\.android\.permission(/.*)? u:object_r:apex_permission_data_file:s0
+/data/misc/apexdata/com\.android\.wifi(/.*)? u:object_r:apex_wifi_data_file:s0
+/data/misc/apexrollback(/.*)? u:object_r:apex_rollback_data_file:s0
+/data/misc/apns(/.*)? u:object_r:radio_data_file:s0
+/data/misc/appcompat(/.*)? u:object_r:appcompat_data_file:s0
+/data/misc/audio(/.*)? u:object_r:audio_data_file:s0
+/data/misc/audioserver(/.*)? u:object_r:audioserver_data_file:s0
+/data/misc/audiohal(/.*)? u:object_r:audiohal_data_file:s0
+/data/misc/bootstat(/.*)? u:object_r:bootstat_data_file:s0
+/data/misc/boottrace(/.*)? u:object_r:boottrace_data_file:s0
+/data/misc/bluetooth(/.*)? u:object_r:bluetooth_data_file:s0
+/data/misc/bluetooth/logs(/.*)? u:object_r:bluetooth_logs_data_file:s0
+/data/misc/bluedroid(/.*)? u:object_r:bluetooth_data_file:s0
+/data/misc/bluedroid/\.a2dp_ctrl u:object_r:bluetooth_socket:s0
+/data/misc/bluedroid/\.a2dp_data u:object_r:bluetooth_socket:s0
+/data/misc/camera(/.*)? u:object_r:camera_data_file:s0
+/data/misc/carrierid(/.*)? u:object_r:radio_data_file:s0
+/data/misc/dhcp(/.*)? u:object_r:dhcp_data_file:s0
+/data/misc/dhcp-6\.8\.2(/.*)? u:object_r:dhcp_data_file:s0
+/data/misc/emergencynumberdb(/.*)? u:object_r:emergency_data_file:s0
+/data/misc/gatekeeper(/.*)? u:object_r:gatekeeper_data_file:s0
+/data/misc/incidents(/.*)? u:object_r:incident_data_file:s0
+/data/misc/installd(/.*)? u:object_r:install_data_file:s0
+/data/misc/keychain(/.*)? u:object_r:keychain_data_file:s0
+/data/misc/credstore(/.*)? u:object_r:credstore_data_file:s0
+/data/misc/keystore(/.*)? u:object_r:keystore_data_file:s0
+/data/misc/logd(/.*)? u:object_r:misc_logd_file:s0
+/data/misc/media(/.*)? u:object_r:media_data_file:s0
+/data/misc/net(/.*)? u:object_r:net_data_file:s0
+/data/misc/network_watchlist(/.*)? u:object_r:network_watchlist_data_file:s0
+/data/misc/nfc/logs(/.*)? u:object_r:nfc_logs_data_file:s0
+/data/misc/perfetto-traces/bugreport(.*)? u:object_r:perfetto_traces_bugreport_data_file:s0
+/data/misc/perfetto-traces(/.*)? u:object_r:perfetto_traces_data_file:s0
+/data/misc/perfetto-configs(/.*)? u:object_r:perfetto_configs_data_file:s0
+/data/misc/prereboot(/.*)? u:object_r:prereboot_data_file:s0
+/data/misc/profcollectd(/.*)? u:object_r:profcollectd_data_file:s0
+/data/misc/radio(/.*)? u:object_r:radio_core_data_file:s0
+/data/misc/recovery(/.*)? u:object_r:recovery_data_file:s0
+/data/misc/shared_relro(/.*)? u:object_r:shared_relro_file:s0
+/data/misc/sms(/.*)? u:object_r:radio_data_file:s0
+/data/misc/snapshotctl_log(/.*)? u:object_r:snapshotctl_log_data_file:s0
+/data/misc/stats-active-metric(/.*)? u:object_r:stats_data_file:s0
+/data/misc/stats-data(/.*)? u:object_r:stats_data_file:s0
+/data/misc/stats-service(/.*)? u:object_r:stats_data_file:s0
+/data/misc/stats-metadata(/.*)? u:object_r:stats_data_file:s0
+/data/misc/systemkeys(/.*)? u:object_r:systemkeys_data_file:s0
+/data/misc/textclassifier(/.*)? u:object_r:textclassifier_data_file:s0
+/data/misc/train-info(/.*)? u:object_r:stats_data_file:s0
+/data/misc/user(/.*)? u:object_r:misc_user_data_file:s0
+/data/misc/vpn(/.*)? u:object_r:vpn_data_file:s0
+/data/misc/wifi(/.*)? u:object_r:wifi_data_file:s0
+/data/misc_ce/[0-9]+/wifi(/.*)? u:object_r:wifi_data_file:s0
+/data/misc/wifi/sockets(/.*)? u:object_r:wpa_socket:s0
+/data/misc/wifi/sockets/wpa_ctrl.* u:object_r:system_wpa_socket:s0
+/data/misc/zoneinfo(/.*)? u:object_r:zoneinfo_data_file:s0
+/data/misc/vold(/.*)? u:object_r:vold_data_file:s0
+/data/misc/iorapd(/.*)? u:object_r:iorapd_data_file:s0
+/data/misc/update_engine(/.*)? u:object_r:update_engine_data_file:s0
+/data/misc/update_engine_log(/.*)? u:object_r:update_engine_log_data_file:s0
+/data/system/dropbox(/.*)? u:object_r:dropbox_data_file:s0
+/data/system/heapdump(/.*)? u:object_r:heapdump_data_file:s0
+/data/misc/trace(/.*)? u:object_r:method_trace_data_file:s0
+/data/misc/wmtrace(/.*)? u:object_r:wm_trace_data_file:s0
+# TODO(calin) label profile reference differently so that only
+# profman run as a special user can write to them
+/data/misc/profiles/cur(/[0-9]+)? u:object_r:user_profile_root_file:s0
+/data/misc/profiles/cur/[0-9]+/.* u:object_r:user_profile_data_file:s0
+/data/misc/profiles/ref(/.*)? u:object_r:user_profile_data_file:s0
+/data/misc/profman(/.*)? u:object_r:profman_dump_data_file:s0
+/data/vendor(/.*)? u:object_r:vendor_data_file:s0
+/data/vendor_ce(/.*)? u:object_r:vendor_data_file:s0
+/data/vendor_de(/.*)? u:object_r:vendor_data_file:s0
+
+# storaged proto files
+/data/misc_de/[0-9]+/storaged(/.*)? u:object_r:storaged_data_file:s0
+/data/misc_ce/[0-9]+/storaged(/.*)? u:object_r:storaged_data_file:s0
+
+# Fingerprint data
+/data/system/users/[0-9]+/fpdata(/.*)? u:object_r:fingerprintd_data_file:s0
+
+# Fingerprint vendor data file
+/data/vendor_de/[0-9]+/fpdata(/.*)? u:object_r:fingerprint_vendor_data_file:s0
+
+# Face vendor data file
+/data/vendor_de/[0-9]+/facedata(/.*)? u:object_r:face_vendor_data_file:s0
+/data/vendor_ce/[0-9]+/facedata(/.*)? u:object_r:face_vendor_data_file:s0
+
+# Iris vendor data file
+/data/vendor_de/[0-9]+/irisdata(/.*)? u:object_r:iris_vendor_data_file:s0
+
+# Bootchart data
+/data/bootchart(/.*)? u:object_r:bootchart_data_file:s0
+
+# App data snapshots (managed by installd).
+/data/misc_de/[0-9]+/rollback(/.*)? u:object_r:rollback_data_file:s0
+/data/misc_ce/[0-9]+/rollback(/.*)? u:object_r:rollback_data_file:s0
+
+# Apex data directories
+/data/misc_de/[0-9]+/apexdata(/.*)? u:object_r:apex_module_data_file:s0
+/data/misc_ce/[0-9]+/apexdata(/.*)? u:object_r:apex_module_data_file:s0
+/data/misc_de/[0-9]+/apexdata/com\.android\.permission(/.*)? u:object_r:apex_permission_data_file:s0
+/data/misc_ce/[0-9]+/apexdata/com\.android\.permission(/.*)? u:object_r:apex_permission_data_file:s0
+/data/misc_de/[0-9]+/apexdata/com\.android\.wifi(/.*)? u:object_r:apex_wifi_data_file:s0
+/data/misc_ce/[0-9]+/apexdata/com\.android\.wifi(/.*)? u:object_r:apex_wifi_data_file:s0
+
+# Apex rollback directories
+/data/misc_de/[0-9]+/apexrollback(/.*)? u:object_r:apex_rollback_data_file:s0
+/data/misc_ce/[0-9]+/apexrollback(/.*)? u:object_r:apex_rollback_data_file:s0
+
+# Incremental directories
+/data/incremental(/.*)? u:object_r:apk_data_file:s0
+/data/incremental/MT_[^/]+/mount/.pending_reads u:object_r:incremental_control_file:s0
+/data/incremental/MT_[^/]+/mount/.log u:object_r:incremental_control_file:s0
+/data/incremental/MT_[^/]+/mount/.blocks_written u:object_r:incremental_control_file:s0
+
+#############################
+# Expanded data files
+#
+/mnt/expand(/.*)? u:object_r:mnt_expand_file:s0
+/mnt/expand/[^/]+(/.*)? u:object_r:system_data_file:s0
+/mnt/expand/[^/]+/app(/.*)? u:object_r:apk_data_file:s0
+/mnt/expand/[^/]+/app/[^/]+/oat(/.*)? u:object_r:dalvikcache_data_file:s0
+# /mnt/expand/..../app/[randomStringA]/[packageName]-[randomStringB]/base.apk layout
+/mnt/expand/[^/]+/app/[^/]+/[^/]+/oat(/.*)? u:object_r:dalvikcache_data_file:s0
+/mnt/expand/[^/]+/app/vmdl[^/]+\.tmp(/.*)? u:object_r:apk_tmp_file:s0
+/mnt/expand/[^/]+/app/vmdl[^/]+\.tmp/oat(/.*)? u:object_r:dalvikcache_data_file:s0
+/mnt/expand/[^/]+/local/tmp(/.*)? u:object_r:shell_data_file:s0
+/mnt/expand/[^/]+/media(/.*)? u:object_r:media_rw_data_file:s0
+/mnt/expand/[^/]+/misc/vold(/.*)? u:object_r:vold_data_file:s0
+
+# coredump directory for userdebug/eng devices
+/cores(/.*)? u:object_r:coredump_file:s0
+
+# Wallpaper files
+/data/system/users/[0-9]+/wallpaper_lock_orig u:object_r:wallpaper_file:s0
+/data/system/users/[0-9]+/wallpaper_lock u:object_r:wallpaper_file:s0
+/data/system/users/[0-9]+/wallpaper_orig u:object_r:wallpaper_file:s0
+/data/system/users/[0-9]+/wallpaper u:object_r:wallpaper_file:s0
+
+# Ringtone files
+/data/system_de/[0-9]+/ringtones(/.*)? u:object_r:ringtone_file:s0
+
+# ShortcutManager icons, e.g.
+# /data/system_ce/0/shortcut_service/bitmaps/com.example.app/1457472879282.png
+/data/system_ce/[0-9]+/shortcut_service/bitmaps(/.*)? u:object_r:shortcut_manager_icons:s0
+
+# User icon files
+/data/system/users/[0-9]+/photo\.png u:object_r:icon_file:s0
+
+# vold per-user data
+/data/misc_de/[0-9]+/vold(/.*)? u:object_r:vold_data_file:s0
+/data/misc_ce/[0-9]+/vold(/.*)? u:object_r:vold_data_file:s0
+
+# iorapd per-user data
+/data/misc_ce/[0-9]+/iorapd(/.*)? u:object_r:iorapd_data_file:s0
+
+# Backup service persistent per-user bookkeeping
+/data/system_ce/[0-9]+/backup(/.*)? u:object_r:backup_data_file:s0
+# Backup service temporary per-user data for inter-change with apps
+/data/system_ce/[0-9]+/backup_stage(/.*)? u:object_r:backup_data_file:s0
+
+#############################
+# efs files
+#
+/efs(/.*)? u:object_r:efs_file:s0
+
+#############################
+# Cache files
+#
+/cache(/.*)? u:object_r:cache_file:s0
+/cache/recovery(/.*)? u:object_r:cache_recovery_file:s0
+# General backup/restore interchange with apps
+/cache/backup_stage(/.*)? u:object_r:cache_backup_file:s0
+# LocalTransport (backup) uses this subtree
+/cache/backup(/.*)? u:object_r:cache_private_backup_file:s0
+
+#############################
+# Overlayfs support directories
+#
+/cache/overlay(/.*)? u:object_r:overlayfs_file:s0
+/mnt/scratch(/.*)? u:object_r:overlayfs_file:s0
+
+/data/cache(/.*)? u:object_r:cache_file:s0
+/data/cache/recovery(/.*)? u:object_r:cache_recovery_file:s0
+# General backup/restore interchange with apps
+/data/cache/backup_stage(/.*)? u:object_r:cache_backup_file:s0
+# LocalTransport (backup) uses this subtree
+/data/cache/backup(/.*)? u:object_r:cache_private_backup_file:s0
+
+#############################
+# Metadata files
+#
+/metadata(/.*)? u:object_r:metadata_file:s0
+/metadata/apex(/.*)? u:object_r:apex_metadata_file:s0
+/metadata/vold(/.*)? u:object_r:vold_metadata_file:s0
+/metadata/gsi(/.*)? u:object_r:gsi_metadata_file:s0
+/metadata/gsi/ota(/.*)? u:object_r:ota_metadata_file:s0
+/metadata/password_slots(/.*)? u:object_r:password_slot_metadata_file:s0
+/metadata/ota(/.*)? u:object_r:ota_metadata_file:s0
+/metadata/bootstat(/.*)? u:object_r:metadata_bootstat_file:s0
+/metadata/staged-install(/.*)? u:object_r:staged_install_file:s0
+/metadata/userspacereboot(/.*)? u:object_r:userspace_reboot_metadata_file:s0
+/metadata/watchdog(/.*)? u:object_r:watchdog_metadata_file:s0
+
+#############################
+# asec containers
+/mnt/asec(/.*)? u:object_r:asec_apk_file:s0
+/mnt/asec/[^/]+/[^/]+\.zip u:object_r:asec_public_file:s0
+/mnt/asec/[^/]+/lib(/.*)? u:object_r:asec_public_file:s0
+/data/app-asec(/.*)? u:object_r:asec_image_file:s0
+
+#############################
+# external storage
+/mnt/media_rw(/.*)? u:object_r:mnt_media_rw_file:s0
+/mnt/user(/.*)? u:object_r:mnt_user_file:s0
+/mnt/pass_through(/.*)? u:object_r:mnt_pass_through_file:s0
+/mnt/sdcard u:object_r:mnt_sdcard_file:s0
+/mnt/runtime(/.*)? u:object_r:storage_file:s0
+/storage(/.*)? u:object_r:storage_file:s0
+
+#############################
+# mount point for read-write vendor partitions
+/mnt/vendor(/.*)? u:object_r:mnt_vendor_file:s0
+
+#############################
+# mount point for read-write product partitions
+/mnt/product(/.*)? u:object_r:mnt_product_file:s0
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index 3f04a78..7946080 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -12,10 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-kernel_version = "5.4"
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
-kernel_target_stem = ":kernel_prebuilts-" + kernel_version
-vendor_ramdisk_target_stem = ":cf_prebuilts_initramfs-" + kernel_version
+kernel_version = "5.10"
+
+kernel_stem = "kernel_prebuilts-" + kernel_version
+kernel_modules_stem = "virt_device_prebuilts_kernel_modules-" + kernel_version
// JAR containing all virtualization host-side tests.
java_test_host {
@@ -25,68 +29,46 @@
libs: ["tradefed"],
data: [
":virt_hostside_tests_kernel",
- ":virt_hostside_tests_initramfs-arm64",
- ":virt_hostside_tests_initramfs-x86_64",
+ ":virt_hostside_tests_initramfs",
+ ":virt_hostside_tests_vm_config",
],
required: [
"virt_hostside_tests_vsock_server",
],
}
-// Give kernel images unique file names.
-genrule {
+prebuilt_etc {
name: "virt_hostside_tests_kernel",
- srcs: [
- kernel_target_stem + "-arm64",
- kernel_target_stem + "-x86_64",
- ],
- out: [
- "virt_hostside_tests_kernel-arm64",
- "virt_hostside_tests_kernel-x86_64",
- ],
- tool_files: ["scripts/place_files.sh"],
- cmd: "$(location scripts/place_files.sh) $(in) -- $(out)",
+ src: "nofile",
+ arch: {
+ arm64: {
+ src: ":" + kernel_stem + "-arm64",
+ },
+ x86_64: {
+ src: ":" + kernel_stem + "-x86_64",
+ },
+ },
}
-// Ramdisk containing /init and test binaries/resources needed inside guest.
-genrule {
- name: "virt_hostside_tests_initramfs_base",
- tools: [
- "mkbootfs",
- "lz4",
+// Copy config files to output directory so that AndroidTest.xml can copy them to the device.
+filegroup {
+ name: "virt_hostside_tests_vm_config",
+ srcs: ["vm_config.json"],
+}
+
+android_filesystem {
+ name: "virt_hostside_tests_initramfs",
+ arch: {
+ arm64: {
+ deps: [kernel_modules_stem + "-arm64"],
+ },
+ x86_64: {
+ deps: [kernel_modules_stem + "-x86_64"],
+ },
+ },
+ deps: [
+ "virt_hostside_tests_guest_init",
+ "virt_hostside_tests_vsock_client",
],
- tool_files: ["scripts/place_files.sh"],
- out: ["initramfs.lz4"],
- srcs: [
- ":virt_hostside_tests_guest_init",
- ":virt_hostside_tests_vsock_client",
- ],
- cmd: "$(location scripts/place_files.sh) $(in) -- " +
- "$(genDir)/root/init " +
- "$(genDir)/root/bin/vsock_client " +
- "&& $(location mkbootfs) $(genDir)/root | $(location lz4) -fq - $(out)",
-}
-
-// Default rule for producing a combined base + vendor ramdisk.
-genrule_defaults {
- name: "virt_hostside_tests_initramfs_concat",
- srcs: [":virt_hostside_tests_initramfs_base"],
- tools: ["lz4"],
- cmd: "cat $(in) | $(location lz4) -dfq - $(out)",
-}
-
-// Combined base + vendor ramdisk for arm64.
-genrule {
- name: "virt_hostside_tests_initramfs-arm64",
- defaults: ["virt_hostside_tests_initramfs_concat"],
- srcs: [vendor_ramdisk_target_stem + "-arm64"],
- out: ["virt_hostside_tests_initramfs-arm64"],
-}
-
-// Combined base + vendor ramdisk for x86_64.
-genrule {
- name: "virt_hostside_tests_initramfs-x86_64",
- defaults: ["virt_hostside_tests_initramfs_concat"],
- srcs: [vendor_ramdisk_target_stem + "-x86_64"],
- out: ["virt_hostside_tests_initramfs-x86_64"],
+ type: "cpio",
}
diff --git a/tests/hostside/AndroidTest.xml b/tests/hostside/AndroidTest.xml
index 1fd86ef..c97a1df 100644
--- a/tests/hostside/AndroidTest.xml
+++ b/tests/hostside/AndroidTest.xml
@@ -24,18 +24,19 @@
<!-- Kernel has vhost-vsock enabled. -->
<option name="run-command" value="ls /dev/vhost-vsock" />
<!-- CrosVM is installed. -->
- <option name="run-command" value="which crosvm" />
+ <option name="run-command" value="ls /apex/com.android.virt/bin/crosvm" />
+ <!-- Virt Manager is installed. -->
+ <option name="run-command" value="which virtmanager" />
</target_preparer>
<!-- Push test binaries to the device. -->
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="abort-on-push-failure" value="true" />
- <option name="push-file" key="virt_hostside_tests_kernel-arm64" value="/data/local/tmp/virt-test/arm64/kernel" />
- <option name="push-file" key="virt_hostside_tests_initramfs-arm64" value="/data/local/tmp/virt-test/arm64/initramfs" />
- <option name="push-file" key="virt_hostside_tests_kernel-x86_64" value="/data/local/tmp/virt-test/x86_64/kernel" />
- <option name="push-file" key="virt_hostside_tests_initramfs-x86_64" value="/data/local/tmp/virt-test/x86_64/initramfs" />
- <option name="push-file" key="virt_hostside_tests_vsock_server" value="/data/local/tmp/virt-test" />
+ <option name="push-file" key="vm_config.json" value="/data/local/tmp/virt-test/vm_config.json" />
+ <option name="push-file" key="virt_hostside_tests_kernel" value="/data/local/tmp/virt-test/kernel" />
+ <option name="push-file" key="virt_hostside_tests_initramfs.img" value="/data/local/tmp/virt-test/initramfs" />
+ <option name="push-file" key="vsock_server" value="/data/local/tmp/virt-test/vsock_server" />
</target_preparer>
<!-- Root currently needed to run CrosVM.
diff --git a/tests/hostside/java/android/virt/test/VirtTestCase.java b/tests/hostside/java/android/virt/test/VirtTestCase.java
index 7ba6409..5c030a8 100644
--- a/tests/hostside/java/android/virt/test/VirtTestCase.java
+++ b/tests/hostside/java/android/virt/test/VirtTestCase.java
@@ -19,36 +19,22 @@
import static org.junit.Assert.*;
import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.testtype.IAbiReceiver;
import org.junit.Before;
import java.util.ArrayList;
-public abstract class VirtTestCase extends DeviceTestCase implements IAbiReceiver {
+public abstract class VirtTestCase extends DeviceTestCase {
private static final String DEVICE_DIR = "/data/local/tmp/virt-test";
- private static final int CID_RESERVED = 2;
-
- private IAbi mAbi;
-
@Before
public void setUp() throws Exception {
getDevice().waitForDeviceAvailable();
}
- private String getAbiName() {
- String name = mAbi.getName();
- if ("arm64-v8a".equals(name)) {
- name = "arm64";
- }
- return name;
- }
-
protected String getDevicePathForTestBinary(String targetName) throws Exception {
- String path = String.format("%s/%s/%s", DEVICE_DIR, getAbiName(), targetName);
+ String path = String.format("%s/%s", DEVICE_DIR, targetName);
if (!getDevice().doesFileExist(path)) {
throw new IllegalArgumentException(String.format(
"Binary for target %s not found on device at \"%s\"", targetName, path));
@@ -72,36 +58,4 @@
return String.join(" ", strings);
}
- protected String getVmCommand(String guestCmd, Integer cid) throws Exception {
- ArrayList<String> cmd = new ArrayList<>();
-
- cmd.add("crosvm");
- cmd.add("run");
-
- cmd.add("--disable-sandbox");
-
- if (cid != null) {
- if (cid > CID_RESERVED) {
- cmd.add("--cid");
- cmd.add(cid.toString());
- } else {
- throw new IllegalArgumentException("Invalid CID " + cid);
- }
- }
-
- cmd.add("--initrd");
- cmd.add(getDevicePathForTestBinary("initramfs"));
-
- cmd.add("--params");
- cmd.add(String.format("'%s'", guestCmd));
-
- cmd.add(getDevicePathForTestBinary("kernel"));
-
- return String.join(" ", cmd);
- }
-
- @Override
- public void setAbi(IAbi abi) {
- mAbi = abi;
- }
}
diff --git a/tests/hostside/java/android/virt/test/VsockTest.java b/tests/hostside/java/android/virt/test/VsockTest.java
index c82db77..4895c9a 100644
--- a/tests/hostside/java/android/virt/test/VsockTest.java
+++ b/tests/hostside/java/android/virt/test/VsockTest.java
@@ -18,7 +18,6 @@
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.util.CommandResult;
-import com.android.tradefed.util.CommandStatus;
import org.junit.Test;
@@ -32,22 +31,28 @@
private static final TimeUnit TIMEOUT_UNIT = TimeUnit.MINUTES;
private static final int RETRIES = 0;
- private static final Integer HOST_CID = 2;
- private static final Integer GUEST_CID = 42;
private static final Integer GUEST_PORT = 45678;
private static final String TEST_MESSAGE = "HelloWorld";
- private static final String CLIENT_PATH = "bin/vsock_client";
- private static final String SERVER_TARGET = "virt_hostside_tests_vsock_server";
+ private static final String SERVER_TARGET = "vsock_server";
+ private static final String VIRT_MANAGER_COMMAND = "virtmanager";
@Test
public void testVsockServer() throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(2);
final String serverPath = getDevicePathForTestBinary(SERVER_TARGET);
- final String serverCmd = createCommand(serverPath, GUEST_PORT);
- final String clientCmd = createCommand(CLIENT_PATH, HOST_CID, GUEST_PORT, TEST_MESSAGE);
- final String vmCmd = getVmCommand(clientCmd, GUEST_CID);
+ final String vmConfigPath = getDevicePathForTestBinary("vm_config.json");
+ final String serverCmd = createCommand(serverPath, GUEST_PORT, vmConfigPath);
+
+ // Start Virt Manager. This will eventually be a system service, but for now we run it
+ // manually.
+ Future<?> virtManagerTask = executor.submit(() -> {
+ CommandResult res = getDevice().executeShellV2Command(
+ VIRT_MANAGER_COMMAND, TIMEOUT, TIMEOUT_UNIT, RETRIES);
+ CLog.d(res.getStdout());
+ return null;
+ });
// Start server in Android that listens for vsock connections.
// It will receive a message from a client in the guest VM.
@@ -58,27 +63,6 @@
return null;
});
- // Run VM that will connect to the server and send a message to it.
- Future<?> vmTask = executor.submit(() -> {
- CommandResult res = getDevice().executeShellV2Command(
- vmCmd, TIMEOUT, TIMEOUT_UNIT, RETRIES);
- CLog.d(res.getStdout()); // print VMM output into host_log
- assertEquals(CommandStatus.SUCCESS, res.getStatus());
- return null;
- });
-
- // Wait for the VMM to finish sending the message.
- try {
- vmTask.get(TIMEOUT, TIMEOUT_UNIT);
- } catch (Throwable ex) {
- // The VMM either exited with a non-zero code or it timed out.
- // Kill the server process, the test has failed.
- // Note: executeShellV2Command cannot be interrupted. This will wait
- // until `serverTask` times out.
- executor.shutdownNow();
- throw ex;
- }
-
// Wait for the server to finish processing the message.
serverTask.get(TIMEOUT, TIMEOUT_UNIT);
}
diff --git a/tests/hostside/native/init/Android.bp b/tests/hostside/native/init/Android.bp
index a98948b..f2c179b 100644
--- a/tests/hostside/native/init/Android.bp
+++ b/tests/hostside/native/init/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_binary {
name: "virt_hostside_tests_guest_init",
srcs: ["main.cc"],
@@ -22,4 +26,5 @@
"liblog",
"libmodprobe",
],
+ stem: "init",
}
diff --git a/tests/hostside/native/vsock/Android.bp b/tests/hostside/native/vsock/Android.bp
index cbee98d..5151d62 100644
--- a/tests/hostside/native/vsock/Android.bp
+++ b/tests/hostside/native/vsock/Android.bp
@@ -12,15 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_test {
name: "virt_hostside_tests_vsock_server",
srcs: ["server.cc"],
static_libs: [
- "libbase",
- "liblog",
+ // The existence of the library in the system partition is not guaranteed.
+ // Let's have our own copy of it.
+ "android.system.virtmanager-cpp",
],
- static_executable: true,
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libutils",
+ ],
test_suites: ["device-tests"],
+ stem: "vsock_server",
}
cc_binary {
@@ -32,4 +43,5 @@
],
static_executable: true,
installable: false,
+ stem: "vsock_client",
}
diff --git a/tests/hostside/native/vsock/server.cc b/tests/hostside/native/vsock/server.cc
index d4a99d2..1a1aa37 100644
--- a/tests/hostside/native/vsock/server.cc
+++ b/tests/hostside/native/vsock/server.cc
@@ -26,15 +26,21 @@
#include "android-base/logging.h"
#include "android-base/parseint.h"
#include "android-base/unique_fd.h"
+#include "android/system/virtmanager/IVirtManager.h"
+#include "android/system/virtmanager/IVirtualMachine.h"
+#include "binder/IServiceManager.h"
+using namespace android;
using namespace android::base;
+using namespace android::system::virtmanager;
int main(int argc, const char *argv[]) {
unsigned int port;
- if (argc != 2 || !ParseUint(argv[1], &port)) {
- LOG(ERROR) << "Usage: " << argv[0] << " <port>";
+ if (argc != 3 || !ParseUint(argv[1], &port)) {
+ LOG(ERROR) << "Usage: " << argv[0] << " <port> <vm_config.json>";
return EXIT_FAILURE;
}
+ String16 vm_config(argv[2]);
unique_fd server_fd(TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM, 0)));
if (server_fd < 0) {
@@ -61,6 +67,27 @@
return EXIT_FAILURE;
}
+ LOG(INFO) << "Getting Virt Manager";
+ sp<IVirtManager> virt_manager;
+ status_t err = getService<IVirtManager>(String16("android.system.virtmanager"), &virt_manager);
+ if (err != 0) {
+ LOG(ERROR) << "Error getting Virt Manager from Service Manager: " << err;
+ return EXIT_FAILURE;
+ }
+ sp<IVirtualMachine> vm;
+ binder::Status status = virt_manager->startVm(vm_config, &vm);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Error starting VM: " << status;
+ return EXIT_FAILURE;
+ }
+ int32_t cid;
+ status = vm->getCid(&cid);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Error getting CID: " << status;
+ return EXIT_FAILURE;
+ }
+ LOG(INFO) << "VM starting with CID " << cid;
+
LOG(INFO) << "Accepting connection...";
struct sockaddr_vm client_sa;
socklen_t client_sa_len = sizeof(client_sa);
diff --git a/tests/hostside/nofile b/tests/hostside/nofile
new file mode 100644
index 0000000..eb4638a
--- /dev/null
+++ b/tests/hostside/nofile
@@ -0,0 +1,2 @@
+// This file is used as src of virt_hostside_tests_kernel module for the architectures
+// other than arm64 and x86_64
diff --git a/tests/hostside/scripts/place_files.sh b/tests/hostside/scripts/place_files.sh
deleted file mode 100755
index f0a9603..0000000
--- a/tests/hostside/scripts/place_files.sh
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/env bash
-##
-## Copyright (C) 2020 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.
-##
-
-set -euo pipefail
-
-# Wrapper around 'expr' that handles the fact that it returns code 1
-# if the result is zero/null. That messes with 'set -e'.
-function expr {
- eval 'val=$($(which expr) $@); ret=$?'
- if [ "$ret" != 0 -a "$ret" != 1 ]
- then
- return $ret
- fi
- echo "$val"
-}
-
-ARGS=( "$@" )
-NUM_ARGS=${#ARGS[@]}
-
-POS_DIVIDER=-1
-for i in $(seq 0 $(expr $NUM_ARGS - 1))
-do
- if [ "${ARGS[$i]}" == "--" ]
- then
- if [ "$POS_DIVIDER" -eq -1 ]
- then
- POS_DIVIDER=$i
- else
- echo "Multiple dividers in command line inputs" 1>&2
- exit 1
- fi
- fi
-done
-
-if [ "$POS_DIVIDER" -eq -1 ]
-then
- echo "Divider expected among command line inputs" 1>&2
- exit 1
-fi
-
-NUM_INPUT=${POS_DIVIDER}
-NUM_OUTPUT=$(expr $NUM_ARGS - $POS_DIVIDER - 1)
-
-if [ "$NUM_INPUT" -ne "$NUM_OUTPUT" ]
-then
- echo "Number of inputs does not match number of outputs" 1>&2
- exit 1
-fi
-
-for i in $(seq 0 $(expr $NUM_INPUT - 1))
-do
- INPUT="${ARGS[$i]}"
- OUTPUT="${ARGS[$NUM_INPUT + $i + 1]}"
- mkdir -p "$(dirname "$OUTPUT")"
- cp "$INPUT" "$OUTPUT"
-done
diff --git a/tests/hostside/vm_config.json b/tests/hostside/vm_config.json
new file mode 100644
index 0000000..762baec
--- /dev/null
+++ b/tests/hostside/vm_config.json
@@ -0,0 +1,5 @@
+{
+ "kernel": "/data/local/tmp/virt-test/kernel",
+ "initrd": "/data/local/tmp/virt-test/initramfs",
+ "params": "rdinit=/bin/init bin/vsock_client 2 45678 HelloWorld"
+}
diff --git a/virtmanager/.gitignore b/virtmanager/.gitignore
new file mode 100644
index 0000000..96ef6c0
--- /dev/null
+++ b/virtmanager/.gitignore
@@ -0,0 +1,2 @@
+/target
+Cargo.lock
diff --git a/virtmanager/Android.bp b/virtmanager/Android.bp
new file mode 100644
index 0000000..04b7d8b
--- /dev/null
+++ b/virtmanager/Android.bp
@@ -0,0 +1,14 @@
+rust_binary {
+ name: "virtmanager",
+ crate_name: "virtmanager",
+ srcs: ["src/main.rs"],
+ edition: "2018",
+ rustlibs: [
+ "android.system.virtmanager-rust",
+ "libenv_logger",
+ "liblog_rust",
+ "libserde_json",
+ "libserde",
+ "libanyhow",
+ ],
+}
diff --git a/virtmanager/aidl/Android.bp b/virtmanager/aidl/Android.bp
new file mode 100644
index 0000000..e46bd27
--- /dev/null
+++ b/virtmanager/aidl/Android.bp
@@ -0,0 +1,17 @@
+aidl_interface {
+ name: "android.system.virtmanager",
+ srcs: ["**/*.aidl"],
+ // TODO(qwandor): Consider changing this to false, unless we have a Java wrapper.
+ unstable: true,
+ backend: {
+ java: {
+ sdk_version: "module_current",
+ },
+ cpp: {
+ enabled: true,
+ },
+ rust: {
+ enabled: true,
+ },
+ },
+}
diff --git a/virtmanager/aidl/android/system/virtmanager/IVirtManager.aidl b/virtmanager/aidl/android/system/virtmanager/IVirtManager.aidl
new file mode 100644
index 0000000..ade8717
--- /dev/null
+++ b/virtmanager/aidl/android/system/virtmanager/IVirtManager.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.system.virtmanager;
+
+import android.system.virtmanager.IVirtualMachine;
+
+interface IVirtManager {
+ /** Start the VM with the given config file, and return a handle to it. */
+ IVirtualMachine startVm(String configPath);
+}
diff --git a/virtmanager/aidl/android/system/virtmanager/IVirtualMachine.aidl b/virtmanager/aidl/android/system/virtmanager/IVirtualMachine.aidl
new file mode 100644
index 0000000..5f408f8
--- /dev/null
+++ b/virtmanager/aidl/android/system/virtmanager/IVirtualMachine.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.system.virtmanager;
+
+interface IVirtualMachine {
+ /** Get the CID allocated to the VM. */
+ int getCid();
+}
diff --git a/virtmanager/src/main.rs b/virtmanager/src/main.rs
new file mode 100644
index 0000000..abc0d7b
--- /dev/null
+++ b/virtmanager/src/main.rs
@@ -0,0 +1,188 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Android Virt Manager
+
+use android_system_virtmanager::aidl::android::system::virtmanager::IVirtManager::{
+ BnVirtManager, IVirtManager,
+};
+use android_system_virtmanager::aidl::android::system::virtmanager::IVirtualMachine::{
+ BnVirtualMachine, IVirtualMachine,
+};
+use android_system_virtmanager::binder::{self, add_service, Interface, StatusCode, Strong};
+use anyhow::{Context, Error};
+use log::{debug, error, info};
+use serde::{Deserialize, Serialize};
+use std::fs::File;
+use std::io::{self, BufReader};
+use std::process::{Child, Command};
+use std::sync::{Arc, Mutex};
+
+/// The first CID to assign to a guest VM managed by the Virt Manager. CIDs lower than this are
+/// reserved for the host or other usage.
+const FIRST_GUEST_CID: Cid = 10;
+
+const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtmanager";
+const CROSVM_PATH: &str = "/apex/com.android.virt/bin/crosvm";
+
+/// The unique ID of a VM used (together with a port number) for vsock communication.
+type Cid = u32;
+
+/// Configuration for a particular VM to be started.
+#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
+struct VmConfig {
+ kernel: String,
+ initrd: Option<String>,
+ params: Option<String>,
+}
+
+fn main() {
+ env_logger::init();
+ let state = Arc::new(Mutex::new(State::new()));
+ let virt_manager = VirtManager::new(state);
+ let virt_manager = BnVirtManager::new_binder(virt_manager);
+ add_service(BINDER_SERVICE_IDENTIFIER, virt_manager.as_binder()).unwrap();
+ info!("Registered Binder service, joining threadpool.");
+ binder::ProcessState::join_thread_pool();
+}
+
+#[derive(Debug)]
+struct VirtManager {
+ state: Arc<Mutex<State>>,
+}
+
+impl VirtManager {
+ fn new(state: Arc<Mutex<State>>) -> Self {
+ VirtManager { state }
+ }
+}
+
+impl Interface for VirtManager {}
+
+impl IVirtManager for VirtManager {
+ /// Create and start a new VM with the given configuration, assigning it the next available CID.
+ ///
+ /// Returns a binder `IVirtualMachine` object referring to it, as a handle for the client.
+ fn startVm(&self, config_path: &str) -> binder::Result<Strong<dyn IVirtualMachine>> {
+ let state = &mut *self.state.lock().unwrap();
+ let cid = state.next_cid;
+ let child = start_vm(config_path, cid)?;
+ // TODO(qwandor): keep track of which CIDs are currently in use so that we can reuse them.
+ state.next_cid = state.next_cid.checked_add(1).ok_or(StatusCode::UNKNOWN_ERROR)?;
+ Ok(VirtualMachine::create(Arc::new(VmInstance::new(child, cid))))
+ }
+}
+
+/// Implementation of the AIDL IVirtualMachine interface. Used as a handle to a VM.
+#[derive(Debug)]
+struct VirtualMachine {
+ instance: Arc<VmInstance>,
+}
+
+impl VirtualMachine {
+ fn create(instance: Arc<VmInstance>) -> Strong<dyn IVirtualMachine> {
+ let binder = VirtualMachine { instance };
+ BnVirtualMachine::new_binder(binder)
+ }
+}
+
+impl Interface for VirtualMachine {}
+
+impl IVirtualMachine for VirtualMachine {
+ fn getCid(&self) -> binder::Result<i32> {
+ Ok(self.instance.cid as i32)
+ }
+}
+
+/// Information about a particular instance of a VM which is running.
+#[derive(Debug)]
+struct VmInstance {
+ /// The crosvm child process.
+ child: Child,
+ /// The CID assigned to the VM for vsock communication.
+ cid: Cid,
+}
+
+impl VmInstance {
+ /// Create a new `VmInstance` with a single reference for the given process.
+ fn new(child: Child, cid: Cid) -> VmInstance {
+ VmInstance { child, cid }
+ }
+}
+
+impl Drop for VmInstance {
+ fn drop(&mut self) {
+ debug!("Dropping {:?}", self);
+ // TODO: Talk to crosvm to shutdown cleanly.
+ if let Err(e) = self.child.kill() {
+ error!("Error killing crosvm instance: {}", e);
+ }
+ // We need to wait on the process after killing it to avoid zombies.
+ match self.child.wait() {
+ Err(e) => error!("Error waiting for crosvm instance to die: {}", e),
+ Ok(status) => info!("Crosvm exited with status {}", status),
+ }
+ }
+}
+
+/// The mutable state of the Virt Manager. There should only be one instance of this struct.
+#[derive(Debug)]
+struct State {
+ next_cid: Cid,
+}
+
+impl State {
+ fn new() -> Self {
+ State { next_cid: FIRST_GUEST_CID }
+ }
+}
+
+/// Start a new VM instance from the given VM config filename. This assumes the VM is not already
+/// running.
+fn start_vm(config_path: &str, cid: Cid) -> binder::Result<Child> {
+ let config = load_vm_config(config_path).map_err(|e| {
+ error!("Failed to load VM config {}: {:?}", config_path, e);
+ StatusCode::BAD_VALUE
+ })?;
+ Ok(run_vm(&config, cid).map_err(|e| {
+ error!("Failed to start VM {}: {:?}", config_path, e);
+ StatusCode::UNKNOWN_ERROR
+ })?)
+}
+
+/// Load the configuration for the VM with the given ID from a JSON file.
+fn load_vm_config(path: &str) -> Result<VmConfig, Error> {
+ let file = File::open(path).with_context(|| format!("Failed to open {}", path))?;
+ let buffered = BufReader::new(file);
+ Ok(serde_json::from_reader(buffered)?)
+}
+
+/// Start an instance of `crosvm` to manage a new VM.
+fn run_vm(config: &VmConfig, cid: Cid) -> Result<Child, io::Error> {
+ let mut command = Command::new(CROSVM_PATH);
+ // TODO(qwandor): Remove --disable-sandbox.
+ command.arg("run").arg("--disable-sandbox").arg("--cid").arg(cid.to_string());
+ if let Some(initrd) = &config.initrd {
+ command.arg("--initrd").arg(initrd);
+ }
+ if let Some(params) = &config.params {
+ command.arg("--params").arg(params);
+ }
+ // TODO(jiyong): Don't redirect console to the host syslog
+ command.arg("--serial=type=syslog");
+ command.arg(&config.kernel);
+ info!("Running {:?}", command);
+ // TODO: Monitor child process, and remove from VM map if it dies.
+ command.spawn()
+}