Move the parse of last_install to recovery-persist

The recovery-persist used to look for the related recovery logs in
persist storage, and copy them under /data/misc/recovery during the
normal boot process.

As we also want to find out the sideload information from last_install,
it makes more sense to move the parse & report of non-a/b metrics to
recovery-persist. Thus we can avoid the race condition of the file
system between the native code and RecoverySystem.

Bug: 114278989
Test: unit test pass, check the event buffer for metrics report
Change-Id: I32d7b2b831bc74a61a70af9a2f0b8a7e9b3e36ee
diff --git a/recovery-persist.cpp b/recovery-persist.cpp
index d3ade62..ebb42d2 100644
--- a/recovery-persist.cpp
+++ b/recovery-persist.cpp
@@ -35,19 +35,22 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <limits>
 #include <string>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <metricslogger/metrics_logger.h>
 #include <private/android_logger.h> /* private pmsg functions */
 
 #include "logging.h"
+#include "otautil/parse_install_logs.h"
 
-static const char *LAST_LOG_FILE = "/data/misc/recovery/last_log";
-static const char *LAST_PMSG_FILE = "/sys/fs/pstore/pmsg-ramoops-0";
-static const char *LAST_KMSG_FILE = "/data/misc/recovery/last_kmsg";
-static const char *LAST_CONSOLE_FILE = "/sys/fs/pstore/console-ramoops-0";
-static const char *ALT_LAST_CONSOLE_FILE = "/sys/fs/pstore/console-ramoops";
+constexpr const char* LAST_LOG_FILE = "/data/misc/recovery/last_log";
+constexpr const char* LAST_PMSG_FILE = "/sys/fs/pstore/pmsg-ramoops-0";
+constexpr const char* LAST_KMSG_FILE = "/data/misc/recovery/last_kmsg";
+constexpr const char* LAST_CONSOLE_FILE = "/sys/fs/pstore/console-ramoops-0";
+constexpr const char* ALT_LAST_CONSOLE_FILE = "/sys/fs/pstore/console-ramoops";
 
 // close a file, log an error if the error indicator is set
 static void check_and_fclose(FILE *fp, const char *name) {
@@ -109,6 +112,20 @@
     return android::base::WriteStringToFile(buffer, destination.c_str());
 }
 
+// Parses the LAST_INSTALL file and reports the update metrics saved under recovery mode.
+static void report_metrics_from_last_install(const std::string& file_name) {
+  auto metrics = ParseLastInstall(file_name);
+  // TODO(xunchang) report the installation result.
+  for (const auto& [event, value] : metrics) {
+    if (value > std::numeric_limits<int>::max()) {
+      LOG(WARNING) << event << " (" << value << ") exceeds integer max.";
+    } else {
+      LOG(INFO) << "Uploading " << value << " to " << event;
+      android::metricslogger::LogHistogram(event, value);
+    }
+  }
+}
+
 int main(int argc, char **argv) {
 
     /* Is /cache a mount?, we have been delivered where we are not wanted */
@@ -138,14 +155,18 @@
     }
 
     if (has_cache) {
-        /*
-         * TBD: Future location to move content from
-         * /cache/recovery to /data/misc/recovery/
-         */
-        /* if --force-persist flag, then transfer pmsg data anyways */
-        if ((argc <= 1) || !argv[1] || strcmp(argv[1], "--force-persist")) {
-            return 0;
-        }
+      // Collects and reports the non-a/b update metrics from last_install; and removes the file
+      // to avoid duplicate report.
+      report_metrics_from_last_install(LAST_INSTALL_FILE_IN_CACHE);
+      if (unlink(LAST_INSTALL_FILE_IN_CACHE) == -1) {
+        PLOG(ERROR) << "Failed to unlink " << LAST_INSTALL_FILE_IN_CACHE;
+      }
+
+      // TBD: Future location to move content from /cache/recovery to /data/misc/recovery/
+      // if --force-persist flag, then transfer pmsg data anyways
+      if ((argc <= 1) || !argv[1] || strcmp(argv[1], "--force-persist")) {
+        return 0;
+      }
     }
 
     /* Is there something in pmsg? */
@@ -157,6 +178,15 @@
     __android_log_pmsg_file_read(
         LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", logsave, NULL);
 
+    // For those device without /cache, the last_install file has been copied to
+    // /data/misc/recovery from pmsg. Looks for the sideload history only.
+    if (!has_cache) {
+      report_metrics_from_last_install(LAST_INSTALL_FILE);
+      if (unlink(LAST_INSTALL_FILE) == -1) {
+        PLOG(ERROR) << "Failed to unlink " << LAST_INSTALL_FILE;
+      }
+    }
+
     /* Is there a last console log too? */
     if (rotated) {
         if (!access(LAST_CONSOLE_FILE, R_OK)) {