Merge "[LSC] Add LOCAL_LICENSE_KINDS to system/security"
diff --git a/ondevice-signing/Android.bp b/ondevice-signing/Android.bp
index bdc94b7..d73f8fe 100644
--- a/ondevice-signing/Android.bp
+++ b/ondevice-signing/Android.bp
@@ -112,6 +112,7 @@
     "KeystoreKey.cpp",
     "KeystoreHmacKey.cpp",
     "odsign_main.cpp",
+    "StatsReporter.cpp",
   ],
 
   header_libs: ["odrefresh_headers"],
diff --git a/ondevice-signing/StatsReporter.cpp b/ondevice-signing/StatsReporter.cpp
new file mode 100644
index 0000000..65e645a
--- /dev/null
+++ b/ondevice-signing/StatsReporter.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "StatsReporter.h"
+#include <android-base/logging.h>
+#include <stdlib.h>
+#include <string>
+#include <sys/stat.h>
+
+// Keep these constant in sync with COMPOS_METRIC_NAME & METRICS_FILE in OdsignStatsLogger.java.
+constexpr const char* kOdsignMetricsFile = "/data/misc/odsign/metrics/odsign-metrics.txt";
+constexpr const char* kComposMetricName = "comp_os_artifacts_check_record";
+
+StatsReporter::~StatsReporter() {
+    if (comp_os_artifacts_check_record_ == nullptr) {
+        LOG(INFO) << "Metrics report is empty";
+
+        // Remove the metrics file if any old version of the file already exists
+        if (std::filesystem::remove(kOdsignMetricsFile) != 0 &&
+            !((errno = ENOENT) || errno == ENOTDIR)) {
+            PLOG(ERROR) << "Could not remove already present file";
+        }
+        return;
+    }
+
+    std::ofstream odsign_metrics_file_;
+    odsign_metrics_file_.open(kOdsignMetricsFile, std::ios::trunc);
+    if (!odsign_metrics_file_) {
+        PLOG(ERROR) << "Could not open file: " << kOdsignMetricsFile;
+        return;
+    }
+
+    odsign_metrics_file_ << kComposMetricName << ' ';
+    odsign_metrics_file_ << comp_os_artifacts_check_record_->current_artifacts_ok << ' ';
+    odsign_metrics_file_ << comp_os_artifacts_check_record_->comp_os_pending_artifacts_exists
+                         << ' ';
+    odsign_metrics_file_ << comp_os_artifacts_check_record_->use_comp_os_generated_artifacts
+                         << '\n';
+    if (chmod(kOdsignMetricsFile, 0644) != 0) {
+        PLOG(ERROR) << "Could not set correct file permissions for " << kOdsignMetricsFile;
+        return;
+    }
+    odsign_metrics_file_.close();
+    if (!odsign_metrics_file_) {
+        PLOG(ERROR) << "Failed to close the file";
+    }
+}
+
+StatsReporter::CompOsArtifactsCheckRecord* StatsReporter::GetComposArtifactsCheckRecord() {
+    if (comp_os_artifacts_check_record_ == nullptr) {
+        comp_os_artifacts_check_record_ = std::make_unique<CompOsArtifactsCheckRecord>();
+    }
+    return comp_os_artifacts_check_record_.get();
+}
diff --git a/ondevice-signing/StatsReporter.h b/ondevice-signing/StatsReporter.h
new file mode 100644
index 0000000..2682b96
--- /dev/null
+++ b/ondevice-signing/StatsReporter.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <fstream>
+
+// Class to store CompOsArtifactsCheck related metrics.
+// These are flushed to a file kOdsignMetricsFile and consumed by
+// System Server (in class OdsignStatsLogger) & sent to statsd.
+class StatsReporter {
+  public:
+    // Keep sync with EarlyBootCompOsArtifactsCheckReported
+    // definition in proto_logging/stats/atoms.proto.
+    struct CompOsArtifactsCheckRecord {
+        bool current_artifacts_ok = false;
+        bool comp_os_pending_artifacts_exists = false;
+        bool use_comp_os_generated_artifacts = false;
+    };
+
+    // The report is flushed (from buffer) into a file by the destructor.
+    ~StatsReporter();
+
+    // Get pointer to comp_os_artifacts_check_record, caller can then modify it.
+    // Note: pointer remains valid for the lifetime of this StatsReporter.
+    CompOsArtifactsCheckRecord* GetComposArtifactsCheckRecord();
+
+  private:
+    // Temporary buffer which stores the metrics.
+    std::unique_ptr<CompOsArtifactsCheckRecord> comp_os_artifacts_check_record_;
+};
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
index fc55846..04679a5 100644
--- a/ondevice-signing/odsign_main.cpp
+++ b/ondevice-signing/odsign_main.cpp
@@ -33,6 +33,7 @@
 
 #include "CertUtils.h"
 #include "KeystoreKey.h"
+#include "StatsReporter.h"
 #include "VerityUtils.h"
 
 #include "odsign_info.pb.h"
@@ -365,19 +366,26 @@
     return compos_info;
 }
 
-art::odrefresh::ExitCode checkCompOsPendingArtifacts(const SigningKey& signing_key,
-                                                     bool* digests_verified) {
+art::odrefresh::ExitCode CheckCompOsPendingArtifacts(const SigningKey& signing_key,
+                                                     bool* digests_verified,
+                                                     StatsReporter* stats_reporter) {
+    StatsReporter::CompOsArtifactsCheckRecord* compos_check_record =
+        stats_reporter->GetComposArtifactsCheckRecord();
+
     if (!directoryHasContent(kCompOsPendingArtifactsDir)) {
         // No pending CompOS artifacts, all that matters is the current ones.
         return checkArtifacts();
     }
 
+    compos_check_record->comp_os_pending_artifacts_exists = true;
+
     // CompOS has generated some artifacts that may, or may not, match the
     // current state.  But if there are already valid artifacts present the
     // CompOS ones are redundant.
     art::odrefresh::ExitCode odrefresh_status = checkArtifacts();
     if (odrefresh_status != art::odrefresh::ExitCode::kCompilationRequired) {
         if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
+            compos_check_record->current_artifacts_ok = true;
             LOG(INFO) << "Current artifacts are OK, deleting pending artifacts";
             removeDirectory(kCompOsPendingArtifactsDir);
         }
@@ -432,6 +440,7 @@
                     // are pretty bad.
                     return art::odrefresh::ExitCode::kCleanupFailed;
                 }
+                compos_check_record->use_comp_os_generated_artifacts = true;
                 LOG(INFO) << "Persisted CompOS digests.";
                 *digests_verified = true;
                 return odrefresh_status;
@@ -455,6 +464,9 @@
 }  // namespace
 
 int main(int /* argc */, char** argv) {
+    // stats_reporter is a pointer so that we can explicitly delete it
+    // instead of waiting for the program to die & its destrcutor be called
+    auto stats_reporter = std::make_unique<StatsReporter>();
     android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
 
     auto errorScopeGuard = []() {
@@ -516,7 +528,14 @@
 
     bool digests_verified = false;
     art::odrefresh::ExitCode odrefresh_status =
-        useCompOs ? checkCompOsPendingArtifacts(*key, &digests_verified) : checkArtifacts();
+        useCompOs ? CheckCompOsPendingArtifacts(*key, &digests_verified, stats_reporter.get())
+                  : checkArtifacts();
+
+    // Explicitly reset the pointer - We rely on stats_reporter's
+    // destructor for actually writing the buffered metrics. This will otherwise not be called
+    // if the program doesn't exit normally (for ex, killed by init, which actually happens
+    // because odsign (after it finishes) sets kStopServiceProp instructing init to kill it).
+    stats_reporter.reset();
 
     // The artifacts dir doesn't necessarily need to exist; if the existing
     // artifacts on the system partition are valid, those can be used.