Reboot to bootloader if sending module info to KeyMint fails

* Panic in check_send_module_info if sending module info to KeyMint fails.
* Make Keystore a critical service, in order to reboot to bootloader if
  Keystore crashes more than 4 times before sys.boot_completed = true.
* On Keystore start, check if we should send KeyMint module info, and if
  so, send the info. (The check here is whether keystore.module_hash.sent
  != true.)

Bug: 400439023
Test: (1) for the successful case, ran Keystore CTS tests; (2) for
 the unsuccessful case, where we reach the panic in send_module_info,
 confirmed that the phone ended up in fastboot.
Flag: android.security.keystore2.attest_modules

Change-Id: I702828fc3462f5e90ae09c4f654d90c85f28195a
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index 008e6fe..e08a5f2 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -76,6 +76,11 @@
     // Write/update keystore.crash_count system property.
     metrics_store::update_keystore_crash_sysprop();
 
+    // Send KeyMint module information for attestations.
+    // Note that the information should be sent before code from modules starts running.
+    // (This is guaranteed by waiting for `keystore.module_hash.sent` == true during device boot.)
+    Maintenance::check_send_module_info();
+
     // Keystore 2.0 cannot change to the database directory (typically /data/misc/keystore) on
     // startup as Keystore 1.0 did because Keystore 2.0 is intended to run much earlier than
     // Keystore 1.0. Instead we set a global variable to the database path.
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
index 4116bc5..a0f5ee8 100644
--- a/keystore2/src/maintenance.rs
+++ b/keystore2/src/maintenance.rs
@@ -247,17 +247,42 @@
         {
             log::error!("SUPER_KEY.set_up_boot_level_cache failed:\n{:?}\n:(", e);
         }
+        Maintenance::call_on_all_security_levels("earlyBootEnded", |dev| dev.earlyBootEnded(), None)
+    }
 
+    /// Spawns a thread to send module info if it hasn't already been sent. The thread first waits
+    /// for the apex info to be available.
+    /// (Module info would have already been sent in the case of a Keystore restart.)
+    ///
+    /// # Panics
+    ///
+    /// This method, and methods it calls, panic on failure, because a failure to populate module
+    /// information will block the boot process from completing. In this method, this happens if:
+    /// - the `apexd.status` property is unable to be monitored
+    /// - the `keystore.module_hash.sent` property cannot be updated
+    pub fn check_send_module_info() {
+        if rustutils::system_properties::read_bool("keystore.module_hash.sent", false)
+            .unwrap_or(false)
+        {
+            log::info!("Module info has already been sent.");
+            return;
+        }
         if keystore2_flags::attest_modules() {
             std::thread::spawn(move || {
-                Self::watch_apex_info()
-                    .unwrap_or_else(|e| log::error!("watch_apex_info failed, preventing keystore.module_hash.sent from being set to true; this may therefore block boot: {e:?}"));
+                // Wait for apex info to be available before populating.
+                Self::watch_apex_info().unwrap_or_else(|e| {
+                    log::error!("failed to monitor apexd.status property: {e:?}");
+                    panic!("Terminating due to inaccessibility of apexd.status property, blocking boot: {e:?}");
+                });
             });
         } else {
             rustutils::system_properties::write("keystore.module_hash.sent", "true")
-                .context(ks_err!("failed to set keystore.module_hash.sent to true"))?;
+                .unwrap_or_else(|e| {
+                        log::error!("Failed to set keystore.module_hash.sent to true; this will therefore block boot: {e:?}");
+                        panic!("Crashing Keystore because it failed to set keystore.module_hash.sent to true (which blocks boot).");
+                    }
+                );
         }
-        Maintenance::call_on_all_security_levels("earlyBootEnded", |dev| dev.earlyBootEnded(), None)
     }
 
     /// Watch the `apexd.status` system property, and read apex module information once
@@ -273,18 +298,38 @@
             let value = w.read(|_name, value| Ok(value.to_string()));
             log::info!("property '{apex_prop}' is now '{value:?}'");
             if matches!(value.as_deref(), Ok("activated")) {
-                let modules =
-                    Self::read_apex_info().context(ks_err!("failed to read apex info"))?;
-                Self::set_module_info(modules).context(ks_err!("failed to set module info"))?;
-                rustutils::system_properties::write("keystore.module_hash.sent", "true")
-                    .context(ks_err!("failed to set keystore.module_hash.sent to true"))?;
-                break;
+                Self::read_and_set_module_info();
+                return Ok(());
             }
             log::info!("await a change to '{apex_prop}'...");
             w.wait(None).context(ks_err!("property wait failed"))?;
             log::info!("await a change to '{apex_prop}'...notified");
         }
-        Ok(())
+    }
+
+    /// Read apex information (which is assumed to be present) and propagate module
+    /// information to KeyMint instances.
+    ///
+    /// # Panics
+    ///
+    /// This method panics on failure, because a failure to populate module information
+    /// will block the boot process from completing.  This happens if:
+    /// - apex information is not available (precondition)
+    /// - KeyMint instances fail to accept module information
+    /// - the `keystore.module_hash.sent` property cannot be updated
+    fn read_and_set_module_info() {
+        let modules = Self::read_apex_info().unwrap_or_else(|e| {
+            log::error!("failed to read apex info: {e:?}");
+            panic!("Terminating due to unavailability of apex info, blocking boot: {e:?}");
+        });
+        Self::set_module_info(modules).unwrap_or_else(|e| {
+            log::error!("failed to set module info: {e:?}");
+            panic!("Terminating due to KeyMint not accepting module info, blocking boot: {e:?}");
+        });
+        rustutils::system_properties::write("keystore.module_hash.sent", "true").unwrap_or_else(|e| {
+            log::error!("failed to set keystore.module_hash.sent property: {e:?}");
+            panic!("Terminating due to failure to set keystore.module_hash.sent property, blocking boot: {e:?}");
+        });
     }
 
     fn read_apex_info() -> Result<Vec<ModuleInfo>> {