Gather stability prefs into managing objects.

This collects code reading and writing stability prefs from
MetricsLog and MetricsService into StabilityMetricsProvider
and EnvironmentRecorder objects.

Also removes the obsolete stability prefs which are now unneeded.

BUG=693676

Review-Url: https://codereview.chromium.org/2687393004
Cr-Commit-Position: refs/heads/master@{#452301}
diff --git a/components/metrics/BUILD.gn b/components/metrics/BUILD.gn
index 300bb1d..129f1fe 100644
--- a/components/metrics/BUILD.gn
+++ b/components/metrics/BUILD.gn
@@ -27,6 +27,8 @@
     "drive_metrics_provider_win.cc",
     "enabled_state_provider.cc",
     "enabled_state_provider.h",
+    "environment_recorder.cc",
+    "environment_recorder.h",
     "execution_phase.cc",
     "execution_phase.h",
     "file_metrics_provider.cc",
@@ -73,6 +75,8 @@
     "persisted_logs_metrics_impl.h",
     "stability_metrics_helper.cc",
     "stability_metrics_helper.h",
+    "stability_metrics_provider.cc",
+    "stability_metrics_provider.h",
     "system_memory_stats_recorder.h",
     "system_memory_stats_recorder_linux.cc",
     "system_memory_stats_recorder_win.cc",
@@ -311,6 +315,7 @@
     "daily_event_unittest.cc",
     "data_use_tracker_unittest.cc",
     "drive_metrics_provider_unittest.cc",
+    "environment_recorder_unittest.cc",
     "file_metrics_provider_unittest.cc",
     "histogram_encoder_unittest.cc",
     "machine_id_provider_win_unittest.cc",
@@ -325,6 +330,7 @@
     "profiler/profiler_metrics_provider_unittest.cc",
     "profiler/tracking_synchronizer_unittest.cc",
     "stability_metrics_helper_unittest.cc",
+    "stability_metrics_provider_unittest.cc",
     "ui/screen_info_metrics_provider_unittest.cc",
   ]
 
diff --git a/components/metrics/clean_exit_beacon.cc b/components/metrics/clean_exit_beacon.cc
index 8868e0f..4bca5d1c 100644
--- a/components/metrics/clean_exit_beacon.cc
+++ b/components/metrics/clean_exit_beacon.cc
@@ -7,6 +7,7 @@
 #include "base/logging.h"
 #include "build/build_config.h"
 #include "components/metrics/metrics_pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
 #if defined(OS_WIN)
@@ -62,6 +63,11 @@
 CleanExitBeacon::~CleanExitBeacon() {
 }
 
+// static
+void CleanExitBeacon::RegisterPrefs(PrefRegistrySimple* registry) {
+  registry->RegisterBooleanPref(prefs::kStabilityExitedCleanly, true);
+}
+
 void CleanExitBeacon::WriteBeaconValue(bool value) {
   local_state_->SetBoolean(prefs::kStabilityExitedCleanly, value);
 
diff --git a/components/metrics/clean_exit_beacon.h b/components/metrics/clean_exit_beacon.h
index 6b896fa1..e863e41e 100644
--- a/components/metrics/clean_exit_beacon.h
+++ b/components/metrics/clean_exit_beacon.h
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "base/strings/string16.h"
 
+class PrefRegistrySimple;
 class PrefService;
 
 namespace metrics {
@@ -32,6 +33,9 @@
   // Writes the provided beacon value.
   void WriteBeaconValue(bool exited_cleanly);
 
+  // Registers local state prefs used by this class.
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
  private:
   PrefService* const local_state_;
   const bool initial_value_;
diff --git a/components/metrics/environment_recorder.cc b/components/metrics/environment_recorder.cc
new file mode 100644
index 0000000..07ead8c
--- /dev/null
+++ b/components/metrics/environment_recorder.cc
@@ -0,0 +1,96 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/metrics/environment_recorder.h"
+
+#include "base/base64.h"
+#include "base/sha1.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/metrics/metrics_pref_names.h"
+#include "components/metrics/proto/system_profile.pb.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+
+namespace metrics {
+
+namespace {
+
+// Computes a SHA-1 hash of |data| and returns it as a hex string.
+std::string ComputeSHA1(const std::string& data) {
+  const std::string sha1 = base::SHA1HashString(data);
+  return base::HexEncode(sha1.data(), sha1.size());
+}
+
+}  // namespace
+
+EnvironmentRecorder::EnvironmentRecorder(PrefService* local_state)
+    : local_state_(local_state) {}
+
+EnvironmentRecorder::~EnvironmentRecorder() = default;
+
+std::string EnvironmentRecorder::SerializeAndRecordEnvironmentToPrefs(
+    const SystemProfileProto& system_profile) {
+  std::string serialized_system_profile;
+  std::string base64_system_profile;
+  if (system_profile.SerializeToString(&serialized_system_profile)) {
+    // Persist the system profile to disk. In the event of an unclean shutdown,
+    // it will be used as part of the initial stability report.
+    base::Base64Encode(serialized_system_profile, &base64_system_profile);
+    local_state_->SetString(prefs::kStabilitySavedSystemProfile,
+                            base64_system_profile);
+    local_state_->SetString(prefs::kStabilitySavedSystemProfileHash,
+                            ComputeSHA1(serialized_system_profile));
+  }
+
+  return serialized_system_profile;
+}
+
+bool EnvironmentRecorder::LoadEnvironmentFromPrefs(
+    SystemProfileProto* system_profile) {
+  DCHECK(system_profile);
+
+  const std::string base64_system_profile =
+      local_state_->GetString(prefs::kStabilitySavedSystemProfile);
+  if (base64_system_profile.empty())
+    return false;
+  const std::string system_profile_hash =
+      local_state_->GetString(prefs::kStabilitySavedSystemProfileHash);
+
+  std::string serialized_system_profile;
+  return base::Base64Decode(base64_system_profile,
+                            &serialized_system_profile) &&
+         ComputeSHA1(serialized_system_profile) == system_profile_hash &&
+         system_profile->ParseFromString(serialized_system_profile);
+}
+
+void EnvironmentRecorder::ClearEnvironmentFromPrefs() {
+  local_state_->ClearPref(prefs::kStabilitySavedSystemProfile);
+  local_state_->ClearPref(prefs::kStabilitySavedSystemProfileHash);
+}
+
+int64_t EnvironmentRecorder::GetLastBuildtime() {
+  return local_state_->GetInt64(prefs::kStabilityStatsBuildTime);
+}
+
+std::string EnvironmentRecorder::GetLastVersion() {
+  return local_state_->GetString(prefs::kStabilityStatsVersion);
+}
+
+void EnvironmentRecorder::SetBuildtimeAndVersion(int64_t buildtime,
+                                                 const std::string& version) {
+  local_state_->SetInt64(prefs::kStabilityStatsBuildTime, buildtime);
+  local_state_->SetString(prefs::kStabilityStatsVersion, version);
+}
+
+// static
+void EnvironmentRecorder::RegisterPrefs(PrefRegistrySimple* registry) {
+  registry->RegisterStringPref(prefs::kStabilitySavedSystemProfile,
+                               std::string());
+  registry->RegisterStringPref(prefs::kStabilitySavedSystemProfileHash,
+                               std::string());
+  registry->RegisterStringPref(prefs::kStabilityStatsVersion, std::string());
+  registry->RegisterInt64Pref(prefs::kStabilityStatsBuildTime, 0);
+}
+
+}  // namespace metrics
diff --git a/components/metrics/environment_recorder.h b/components/metrics/environment_recorder.h
new file mode 100644
index 0000000..0042d2e
--- /dev/null
+++ b/components/metrics/environment_recorder.h
@@ -0,0 +1,59 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_METRICS_ENVIRONMENT_RECORDER_H_
+#define COMPONENTS_METRICS_ENVIRONMENT_RECORDER_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+class PrefService;
+class PrefRegistrySimple;
+
+namespace metrics {
+
+class SystemProfileProto;
+
+// Stores system profile information to prefs for creating stability logs
+// in the next launch of chrome, and reads data from previous launches.
+class EnvironmentRecorder {
+ public:
+  explicit EnvironmentRecorder(PrefService* local_state);
+  ~EnvironmentRecorder();
+
+  // Serializes the system profile and records it in prefs for the next
+  // session.  Returns the uncompressed serialized proto for passing to crash
+  // reports, or the empty string if the proto can't be serialized.
+  std::string SerializeAndRecordEnvironmentToPrefs(
+      const SystemProfileProto& system_profile);
+
+  // Loads the system_profile data stored in a previous chrome session, and
+  // stores it in the |system_profile| object.
+  // Returns true iff a system profile was successfully read.
+  bool LoadEnvironmentFromPrefs(SystemProfileProto* system_profile);
+
+  // Deletes system profile data from prefs.
+  void ClearEnvironmentFromPrefs();
+
+  // Stores the buildtime of the current binary and version in prefs.
+  void SetBuildtimeAndVersion(int64_t buildtime, const std::string& version);
+
+  // Gets the buildtime stored in prefs.
+  int64_t GetLastBuildtime();
+
+  // Gets the version stored in prefs.
+  std::string GetLastVersion();
+
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
+ private:
+  PrefService* local_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(EnvironmentRecorder);
+};
+
+}  // namespace metrics
+
+#endif  // COMPONENTS_METRICS_ENVIRONMENT_RECORDER_H_
diff --git a/components/metrics/environment_recorder_unittest.cc b/components/metrics/environment_recorder_unittest.cc
new file mode 100644
index 0000000..5c2ae8b
--- /dev/null
+++ b/components/metrics/environment_recorder_unittest.cc
@@ -0,0 +1,75 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/metrics/environment_recorder.h"
+
+#include "components/metrics/metrics_pref_names.h"
+#include "components/metrics/proto/system_profile.pb.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace metrics {
+
+class EnvironmentRecorderTest : public testing::Test {
+ public:
+  EnvironmentRecorderTest() {
+    EnvironmentRecorder::RegisterPrefs(prefs_.registry());
+  }
+
+  ~EnvironmentRecorderTest() override {}
+
+ protected:
+  TestingPrefServiceSimple prefs_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(EnvironmentRecorderTest);
+};
+
+TEST_F(EnvironmentRecorderTest, LoadEnvironmentFromPrefs) {
+  const char* kSystemProfilePref = prefs::kStabilitySavedSystemProfile;
+  const char* kSystemProfileHashPref = prefs::kStabilitySavedSystemProfileHash;
+
+  // The pref value is empty, so loading it from prefs should fail.
+  {
+    EnvironmentRecorder recorder(&prefs_);
+    SystemProfileProto system_profile;
+    EXPECT_FALSE(recorder.LoadEnvironmentFromPrefs(&system_profile));
+    EXPECT_FALSE(system_profile.has_app_version());
+  }
+
+  // Do a RecordEnvironment() call and check whether the pref is recorded.
+  {
+    EnvironmentRecorder recorder(&prefs_);
+    SystemProfileProto system_profile;
+    system_profile.set_app_version("bogus version");
+    std::string serialized_profile =
+        recorder.SerializeAndRecordEnvironmentToPrefs(system_profile);
+    EXPECT_FALSE(serialized_profile.empty());
+    EXPECT_FALSE(prefs_.GetString(kSystemProfilePref).empty());
+    EXPECT_FALSE(prefs_.GetString(kSystemProfileHashPref).empty());
+  }
+
+  // Load it and check that it has the right value.
+  {
+    EnvironmentRecorder recorder(&prefs_);
+    SystemProfileProto system_profile;
+    EXPECT_TRUE(recorder.LoadEnvironmentFromPrefs(&system_profile));
+    EXPECT_EQ("bogus version", system_profile.app_version());
+    // Ensure that the call did not clear the prefs.
+    EXPECT_FALSE(prefs_.GetString(kSystemProfilePref).empty());
+    EXPECT_FALSE(prefs_.GetString(kSystemProfileHashPref).empty());
+  }
+
+  // Ensure that a non-matching hash results in the pref being invalid.
+  {
+    // Set the hash to a bad value.
+    prefs_.SetString(kSystemProfileHashPref, "deadbeef");
+    EnvironmentRecorder recorder(&prefs_);
+    SystemProfileProto system_profile;
+    EXPECT_FALSE(recorder.LoadEnvironmentFromPrefs(&system_profile));
+    EXPECT_FALSE(system_profile.has_app_version());
+  }
+}
+
+}  // namespace metrics
diff --git a/components/metrics/metrics_log.cc b/components/metrics/metrics_log.cc
index e2740cb..ac35ce3 100644
--- a/components/metrics/metrics_log.cc
+++ b/components/metrics/metrics_log.cc
@@ -9,19 +9,15 @@
 #include <algorithm>
 #include <string>
 
-#include "base/base64.h"
 #include "base/build_time.h"
 #include "base/cpu.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/histogram_samples.h"
 #include "base/metrics/metrics_hashes.h"
-#include "base/sha1.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
 #include "base/sys_info.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "components/metrics/environment_recorder.h"
 #include "components/metrics/histogram_encoder.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/metrics/metrics_provider.h"
@@ -53,12 +49,6 @@
   return id.size() < 16;
 }
 
-// Computes a SHA-1 hash of |data| and returns it as a hex string.
-std::string ComputeSHA1(const std::string& data) {
-  const std::string sha1 = base::SHA1HashString(data);
-  return base::HexEncode(sha1.data(), sha1.size());
-}
-
 void WriteFieldTrials(const std::vector<ActiveGroupId>& field_trial_ids,
                       SystemProfileProto* system_profile) {
   for (std::vector<ActiveGroupId>::const_iterator it =
@@ -109,21 +99,7 @@
 
 // static
 void MetricsLog::RegisterPrefs(PrefRegistrySimple* registry) {
-  registry->RegisterIntegerPref(prefs::kStabilityCrashCount, 0);
-  registry->RegisterIntegerPref(prefs::kStabilityIncompleteSessionEndCount, 0);
-  registry->RegisterIntegerPref(prefs::kStabilityLaunchCount, 0);
-  registry->RegisterIntegerPref(prefs::kStabilityBreakpadRegistrationFail, 0);
-  registry->RegisterIntegerPref(
-      prefs::kStabilityBreakpadRegistrationSuccess, 0);
-  registry->RegisterIntegerPref(prefs::kStabilityDebuggerPresent, 0);
-  registry->RegisterIntegerPref(prefs::kStabilityDebuggerNotPresent, 0);
-  registry->RegisterStringPref(prefs::kStabilitySavedSystemProfile,
-                               std::string());
-  registry->RegisterStringPref(prefs::kStabilitySavedSystemProfileHash,
-                               std::string());
-  registry->RegisterIntegerPref(prefs::kStabilityDeferredCount, 0);
-  registry->RegisterIntegerPref(prefs::kStabilityDiscardCount, 0);
-  registry->RegisterIntegerPref(prefs::kStabilityVersionMismatchCount, 0);
+  EnvironmentRecorder::RegisterPrefs(registry);
 }
 
 // static
@@ -210,20 +186,11 @@
   DCHECK(HasEnvironment());
   DCHECK(!HasStabilityMetrics());
 
-  PrefService* pref = local_state_;
-  DCHECK(pref);
-
-  // Get stability attributes out of Local State, zeroing out stored values.
-  // NOTE: This could lead to some data loss if this report isn't successfully
-  //       sent, but that's true for all the metrics.
-
-  WriteRequiredStabilityAttributes(pref);
-
   // Record recent delta for critical stability metrics.  We can't wait for a
   // restart to gather these, as that delay biases our observation away from
   // users that run happily for a looooong time.  We send increments with each
   // uma log upload, just as we send histogram data.
-  WriteRealtimeStabilityAttributes(pref, incremental_uptime, uptime);
+  WriteRealtimeStabilityAttributes(incremental_uptime, uptime);
 
   SystemProfileProto* system_profile = uma_proto()->mutable_system_profile();
   for (size_t i = 0; i < metrics_providers.size(); ++i) {
@@ -231,71 +198,6 @@
       metrics_providers[i]->ProvideInitialStabilityMetrics(system_profile);
     metrics_providers[i]->ProvideStabilityMetrics(system_profile);
   }
-
-  SystemProfileProto::Stability* stability =
-      system_profile->mutable_stability();
-
-  int incomplete_shutdown_count =
-      pref->GetInteger(prefs::kStabilityIncompleteSessionEndCount);
-  if (incomplete_shutdown_count) {
-    pref->SetInteger(prefs::kStabilityIncompleteSessionEndCount, 0);
-    stability->set_incomplete_shutdown_count(incomplete_shutdown_count);
-  }
-
-  int breakpad_registration_success_count =
-      pref->GetInteger(prefs::kStabilityBreakpadRegistrationSuccess);
-  if (breakpad_registration_success_count) {
-    pref->SetInteger(prefs::kStabilityBreakpadRegistrationSuccess, 0);
-    stability->set_breakpad_registration_success_count(
-        breakpad_registration_success_count);
-  }
-
-  int breakpad_registration_failure_count =
-      pref->GetInteger(prefs::kStabilityBreakpadRegistrationFail);
-  if (breakpad_registration_failure_count) {
-    pref->SetInteger(prefs::kStabilityBreakpadRegistrationFail, 0);
-    stability->set_breakpad_registration_failure_count(
-        breakpad_registration_failure_count);
-  }
-
-  int debugger_present_count =
-      pref->GetInteger(prefs::kStabilityDebuggerPresent);
-  if (debugger_present_count) {
-    pref->SetInteger(prefs::kStabilityDebuggerPresent, 0);
-    stability->set_debugger_present_count(debugger_present_count);
-  }
-
-  int debugger_not_present_count =
-      pref->GetInteger(prefs::kStabilityDebuggerNotPresent);
-  if (debugger_not_present_count) {
-    pref->SetInteger(prefs::kStabilityDebuggerNotPresent, 0);
-    stability->set_debugger_not_present_count(debugger_not_present_count);
-  }
-
-  // Note: only logging the following histograms for non-zero values.
-
-  int deferred_count = pref->GetInteger(prefs::kStabilityDeferredCount);
-  if (deferred_count) {
-    local_state_->SetInteger(prefs::kStabilityDeferredCount, 0);
-    UMA_STABILITY_HISTOGRAM_COUNTS_100(
-        "Stability.Internals.InitialStabilityLogDeferredCount", deferred_count);
-  }
-
-  int discard_count = local_state_->GetInteger(prefs::kStabilityDiscardCount);
-  if (discard_count) {
-    local_state_->SetInteger(prefs::kStabilityDiscardCount, 0);
-    UMA_STABILITY_HISTOGRAM_COUNTS_100("Stability.Internals.DataDiscardCount",
-                                       discard_count);
-  }
-
-  int version_mismatch_count =
-      local_state_->GetInteger(prefs::kStabilityVersionMismatchCount);
-  if (version_mismatch_count) {
-    local_state_->SetInteger(prefs::kStabilityVersionMismatchCount, 0);
-    UMA_STABILITY_HISTOGRAM_COUNTS_100(
-        "Stability.Internals.VersionMismatchCount",
-        version_mismatch_count);
-  }
 }
 
 void MetricsLog::RecordGeneralMetrics(
@@ -344,26 +246,7 @@
   return uma_proto()->system_profile().stability().has_launch_count();
 }
 
-// The server refuses data that doesn't have certain values.  crashcount and
-// launchcount are currently "required" in the "stability" group.
-// TODO(isherman): Stop writing these attributes specially once the migration to
-// protobufs is complete.
-void MetricsLog::WriteRequiredStabilityAttributes(PrefService* pref) {
-  int launch_count = pref->GetInteger(prefs::kStabilityLaunchCount);
-  if (launch_count)
-    pref->SetInteger(prefs::kStabilityLaunchCount, 0);
-  int crash_count = pref->GetInteger(prefs::kStabilityCrashCount);
-  if (crash_count)
-    pref->SetInteger(prefs::kStabilityCrashCount, 0);
-
-  SystemProfileProto::Stability* stability =
-      uma_proto()->mutable_system_profile()->mutable_stability();
-  stability->set_launch_count(launch_count);
-  stability->set_crash_count(crash_count);
-}
-
 void MetricsLog::WriteRealtimeStabilityAttributes(
-    PrefService* pref,
     base::TimeDelta incremental_uptime,
     base::TimeDelta uptime) {
   // Update the stats which are critical for real-time stability monitoring.
@@ -419,41 +302,17 @@
   for (size_t i = 0; i < metrics_providers.size(); ++i)
     metrics_providers[i]->ProvideSystemProfileMetrics(system_profile);
 
-  std::string serialized_system_profile;
-  std::string base64_system_profile;
-  if (system_profile->SerializeToString(&serialized_system_profile)) {
-    // Persist the system profile to disk. In the event of an unclean shutdown,
-    // it will be used as part of the initial stability report.
-    base::Base64Encode(serialized_system_profile, &base64_system_profile);
-    PrefService* local_state = local_state_;
-    local_state->SetString(prefs::kStabilitySavedSystemProfile,
-                           base64_system_profile);
-    local_state->SetString(prefs::kStabilitySavedSystemProfileHash,
-                           ComputeSHA1(serialized_system_profile));
-  }
-
-  return serialized_system_profile;
+  EnvironmentRecorder recorder(local_state_);
+  return recorder.SerializeAndRecordEnvironmentToPrefs(*system_profile);
 }
 
 bool MetricsLog::LoadSavedEnvironmentFromPrefs(std::string* app_version) {
   DCHECK(app_version);
   app_version->clear();
 
-  PrefService* local_state = local_state_;
-  const std::string base64_system_profile =
-      local_state->GetString(prefs::kStabilitySavedSystemProfile);
-  if (base64_system_profile.empty())
-    return false;
-  const std::string system_profile_hash =
-      local_state->GetString(prefs::kStabilitySavedSystemProfileHash);
-
   SystemProfileProto* system_profile = uma_proto()->mutable_system_profile();
-  std::string serialized_system_profile;
-
-  bool success =
-      base::Base64Decode(base64_system_profile, &serialized_system_profile) &&
-      ComputeSHA1(serialized_system_profile) == system_profile_hash &&
-      system_profile->ParseFromString(serialized_system_profile);
+  EnvironmentRecorder recorder(local_state_);
+  bool success = recorder.LoadEnvironmentFromPrefs(system_profile);
   if (success)
     *app_version = system_profile->app_version();
   return success;
diff --git a/components/metrics/metrics_log.h b/components/metrics/metrics_log.h
index 5a01964a..4f28b43 100644
--- a/components/metrics/metrics_log.h
+++ b/components/metrics/metrics_log.h
@@ -19,7 +19,6 @@
 #include "components/metrics/metrics_service_client.h"
 #include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
 
-class PrefRegistrySimple;
 class PrefService;
 
 namespace base {
@@ -173,15 +172,11 @@
   // call to RecordStabilityMetrics().
   bool HasStabilityMetrics() const;
 
-  // Within the stability group, write required attributes.
-  void WriteRequiredStabilityAttributes(PrefService* pref);
-
   // Within the stability group, write attributes that need to be updated asap
   // and can't be delayed until the user decides to restart chromium.
   // Delaying these stats would bias metrics away from happy long lived
   // chromium processes (ones that don't crash, and keep on running).
-  void WriteRealtimeStabilityAttributes(PrefService* pref,
-                                        base::TimeDelta incremental_uptime,
+  void WriteRealtimeStabilityAttributes(base::TimeDelta incremental_uptime,
                                         base::TimeDelta uptime);
 
   // closed_ is true when record has been packed up for sending, and should
diff --git a/components/metrics/metrics_log_unittest.cc b/components/metrics/metrics_log_unittest.cc
index 0d0274a..f1a7792 100644
--- a/components/metrics/metrics_log_unittest.cc
+++ b/components/metrics/metrics_log_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/sys_info.h"
 #include "base/time/time.h"
+#include "components/metrics/environment_recorder.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/metrics/metrics_state_manager.h"
 #include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
@@ -103,7 +104,7 @@
 class MetricsLogTest : public testing::Test {
  public:
   MetricsLogTest() {
-    MetricsLog::RegisterPrefs(prefs_.registry());
+    EnvironmentRecorder::RegisterPrefs(prefs_.registry());
     MetricsStateManager::RegisterPrefs(prefs_.registry());
   }
 
@@ -295,83 +296,12 @@
   CheckSystemProfile(log.system_profile());
 
   // Check that the system profile has also been written to prefs.
-  const std::string base64_system_profile =
-      prefs_.GetString(prefs::kStabilitySavedSystemProfile);
-  EXPECT_FALSE(base64_system_profile.empty());
-  std::string serialied_system_profile;
-  EXPECT_TRUE(base::Base64Decode(base64_system_profile,
-                                 &serialied_system_profile));
   SystemProfileProto decoded_system_profile;
-  EXPECT_TRUE(decoded_system_profile.ParseFromString(serialied_system_profile));
+  EnvironmentRecorder recorder(&prefs_);
+  EXPECT_TRUE(recorder.LoadEnvironmentFromPrefs(&decoded_system_profile));
   CheckSystemProfile(decoded_system_profile);
 }
 
-TEST_F(MetricsLogTest, LoadSavedEnvironmentFromPrefs) {
-  const char* kSystemProfilePref = prefs::kStabilitySavedSystemProfile;
-  const char* kSystemProfileHashPref =
-      prefs::kStabilitySavedSystemProfileHash;
-
-  TestMetricsServiceClient client;
-  client.set_version_string("bogus version");
-
-  // The pref value is empty, so loading it from prefs should fail.
-  {
-    TestMetricsLog log(
-        kClientId, kSessionId, MetricsLog::ONGOING_LOG, &client, &prefs_);
-    std::string app_version;
-    EXPECT_FALSE(log.LoadSavedEnvironmentFromPrefs(&app_version));
-    EXPECT_TRUE(app_version.empty());
-  }
-
-  // Do a RecordEnvironment() call and check whether the pref is recorded.
-  {
-    TestMetricsLog log(
-        kClientId, kSessionId, MetricsLog::ONGOING_LOG, &client, &prefs_);
-    log.RecordEnvironment(std::vector<std::unique_ptr<MetricsProvider>>(),
-                          std::vector<variations::ActiveGroupId>(),
-                          kInstallDate, kEnabledDate);
-    EXPECT_FALSE(prefs_.GetString(kSystemProfilePref).empty());
-    EXPECT_FALSE(prefs_.GetString(kSystemProfileHashPref).empty());
-  }
-
-  {
-    TestMetricsLog log(
-        kClientId, kSessionId, MetricsLog::ONGOING_LOG, &client, &prefs_);
-    std::string app_version;
-    EXPECT_TRUE(log.LoadSavedEnvironmentFromPrefs(&app_version));
-    EXPECT_EQ("bogus version", app_version);
-    // Check some values in the system profile.
-    EXPECT_EQ(kInstallDateExpected, log.system_profile().install_date());
-    EXPECT_EQ(kEnabledDateExpected, log.system_profile().uma_enabled_date());
-    // Ensure that the call did not clear the prefs.
-    EXPECT_FALSE(prefs_.GetString(kSystemProfilePref).empty());
-    EXPECT_FALSE(prefs_.GetString(kSystemProfileHashPref).empty());
-  }
-
-  // Ensure that a non-matching hash results in the pref being invalid.
-  {
-    TestMetricsLog log(
-        kClientId, kSessionId, MetricsLog::ONGOING_LOG, &client, &prefs_);
-    // Call RecordEnvironment() to record the pref again.
-    log.RecordEnvironment(std::vector<std::unique_ptr<MetricsProvider>>(),
-                          std::vector<variations::ActiveGroupId>(),
-                          kInstallDate, kEnabledDate);
-  }
-
-  {
-    // Set the hash to a bad value.
-    prefs_.SetString(kSystemProfileHashPref, "deadbeef");
-    TestMetricsLog log(
-        kClientId, kSessionId, MetricsLog::ONGOING_LOG, &client, &prefs_);
-    std::string app_version;
-    EXPECT_FALSE(log.LoadSavedEnvironmentFromPrefs(&app_version));
-    EXPECT_TRUE(app_version.empty());
-    // Ensure that the prefs are not cleared, even if the call failed.
-    EXPECT_FALSE(prefs_.GetString(kSystemProfilePref).empty());
-    EXPECT_FALSE(prefs_.GetString(kSystemProfileHashPref).empty());
-  }
-}
-
 TEST_F(MetricsLogTest, RecordEnvironmentEnableDefault) {
   TestMetricsServiceClient client;
   TestMetricsLog log_unknown(kClientId, kSessionId, MetricsLog::ONGOING_LOG,
@@ -426,17 +356,6 @@
                         kEnabledDate);
   log.RecordStabilityMetrics(metrics_providers, base::TimeDelta(),
                              base::TimeDelta());
-  const SystemProfileProto_Stability& stability =
-      log.system_profile().stability();
-  // Required metrics:
-  EXPECT_TRUE(stability.has_launch_count());
-  EXPECT_TRUE(stability.has_crash_count());
-  // Initial log metrics: only expected if non-zero.
-  EXPECT_FALSE(stability.has_incomplete_shutdown_count());
-  EXPECT_FALSE(stability.has_breakpad_registration_success_count());
-  EXPECT_FALSE(stability.has_breakpad_registration_failure_count());
-  EXPECT_FALSE(stability.has_debugger_present_count());
-  EXPECT_FALSE(stability.has_debugger_not_present_count());
 
   // The test provider should have been called upon to provide initial
   // stability and regular stability metrics.
@@ -456,17 +375,6 @@
                         kEnabledDate);
   log.RecordStabilityMetrics(metrics_providers, base::TimeDelta(),
                              base::TimeDelta());
-  const SystemProfileProto_Stability& stability =
-      log.system_profile().stability();
-  // Required metrics:
-  EXPECT_TRUE(stability.has_launch_count());
-  EXPECT_TRUE(stability.has_crash_count());
-  // Initial log metrics: only expected if non-zero.
-  EXPECT_FALSE(stability.has_incomplete_shutdown_count());
-  EXPECT_FALSE(stability.has_breakpad_registration_success_count());
-  EXPECT_FALSE(stability.has_breakpad_registration_failure_count());
-  EXPECT_FALSE(stability.has_debugger_present_count());
-  EXPECT_FALSE(stability.has_debugger_not_present_count());
 
   // The test provider should have been called upon to provide regular but not
   // initial stability metrics.
diff --git a/components/metrics/metrics_pref_names.cc b/components/metrics/metrics_pref_names.cc
index c5ad9c6..ea3fd38 100644
--- a/components/metrics/metrics_pref_names.cc
+++ b/components/metrics/metrics_pref_names.cc
@@ -128,19 +128,10 @@
 const char kStabilityIncompleteSessionEndCount[] =
     "user_experience_metrics.stability.incomplete_session_end_count";
 
-// Time when the app was last known to be running, in seconds since
-// the epoch.
-const char kStabilityLastTimestampSec[] =
-    "user_experience_metrics.stability.last_timestamp_sec";
-
 // Number of times the application was launched since last report.
 const char kStabilityLaunchCount[] =
     "user_experience_metrics.stability.launch_count";
 
-// Time when the app was last launched, in seconds since the epoch.
-const char kStabilityLaunchTimeSec[] =
-    "user_experience_metrics.stability.launch_time_sec";
-
 // Number of times a page load event occurred since the last report.
 const char kStabilityPageLoadCount[] =
     "user_experience_metrics.stability.page_load_count";
diff --git a/components/metrics/metrics_pref_names.h b/components/metrics/metrics_pref_names.h
index df25e38..a918802 100644
--- a/components/metrics/metrics_pref_names.h
+++ b/components/metrics/metrics_pref_names.h
@@ -30,6 +30,8 @@
 extern const char kMetricsReportingEnabledTimestamp[];
 extern const char kMetricsSessionID[];
 extern const char kMetricsLastSeenPrefix[];
+
+// Preferences for recording stability logs.
 extern const char kStabilityBreakpadRegistrationSuccess[];
 extern const char kStabilityBreakpadRegistrationFail[];
 extern const char kStabilityChildProcessCrashCount[];
@@ -44,9 +46,7 @@
 extern const char kStabilityExtensionRendererLaunchCount[];
 extern const char kStabilityExitedCleanly[];
 extern const char kStabilityIncompleteSessionEndCount[];
-extern const char kStabilityLastTimestampSec[];
 extern const char kStabilityLaunchCount[];
-extern const char kStabilityLaunchTimeSec[];
 extern const char kStabilityPageLoadCount[];
 extern const char kStabilityRendererCrashCount[];
 extern const char kStabilityRendererFailedLaunchCount[];
@@ -58,6 +58,8 @@
 extern const char kStabilityStatsBuildTime[];
 extern const char kStabilityStatsVersion[];
 extern const char kStabilityVersionMismatchCount[];
+
+// Preferences for generating metrics at uninstall time.
 extern const char kUninstallLaunchCount[];
 extern const char kUninstallMetricsPageLoadCount[];
 extern const char kUninstallMetricsUptimeSec[];
diff --git a/components/metrics/metrics_service.cc b/components/metrics/metrics_service.cc
index 26c0d9b1..d7b5027 100644
--- a/components/metrics/metrics_service.cc
+++ b/components/metrics/metrics_service.cc
@@ -148,6 +148,7 @@
 #include "base/tracked_objects.h"
 #include "build/build_config.h"
 #include "components/metrics/data_use_tracker.h"
+#include "components/metrics/environment_recorder.h"
 #include "components/metrics/metrics_log.h"
 #include "components/metrics/metrics_log_manager.h"
 #include "components/metrics/metrics_log_uploader.h"
@@ -157,6 +158,7 @@
 #include "components/metrics/metrics_service_client.h"
 #include "components/metrics/metrics_state_manager.h"
 #include "components/metrics/metrics_upload_scheduler.h"
+#include "components/metrics/stability_metrics_provider.h"
 #include "components/metrics/url_constants.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
@@ -235,19 +237,15 @@
 
 // static
 void MetricsService::RegisterPrefs(PrefRegistrySimple* registry) {
+  CleanExitBeacon::RegisterPrefs(registry);
   MetricsStateManager::RegisterPrefs(registry);
   MetricsLog::RegisterPrefs(registry);
+  StabilityMetricsProvider::RegisterPrefs(registry);
   DataUseTracker::RegisterPrefs(registry);
+  ExecutionPhaseManager::RegisterPrefs(registry);
 
   registry->RegisterInt64Pref(prefs::kInstallDate, 0);
 
-  registry->RegisterInt64Pref(prefs::kStabilityLaunchTimeSec, 0);
-  registry->RegisterInt64Pref(prefs::kStabilityLastTimestampSec, 0);
-  registry->RegisterStringPref(prefs::kStabilityStatsVersion, std::string());
-  registry->RegisterInt64Pref(prefs::kStabilityStatsBuildTime, 0);
-  registry->RegisterBooleanPref(prefs::kStabilityExitedCleanly, true);
-  ExecutionPhaseManager::RegisterPrefs(registry);
-  registry->RegisterBooleanPref(prefs::kStabilitySessionEndCompleted, true);
   registry->RegisterIntegerPref(prefs::kMetricsSessionID, -1);
 
   registry->RegisterListPref(prefs::kMetricsInitialLogs);
@@ -284,6 +282,9 @@
   int64_t install_date = local_state_->GetInt64(prefs::kInstallDate);
   if (install_date == 0)
     local_state_->SetInt64(prefs::kInstallDate, base::Time::Now().ToTimeT());
+
+  RegisterMetricsProvider(std::unique_ptr<metrics::MetricsProvider>(
+      new StabilityMetricsProvider(local_state_)));
 }
 
 MetricsService::~MetricsService() {
@@ -505,36 +506,17 @@
 }
 
 void MetricsService::RecordBreakpadRegistration(bool success) {
-  if (!success)
-    IncrementPrefValue(prefs::kStabilityBreakpadRegistrationFail);
-  else
-    IncrementPrefValue(prefs::kStabilityBreakpadRegistrationSuccess);
+  StabilityMetricsProvider(local_state_).RecordBreakpadRegistration(success);
 }
 
 void MetricsService::RecordBreakpadHasDebugger(bool has_debugger) {
-  if (!has_debugger)
-    IncrementPrefValue(prefs::kStabilityDebuggerNotPresent);
-  else
-    IncrementPrefValue(prefs::kStabilityDebuggerPresent);
+  StabilityMetricsProvider(local_state_)
+      .RecordBreakpadHasDebugger(has_debugger);
 }
 
 void MetricsService::ClearSavedStabilityMetrics() {
   for (auto& provider : metrics_providers_)
     provider->ClearSavedStabilityMetrics();
-
-  // Reset the prefs that are managed by MetricsService/MetricsLog directly.
-  local_state_->SetInteger(prefs::kStabilityBreakpadRegistrationSuccess, 0);
-  local_state_->SetInteger(prefs::kStabilityBreakpadRegistrationFail, 0);
-  local_state_->SetInteger(prefs::kStabilityCrashCount, 0);
-  local_state_->SetInteger(prefs::kStabilityDebuggerPresent, 0);
-  local_state_->SetInteger(prefs::kStabilityDebuggerNotPresent, 0);
-  local_state_->SetInteger(prefs::kStabilityIncompleteSessionEndCount, 0);
-  local_state_->SetInteger(prefs::kStabilityLaunchCount, 0);
-  local_state_->SetBoolean(prefs::kStabilitySessionEndCompleted, true);
-  local_state_->SetInteger(prefs::kStabilityDeferredCount, 0);
-  // Note: kStabilityDiscardCount is not cleared as its intent is to measure
-  // the number of times data is discarded, even across versions.
-  local_state_->SetInteger(prefs::kStabilityVersionMismatchCount, 0);
 }
 
 void MetricsService::PushExternalLog(const std::string& log) {
@@ -564,19 +546,12 @@
   const int64_t buildtime = MetricsLog::GetBuildTime();
   const std::string version = client_->GetVersionString();
 
-  // Delete deprecated prefs
-  // TODO(holte): Remove these in M58
-  local_state_->ClearPref(prefs::kStabilityLaunchTimeSec);
-  local_state_->ClearPref(prefs::kStabilityLastTimestampSec);
-
   bool version_changed = false;
-  int64_t previous_buildtime =
-      local_state_->GetInt64(prefs::kStabilityStatsBuildTime);
-  std::string previous_version =
-      local_state_->GetString(prefs::kStabilityStatsVersion);
+  EnvironmentRecorder recorder(local_state_);
+  int64_t previous_buildtime = recorder.GetLastBuildtime();
+  std::string previous_version = recorder.GetLastVersion();
   if (previous_buildtime != buildtime || previous_version != version) {
-    local_state_->SetString(prefs::kStabilityStatsVersion, version);
-    local_state_->SetInt64(prefs::kStabilityStatsBuildTime, buildtime);
+    recorder.SetBuildtimeAndVersion(buildtime, version);
     version_changed = true;
   }
 
@@ -584,8 +559,9 @@
 
   session_id_ = local_state_->GetInteger(prefs::kMetricsSessionID);
 
+  StabilityMetricsProvider provider(local_state_);
   if (!clean_exit_beacon_.exited_cleanly()) {
-    IncrementPrefValue(prefs::kStabilityCrashCount);
+    provider.LogCrash();
     // Reset flag, and wait until we call LogNeedForCleanShutdown() before
     // monitoring.
     clean_exit_beacon_.WriteBeaconValue(true);
@@ -608,7 +584,7 @@
     if (state_manager_->IsMetricsReportingEnabled()) {
       has_initial_stability_log = PrepareInitialStabilityLog(previous_version);
       if (!has_initial_stability_log)
-        IncrementPrefValue(prefs::kStabilityDeferredCount);
+        provider.LogStabilityLogDeferred();
     }
   }
 
@@ -620,7 +596,7 @@
   // normally results in stats being accumulated).
   if (version_changed && !has_initial_stability_log) {
     ClearSavedStabilityMetrics();
-    IncrementPrefValue(prefs::kStabilityDiscardCount);
+    provider.LogStabilityDataDiscarded();
   }
 
   // If the version changed, the system profile is obsolete and needs to be
@@ -630,25 +606,17 @@
   // stability log, an operation that requires the previous version's system
   // profile. At this point, stability metrics pertaining to the previous
   // version have been cleared.
-  if (version_changed) {
-    local_state_->ClearPref(prefs::kStabilitySavedSystemProfile);
-    local_state_->ClearPref(prefs::kStabilitySavedSystemProfileHash);
-  }
+  if (version_changed)
+    recorder.ClearEnvironmentFromPrefs();
 
   // Update session ID.
   ++session_id_;
   local_state_->SetInteger(prefs::kMetricsSessionID, session_id_);
 
-  // Stability bookkeeping
-  IncrementPrefValue(prefs::kStabilityLaunchCount);
-
+  // Notify stability metrics providers about the launch.
+  provider.LogLaunch();
   SetExecutionPhase(ExecutionPhase::START_METRICS_RECORDING, local_state_);
-
-  if (!local_state_->GetBoolean(prefs::kStabilitySessionEndCompleted)) {
-    IncrementPrefValue(prefs::kStabilityIncompleteSessionEndCount);
-    // This is marked false when we get a WM_ENDSESSION.
-    local_state_->SetBoolean(prefs::kStabilitySessionEndCompleted, true);
-  }
+  provider.CheckLastSessionEndCompleted();
 
   // Call GetUptimes() for the first time, thus allowing all later calls
   // to record incremental uptimes accurately.
@@ -965,7 +933,7 @@
     return false;
   }
   if (system_profile_app_version != prefs_previous_version)
-    IncrementPrefValue(prefs::kStabilityVersionMismatchCount);
+    StabilityMetricsProvider(local_state_).LogStabilityVersionMismatch();
 
   log_manager_.PauseCurrentLog();
   log_manager_.BeginLoggingWithLog(std::move(initial_stability_log));
@@ -1096,11 +1064,6 @@
   }
 }
 
-void MetricsService::IncrementPrefValue(const char* path) {
-  int value = local_state_->GetInteger(path);
-  local_state_->SetInteger(path, value + 1);
-}
-
 void MetricsService::IncrementLongPrefsValue(const char* path) {
   int64_t value = local_state_->GetInt64(path);
   local_state_->SetInt64(path, value + 1);
@@ -1252,7 +1215,7 @@
   client_->OnLogCleanShutdown();
   clean_exit_beacon_.WriteBeaconValue(true);
   SetExecutionPhase(ExecutionPhase::SHUTDOWN_COMPLETE, local_state_);
-  local_state_->SetBoolean(prefs::kStabilitySessionEndCompleted, end_completed);
+  StabilityMetricsProvider(local_state_).MarkSessionEndCompleted(end_completed);
 }
 
 }  // namespace metrics
diff --git a/components/metrics/metrics_service.h b/components/metrics/metrics_service.h
index f283b23..42f311f 100644
--- a/components/metrics/metrics_service.h
+++ b/components/metrics/metrics_service.h
@@ -332,9 +332,6 @@
   // Called after transmission completes (either successfully or with failure).
   void OnLogUploadComplete(int response_code);
 
-  // Reads, increments and then sets the specified integer preference.
-  void IncrementPrefValue(const char* path);
-
   // Reads, increments and then sets the specified long preference that is
   // stored as a string.
   void IncrementLongPrefsValue(const char* path);
diff --git a/components/metrics/metrics_service_unittest.cc b/components/metrics/metrics_service_unittest.cc
index f4a264ca..2d5e32fd 100644
--- a/components/metrics/metrics_service_unittest.cc
+++ b/components/metrics/metrics_service_unittest.cc
@@ -20,6 +20,7 @@
 #include "base/threading/platform_thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/metrics/client_info.h"
+#include "components/metrics/environment_recorder.h"
 #include "components/metrics/metrics_log.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/metrics/metrics_state_manager.h"
@@ -212,10 +213,9 @@
 
   // Record stability build time and version from previous session, so that
   // stability metrics (including exited cleanly flag) won't be cleared.
-  GetLocalState()->SetInt64(prefs::kStabilityStatsBuildTime,
-                            MetricsLog::GetBuildTime());
-  GetLocalState()->SetString(prefs::kStabilityStatsVersion,
-                             client.GetVersionString());
+  EnvironmentRecorder(GetLocalState())
+      .SetBuildtimeAndVersion(MetricsLog::GetBuildTime(),
+                              client.GetVersionString());
 
   // Set the clean exit flag, as that will otherwise cause a stabilty
   // log to be produced, irrespective provider requests.
@@ -284,10 +284,9 @@
 
   // Record stability build time and version from previous session, so that
   // stability metrics (including exited cleanly flag) won't be cleared.
-  GetLocalState()->SetInt64(prefs::kStabilityStatsBuildTime,
-                            MetricsLog::GetBuildTime());
-  GetLocalState()->SetString(prefs::kStabilityStatsVersion,
-                             client.GetVersionString());
+  EnvironmentRecorder(GetLocalState())
+      .SetBuildtimeAndVersion(MetricsLog::GetBuildTime(),
+                              client.GetVersionString());
 
   GetLocalState()->SetBoolean(prefs::kStabilityExitedCleanly, false);
 
diff --git a/components/metrics/stability_metrics_provider.cc b/components/metrics/stability_metrics_provider.cc
new file mode 100644
index 0000000..a4b5280
--- /dev/null
+++ b/components/metrics/stability_metrics_provider.cc
@@ -0,0 +1,179 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/metrics/stability_metrics_provider.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "components/metrics/metrics_pref_names.h"
+#include "components/metrics/proto/system_profile.pb.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+
+namespace metrics {
+
+StabilityMetricsProvider::StabilityMetricsProvider(PrefService* local_state)
+    : local_state_(local_state) {}
+
+StabilityMetricsProvider::~StabilityMetricsProvider() = default;
+
+// static
+void StabilityMetricsProvider::RegisterPrefs(PrefRegistrySimple* registry) {
+  registry->RegisterIntegerPref(prefs::kStabilityCrashCount, 0);
+  registry->RegisterIntegerPref(prefs::kStabilityIncompleteSessionEndCount, 0);
+  registry->RegisterBooleanPref(prefs::kStabilitySessionEndCompleted, true);
+  registry->RegisterIntegerPref(prefs::kStabilityLaunchCount, 0);
+  registry->RegisterIntegerPref(prefs::kStabilityBreakpadRegistrationFail, 0);
+  registry->RegisterIntegerPref(prefs::kStabilityBreakpadRegistrationSuccess,
+                                0);
+  registry->RegisterIntegerPref(prefs::kStabilityDebuggerPresent, 0);
+  registry->RegisterIntegerPref(prefs::kStabilityDebuggerNotPresent, 0);
+  registry->RegisterIntegerPref(prefs::kStabilityDeferredCount, 0);
+  registry->RegisterIntegerPref(prefs::kStabilityDiscardCount, 0);
+  registry->RegisterIntegerPref(prefs::kStabilityVersionMismatchCount, 0);
+}
+
+void StabilityMetricsProvider::ClearSavedStabilityMetrics() {
+  local_state_->SetInteger(prefs::kStabilityCrashCount, 0);
+  local_state_->SetInteger(prefs::kStabilityIncompleteSessionEndCount, 0);
+  local_state_->SetInteger(prefs::kStabilityBreakpadRegistrationSuccess, 0);
+  local_state_->SetInteger(prefs::kStabilityBreakpadRegistrationFail, 0);
+  local_state_->SetInteger(prefs::kStabilityDebuggerPresent, 0);
+  local_state_->SetInteger(prefs::kStabilityDebuggerNotPresent, 0);
+  local_state_->SetInteger(prefs::kStabilityLaunchCount, 0);
+  local_state_->SetBoolean(prefs::kStabilitySessionEndCompleted, true);
+  local_state_->SetInteger(prefs::kStabilityDeferredCount, 0);
+  // Note: kStabilityDiscardCount is not cleared as its intent is to measure
+  // the number of times data is discarded, even across versions.
+  local_state_->SetInteger(prefs::kStabilityVersionMismatchCount, 0);
+}
+
+void StabilityMetricsProvider::ProvideStabilityMetrics(
+    SystemProfileProto* system_profile) {
+  SystemProfileProto::Stability* stability =
+      system_profile->mutable_stability();
+
+  int launch_count = local_state_->GetInteger(prefs::kStabilityLaunchCount);
+  if (launch_count) {
+    local_state_->SetInteger(prefs::kStabilityLaunchCount, 0);
+    stability->set_launch_count(launch_count);
+  }
+  int crash_count = local_state_->GetInteger(prefs::kStabilityCrashCount);
+  if (crash_count) {
+    local_state_->SetInteger(prefs::kStabilityCrashCount, 0);
+    stability->set_crash_count(crash_count);
+  }
+
+  int incomplete_shutdown_count =
+      local_state_->GetInteger(prefs::kStabilityIncompleteSessionEndCount);
+  if (incomplete_shutdown_count) {
+    local_state_->SetInteger(prefs::kStabilityIncompleteSessionEndCount, 0);
+    stability->set_incomplete_shutdown_count(incomplete_shutdown_count);
+  }
+
+  int breakpad_registration_success_count =
+      local_state_->GetInteger(prefs::kStabilityBreakpadRegistrationSuccess);
+  if (breakpad_registration_success_count) {
+    local_state_->SetInteger(prefs::kStabilityBreakpadRegistrationSuccess, 0);
+    stability->set_breakpad_registration_success_count(
+        breakpad_registration_success_count);
+  }
+
+  int breakpad_registration_failure_count =
+      local_state_->GetInteger(prefs::kStabilityBreakpadRegistrationFail);
+  if (breakpad_registration_failure_count) {
+    local_state_->SetInteger(prefs::kStabilityBreakpadRegistrationFail, 0);
+    stability->set_breakpad_registration_failure_count(
+        breakpad_registration_failure_count);
+  }
+
+  int debugger_present_count =
+      local_state_->GetInteger(prefs::kStabilityDebuggerPresent);
+  if (debugger_present_count) {
+    local_state_->SetInteger(prefs::kStabilityDebuggerPresent, 0);
+    stability->set_debugger_present_count(debugger_present_count);
+  }
+
+  int debugger_not_present_count =
+      local_state_->GetInteger(prefs::kStabilityDebuggerNotPresent);
+  if (debugger_not_present_count) {
+    local_state_->SetInteger(prefs::kStabilityDebuggerNotPresent, 0);
+    stability->set_debugger_not_present_count(debugger_not_present_count);
+  }
+
+  // Note: only logging the following histograms for non-zero values.
+  int deferred_count = local_state_->GetInteger(prefs::kStabilityDeferredCount);
+  if (deferred_count) {
+    local_state_->SetInteger(prefs::kStabilityDeferredCount, 0);
+    UMA_STABILITY_HISTOGRAM_COUNTS_100(
+        "Stability.Internals.InitialStabilityLogDeferredCount", deferred_count);
+  }
+
+  int discard_count = local_state_->GetInteger(prefs::kStabilityDiscardCount);
+  if (discard_count) {
+    local_state_->SetInteger(prefs::kStabilityDiscardCount, 0);
+    UMA_STABILITY_HISTOGRAM_COUNTS_100("Stability.Internals.DataDiscardCount",
+                                       discard_count);
+  }
+
+  int version_mismatch_count =
+      local_state_->GetInteger(prefs::kStabilityVersionMismatchCount);
+  if (version_mismatch_count) {
+    local_state_->SetInteger(prefs::kStabilityVersionMismatchCount, 0);
+    UMA_STABILITY_HISTOGRAM_COUNTS_100(
+        "Stability.Internals.VersionMismatchCount", version_mismatch_count);
+  }
+}
+
+void StabilityMetricsProvider::RecordBreakpadRegistration(bool success) {
+  if (!success)
+    IncrementPrefValue(prefs::kStabilityBreakpadRegistrationFail);
+  else
+    IncrementPrefValue(prefs::kStabilityBreakpadRegistrationSuccess);
+}
+
+void StabilityMetricsProvider::RecordBreakpadHasDebugger(bool has_debugger) {
+  if (!has_debugger)
+    IncrementPrefValue(prefs::kStabilityDebuggerNotPresent);
+  else
+    IncrementPrefValue(prefs::kStabilityDebuggerPresent);
+}
+
+void StabilityMetricsProvider::CheckLastSessionEndCompleted() {
+  if (!local_state_->GetBoolean(prefs::kStabilitySessionEndCompleted)) {
+    IncrementPrefValue(prefs::kStabilityIncompleteSessionEndCount);
+    // This is marked false when we get a WM_ENDSESSION.
+    MarkSessionEndCompleted(true);
+  }
+}
+
+void StabilityMetricsProvider::MarkSessionEndCompleted(bool end_completed) {
+  local_state_->SetBoolean(prefs::kStabilitySessionEndCompleted, end_completed);
+}
+
+void StabilityMetricsProvider::LogCrash() {
+  IncrementPrefValue(prefs::kStabilityCrashCount);
+}
+
+void StabilityMetricsProvider::LogStabilityLogDeferred() {
+  IncrementPrefValue(prefs::kStabilityDeferredCount);
+}
+
+void StabilityMetricsProvider::LogStabilityDataDiscarded() {
+  IncrementPrefValue(prefs::kStabilityDiscardCount);
+}
+
+void StabilityMetricsProvider::LogLaunch() {
+  IncrementPrefValue(prefs::kStabilityLaunchCount);
+}
+
+void StabilityMetricsProvider::LogStabilityVersionMismatch() {
+  IncrementPrefValue(prefs::kStabilityVersionMismatchCount);
+}
+
+void StabilityMetricsProvider::IncrementPrefValue(const char* path) {
+  int value = local_state_->GetInteger(path);
+  local_state_->SetInteger(path, value + 1);
+}
+
+}  // namespace metrics
diff --git a/components/metrics/stability_metrics_provider.h b/components/metrics/stability_metrics_provider.h
new file mode 100644
index 0000000..4e16ed67
--- /dev/null
+++ b/components/metrics/stability_metrics_provider.h
@@ -0,0 +1,53 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_METRICS_STABILITY_METRICS_PROVIDER_H_
+#define COMPONENTS_METRICS_STABILITY_METRICS_PROVIDER_H_
+
+#include "components/metrics/metrics_provider.h"
+
+class PrefService;
+class PrefRegistrySimple;
+
+namespace metrics {
+
+class SystemProfileProto;
+
+// Stores and loads system information to prefs for stability logs.
+class StabilityMetricsProvider : public MetricsProvider {
+ public:
+  StabilityMetricsProvider(PrefService* local_state);
+  ~StabilityMetricsProvider() override;
+
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
+  void RecordBreakpadRegistration(bool success);
+  void RecordBreakpadHasDebugger(bool has_debugger);
+
+  void CheckLastSessionEndCompleted();
+  void MarkSessionEndCompleted(bool end_completed);
+
+  void LogCrash();
+  void LogStabilityLogDeferred();
+  void LogStabilityDataDiscarded();
+  void LogLaunch();
+  void LogStabilityVersionMismatch();
+
+ private:
+  // Increments an Integer pref value specified by |path|.
+  void IncrementPrefValue(const char* path);
+
+  // MetricsProvider:
+  void ClearSavedStabilityMetrics() override;
+  void ProvideStabilityMetrics(
+      SystemProfileProto* system_profile_proto) override;
+
+  PrefService* local_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(StabilityMetricsProvider);
+};
+
+}  // namespace metrics
+
+#endif  // COMPONENTS_METRICS_STABILITY_METRICS_PROVIDER_H_
diff --git a/components/metrics/stability_metrics_provider_unittest.cc b/components/metrics/stability_metrics_provider_unittest.cc
new file mode 100644
index 0000000..7b2cb61
--- /dev/null
+++ b/components/metrics/stability_metrics_provider_unittest.cc
@@ -0,0 +1,76 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/metrics/stability_metrics_provider.h"
+
+#include "components/metrics/proto/system_profile.pb.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace metrics {
+
+class StabilityMetricsProviderTest : public testing::Test {
+ public:
+  StabilityMetricsProviderTest() {
+    StabilityMetricsProvider::RegisterPrefs(prefs_.registry());
+  }
+
+  ~StabilityMetricsProviderTest() override {}
+
+ protected:
+  TestingPrefServiceSimple prefs_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(StabilityMetricsProviderTest);
+};
+
+TEST_F(StabilityMetricsProviderTest, ProvideStabilityMetrics) {
+  StabilityMetricsProvider stability_provider(&prefs_);
+  MetricsProvider* provider = &stability_provider;
+  SystemProfileProto system_profile;
+  provider->ProvideStabilityMetrics(&system_profile);
+
+  const SystemProfileProto_Stability& stability = system_profile.stability();
+  // Initial log metrics: only expected if non-zero.
+  EXPECT_FALSE(stability.has_launch_count());
+  EXPECT_FALSE(stability.has_crash_count());
+  EXPECT_FALSE(stability.has_incomplete_shutdown_count());
+  EXPECT_FALSE(stability.has_breakpad_registration_success_count());
+  EXPECT_FALSE(stability.has_breakpad_registration_failure_count());
+  EXPECT_FALSE(stability.has_debugger_present_count());
+  EXPECT_FALSE(stability.has_debugger_not_present_count());
+}
+
+TEST_F(StabilityMetricsProviderTest, RecordStabilityMetrics) {
+  {
+    StabilityMetricsProvider recorder(&prefs_);
+    recorder.LogLaunch();
+    recorder.LogCrash();
+    recorder.MarkSessionEndCompleted(false);
+    recorder.CheckLastSessionEndCompleted();
+    recorder.RecordBreakpadRegistration(true);
+    recorder.RecordBreakpadRegistration(false);
+    recorder.RecordBreakpadHasDebugger(true);
+    recorder.RecordBreakpadHasDebugger(false);
+  }
+
+  {
+    StabilityMetricsProvider stability_provider(&prefs_);
+    MetricsProvider* provider = &stability_provider;
+    SystemProfileProto system_profile;
+    provider->ProvideStabilityMetrics(&system_profile);
+
+    const SystemProfileProto_Stability& stability = system_profile.stability();
+    // Initial log metrics: only expected if non-zero.
+    EXPECT_EQ(1, stability.launch_count());
+    EXPECT_EQ(1, stability.crash_count());
+    EXPECT_EQ(1, stability.incomplete_shutdown_count());
+    EXPECT_EQ(1, stability.breakpad_registration_success_count());
+    EXPECT_EQ(1, stability.breakpad_registration_failure_count());
+    EXPECT_EQ(1, stability.debugger_present_count());
+    EXPECT_EQ(1, stability.debugger_not_present_count());
+  }
+}
+
+}  // namespace metrics