microdroid_manager: pass apex pubkeys to apexd

Microdroid_manager passes apex pubkeys from the instance.img so that
apexd uses them to verify APEXes.

Bug: 199371341
Test: MicrodroidHostTestCases
Change-Id: I9260e456a00e767a79c8121eb6b391978ece0ae3
diff --git a/microdroid/init.rc b/microdroid/init.rc
index 347f514..23434bb 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -18,12 +18,11 @@
     start ueventd
 
     mkdir /mnt/apk 0755 system system
+    # Microdroid_manager starts apkdmverity/zipfuse/apexd
     start microdroid_manager
 
-    # Exec apexd in the VM mode to avoid unnecessary overhead of normal mode.
-    # (e.g. session management)
-    exec - root system -- /system/bin/apexd --vm
-
+    # Wait for apexd to finish activating APEXes before starting more processes.
+    wait_for_prop apexd.status activated
     perform_apex_config
 
     # Notify to microdroid_manager that perform_apex_config is done.
@@ -170,6 +169,12 @@
     mkdir /data/local 0751 root root
     mkdir /data/local/tmp 0771 shell shell
 
+service apexd-vm /system/bin/apexd --vm
+    user root
+    group system
+    oneshot
+    disabled
+
 service ueventd /system/bin/ueventd
     class core
     critical
diff --git a/microdroid/payload/metadata.proto b/microdroid/payload/metadata.proto
index 0fa0650..4c32dde 100644
--- a/microdroid/payload/metadata.proto
+++ b/microdroid/payload/metadata.proto
@@ -38,11 +38,7 @@
 
   // Optional.
   // When specified, the public key used to sign the apex should match with it.
-  string publicKey = 3;
-
-  // Optional.
-  // When specified, the root digest of the apex should match with it.
-  string rootDigest = 4;
+  bytes public_key = 3;
 }
 
 message ApkPayload {
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 6db3cab..204feab 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -19,16 +19,16 @@
 mod payload;
 
 use crate::instance::{ApkData, InstanceDisk, MicrodroidData, RootHash};
-use anyhow::{anyhow, bail, Context, Result};
+use anyhow::{anyhow, bail, ensure, Context, Result};
 use apkverify::verify;
 use binder::unstable_api::{new_spibinder, AIBinder};
 use binder::{FromIBinder, Strong};
 use idsig::V4Signature;
 use log::{error, info, warn};
-use microdroid_metadata::Metadata;
+use microdroid_metadata::{write_metadata, Metadata};
 use microdroid_payload_config::{Task, TaskType, VmPayloadConfig};
 use nix::ioctl_read_bad;
-use payload::{get_apex_data_from_payload, load_metadata};
+use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
 use rustutils::system_properties;
 use rustutils::system_properties::PropertyWatcher;
 use std::fs::{self, File, OpenOptions};
@@ -162,9 +162,23 @@
     // Start apkdmverity and wait for the dm-verify block
     system_properties::write("ctl.start", "apkdmverity")?;
 
-    // While waiting for apkdmverity to mount APK, gathers APEX pubkeys used by APEXd.
-    // These will be compared ones from instance.img.
+    // While waiting for apkdmverity to mount APK, gathers APEX pubkeys from payload.
     let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
+    if let Some(saved_data) = saved_data.map(|d| &d.apex_data) {
+        // For APEX payload, we don't support updating their pubkeys
+        ensure!(saved_data == &apex_data_from_payload, "APEX payloads has changed.");
+        let apex_metadata = to_metadata(&apex_data_from_payload);
+        // Pass metadata(with pubkeys) to apexd so that it uses the passed metadata
+        // instead of the default one (/dev/block/by-name/payload-metadata)
+        OpenOptions::new()
+            .create_new(true)
+            .write(true)
+            .open("/apex/vm-payload-metadata")
+            .context("Failed to open /apex/vm-payload-metadata")
+            .and_then(|f| write_metadata(&apex_metadata, f))?;
+    }
+    // Start apexd to activate APEXes
+    system_properties::write("ctl.start", "apexd-vm")?;
 
     ioutil::wait_for_file(DM_MOUNTED_APK_PATH, WAIT_TIMEOUT)?;
 
diff --git a/microdroid_manager/src/payload.rs b/microdroid_manager/src/payload.rs
index bfc6c09..bf9d9f9 100644
--- a/microdroid_manager/src/payload.rs
+++ b/microdroid_manager/src/payload.rs
@@ -18,7 +18,7 @@
 use crate::ioutil::wait_for_file;
 use anyhow::Result;
 use log::info;
-use microdroid_metadata::{read_metadata, Metadata};
+use microdroid_metadata::{read_metadata, ApexPayload, Metadata};
 use std::fs::File;
 use std::io::Read;
 use std::time::Duration;
@@ -35,9 +35,9 @@
     read_metadata(file)
 }
 
-/// Loads (name, pubkey) from payload apexes and returns them as sorted by name.
+/// Loads (name, pubkey) from payload APEXes
 pub fn get_apex_data_from_payload(metadata: &Metadata) -> Result<Vec<ApexData>> {
-    let mut apex_data: Vec<ApexData> = metadata
+    metadata
         .apexes
         .iter()
         .map(|apex| {
@@ -46,9 +46,7 @@
             let pubkey = get_pubkey_from_apex(&partition)?;
             Ok(ApexData { name, pubkey })
         })
-        .collect::<Result<Vec<_>>>()?;
-    apex_data.sort_by(|a, b| a.name.cmp(&b.name));
-    Ok(apex_data)
+        .collect()
 }
 
 fn get_pubkey_from_apex(path: &str) -> Result<Vec<u8>> {
@@ -59,3 +57,18 @@
     pubkey_file.read_to_end(&mut pubkey)?;
     Ok(pubkey)
 }
+
+/// Convert vector of ApexData into Metadata
+pub fn to_metadata(apex_data: &[ApexData]) -> Metadata {
+    Metadata {
+        apexes: apex_data
+            .iter()
+            .map(|data| ApexPayload {
+                name: data.name.clone(),
+                public_key: data.pubkey.clone(),
+                ..Default::default()
+            })
+            .collect(),
+        ..Default::default()
+    }
+}