Populate apex module info

Once early boot has ended, monitor the `apexd.status` property and when
it is activated, query apexd for the modules.

Use this to populate the module information in Keystore and send the
hash of the encoding to KeyMint(s).

Bug: 369375199
Test: VtsAidlKeyMintTargetTest
Flag: android.security.keystore2.attest_modules
Change-Id: I1cc84482c67a642bea6a0e031c1fc85084940e46
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
index efac501..16cc34f 100644
--- a/keystore2/src/maintenance.rs
+++ b/keystore2/src/maintenance.rs
@@ -30,6 +30,9 @@
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     ErrorCode::ErrorCode, IKeyMintDevice::IKeyMintDevice, KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue, SecurityLevel::SecurityLevel, Tag::Tag,
 };
+use apex_aidl_interface::aidl::android::apex::{
+    IApexService::IApexService,
+};
 use android_security_maintenance::aidl::android::security::maintenance::IKeystoreMaintenance::{
     BnKeystoreMaintenance, IKeystoreMaintenance,
 };
@@ -42,9 +45,11 @@
 use android_system_keystore2::aidl::android::system::keystore2::KeyDescriptor::KeyDescriptor;
 use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
 use anyhow::{anyhow, Context, Result};
+use binder::wait_for_interface;
 use bssl_crypto::digest;
 use der::{DerOrd, Encode, asn1::OctetString, asn1::SetOfVec, Sequence};
 use keystore2_crypto::Password;
+use rustutils::system_properties::PropertyWatcher;
 use std::cmp::Ordering;
 
 /// Reexport Domain for the benefit of DeleteListener
@@ -54,10 +59,10 @@
 pub const KEYMINT_V4: i32 = 400;
 
 /// Module information structure for DER-encoding.
-#[derive(Sequence, Debug)]
+#[derive(Sequence, Debug, PartialEq, Eq)]
 struct ModuleInfo {
     name: OctetString,
-    version: i32,
+    version: i64,
 }
 
 impl DerOrd for ModuleInfo {
@@ -239,9 +244,68 @@
             log::error!("SUPER_KEY.set_up_boot_level_cache failed:\n{:?}\n:(", e);
         }
 
+        if keystore2_flags::attest_modules() {
+            std::thread::spawn(move || {
+                Self::watch_apex_info()
+                    .unwrap_or_else(|e| log::error!("watch_apex_info failed: {e:?}"));
+            });
+        }
         Maintenance::call_on_all_security_levels("earlyBootEnded", |dev| dev.earlyBootEnded(), None)
     }
 
+    /// Watch the `apexd.status` system property, and read apex module information once
+    /// it is `activated`.
+    ///
+    /// Blocks waiting for system property changes, so must be run in its own thread.
+    fn watch_apex_info() -> Result<()> {
+        let prop = "apexd.status";
+        log::info!("start monitoring '{prop}' property");
+        let mut w = PropertyWatcher::new(prop).context(ks_err!("PropertyWatcher::new failed"))?;
+        loop {
+            let value = w.read(|_name, value| Ok(value.to_string()));
+            // The status for apexd moves from "starting" to "activated" to "ready"; the apex
+            // info file should be populated for either of the latter two states, so cope with
+            // both in case we miss a state change.
+            log::info!("property '{prop}' is now '{value:?}'");
+            if matches!(value.as_deref(), Ok("activated") | Ok("ready")) {
+                match Self::read_apex_info() {
+                    Ok(modules) => {
+                        Self::set_module_info(modules)
+                            .context(ks_err!("failed to set module info"))?;
+                        break;
+                    }
+                    Err(e) => {
+                        log::error!(
+                            "failed to read apex info, try again on next state change: {e:?}"
+                        );
+                    }
+                }
+            }
+
+            log::info!("await a change to '{prop}'...");
+            w.wait(None).context(ks_err!("property wait failed"))?;
+            log::info!("await a change to '{prop}'...notified");
+        }
+        Ok(())
+    }
+
+    fn read_apex_info() -> Result<Vec<ModuleInfo>> {
+        let _wp = wd::watch("read_apex_info via IApexService.getActivePackages()");
+        let apexd: Strong<dyn IApexService> =
+            wait_for_interface("apexservice").context("failed to AIDL connect to apexd")?;
+        let packages = apexd.getActivePackages().context("failed to retrieve active packages")?;
+        packages
+            .into_iter()
+            .map(|pkg| {
+                log::info!("apex modules += {} version {}", pkg.moduleName, pkg.versionCode);
+                let name = OctetString::new(pkg.moduleName.as_bytes()).map_err(|e| {
+                    anyhow!("failed to convert '{}' to OCTET_STRING: {e:?}", pkg.moduleName)
+                })?;
+                Ok(ModuleInfo { name, version: pkg.versionCode })
+            })
+            .collect()
+    }
+
     fn migrate_key_namespace(source: &KeyDescriptor, destination: &KeyDescriptor) -> Result<()> {
         let calling_uid = ThreadState::get_calling_uid();
 
@@ -376,8 +440,8 @@
         Ok(())
     }
 
-    #[allow(dead_code)]
     fn set_module_info(module_info: Vec<ModuleInfo>) -> Result<()> {
+        log::info!("set_module_info with {} modules", module_info.len());
         let encoding = Self::encode_module_info(module_info)
             .map_err(|e| anyhow!({ e }))
             .context(ks_err!("Failed to encode module_info"))?;
@@ -409,7 +473,6 @@
         )
     }
 
-    #[allow(dead_code)]
     fn encode_module_info(module_info: Vec<ModuleInfo>) -> Result<Vec<u8>, der::Error> {
         SetOfVec::<ModuleInfo>::from_iter(module_info.into_iter())?.to_der()
     }