Merge "Remove unused .proto"
diff --git a/compos/apk/assets/vm_config_system_ext.json b/compos/apk/assets/vm_config_system_ext.json
new file mode 100644
index 0000000..e60dee7
--- /dev/null
+++ b/compos/apk/assets/vm_config_system_ext.json
@@ -0,0 +1,33 @@
+{
+  "version": 1,
+  "os": {
+    "name": "microdroid"
+  },
+  "task": {
+    "type": "executable",
+    "command": "/apex/com.android.compos/bin/compsvc"
+  },
+  "extra_apks": [
+    {
+      "path": "/system/etc/security/fsverity/BuildManifest.apk"
+    },
+    {
+      "path": "/system_ext/etc/security/fsverity/BuildManifestSystemExt.apk"
+    }
+  ],
+  "apexes": [
+    {
+      "name": "com.android.art"
+    },
+    {
+      "name": "com.android.compos"
+    },
+    {
+      "name": "com.android.sdkext"
+    },
+    {
+      "name": "{CLASSPATH}"
+    }
+  ],
+  "export_tombstones": true
+}
diff --git a/compos/apk/assets/vm_config_system_ext_staged.json b/compos/apk/assets/vm_config_system_ext_staged.json
new file mode 100644
index 0000000..99a4160
--- /dev/null
+++ b/compos/apk/assets/vm_config_system_ext_staged.json
@@ -0,0 +1,34 @@
+{
+  "version": 1,
+  "os": {
+    "name": "microdroid"
+  },
+  "task": {
+    "type": "executable",
+    "command": "/apex/com.android.compos/bin/compsvc"
+  },
+  "prefer_staged": true,
+  "extra_apks": [
+    {
+      "path": "/system/etc/security/fsverity/BuildManifest.apk"
+    },
+    {
+      "path": "/system_ext/etc/security/fsverity/BuildManifestSystemExt.apk"
+    }
+  ],
+  "apexes": [
+    {
+      "name": "com.android.art"
+    },
+    {
+      "name": "com.android.compos"
+    },
+    {
+      "name": "com.android.sdkext"
+    },
+    {
+      "name": "{CLASSPATH}"
+    }
+  ],
+  "export_tombstones": true
+}
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index 5ea5c06..49396d7 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -17,7 +17,10 @@
 //! Support for starting CompOS in a VM and connecting to the service
 
 use crate::timeouts::TIMEOUTS;
-use crate::{COMPOS_APEX_ROOT, COMPOS_DATA_ROOT, COMPOS_VSOCK_PORT, DEFAULT_VM_CONFIG_PATH};
+use crate::{
+    get_vm_config_path, BUILD_MANIFEST_APK_PATH, BUILD_MANIFEST_SYSTEM_EXT_APK_PATH,
+    COMPOS_APEX_ROOT, COMPOS_DATA_ROOT, COMPOS_VSOCK_PORT,
+};
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
     IVirtualizationService::IVirtualizationService,
     VirtualMachineAppConfig::{DebugLevel::DebugLevel, VirtualMachineAppConfig},
@@ -47,10 +50,10 @@
     pub cpus: Option<NonZeroU32>,
     /// List of task profiles to apply to the VM
     pub task_profiles: Vec<String>,
-    /// If present, overrides the path to the VM config JSON file
-    pub config_path: Option<String>,
     /// If present, overrides the amount of RAM to give the VM
     pub memory_mib: Option<i32>,
+    /// Whether the VM prefers staged APEXes or activated ones (false; default)
+    pub prefer_staged: bool,
 }
 
 impl ComposClient {
@@ -60,6 +63,7 @@
         instance_image: File,
         idsig: &Path,
         idsig_manifest_apk: &Path,
+        idsig_manifest_ext_apk: &Path,
         parameters: &VmParameters,
     ) -> Result<Self> {
         let protected_vm = want_protected_vm()?;
@@ -74,11 +78,27 @@
         let apk_fd = ParcelFileDescriptor::new(apk_fd);
         let idsig_fd = prepare_idsig(service, &apk_fd, idsig)?;
 
-        let manifest_apk_fd = File::open("/system/etc/security/fsverity/BuildManifest.apk")
+        let manifest_apk_fd = File::open(BUILD_MANIFEST_APK_PATH)
             .context("Failed to open build manifest APK file")?;
         let manifest_apk_fd = ParcelFileDescriptor::new(manifest_apk_fd);
         let idsig_manifest_apk_fd = prepare_idsig(service, &manifest_apk_fd, idsig_manifest_apk)?;
 
+        // Prepare a few things based on whether /system_ext exists, including:
+        // 1. generate the additional idsig FD for the APK from /system_ext, then pass to VS
+        // 2. select the correct VM config json
+        let (extra_idsigs, has_system_ext) =
+            if let Ok(manifest_ext_apk_fd) = File::open(BUILD_MANIFEST_SYSTEM_EXT_APK_PATH) {
+                // Optional idsig in /system_ext is found, so prepare additionally.
+                let manifest_ext_apk_fd = ParcelFileDescriptor::new(manifest_ext_apk_fd);
+                let idsig_manifest_ext_apk_fd =
+                    prepare_idsig(service, &manifest_ext_apk_fd, idsig_manifest_ext_apk)?;
+
+                (vec![idsig_manifest_apk_fd, idsig_manifest_ext_apk_fd], true)
+            } else {
+                (vec![idsig_manifest_apk_fd], false)
+            };
+        let config_path = get_vm_config_path(has_system_ext, parameters.prefer_staged);
+
         let debug_level = match (protected_vm, parameters.debug_mode) {
             (_, true) => DebugLevel::FULL,
             (false, false) => DebugLevel::APP_ONLY,
@@ -97,15 +117,14 @@
             (Some(console_fd), Some(log_fd))
         };
 
-        let config_path = parameters.config_path.as_deref().unwrap_or(DEFAULT_VM_CONFIG_PATH);
         let config = VirtualMachineConfig::AppConfig(VirtualMachineAppConfig {
             name: String::from("Compos"),
             apk: Some(apk_fd),
             idsig: Some(idsig_fd),
             instanceImage: Some(instance_fd),
-            configPath: config_path.to_owned(),
+            configPath: config_path,
             debugLevel: debug_level,
-            extraIdsigs: vec![idsig_manifest_apk_fd],
+            extraIdsigs: extra_idsigs,
             protectedVm: protected_vm,
             memoryMib: parameters.memory_mib.unwrap_or(0), // 0 means use the default
             numCpus: parameters.cpus.map_or(1, NonZeroU32::get) as i32,
diff --git a/compos/common/lib.rs b/compos/common/lib.rs
index a5b1ea8..c9555d5 100644
--- a/compos/common/lib.rs
+++ b/compos/common/lib.rs
@@ -48,16 +48,31 @@
 /// The file that holds the idsig for the CompOS Payload APK.
 pub const IDSIG_FILE: &str = "idsig";
 
-/// The file that holds the idsig for the build manifest APK (that makes enumerated files from
-/// /system available in CompOS).
+/// The file that holds the idsig for the build manifest APK that makes enumerated files from
+/// /system available in CompOS.
 pub const IDSIG_MANIFEST_APK_FILE: &str = "idsig_manifest_apk";
 
-/// The path within our config APK of our default VM configuration file, used at boot time.
-pub const DEFAULT_VM_CONFIG_PATH: &str = "assets/vm_config.json";
-
-/// The path within our config APK of the VM configuration file we use when compiling staged
-/// APEXes before reboot.
-pub const PREFER_STAGED_VM_CONFIG_PATH: &str = "assets/vm_config_staged.json";
+/// The file that holds the idsig for the build manifest APK that makes enumerated files from
+/// /system_ext available in CompOS.
+pub const IDSIG_MANIFEST_EXT_APK_FILE: &str = "idsig_manifest_ext_apk";
 
 /// Number of CPUs to run dex2oat (actually the entire compos VM) with
 pub const DEX2OAT_THREADS_PROP_NAME: &str = "dalvik.vm.boot-dex2oat-threads";
+
+/// The Android path of fs-verity build manifest APK for /system.
+pub const BUILD_MANIFEST_APK_PATH: &str = "/system/etc/security/fsverity/BuildManifest.apk";
+
+/// The Android path of fs-verity build manifest APK for /system_ext.
+pub const BUILD_MANIFEST_SYSTEM_EXT_APK_PATH: &str =
+    "/system_ext/etc/security/fsverity/BuildManifestSystemExt.apk";
+
+/// Returns the path of proper VM config for the current device.
+pub fn get_vm_config_path(has_system_ext: bool, prefer_staged: bool) -> String {
+    match (has_system_ext, prefer_staged) {
+        (false, false) => "assets/vm_config.json",
+        (false, true) => "assets/vm_config_staged.json",
+        (true, false) => "assets/vm_config_system_ext.json",
+        (true, true) => "assets/vm_config_system_ext_staged.json",
+    }
+    .to_owned()
+}
diff --git a/compos/composd/src/instance_manager.rs b/compos/composd/src/instance_manager.rs
index 451222e..c3d6592 100644
--- a/compos/composd/src/instance_manager.rs
+++ b/compos/composd/src/instance_manager.rs
@@ -22,10 +22,7 @@
 use anyhow::{bail, Result};
 use binder::Strong;
 use compos_common::compos_client::VmParameters;
-use compos_common::{
-    CURRENT_INSTANCE_DIR, DEX2OAT_THREADS_PROP_NAME, PREFER_STAGED_VM_CONFIG_PATH,
-    TEST_INSTANCE_DIR,
-};
+use compos_common::{CURRENT_INSTANCE_DIR, DEX2OAT_THREADS_PROP_NAME, TEST_INSTANCE_DIR};
 use rustutils::system_properties;
 use std::num::NonZeroU32;
 use std::str::FromStr;
@@ -47,16 +44,14 @@
 
     pub fn start_current_instance(&self) -> Result<CompOsInstance> {
         let mut vm_parameters = new_vm_parameters()?;
-        vm_parameters.config_path = Some(PREFER_STAGED_VM_CONFIG_PATH.to_owned());
+        vm_parameters.prefer_staged = true;
         self.start_instance(CURRENT_INSTANCE_DIR, vm_parameters)
     }
 
     pub fn start_test_instance(&self, prefer_staged: bool) -> Result<CompOsInstance> {
         let mut vm_parameters = new_vm_parameters()?;
         vm_parameters.debug_mode = true;
-        if prefer_staged {
-            vm_parameters.config_path = Some(PREFER_STAGED_VM_CONFIG_PATH.to_owned());
-        }
+        vm_parameters.prefer_staged = prefer_staged;
         self.start_instance(TEST_INSTANCE_DIR, vm_parameters)
     }
 
diff --git a/compos/composd/src/instance_starter.rs b/compos/composd/src/instance_starter.rs
index aaa4695..fc4c58b 100644
--- a/compos/composd/src/instance_starter.rs
+++ b/compos/composd/src/instance_starter.rs
@@ -24,7 +24,10 @@
 use binder::{LazyServiceGuard, ParcelFileDescriptor, Strong};
 use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
 use compos_common::compos_client::{ComposClient, VmParameters};
-use compos_common::{COMPOS_DATA_ROOT, IDSIG_FILE, IDSIG_MANIFEST_APK_FILE, INSTANCE_IMAGE_FILE};
+use compos_common::{
+    COMPOS_DATA_ROOT, IDSIG_FILE, IDSIG_MANIFEST_APK_FILE, IDSIG_MANIFEST_EXT_APK_FILE,
+    INSTANCE_IMAGE_FILE,
+};
 use log::info;
 use std::fs;
 use std::path::{Path, PathBuf};
@@ -66,6 +69,7 @@
     instance_image: PathBuf,
     idsig: PathBuf,
     idsig_manifest_apk: PathBuf,
+    idsig_manifest_ext_apk: PathBuf,
     vm_parameters: VmParameters,
 }
 
@@ -76,12 +80,14 @@
         let instance_image = instance_root_path.join(INSTANCE_IMAGE_FILE);
         let idsig = instance_root_path.join(IDSIG_FILE);
         let idsig_manifest_apk = instance_root_path.join(IDSIG_MANIFEST_APK_FILE);
+        let idsig_manifest_ext_apk = instance_root_path.join(IDSIG_MANIFEST_EXT_APK_FILE);
         Self {
             instance_name: instance_name.to_owned(),
             instance_root,
             instance_image,
             idsig,
             idsig_manifest_apk,
+            idsig_manifest_ext_apk,
             vm_parameters,
         }
     }
@@ -102,6 +108,7 @@
         // Delete existing idsig files. Ignore error in case idsig doesn't exist.
         let _ = fs::remove_file(&self.idsig);
         let _ = fs::remove_file(&self.idsig_manifest_apk);
+        let _ = fs::remove_file(&self.idsig_manifest_ext_apk);
 
         let instance = self.start_vm(virtualization_service)?;
 
@@ -126,6 +133,7 @@
             instance_image,
             &self.idsig,
             &self.idsig_manifest_apk,
+            &self.idsig_manifest_ext_apk,
             &self.vm_parameters,
         )
         .context("Starting VM")?;
diff --git a/compos/composd/src/odrefresh_task.rs b/compos/composd/src/odrefresh_task.rs
index 5c926b1..a07a7f9 100644
--- a/compos/composd/src/odrefresh_task.rs
+++ b/compos/composd/src/odrefresh_task.rs
@@ -161,9 +161,18 @@
     let output_dir_raw_fd = output_dir_fd.as_raw_fd();
     let staging_dir_raw_fd = staging_dir_fd.as_raw_fd();
 
+    // Get the /system_ext FD differently because it may not exist.
+    // TODO(245761690): pass system_ext_dir_raw_fd to service.odrefresh(...)
+    let (_system_ext_dir_raw_fd, ro_dir_fds) =
+        if let Ok(system_ext_dir_fd) = open_dir(Path::new("/system_ext")) {
+            (system_ext_dir_fd.as_raw_fd(), vec![system_dir_fd, system_ext_dir_fd])
+        } else {
+            (-1, vec![system_dir_fd])
+        };
+
     // Spawn a fd_server to serve the FDs.
     let fd_server_config = FdServerConfig {
-        ro_dir_fds: vec![system_dir_fd],
+        ro_dir_fds,
         rw_dir_fds: vec![staging_dir_fd, output_dir_fd],
         ..Default::default()
     };
diff --git a/compos/verify/verify.rs b/compos/verify/verify.rs
index 3abdc74..5b7a8ad 100644
--- a/compos/verify/verify.rs
+++ b/compos/verify/verify.rs
@@ -28,7 +28,7 @@
 };
 use compos_common::{
     COMPOS_DATA_ROOT, CURRENT_INSTANCE_DIR, IDSIG_FILE, IDSIG_MANIFEST_APK_FILE,
-    INSTANCE_IMAGE_FILE, TEST_INSTANCE_DIR,
+    IDSIG_MANIFEST_EXT_APK_FILE, INSTANCE_IMAGE_FILE, TEST_INSTANCE_DIR,
 };
 use log::error;
 use std::fs::File;
@@ -93,6 +93,7 @@
     let instance_image = instance_dir.join(INSTANCE_IMAGE_FILE);
     let idsig = instance_dir.join(IDSIG_FILE);
     let idsig_manifest_apk = instance_dir.join(IDSIG_MANIFEST_APK_FILE);
+    let idsig_manifest_ext_apk = instance_dir.join(IDSIG_MANIFEST_EXT_APK_FILE);
 
     let instance_image = File::open(instance_image).context("Failed to open instance image")?;
 
@@ -111,6 +112,7 @@
         instance_image,
         &idsig,
         &idsig_manifest_apk,
+        &idsig_manifest_ext_apk,
         &VmParameters { debug_mode: args.debug, ..Default::default() },
     )?;
 
diff --git a/libs/idsig/src/apksigv4.rs b/libs/idsig/src/apksigv4.rs
index c1b6495..434a429 100644
--- a/libs/idsig/src/apksigv4.rs
+++ b/libs/idsig/src/apksigv4.rs
@@ -299,7 +299,7 @@
 
     #[test]
     fn parse_idsig_file() {
-        let idsig = Cursor::new(include_bytes!("../testdata/test.apk.idsig"));
+        let idsig = Cursor::new(include_bytes!("../testdata/v4-digest-v3-Sha256withEC.apk.idsig"));
         let parsed = V4Signature::from(idsig).unwrap();
 
         assert_eq!(Version::V2, parsed.version);
@@ -309,32 +309,32 @@
         assert_eq!(12, hi.log2_blocksize);
         assert_eq!("", hexstring_from(hi.salt.as_ref()));
         assert_eq!(
-            "ce1194fdb3cb2537daf0ac8cdf4926754adcbce5abeece7945fe25d204a0df6a",
+            "77f063b48b63f846690fa76450a8d3b61a295b6158f50592e873f76dbeeb0201",
             hexstring_from(hi.raw_root_hash.as_ref())
         );
 
         let si = parsed.signing_info;
         assert_eq!(
-            "b5225523a813fb84ed599dd649698c080bcfed4fb19ddb00283a662a2683bc15",
+            "c02fe2eddeb3078801828b930de546ea4f98d37fb98b40c7c7ed169b0d713583",
             hexstring_from(si.apk_digest.as_ref())
         );
         assert_eq!("", hexstring_from(si.additional_data.as_ref()));
         assert_eq!(
-            "303d021c77304d0f4732a90372bbfce095223e4ba82427ceb381f69bc6762d78021d008b99924\
-                   a8585c38d7f654835eb219ae9e176b44e86dcb23153e3d9d6",
+            "3046022100fb6383ba300dc7e1e6931a25b381398a16e5575baefd82afd12ba88660d9a6\
+            4c022100ebdcae13ab18c4e30bf6ae634462e526367e1ba26c2647a1d87a0f42843fc128",
             hexstring_from(si.signature.as_ref())
         );
-        assert_eq!(SignatureAlgorithmID::DsaWithSha256, si.signature_algorithm_id);
+        assert_eq!(SignatureAlgorithmID::EcdsaWithSha256, si.signature_algorithm_id);
 
-        assert_eq!(36864, parsed.merkle_tree_size);
-        assert_eq!(2251, parsed.merkle_tree_offset);
+        assert_eq!(4096, parsed.merkle_tree_size);
+        assert_eq!(648, parsed.merkle_tree_offset);
     }
 
     /// Parse an idsig file into V4Signature and write it. The written date must be the same as
     /// the input file.
     #[test]
     fn parse_and_compose() {
-        let input = Cursor::new(include_bytes!("../testdata/test.apk.idsig"));
+        let input = Cursor::new(include_bytes!("../testdata/v4-digest-v3-Sha256withEC.apk.idsig"));
         let mut parsed = V4Signature::from(input.clone()).unwrap();
 
         let mut output = Cursor::new(Vec::new());
@@ -347,11 +347,11 @@
     /// as those in the idsig file created by the signapk tool.
     #[test]
     fn digest_from_apk() {
-        let mut input = Cursor::new(include_bytes!("../testdata/test.apk"));
+        let mut input = Cursor::new(include_bytes!("../testdata/v4-digest-v3-Sha256withEC.apk"));
         let mut created =
             V4Signature::create(&mut input, 4096, &[], HashAlgorithm::SHA256).unwrap();
 
-        let golden = Cursor::new(include_bytes!("../testdata/test.apk.idsig"));
+        let golden = Cursor::new(include_bytes!("../testdata/v4-digest-v3-Sha256withEC.apk.idsig"));
         let mut golden = V4Signature::from(golden).unwrap();
 
         // Compare the root hash
diff --git a/libs/idsig/testdata/test.apk b/libs/idsig/testdata/test.apk
deleted file mode 100644
index cbee532..0000000
--- a/libs/idsig/testdata/test.apk
+++ /dev/null
Binary files differ
diff --git a/libs/idsig/testdata/test.apk.idsig b/libs/idsig/testdata/test.apk.idsig
deleted file mode 100644
index 8c112de..0000000
--- a/libs/idsig/testdata/test.apk.idsig
+++ /dev/null
Binary files differ
diff --git a/libs/idsig/testdata/v4-digest-v3-Sha256withEC.apk b/libs/idsig/testdata/v4-digest-v3-Sha256withEC.apk
new file mode 100644
index 0000000..b214336
--- /dev/null
+++ b/libs/idsig/testdata/v4-digest-v3-Sha256withEC.apk
Binary files differ
diff --git a/libs/idsig/testdata/v4-digest-v3-Sha256withEC.apk.idsig b/libs/idsig/testdata/v4-digest-v3-Sha256withEC.apk.idsig
new file mode 100644
index 0000000..19d38d5
--- /dev/null
+++ b/libs/idsig/testdata/v4-digest-v3-Sha256withEC.apk.idsig
Binary files differ