[Event Sequence] Add reset counter to every sequenced event.

Client design doc: go/cros-events-client-dd

Bug: 1350322
Change-Id: I1c332f45fb0427603cf832b2505e69d007aee341
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4148192
Reviewed-by: Tony Yeoman <[email protected]>
Commit-Queue: Jong Ahn <[email protected]>
Reviewed-by: Dominic Battré <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1094257}
diff --git a/chrome/browser/metrics/structured/BUILD.gn b/chrome/browser/metrics/structured/BUILD.gn
index df9cd4f..f4a8a0f 100644
--- a/chrome/browser/metrics/structured/BUILD.gn
+++ b/chrome/browser/metrics/structured/BUILD.gn
@@ -13,6 +13,8 @@
     "chrome_structured_metrics_recorder.h",
     "cros_events_processor.cc",
     "cros_events_processor.h",
+    "structured_metric_prefs.cc",
+    "structured_metric_prefs.h",
   ]
 
   deps = [
@@ -33,6 +35,7 @@
     ]
 
     deps += [
+      "//chrome/browser:browser_process",
       "//chrome/browser/ash/crosapi",
       "//components/user_manager:user_manager",
     ]
diff --git a/chrome/browser/metrics/structured/chrome_structured_metrics_recorder.cc b/chrome/browser/metrics/structured/chrome_structured_metrics_recorder.cc
index 3ebb0d07..25468d90 100644
--- a/chrome/browser/metrics/structured/chrome_structured_metrics_recorder.cc
+++ b/chrome/browser/metrics/structured/chrome_structured_metrics_recorder.cc
@@ -8,12 +8,12 @@
 
 #include "base/no_destructor.h"
 #include "base/task/sequenced_task_runner.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/metrics/structured/cros_events_processor.h"
 #include "components/metrics/structured/histogram_util.h"
 #include "components/metrics/structured/structured_metrics_features.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/browser_process.h" // nogncheck
 #include "chrome/browser/metrics/structured/ash_structured_metrics_recorder.h"  // nogncheck
 #elif BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "base/task/current_thread.h"
@@ -58,26 +58,23 @@
   return chrome_recorder.get();
 }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 // static
 void ChromeStructuredMetricsRecorder::RegisterLocalStatePrefs(
     PrefRegistrySimple* registry) {
   cros_event::CrOSEventsProcessor::RegisterLocalStatePrefs(registry);
 }
-
-// static
-void ChromeStructuredMetricsRecorder::RegisterUserProfilePrefs(
-    PrefRegistrySimple* registry) {
-  cros_event::CrOSEventsProcessor::RegisterUserProfilePrefs(registry);
-}
+#endif
 
 void ChromeStructuredMetricsRecorder::Initialize() {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   // Adds CrOSEvents processor if feature is enabled.
   if (base::FeatureList::IsEnabled(kEventSequenceLogging)) {
     StructuredMetricsClient::Get()->AddEventsProcessor(
-        std::make_unique<cros_event::CrOSEventsProcessor>());
+        std::make_unique<cros_event::CrOSEventsProcessor>(
+            g_browser_process->local_state()));
   }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
   auto* ash_recorder =
       static_cast<AshStructuredMetricsRecorder*>(delegate_.get());
   ash_recorder->Initialize();
diff --git a/chrome/browser/metrics/structured/chrome_structured_metrics_recorder.h b/chrome/browser/metrics/structured/chrome_structured_metrics_recorder.h
index 795686d..9a6c53f 100644
--- a/chrome/browser/metrics/structured/chrome_structured_metrics_recorder.h
+++ b/chrome/browser/metrics/structured/chrome_structured_metrics_recorder.h
@@ -8,13 +8,13 @@
 #include <memory>
 
 #include "base/no_destructor.h"
+#include "build/chromeos_buildflags.h"
 #include "components/metrics/structured/event.h"
 #include "components/metrics/structured/structured_events.h"
 #include "components/metrics/structured/structured_metrics_client.h"
 #include "components/prefs/pref_registry_simple.h"
 
-namespace metrics {
-namespace structured {
+namespace metrics::structured {
 
 namespace {
 using RecordingDelegate = StructuredMetricsClient::RecordingDelegate;
@@ -33,9 +33,13 @@
   // Pointer to singleton.
   static ChromeStructuredMetricsRecorder* Get();
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   // Registers prefs.
+  //
+  // TODO(crbug/1350322): Once reset counter is available to use from platform2,
+  // remove this and use the more relaible reset counter.
   static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
-  static void RegisterUserProfilePrefs(PrefRegistrySimple* registry);
+#endif
 
   ChromeStructuredMetricsRecorder(
       const ChromeStructuredMetricsRecorder& recorder) = delete;
@@ -67,7 +71,6 @@
   bool is_initialized_ = false;
 };
 
-}  // namespace structured
-}  // namespace metrics
+}  // namespace metrics::structured
 
 #endif  // CHROME_BROWSER_METRICS_STRUCTURED_CHROME_STRUCTURED_METRICS_RECORDER_H_
diff --git a/chrome/browser/metrics/structured/cros_events_processor.cc b/chrome/browser/metrics/structured/cros_events_processor.cc
index f008a06..68046fb0 100644
--- a/chrome/browser/metrics/structured/cros_events_processor.cc
+++ b/chrome/browser/metrics/structured/cros_events_processor.cc
@@ -4,22 +4,57 @@
 
 #include "chrome/browser/metrics/structured/cros_events_processor.h"
 
+#include "base/logging.h"
+#include "base/system/sys_info.h"
+#include "chrome/browser/metrics/structured/structured_metric_prefs.h"
+
 namespace metrics::structured::cros_event {
 
+CrOSEventsProcessor::CrOSEventsProcessor(PrefService* pref_service)
+    : pref_service_(pref_service) {}
 CrOSEventsProcessor::~CrOSEventsProcessor() = default;
 
 // static
 void CrOSEventsProcessor::RegisterLocalStatePrefs(
-    PrefRegistrySimple* registry) {}
-
-// static
-void CrOSEventsProcessor::RegisterUserProfilePrefs(
-    PrefRegistrySimple* registry) {}
+    PrefRegistrySimple* registry) {
+  // These prefs are modified multiple times per minute and should be registered
+  // as LOSSY_PREF since persisting prefs constantly is expensive.
+  registry->RegisterIntegerPref(kEventSequenceResetCounter, 0,
+                                PrefRegistry::LOSSY_PREF);
+  registry->RegisterInt64Pref(kEventSequenceLastSystemUptime, 0,
+                              PrefRegistry::LOSSY_PREF);
+}
 
 bool CrOSEventsProcessor::ShouldProcessOnEventRecord(const Event& event) {
   return event.IsEventSequenceType();
 }
 
-void CrOSEventsProcessor::OnEventsRecord(Event* event) {}
+void CrOSEventsProcessor::SetCurrentUptimeForTesting(int64_t current_uptime) {
+  current_uptime_for_testing_ = current_uptime;
+}
+
+void CrOSEventsProcessor::OnEventsRecord(Event* event) {
+  auto current_reset_counter =
+      pref_service_->GetInteger(kEventSequenceResetCounter);
+  auto last_system_uptime =
+      pref_service_->GetInt64(kEventSequenceLastSystemUptime);
+
+  auto current_uptime = current_uptime_for_testing_
+                            ? current_uptime_for_testing_
+                            : base::SysInfo::Uptime().InMilliseconds();
+
+  // If the last uptime is larger than the current uptime, a reset most likely
+  // happened.
+  if (last_system_uptime > current_uptime) {
+    ++current_reset_counter;
+    pref_service_->SetInteger(kEventSequenceResetCounter,
+                              current_reset_counter);
+  }
+
+  pref_service_->SetInt64(kEventSequenceLastSystemUptime, current_uptime);
+
+  event->SetEventSequenceMetadata(
+      Event::EventSequenceMetadata(current_reset_counter));
+}
 
 }  // namespace metrics::structured::cros_event
diff --git a/chrome/browser/metrics/structured/cros_events_processor.h b/chrome/browser/metrics/structured/cros_events_processor.h
index 1328fa8..a68e642 100644
--- a/chrome/browser/metrics/structured/cros_events_processor.h
+++ b/chrome/browser/metrics/structured/cros_events_processor.h
@@ -7,21 +7,31 @@
 
 #include "components/metrics/structured/events_processor_interface.h"
 #include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
 
 namespace metrics::structured::cros_event {
 
+// Post-processor that will process only sequenceable events and attach metadata
+// to the events.
 class CrOSEventsProcessor : public EventsProcessorInterface {
  public:
+  explicit CrOSEventsProcessor(PrefService* pref_service);
   ~CrOSEventsProcessor() override;
 
   // Registers device-level prefs.
   static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
 
-  // Registers user-level prefs.
-  static void RegisterUserProfilePrefs(PrefRegistrySimple* registry);
-
+  // EventsProcessorInterface:
   bool ShouldProcessOnEventRecord(const Event& event) override;
   void OnEventsRecord(Event* event) override;
+
+  // Used only to set the current uptime to check against for testing.
+  // If this value is not set explicitly, system clock will be used.
+  void SetCurrentUptimeForTesting(int64_t current_uptime);
+
+ private:
+  PrefService* pref_service_;
+  int64_t current_uptime_for_testing_ = 0;
 };
 
 }  // namespace metrics::structured::cros_event
diff --git a/chrome/browser/metrics/structured/cros_events_processor_unittest.cc b/chrome/browser/metrics/structured/cros_events_processor_unittest.cc
new file mode 100644
index 0000000..9efd05a
--- /dev/null
+++ b/chrome/browser/metrics/structured/cros_events_processor_unittest.cc
@@ -0,0 +1,81 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/metrics/structured/cros_events_processor.h"
+
+#include "base/time/time.h"
+#include "chrome/browser/metrics/structured/structured_metric_prefs.h"
+#include "components/metrics/structured/structured_events.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace metrics::structured::cros_event {
+
+namespace {
+
+class CrOSEventsProcessorTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    processor_ = std::make_unique<CrOSEventsProcessor>(&test_pref_service_);
+    CrOSEventsProcessor::RegisterLocalStatePrefs(test_pref_service_.registry());
+  }
+
+  void Initialize(int64_t last_uptime, int reset_counter) {
+    test_pref_service_.SetInteger(kEventSequenceResetCounter, reset_counter);
+    test_pref_service_.SetInt64(kEventSequenceLastSystemUptime, last_uptime);
+  }
+
+  int GetResetCounter() {
+    return test_pref_service_.GetInteger(kEventSequenceResetCounter);
+  }
+
+  int64_t GetLastSystemUptime() {
+    return test_pref_service_.GetInt64(kEventSequenceLastSystemUptime);
+  }
+
+  TestingPrefServiceSimple test_pref_service_;
+  std::unique_ptr<cros_event::CrOSEventsProcessor> processor_;
+};
+
+}  // namespace
+
+TEST_F(CrOSEventsProcessorTest, CheckResetCounterUpdatedOnReset) {
+  int64_t last_uptime = 20;
+  int reset_counter = 10;
+
+  Initialize(last_uptime, reset_counter);
+  // Sets the current uptime to be less than the last uptime to emulate a reset
+  // counter scenario.
+  processor_->SetCurrentUptimeForTesting(last_uptime - 1);
+
+  events::v2::cr_os_events::Test1 test_event;
+  test_event.SetRecordedTimeSinceBoot(base::Milliseconds(last_uptime));
+  processor_->OnEventsRecord(&test_event);
+
+  EXPECT_EQ(test_event.event_sequence_metadata().reset_counter,
+            reset_counter + 1);
+  EXPECT_EQ(GetResetCounter(), reset_counter + 1);
+  EXPECT_EQ(GetLastSystemUptime(), last_uptime - 1);
+}
+
+TEST_F(CrOSEventsProcessorTest, ResetCounterNotUpdated) {
+  int64_t last_uptime = 20;
+  int reset_counter = 10;
+
+  Initialize(last_uptime, reset_counter);
+  // Sets the current uptime to be greater than the last uptime to emulate a
+  // scenario where reset counter should not be incremented.
+  processor_->SetCurrentUptimeForTesting(last_uptime + 1);
+
+  events::v2::cr_os_events::Test1 test_event;
+  test_event.SetRecordedTimeSinceBoot(base::Milliseconds(last_uptime));
+  processor_->OnEventsRecord(&test_event);
+
+  EXPECT_EQ(test_event.event_sequence_metadata().reset_counter, reset_counter);
+  EXPECT_EQ(GetResetCounter(), reset_counter);
+  EXPECT_EQ(GetLastSystemUptime(), last_uptime + 1);
+}
+
+}  // namespace metrics::structured::cros_event
diff --git a/chrome/browser/metrics/structured/structured_metric_prefs.cc b/chrome/browser/metrics/structured/structured_metric_prefs.cc
new file mode 100644
index 0000000..6d329dc
--- /dev/null
+++ b/chrome/browser/metrics/structured/structured_metric_prefs.cc
@@ -0,0 +1,21 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/metrics/structured/structured_metric_prefs.h"
+
+namespace metrics::structured {
+
+// Keeps track of the system uptime of the last recorded uptime. This is used to
+// determine whether the reset counter or not.
+//
+// TODO(crbug/1350322): Remove this and kEventSequenceResetCounter once
+// implementation for handling resets for CrOS is implemented in platform2.
+const char kEventSequenceLastSystemUptime[] =
+    "metrics.event_sequence.last_system_uptime";
+
+// Keeps track of the device reset counter.
+const char kEventSequenceResetCounter[] =
+    "metrics.event_sequence.reset_counter";
+
+}  // namespace metrics::structured
diff --git a/chrome/browser/metrics/structured/structured_metric_prefs.h b/chrome/browser/metrics/structured/structured_metric_prefs.h
new file mode 100644
index 0000000..fc6861d
--- /dev/null
+++ b/chrome/browser/metrics/structured/structured_metric_prefs.h
@@ -0,0 +1,16 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_METRICS_STRUCTURED_STRUCTURED_METRIC_PREFS_H_
+#define CHROME_BROWSER_METRICS_STRUCTURED_STRUCTURED_METRIC_PREFS_H_
+
+namespace metrics::structured {
+
+// Document each in the .cc file.
+extern const char kEventSequenceResetCounter[];
+extern const char kEventSequenceLastSystemUptime[];
+
+}  // namespace metrics::structured
+
+#endif  // CHROME_BROWSER_METRICS_STRUCTURED_STRUCTURED_METRIC_PREFS_H_
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 576bf34..bb8cc82d 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -399,6 +399,7 @@
 #include "chrome/browser/extensions/extension_assets_manager_chromeos.h"
 #include "chrome/browser/media/protected_media_identifier_permission_context.h"
 #include "chrome/browser/metrics/chromeos_metrics_provider.h"
+#include "chrome/browser/metrics/structured/chrome_structured_metrics_recorder.h"
 #include "chrome/browser/ui/ash/shelf/chrome_shelf_prefs.h"
 #include "chrome/browser/ui/webui/ash/login/enable_debugging_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/signin_screen_handler.h"
@@ -1271,6 +1272,8 @@
       registry);
   extensions::login_api::RegisterLocalStatePrefs(registry);
   ::onc::RegisterPrefs(registry);
+  metrics::structured::ChromeStructuredMetricsRecorder::RegisterLocalStatePrefs(
+      registry);
   policy::ActiveDirectoryDeviceStateUploader::RegisterLocalStatePrefs(registry);
   policy::ActiveDirectoryMigrationManager::RegisterLocalStatePrefs(registry);
   policy::AdbSideloadingAllowanceModePolicyHandler::RegisterPrefs(registry);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index c2c6f634..c427dc0 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -7502,6 +7502,7 @@
       "../browser/metrics/perf/process_type_collector_unittest.cc",
       "../browser/metrics/perf/profile_provider_chromeos_unittest.cc",
       "../browser/metrics/perf/windowed_incognito_observer_unittest.cc",
+      "../browser/metrics/structured/cros_events_processor_unittest.cc",
       "../browser/nearby_sharing/bluetooth_advertising_interval_client_unittest.cc",
       "../browser/nearby_sharing/fast_initiation/fast_initiation_advertiser_unittest.cc",
       "../browser/nearby_sharing/fast_initiation/fast_initiation_scanner_feature_usage_metrics_unittest.cc",
@@ -7655,6 +7656,7 @@
       "//chrome/browser/chromeos",
       "//chrome/browser/chromeos/launcher_search:search_util",
       "//chrome/browser/enterprise/connectors/device_trust/attestation/ash",
+      "//chrome/browser/metrics/structured",
       "//chrome/browser/nearby_sharing:share_target",
       "//chrome/browser/nearby_sharing/certificates",
       "//chrome/browser/nearby_sharing/certificates:test_support",
@@ -7740,6 +7742,7 @@
       "//components/app_restore:unit_tests",
       "//components/arc:arc_test_support",
       "//components/arc/common",
+      "//components/metrics/structured:structured_events",
       "//components/omnibox/browser:vector_icons",
       "//components/services/app_service/public/cpp:app_update",
       "//components/services/app_service/public/cpp:instance_update",