Add weekly crash counters, refactor metrics_daemon, respect opt-in in library.

BUG=5340,5814

Change-Id: I2c207055f1ebe48051193395e2dbe38d9140b025

Review URL: http://codereview.chromium.org/3171023
diff --git a/metrics/c_metrics_library.cc b/metrics/c_metrics_library.cc
index 91e9ca6..e97f9ac 100644
--- a/metrics/c_metrics_library.cc
+++ b/metrics/c_metrics_library.cc
@@ -26,8 +26,8 @@
 }
 
 extern "C" int CMetricsLibrarySendToUMA(CMetricsLibrary handle,
-					const char* name, int sample,
-					int min, int max, int nbuckets) {
+                                        const char* name, int sample,
+                                        int min, int max, int nbuckets) {
   MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
   if (lib == NULL)
     return 0;
@@ -35,10 +35,17 @@
 }
 
 extern "C" int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle,
-					    const char* name, int sample,
-					    int max) {
+                                            const char* name, int sample,
+                                            int max) {
   MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
   if (lib == NULL)
     return 0;
   return lib->SendEnumToUMA(std::string(name), sample, max);
 }
+
+extern "C" int CMetricsLibraryAreMetricsEnabled(CMetricsLibrary handle) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  if (lib == NULL)
+    return 0;
+  return lib->AreMetricsEnabled();
+}
diff --git a/metrics/c_metrics_library.h b/metrics/c_metrics_library.h
index 6d1e6f9..e691ad6 100644
--- a/metrics/c_metrics_library.h
+++ b/metrics/c_metrics_library.h
@@ -21,13 +21,17 @@
 
 // C wrapper for MetricsLibrary::SendToUMA.
 int CMetricsLibrarySendToUMA(CMetricsLibrary handle,
-			     const char* name, int sample,
-			     int min, int max, int nbuckets);
+                             const char* name, int sample,
+                             int min, int max, int nbuckets);
 
 // C wrapper for MetricsLibrary::SendEnumToUMA.
 int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle,
-				 const char* name, int sample, int max);
+                                 const char* name, int sample, int max);
+
+// C wrapper for MetricsLibrary::AreMetricsEnabled.
+int CMetricsLibraryAreMetricsEnabled(CMetricsLibrary handle);
+
 #if defined(__cplusplus)
 }
 #endif
-#endif	// C_METRICS_LIBRARY_H_
+#endif  // C_METRICS_LIBRARY_H_
diff --git a/metrics/counter.cc b/metrics/counter.cc
index 4de1db1..95d1faf 100644
--- a/metrics/counter.cc
+++ b/metrics/counter.cc
@@ -8,6 +8,7 @@
 
 #include <base/eintr_wrapper.h>
 #include <base/logging.h>
+#include "metrics_library.h"
 
 namespace chromeos_metrics {
 
@@ -31,8 +32,7 @@
 
 // TaggedCounter implementation.
 TaggedCounter::TaggedCounter()
-    : filename_(NULL),
-      reporter_(NULL),
+    : reporter_(NULL),
       reporter_handle_(NULL),
       record_state_(kRecordInvalid) {}
 
@@ -72,12 +72,13 @@
   }
 
   DLOG(INFO) << "tag: " << tag << " count: " << count << " flush: " << flush;
-  DCHECK(filename_);
+  DCHECK(!filename_.empty());
 
   // NOTE: The assumption is that this TaggedCounter object is the
   // sole owner of the persistent storage file so no locking is
   // necessary.
-  int fd = HANDLE_EINTR(open(filename_, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+  int fd = HANDLE_EINTR(open(filename_.c_str(),
+                             O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
   if (fd < 0) {
     PLOG(WARNING) << "Unable to open the persistent counter file";
     return;
@@ -183,21 +184,57 @@
   }
 }
 
+MetricsLibraryInterface* TaggedCounterReporter::metrics_lib_ = NULL;
+
+TaggedCounterReporter::TaggedCounterReporter()
+    : tagged_counter_(new TaggedCounter()),
+      min_(0),
+      max_(0),
+      buckets_(0) {
+}
+
+TaggedCounterReporter::~TaggedCounterReporter() {
+}
+
+void TaggedCounterReporter::Init(const char* filename,
+                                 const char* histogram_name,
+                                 int min,
+                                 int max,
+                                 int buckets) {
+  tagged_counter_->Init(filename, Report, this);
+  histogram_name_ = histogram_name;
+  min_ = min;
+  max_ = max;
+  buckets_ = buckets;
+  CHECK(min_ >= 0);
+  CHECK(max_ > min_);
+  CHECK(buckets_ > 0);
+}
+
+void TaggedCounterReporter::Report(void* handle, int32 tag, int32 count) {
+  TaggedCounterReporter* this_reporter =
+      reinterpret_cast<TaggedCounterReporter*>(handle);
+  DLOG(INFO) << "received metric: " << this_reporter->histogram_name_
+             << " " << count << " " << this_reporter->min_ << " "
+             << this_reporter->max_ << " " << this_reporter->buckets_;
+  CHECK(metrics_lib_ != NULL);
+  CHECK(this_reporter->buckets_ > 0);
+  metrics_lib_->SendToUMA(this_reporter->histogram_name_,
+                          count,
+                          this_reporter->min_,
+                          this_reporter->max_,
+                          this_reporter->buckets_);
+}
+
 FrequencyCounter::FrequencyCounter() : cycle_duration_(1) {
 }
 
 FrequencyCounter::~FrequencyCounter() {
 }
 
-void FrequencyCounter::Init(const char* filename,
-                            TaggedCounterInterface::Reporter reporter,
-                            void* reporter_handle,
+void FrequencyCounter::Init(TaggedCounterInterface* tagged_counter,
                             time_t cycle_duration) {
-  // Allow tests to inject tagged_counter_ dependency.
-  if (tagged_counter_.get() == NULL) {
-    tagged_counter_.reset(new TaggedCounter());
-  }
-  tagged_counter_->Init(filename, reporter, reporter_handle);
+  tagged_counter_.reset(tagged_counter);
   DCHECK(cycle_duration > 0);
   cycle_duration_ = cycle_duration;
 }
diff --git a/metrics/counter.h b/metrics/counter.h
index a5e5302..e55cded 100644
--- a/metrics/counter.h
+++ b/metrics/counter.h
@@ -5,12 +5,15 @@
 #ifndef METRICS_COUNTER_H_
 #define METRICS_COUNTER_H_
 
+#include <string>
 #include <time.h>
 
 #include <base/basictypes.h>
 #include <base/scoped_ptr.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
+class MetricsLibraryInterface;
+
 namespace chromeos_metrics {
 
 // Constants useful for frequency statistics.
@@ -44,16 +47,6 @@
 
   virtual ~TaggedCounterInterface() {}
 
-  // Initializes the counter by providing the persistent storage
-  // location |filename| and a |reporter| callback for reporting
-  // aggregated counts. |reporter_handle| is sent to the |reporter|
-  // along with the aggregated counts.
-  //
-  // NOTE: The assumption is that this object is the sole owner of the
-  // persistent storage file so no locking is currently implemented.
-  virtual void Init(const char* filename,
-                    Reporter reporter, void* reporter_handle) = 0;
-
   // Adds |count| of events for the given |tag|. If there's an
   // existing aggregated count for a different tag, it's reported
   // through the reporter callback and discarded.
@@ -67,12 +60,21 @@
 class TaggedCounter : public TaggedCounterInterface {
  public:
   TaggedCounter();
-  ~TaggedCounter();
+  virtual ~TaggedCounter();
+
+  // Initializes the counter by providing the persistent storage
+  // location |filename| and a |reporter| callback for reporting
+  // aggregated counts. |reporter_handle| is sent to the |reporter|
+  // along with the aggregated counts.
+  //
+  // NOTE: The assumption is that this object is the sole owner of the
+  // persistent storage file so no locking is currently implemented.
+  virtual void Init(const char* filename,
+                    Reporter reporter, void* reporter_handle);
 
   // Implementation of interface methods.
-  void Init(const char* filename, Reporter reporter, void* reporter_handle);
-  void Update(int32 tag, int32 count);
-  void Flush();
+  virtual void Update(int32 tag, int32 count);
+  virtual void Flush();
 
  private:
   friend class RecordTest;
@@ -146,7 +148,7 @@
   void WriteRecord(int fd);
 
   // Persistent storage file path.
-  const char* filename_;
+  std::string filename_;
 
   // Aggregated data reporter callback and handle to pass-through.
   Reporter reporter_;
@@ -159,6 +161,71 @@
   RecordState record_state_;
 };
 
+// TaggedCounterReporter provides a TaggedCounterInterface which both
+// counts tagged events and reports them up through the metrics
+// library to UMA.
+class TaggedCounterReporter : public TaggedCounterInterface {
+ public:
+  TaggedCounterReporter();
+  virtual ~TaggedCounterReporter();
+
+  // Set the metrics library used by all TaggedCounterReporter
+  // instances.  We assume there is only one metrics library
+  // shared amongst all reporters.
+  static void SetMetricsLibraryInterface(MetricsLibraryInterface* metrics_lib) {
+    metrics_lib_ = metrics_lib;
+  }
+
+  // Initializes the counter by providing the persistent storage
+  // location |filename|, a |histogram_name| (a linear histogram) to
+  // report to with |min|, |max|, and |buckets| attributes for the
+  // histogram.
+  virtual void Init(const char* filename,
+                    const char* histogram_name,
+                    int min,
+                    int max,
+                    int buckets);
+
+  // Implementation of interface method.
+  virtual void Update(int32 tag, int32 count) {
+    tagged_counter_->Update(tag, count);
+  }
+  // Implementation of interface method.
+  virtual void Flush() {
+    tagged_counter_->Flush();
+  }
+
+  // Accessor functions.
+  const std::string& histogram_name() const {
+    return histogram_name_;
+  }
+
+  int min() const {
+    return min_;
+  }
+
+  int max() const {
+    return max_;
+  }
+
+  int buckets() const {
+    return buckets_;
+  }
+
+ protected:
+  friend class TaggedCounterReporterTest;
+  FRIEND_TEST(TaggedCounterReporterTest, Report);
+
+  static void Report(void* handle, int32 tag, int32 count);
+
+  static MetricsLibraryInterface* metrics_lib_;
+  scoped_ptr<TaggedCounter> tagged_counter_;
+  std::string histogram_name_;
+  int min_;
+  int max_;
+  int buckets_;
+};
+
 // FrequencyCounter uses TaggedCounter to maintain a persistent
 // storage of the number of events that occur in a given cycle
 // duration (in other words, a frequency count).  For example, to
@@ -172,13 +239,11 @@
   FrequencyCounter();
   virtual ~FrequencyCounter();
 
-  // Initialize a frequency counter, which is necessary before first use.
-  // |filename|, |reporter|, and |reporter_handle| are used as in
-  // TaggedCounter::Init.  |cycle_duration| is the number of seconds
-  // in a cycle.
-  virtual void Init(const char* filename,
-                    TaggedCounterInterface::Reporter reporter,
-                    void* reporter_handle,
+  // Initialize a frequency counter, which is necessary before first
+  // use.  |tagged_counter| is used to store the counts, its memory
+  // will be managed by this FrequencyCounter.  |cycle_duration| is
+  // the number of seconds in a cycle.
+  virtual void Init(TaggedCounterInterface* tagged_counter,
                     time_t cycle_duration);
   // Record that an event occurred.  |count| is the number of concurrent
   // events that have occurred.  The time is implicitly assumed to be the
@@ -187,9 +252,29 @@
     UpdateInternal(count, time(NULL));
   }
 
+  // Update the frequency counter based on the current time.  If a
+  // cycle has finished, this will have the effect of flushing the
+  // cycle's count, without first requiring another update to the
+  // frequency counter.  The more often this is called, the lower the
+  // latency to have a new sample submitted.
+  virtual void FlushFinishedCycles() {
+    Update(0);
+  }
+
+  // Accessor function.
+  const TaggedCounterInterface& tagged_counter() const {
+    return *tagged_counter_;
+  }
+
+  time_t cycle_duration() const {
+    return cycle_duration_;
+  }
+
  private:
   friend class FrequencyCounterTest;
   FRIEND_TEST(FrequencyCounterTest, UpdateInternal);
+  FRIEND_TEST(FrequencyCounterTest, GetCycleNumberForWeek);
+  FRIEND_TEST(FrequencyCounterTest, GetCycleNumberForDay);
 
   void UpdateInternal(int32 count, time_t now);
   int32 GetCycleNumber(time_t now);
diff --git a/metrics/counter_mock.h b/metrics/counter_mock.h
index 701cce4..cf2a486 100644
--- a/metrics/counter_mock.h
+++ b/metrics/counter_mock.h
@@ -13,7 +13,7 @@
 
 namespace chromeos_metrics {
 
-class TaggedCounterMock : public TaggedCounterInterface {
+class TaggedCounterMock : public TaggedCounter {
  public:
   MOCK_METHOD3(Init, void(const char* filename,
                           Reporter reporter, void* reporter_handle));
@@ -21,6 +21,17 @@
   MOCK_METHOD0(Flush, void());
 };
 
+class TaggedCounterReporterMock : public TaggedCounterReporter {
+ public:
+  MOCK_METHOD5(Init, void(const char* filename,
+                          const char* histogram_name,
+                          int min,
+                          int max,
+                          int nbuckets));
+  MOCK_METHOD2(Update, void(int32 tag, int32 count));
+  MOCK_METHOD0(Flush, void());
+};
+
 class FrequencyCounterMock : public FrequencyCounter {
  public:
   MOCK_METHOD4(Init, void(const char* filename,
@@ -28,6 +39,7 @@
                           void* reporter_handle,
                           time_t cycle_duration));
   MOCK_METHOD1(Update, void(int32 count));
+  MOCK_METHOD0(FlushFinishedCycles, void());
 };
 
 }  // namespace chromeos_metrics
diff --git a/metrics/counter_test.cc b/metrics/counter_test.cc
index eb68b2a..3580516 100644
--- a/metrics/counter_test.cc
+++ b/metrics/counter_test.cc
@@ -13,6 +13,7 @@
 
 #include "counter.h"
 #include "counter_mock.h"  // For TaggedCounterMock.
+#include "metrics_library_mock.h"
 
 using ::testing::_;
 using ::testing::MockFunction;
@@ -37,7 +38,7 @@
 class TaggedCounterTest : public testing::Test {
  protected:
   virtual void SetUp() {
-    EXPECT_EQ(NULL, counter_.filename_);
+    EXPECT_TRUE(counter_.filename_.empty());
     EXPECT_TRUE(NULL == counter_.reporter_);
     EXPECT_EQ(NULL, counter_.reporter_handle_);
     EXPECT_EQ(TaggedCounter::kRecordInvalid, counter_.record_state_);
@@ -256,14 +257,85 @@
   EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_);
 }
 
+static const char kTestFilename[] = "test_filename";
+static const char kTestHistogram[] = "test_histogram";
+const int kHistogramMin = 15;
+const int kHistogramMax = 1024;
+const int kHistogramBuckets = 23;
+
+class TaggedCounterReporterTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    tagged_counter_ = new StrictMock<TaggedCounterMock>();
+    reporter_.tagged_counter_.reset(tagged_counter_);
+    metrics_lib_.reset(new StrictMock<MetricsLibraryMock>);
+    reporter_.SetMetricsLibraryInterface(metrics_lib_.get());
+    ASSERT_TRUE(metrics_lib_.get() == reporter_.metrics_lib_);
+  }
+  virtual void TearDown() {
+    reporter_.SetMetricsLibraryInterface(NULL);
+  }
+
+  void DoInit();
+  StrictMock<TaggedCounterMock>* tagged_counter_;
+  TaggedCounterReporter reporter_;
+  scoped_ptr<MetricsLibraryMock> metrics_lib_;
+};
+
+void TaggedCounterReporterTest::DoInit() {
+  EXPECT_CALL(*tagged_counter_,
+              Init(kTestFilename,
+                   TaggedCounterReporter::Report,
+                   &reporter_))
+      .Times(1)
+      .RetiresOnSaturation();
+  reporter_.Init(kTestFilename,
+                 kTestHistogram,
+                 kHistogramMin,
+                 kHistogramMax,
+                 kHistogramBuckets);
+  EXPECT_EQ(kTestHistogram, reporter_.histogram_name_);
+  EXPECT_EQ(kHistogramBuckets, reporter_.buckets_);
+  EXPECT_EQ(kHistogramMax, reporter_.max_);
+  EXPECT_EQ(kHistogramMin, reporter_.min_);
+}
+
+TEST_F(TaggedCounterReporterTest, Init) {
+  DoInit();
+}
+
+TEST_F(TaggedCounterReporterTest, Update) {
+  DoInit();
+  EXPECT_CALL(*tagged_counter_, Update(1, 2))
+      .Times(1)
+      .RetiresOnSaturation();
+  reporter_.Update(1, 2);
+}
+
+TEST_F(TaggedCounterReporterTest, Flush) {
+  DoInit();
+  EXPECT_CALL(*tagged_counter_, Flush())
+      .Times(1)
+      .RetiresOnSaturation();
+  reporter_.Flush();
+}
+
+TEST_F(TaggedCounterReporterTest, Report) {
+  DoInit();
+  EXPECT_CALL(*metrics_lib_, SendToUMA(kTestHistogram,
+                                       301,
+                                       kHistogramMin,
+                                       kHistogramMax,
+                                       kHistogramBuckets))
+      .Times(1)
+      .RetiresOnSaturation();
+  reporter_.Report(&reporter_, 127, 301);
+}
+
 class FrequencyCounterTest : public testing::Test {
  protected:
   virtual void SetUp() {
-    tagged_counter_ = new StrictMock<TaggedCounterMock>;
-    frequency_counter_.tagged_counter_.reset(tagged_counter_);
-  }
-
-  static void FakeReporter(void *, int32, int32) {
+    tagged_counter_ = NULL;
   }
 
   void CheckInit(int32 cycle_duration);
@@ -276,14 +348,10 @@
 };
 
 void FrequencyCounterTest::CheckInit(int32 cycle_duration) {
-  EXPECT_CALL(*tagged_counter_, Init(kTestRecordFile, FakeReporter, this))
-      .Times(1)
-      .RetiresOnSaturation();
-  frequency_counter_.Init(kTestRecordFile,
-                          FakeReporter,
-                          this,
-                          cycle_duration);
+  tagged_counter_ = new StrictMock<TaggedCounterMock>;
+  frequency_counter_.Init(tagged_counter_, cycle_duration);
   EXPECT_EQ(cycle_duration, frequency_counter_.cycle_duration_);
+  EXPECT_EQ(tagged_counter_, frequency_counter_.tagged_counter_.get());
 }
 
 TEST_F(FrequencyCounterTest, Init) {
@@ -292,10 +360,12 @@
 
 void FrequencyCounterTest::CheckCycleNumber(int32 cycle_duration) {
   CheckInit(cycle_duration);
-  EXPECT_EQ(150, frequency_counter_.GetCycleNumber(cycle_duration * 150));
-  EXPECT_EQ(150, frequency_counter_.GetCycleNumber(cycle_duration * 150 +
-                                                   cycle_duration - 1));
-  EXPECT_EQ(151, frequency_counter_.GetCycleNumber(cycle_duration * 151 + 1));
+  EXPECT_EQ(150, frequency_counter_.GetCycleNumber(
+      cycle_duration * 150));
+  EXPECT_EQ(150, frequency_counter_.GetCycleNumber(
+      cycle_duration * 150 + cycle_duration - 1));
+  EXPECT_EQ(151, frequency_counter_.GetCycleNumber(
+      cycle_duration * 151 + 1));
   EXPECT_EQ(0, frequency_counter_.GetCycleNumber(0));
 }
 
@@ -310,7 +380,9 @@
 
 TEST_F(FrequencyCounterTest, UpdateInternal) {
   CheckInit(kSecondsPerWeek);
-  EXPECT_CALL(*tagged_counter_, Update(150, 2));
+  EXPECT_CALL(*tagged_counter_, Update(150, 2))
+      .Times(1)
+      .RetiresOnSaturation();
   frequency_counter_.UpdateInternal(2, kSecondsPerWeek * 150);
 }
 
diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc
index 0171fde..4d50cb5 100644
--- a/metrics/metrics_daemon.cc
+++ b/metrics/metrics_daemon.cc
@@ -4,10 +4,11 @@
 
 #include "metrics_daemon.h"
 
-#include <dbus/dbus-glib-lowlevel.h>
+#include <string.h>
 
 #include <base/file_util.h>
 #include <base/logging.h>
+#include <dbus/dbus-glib-lowlevel.h>
 
 #include "counter.h"
 
@@ -43,7 +44,7 @@
 static const char kUncleanShutdownDetectedFile[] =
       "/tmp/unclean-shutdown-detected";
 
-// static metrics parameters.
+// static metrics parameters
 const char MetricsDaemon::kMetricDailyUseTimeName[] =
     "Logging.DailyUseTime";
 const int MetricsDaemon::kMetricDailyUseTimeMin = 1;
@@ -73,16 +74,26 @@
 // crash frequency metrics
 const char MetricsDaemon::kMetricAnyCrashesDailyName[] =
     "Logging.AnyCrashesDaily";
+const char MetricsDaemon::kMetricAnyCrashesWeeklyName[] =
+    "Logging.AnyCrashesWeekly";
 const char MetricsDaemon::kMetricKernelCrashesDailyName[] =
     "Logging.KernelCrashesDaily";
+const char MetricsDaemon::kMetricKernelCrashesWeeklyName[] =
+    "Logging.KernelCrashesWeekly";
 const char MetricsDaemon::kMetricUncleanShutdownsDailyName[] =
     "Logging.UncleanShutdownsDaily";
+const char MetricsDaemon::kMetricUncleanShutdownsWeeklyName[] =
+    "Logging.UncleanShutdownsWeekly";
 const char MetricsDaemon::kMetricUserCrashesDailyName[] =
     "Logging.UserCrashesDaily";
-const char MetricsDaemon::kMetricCrashesDailyMin = 1;
-const char MetricsDaemon::kMetricCrashesDailyMax = 100;
-const char MetricsDaemon::kMetricCrashesDailyBuckets = 50;
+const char MetricsDaemon::kMetricUserCrashesWeeklyName[] =
+    "Logging.UserCrashesWeekly";
+const char MetricsDaemon::kMetricCrashFrequencyMin = 1;
+const char MetricsDaemon::kMetricCrashFrequencyMax = 100;
+const char MetricsDaemon::kMetricCrashFrequencyBuckets = 50;
 
+// persistent metrics path
+const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics";
 
 
 // static
@@ -182,7 +193,17 @@
       usemon_interval_(0),
       usemon_source_(NULL) {}
 
-MetricsDaemon::~MetricsDaemon() {}
+MetricsDaemon::~MetricsDaemon() {
+  DeleteFrequencyCounters();
+}
+
+void MetricsDaemon::DeleteFrequencyCounters() {
+  for (FrequencyCounters::iterator i = frequency_counters_.begin();
+       i != frequency_counters_.end(); ++i) {
+    delete i->second;
+    i->second = NULL;
+  }
+}
 
 void MetricsDaemon::Run(bool run_as_daemon) {
   if (run_as_daemon && daemon(0, 0) != 0)
@@ -199,64 +220,72 @@
   Loop();
 }
 
+FilePath MetricsDaemon::GetHistogramPath(const char* histogram_name) {
+  return FilePath(kMetricsPath).Append(histogram_name);
+}
+
+void MetricsDaemon::ConfigureCrashIntervalReporter(
+    const char* histogram_name,
+    scoped_ptr<chromeos_metrics::TaggedCounterReporter>* reporter) {
+  reporter->reset(new chromeos_metrics::TaggedCounterReporter());
+  FilePath file_path = GetHistogramPath(histogram_name);
+  (*reporter)->Init(file_path.value().c_str(),
+                    histogram_name,
+                    kMetricCrashIntervalMin,
+                    kMetricCrashIntervalMax,
+                    kMetricCrashIntervalBuckets);
+}
+
+void MetricsDaemon::ConfigureCrashFrequencyReporter(
+    const char* histogram_name) {
+  scoped_ptr<chromeos_metrics::TaggedCounterReporter> reporter(
+      new chromeos_metrics::TaggedCounterReporter());
+  FilePath file_path = GetHistogramPath(histogram_name);
+  reporter->Init(file_path.value().c_str(),
+                 histogram_name,
+                 kMetricCrashFrequencyMin,
+                 kMetricCrashFrequencyMax,
+                 kMetricCrashFrequencyBuckets);
+  scoped_ptr<chromeos_metrics::FrequencyCounter> new_counter(
+      new chromeos_metrics::FrequencyCounter());
+  time_t cycle_duration = strstr(histogram_name, "Weekly") != NULL ?
+      chromeos_metrics::kSecondsPerWeek :
+      chromeos_metrics::kSecondsPerDay;
+  new_counter->Init(
+      static_cast<chromeos_metrics::TaggedCounterInterface*>(
+          reporter.release()),
+      cycle_duration);
+  frequency_counters_[histogram_name] = new_counter.release();
+}
+
 void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib) {
   testing_ = testing;
   DCHECK(metrics_lib != NULL);
   metrics_lib_ = metrics_lib;
+  chromeos_metrics::TaggedCounterReporter::
+      SetMetricsLibraryInterface(metrics_lib);
 
   static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage";
   daily_use_.reset(new chromeos_metrics::TaggedCounter());
   daily_use_->Init(kDailyUseRecordFile, &ReportDailyUse, this);
 
-  static const char kUserCrashIntervalRecordFile[] =
-      "/var/log/metrics/user-crash-interval";
-  user_crash_interval_.reset(new chromeos_metrics::TaggedCounter());
-  user_crash_interval_->Init(kUserCrashIntervalRecordFile,
-                             &ReportUserCrashInterval, this);
+  ConfigureCrashIntervalReporter(kMetricKernelCrashIntervalName,
+                                 &kernel_crash_interval_);
+  ConfigureCrashIntervalReporter(kMetricUncleanShutdownIntervalName,
+                                 &unclean_shutdown_interval_);
+  ConfigureCrashIntervalReporter(kMetricUserCrashIntervalName,
+                                 &user_crash_interval_);
 
-  static const char kKernelCrashIntervalRecordFile[] =
-      "/var/log/metrics/kernel-crash-interval";
-  kernel_crash_interval_.reset(new chromeos_metrics::TaggedCounter());
-  kernel_crash_interval_->Init(kKernelCrashIntervalRecordFile,
-                               &ReportKernelCrashInterval, this);
-
-  static const char kUncleanShutdownDetectedFile[] =
-      "/var/log/metrics/unclean-shutdown-interval";
-  unclean_shutdown_interval_.reset(new chromeos_metrics::TaggedCounter());
-  unclean_shutdown_interval_->Init(kUncleanShutdownDetectedFile,
-                                   &ReportUncleanShutdownInterval, this);
-
-  static const char kUserCrashesDailyRecordFile[] =
-      "/var/log/metrics/user-crashes-daily";
-  user_crashes_daily_.reset(new chromeos_metrics::FrequencyCounter());
-  user_crashes_daily_->Init(kUserCrashesDailyRecordFile,
-                            &ReportUserCrashesDaily,
-                            this,
-                            chromeos_metrics::kSecondsPerDay);
-
-  static const char kKernelCrashesDailyRecordFile[] =
-      "/var/log/metrics/kernel-crashes-daily";
-  kernel_crashes_daily_.reset(new chromeos_metrics::FrequencyCounter());
-  kernel_crashes_daily_->Init(kKernelCrashesDailyRecordFile,
-                              &ReportKernelCrashesDaily,
-                              this,
-                              chromeos_metrics::kSecondsPerDay);
-
-  static const char kUncleanShutdownsDailyRecordFile[] =
-      "/var/log/metrics/unclean-shutdowns-daily";
-  unclean_shutdowns_daily_.reset(new chromeos_metrics::FrequencyCounter());
-  unclean_shutdowns_daily_->Init(kUncleanShutdownsDailyRecordFile,
-                                 &ReportUncleanShutdownsDaily,
-                                 this,
-                                 chromeos_metrics::kSecondsPerDay);
-
-  static const char kAnyCrashesUserCrashDailyRecordFile[] =
-      "/var/log/metrics/any-crashes-daily";
-  any_crashes_daily_.reset(new chromeos_metrics::FrequencyCounter());
-  any_crashes_daily_->Init(kAnyCrashesUserCrashDailyRecordFile,
-                           &ReportAnyCrashesDaily,
-                           this,
-                           chromeos_metrics::kSecondsPerDay);
+  DeleteFrequencyCounters();
+  ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
+  ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
+  ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName);
+  ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName);
+  ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName);
+  ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsDailyName);
+  ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName);
+  ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName);
+  ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName);
 
   // Don't setup D-Bus and GLib in test mode.
   if (testing)
@@ -453,6 +482,12 @@
   user_crash_interval_->Update(0, seconds);
   kernel_crash_interval_->Update(0, seconds);
 
+  // Flush finished cycles of all frequency counters.
+  for (FrequencyCounters::iterator i = frequency_counters_.begin();
+       i != frequency_counters_.end(); ++i) {
+    i->second->FlushFinishedCycles();
+  }
+
   // Schedules a use monitor on inactive->active transitions and
   // unschedules it on active->inactive transitions.
   if (!user_active_ && active)
@@ -473,8 +508,10 @@
   // Reports the active use time since the last crash and resets it.
   user_crash_interval_->Flush();
 
-  user_crashes_daily_->Update(1);
-  any_crashes_daily_->Update(1);
+  frequency_counters_[kMetricUserCrashesDailyName]->Update(1);
+  frequency_counters_[kMetricUserCrashesWeeklyName]->Update(1);
+  frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
+  frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
 }
 
 void MetricsDaemon::ProcessKernelCrash() {
@@ -484,8 +521,10 @@
   // Reports the active use time since the last crash and resets it.
   kernel_crash_interval_->Flush();
 
-  kernel_crashes_daily_->Update(1);
-  any_crashes_daily_->Update(1);
+  frequency_counters_[kMetricKernelCrashesDailyName]->Update(1);
+  frequency_counters_[kMetricKernelCrashesWeeklyName]->Update(1);
+  frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
+  frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
 }
 
 void MetricsDaemon::ProcessUncleanShutdown() {
@@ -495,8 +534,10 @@
   // Reports the active use time since the last crash and resets it.
   unclean_shutdown_interval_->Flush();
 
-  unclean_shutdowns_daily_->Update(1);
-  any_crashes_daily_->Update(1);
+  frequency_counters_[kMetricUncleanShutdownsDailyName]->Update(1);
+  frequency_counters_[kMetricUncleanShutdownsWeeklyName]->Update(1);
+  frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
+  frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
 }
 
 bool MetricsDaemon::CheckSystemCrash(const std::string& crash_file) {
@@ -584,69 +625,6 @@
                      kMetricDailyUseTimeBuckets);
 }
 
-// static
-void MetricsDaemon::ReportCrashInterval(const char* histogram_name,
-                                        void* handle, int count) {
-  MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
-  daemon->SendMetric(histogram_name, count,
-                     kMetricCrashIntervalMin,
-                     kMetricCrashIntervalMax,
-                     kMetricCrashIntervalBuckets);
-}
-
-// static
-void MetricsDaemon::ReportUserCrashInterval(void* handle,
-                                            int tag, int count) {
-  ReportCrashInterval(kMetricUserCrashIntervalName, handle, count);
-}
-
-// static
-void MetricsDaemon::ReportKernelCrashInterval(void* handle,
-                                                int tag, int count) {
-  ReportCrashInterval(kMetricKernelCrashIntervalName, handle, count);
-}
-
-// static
-void MetricsDaemon::ReportUncleanShutdownInterval(void* handle,
-                                                    int tag, int count) {
-  ReportCrashInterval(kMetricUncleanShutdownIntervalName, handle, count);
-}
-
-// static
-void MetricsDaemon::ReportCrashesDailyFrequency(const char* histogram_name,
-                                                void* handle,
-                                                int count) {
-  MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
-  daemon->SendMetric(histogram_name, count,
-                     kMetricCrashesDailyMin,
-                     kMetricCrashesDailyMax,
-                     kMetricCrashesDailyBuckets);
-}
-
-// static
-void MetricsDaemon::ReportUserCrashesDaily(void* handle,
-                                           int tag, int count) {
-  ReportCrashesDailyFrequency(kMetricUserCrashesDailyName, handle, count);
-}
-
-// static
-void MetricsDaemon::ReportKernelCrashesDaily(void* handle,
-                                             int tag, int count) {
-  ReportCrashesDailyFrequency(kMetricKernelCrashesDailyName, handle, count);
-}
-
-// static
-void MetricsDaemon::ReportUncleanShutdownsDaily(void* handle,
-                                                int tag, int count) {
-  ReportCrashesDailyFrequency(kMetricUncleanShutdownsDailyName, handle, count);
-}
-
-// static
-void MetricsDaemon::ReportAnyCrashesDaily(void* handle, int tag, int count) {
-  ReportCrashesDailyFrequency(kMetricAnyCrashesDailyName, handle, count);
-}
-
-
 void MetricsDaemon::SendMetric(const string& name, int sample,
                                int min, int max, int nbuckets) {
   DLOG(INFO) << "received metric: " << name << " " << sample << " "
diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h
index f98c34c..d400bf0 100644
--- a/metrics/metrics_daemon.h
+++ b/metrics/metrics_daemon.h
@@ -7,7 +7,9 @@
 
 #include <dbus/dbus.h>
 #include <glib.h>
+#include <map>
 
+#include <base/file_path.h>
 #include <base/scoped_ptr.h>
 #include <base/time.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
@@ -16,7 +18,8 @@
 
 namespace chromeos_metrics {
 class FrequencyCounter;
-class TaggedCounterInterface;
+class TaggedCounter;
+class TaggedCounterReporter;
 }
 
 class MetricsDaemon {
@@ -35,6 +38,10 @@
  private:
   friend class MetricsDaemonTest;
   FRIEND_TEST(MetricsDaemonTest, CheckSystemCrash);
+  FRIEND_TEST(MetricsDaemonTest, ComputeEpochNoCurrent);
+  FRIEND_TEST(MetricsDaemonTest, ComputeEpochNoLast);
+  FRIEND_TEST(MetricsDaemonTest, GetHistogramPath);
+  FRIEND_TEST(MetricsDaemonTest, IsNewEpoch);
   FRIEND_TEST(MetricsDaemonTest, LookupNetworkState);
   FRIEND_TEST(MetricsDaemonTest, LookupPowerState);
   FRIEND_TEST(MetricsDaemonTest, LookupScreenSaverState);
@@ -89,11 +96,15 @@
     int seconds_;
   };
 
+  typedef std::map<std::string, chromeos_metrics::FrequencyCounter*>
+    FrequencyCounters;
+
   // Metric parameters.
   static const char kMetricAnyCrashesDailyName[];
-  static const char kMetricCrashesDailyBuckets;
-  static const char kMetricCrashesDailyMax;
-  static const char kMetricCrashesDailyMin;
+  static const char kMetricAnyCrashesWeeklyName[];
+  static const char kMetricCrashFrequencyBuckets;
+  static const char kMetricCrashFrequencyMax;
+  static const char kMetricCrashFrequencyMin;
   static const int  kMetricCrashIntervalBuckets;
   static const int  kMetricCrashIntervalMax;
   static const int  kMetricCrashIntervalMin;
@@ -102,14 +113,18 @@
   static const int  kMetricDailyUseTimeMin;
   static const char kMetricDailyUseTimeName[];
   static const char kMetricKernelCrashesDailyName[];
+  static const char kMetricKernelCrashesWeeklyName[];
   static const char kMetricKernelCrashIntervalName[];
+  static const char kMetricsPath[];
   static const int  kMetricTimeToNetworkDropBuckets;
   static const int  kMetricTimeToNetworkDropMax;
   static const int  kMetricTimeToNetworkDropMin;
   static const char kMetricTimeToNetworkDropName[];
   static const char kMetricUncleanShutdownIntervalName[];
   static const char kMetricUncleanShutdownsDailyName[];
+  static const char kMetricUncleanShutdownsWeeklyName[];
   static const char kMetricUserCrashesDailyName[];
+  static const char kMetricUserCrashesWeeklyName[];
   static const char kMetricUserCrashIntervalName[];
 
   // D-Bus message match strings.
@@ -124,6 +139,20 @@
   // Array of user session states.
   static const char* kSessionStates_[kNumberSessionStates];
 
+  // Clears and deletes the data contained in frequency_counters_.
+  void DeleteFrequencyCounters();
+
+  // Configures the given crash interval reporter.
+  void ConfigureCrashIntervalReporter(
+      const char* histogram_name,
+      scoped_ptr<chromeos_metrics::TaggedCounterReporter>* reporter);
+
+  // Configures the given frequency counter reporter.
+  void ConfigureCrashFrequencyReporter(const char* histogram_name);
+
+  // Returns file path to persistent file for generating given histogram.
+  FilePath GetHistogramPath(const char* histogram_name);
+
   // Creates the event loop and enters it.
   void Loop();
 
@@ -203,48 +232,15 @@
   // Unschedules a scheduled use monitor, if any.
   void UnscheduleUseMonitor();
 
+  // Report daily use through UMA.
+  static void ReportDailyUse(void* handle, int tag, int count);
+
   // Sends a regular (exponential) histogram sample to Chrome for
   // transport to UMA. See MetricsLibrary::SendToUMA in
   // metrics_library.h for a description of the arguments.
   void SendMetric(const std::string& name, int sample,
                   int min, int max, int nbuckets);
 
-  // TaggedCounter callback to process aggregated daily usage data and
-  // send to UMA.
-  static void ReportDailyUse(void* data, int tag, int count);
-
-  // Helper to report a crash interval to UMA.
-  static void ReportCrashInterval(const char* histogram_name,
-                                  void* handle, int count);
-
-  // TaggedCounter callback to process time between user-space process
-  // crashes and send to UMA.
-  static void ReportUserCrashInterval(void* data, int tag, int count);
-
-  // TaggedCounter callback to process time between kernel crashes and
-  // send to UMA.
-  static void ReportKernelCrashInterval(void* data, int tag, int count);
-
-  // TaggedCounter callback to process time between unclean shutdowns and
-  // send to UMA.
-  static void ReportUncleanShutdownInterval(void* data, int tag, int count);
-
-  // Helper to report a daily crash frequency to UMA.
-  static void ReportCrashesDailyFrequency(const char* histogram_name,
-                                          void* handle, int count);
-
-  // TaggedCounter callback to report daily crash frequency to UMA.
-  static void ReportUserCrashesDaily(void* handle, int tag, int count);
-
-  // TaggedCounter callback to report kernel crash frequency to UMA.
-  static void ReportKernelCrashesDaily(void* handle, int tag, int count);
-
-  // TaggedCounter callback to report unclean shutdown frequency to UMA.
-  static void ReportUncleanShutdownsDaily(void* handle, int tag, int count);
-
-  // TaggedCounter callback to report frequency of any crashes to UMA.
-  static void ReportAnyCrashesDaily(void* handle, int tag, int count);
-
   // Test mode.
   bool testing_;
 
@@ -275,30 +271,20 @@
   base::Time user_active_last_;
 
   // Daily active use time in seconds.
-  scoped_ptr<chromeos_metrics::TaggedCounterInterface> daily_use_;
+  scoped_ptr<chromeos_metrics::TaggedCounter> daily_use_;
 
   // Active use time between user-space process crashes.
-  scoped_ptr<chromeos_metrics::TaggedCounterInterface> user_crash_interval_;
+  scoped_ptr<chromeos_metrics::TaggedCounterReporter> user_crash_interval_;
 
   // Active use time between kernel crashes.
-  scoped_ptr<chromeos_metrics::TaggedCounterInterface> kernel_crash_interval_;
+  scoped_ptr<chromeos_metrics::TaggedCounterReporter> kernel_crash_interval_;
 
   // Active use time between unclean shutdowns crashes.
-  scoped_ptr<chromeos_metrics::TaggedCounterInterface>
+  scoped_ptr<chromeos_metrics::TaggedCounterReporter>
       unclean_shutdown_interval_;
 
-  // Daily count of user-space process crashes.
-  scoped_ptr<chromeos_metrics::FrequencyCounter> user_crashes_daily_;
-
-  // Daily count of kernel crashes.
-  scoped_ptr<chromeos_metrics::FrequencyCounter> kernel_crashes_daily_;
-
-  // Daily count of unclean shutdowns.
-  scoped_ptr<chromeos_metrics::FrequencyCounter> unclean_shutdowns_daily_;
-
-  // Daily count of any crashes (user-space processes, kernel, or
-  // unclean shutdowns).
-  scoped_ptr<chromeos_metrics::FrequencyCounter> any_crashes_daily_;
+  // Map of all frequency counters, to simplify flushing them.
+  FrequencyCounters frequency_counters_;
 
   // Sleep period until the next daily usage aggregation performed by
   // the daily use monitor (see ScheduleUseMonitor).
diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc
index e551cf9..9ba63e8 100644
--- a/metrics/metrics_daemon_test.cc
+++ b/metrics/metrics_daemon_test.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <utime.h>
+
 #include <base/file_util.h>
 #include <gtest/gtest.h>
 
@@ -11,14 +13,21 @@
 
 using base::Time;
 using base::TimeTicks;
+using chromeos_metrics::FrequencyCounter;
 using chromeos_metrics::FrequencyCounterMock;
 using chromeos_metrics::TaggedCounterMock;
+using chromeos_metrics::TaggedCounterReporter;
+using chromeos_metrics::TaggedCounterReporterMock;
 using ::testing::_;
 using ::testing::Return;
 using ::testing::StrictMock;
 
 static const int kSecondsPerDay = 24 * 60 * 60;
 
+static const char kTestDir[] = "test";
+static const char kLastFile[] = "test/last";
+static const char kCurrentFile[] = "test/current";
+
 // This class allows a TimeTicks object to be initialized with seconds
 // (rather than microseconds) through the protected TimeTicks(int64)
 // constructor.
@@ -48,6 +57,38 @@
     EXPECT_EQ(NULL, daemon_.user_crash_interval_.get());
     daemon_.Init(true, &metrics_lib_);
 
+    // Check configuration of a few histograms.
+    FrequencyCounter* frequency_counter =
+        daemon_.frequency_counters_[MetricsDaemon::kMetricAnyCrashesDailyName];
+    const TaggedCounterReporter* reporter = GetReporter(frequency_counter);
+    EXPECT_EQ(MetricsDaemon::kMetricAnyCrashesDailyName,
+              reporter->histogram_name());
+    EXPECT_EQ(chromeos_metrics::kSecondsPerDay,
+              frequency_counter->cycle_duration());
+    EXPECT_EQ(MetricsDaemon::kMetricCrashFrequencyMin, reporter->min());
+    EXPECT_EQ(MetricsDaemon::kMetricCrashFrequencyMax, reporter->max());
+    EXPECT_EQ(MetricsDaemon::kMetricCrashFrequencyBuckets, reporter->buckets());
+
+    frequency_counter =
+        daemon_.frequency_counters_[MetricsDaemon::kMetricAnyCrashesWeeklyName];
+    reporter = GetReporter(frequency_counter);
+    EXPECT_EQ(MetricsDaemon::kMetricAnyCrashesWeeklyName,
+              reporter->histogram_name());
+    EXPECT_EQ(chromeos_metrics::kSecondsPerWeek,
+              frequency_counter->cycle_duration());
+
+    EXPECT_EQ(MetricsDaemon::kMetricKernelCrashIntervalName,
+              daemon_.kernel_crash_interval_->histogram_name());
+    EXPECT_EQ(MetricsDaemon::kMetricCrashIntervalMin,
+              daemon_.kernel_crash_interval_->min());
+    EXPECT_EQ(MetricsDaemon::kMetricCrashIntervalMax,
+              daemon_.kernel_crash_interval_->max());
+    EXPECT_EQ(MetricsDaemon::kMetricCrashIntervalBuckets,
+              daemon_.kernel_crash_interval_->buckets());
+
+    EXPECT_EQ(MetricsDaemon::kMetricUncleanShutdownIntervalName,
+              daemon_.unclean_shutdown_interval_->histogram_name());
+
     // Tests constructor initialization. Switches to mock counters.
     EXPECT_TRUE(NULL != daemon_.daily_use_.get());
     EXPECT_TRUE(NULL != daemon_.kernel_crash_interval_.get());
@@ -56,20 +97,20 @@
     // Allocates mock counter and transfers ownership.
     daily_use_ = new StrictMock<TaggedCounterMock>();
     daemon_.daily_use_.reset(daily_use_);
-    kernel_crash_interval_ = new StrictMock<TaggedCounterMock>();
+    kernel_crash_interval_ = new StrictMock<TaggedCounterReporterMock>();
     daemon_.kernel_crash_interval_.reset(kernel_crash_interval_);
-    user_crash_interval_ = new StrictMock<TaggedCounterMock>();
+    user_crash_interval_ = new StrictMock<TaggedCounterReporterMock>();
     daemon_.user_crash_interval_.reset(user_crash_interval_);
-    unclean_shutdown_interval_ = new StrictMock<TaggedCounterMock>();
+    unclean_shutdown_interval_ = new StrictMock<TaggedCounterReporterMock>();
     daemon_.unclean_shutdown_interval_.reset(unclean_shutdown_interval_);
-    kernel_crashes_daily_ = new StrictMock<FrequencyCounterMock>();
-    daemon_.kernel_crashes_daily_.reset(kernel_crashes_daily_);
-    user_crashes_daily_ = new StrictMock<FrequencyCounterMock>();
-    daemon_.user_crashes_daily_.reset(user_crashes_daily_);
-    unclean_shutdowns_daily_ = new StrictMock<FrequencyCounterMock>();
-    daemon_.unclean_shutdowns_daily_.reset(unclean_shutdowns_daily_);
-    any_crashes_daily_ = new StrictMock<FrequencyCounterMock>();
-    daemon_.any_crashes_daily_.reset(any_crashes_daily_);
+
+    // Reset all frequency counter reporters to mocks for further testing.
+    MetricsDaemon::FrequencyCounters::iterator i;
+    for (i = daemon_.frequency_counters_.begin();
+         i != daemon_.frequency_counters_.end(); ++i) {
+      delete i->second;
+      i->second = new StrictMock<FrequencyCounterMock>();
+    }
 
     EXPECT_FALSE(daemon_.user_active_);
     EXPECT_TRUE(daemon_.user_active_last_.is_null());
@@ -77,10 +118,29 @@
     EXPECT_TRUE(daemon_.network_state_last_.is_null());
     EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_);
     EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_);
+
+    file_util::Delete(FilePath(kTestDir), true);
+    file_util::CreateDirectory(FilePath(kTestDir));
   }
 
   virtual void TearDown() {}
 
+  const TaggedCounterReporter*
+  GetReporter(FrequencyCounter* frequency_counter) const {
+    return static_cast<const TaggedCounterReporter*>(
+        &frequency_counter->tagged_counter());
+  }
+
+  void ExpectFrequencyFlushCalls() {
+    MetricsDaemon::FrequencyCounters::iterator i;
+    for (i = daemon_.frequency_counters_.begin();
+         i != daemon_.frequency_counters_.end(); ++i) {
+      FrequencyCounterMock* mock =
+          static_cast<FrequencyCounterMock*>(i->second);
+      EXPECT_CALL(*mock, FlushFinishedCycles());
+    }
+  }
+
   // Adds active use aggregation counters update expectations that the
   // specified tag/count update will be generated.
   void ExpectActiveUseUpdate(int daily_tag, int count) {
@@ -93,6 +153,7 @@
     EXPECT_CALL(*user_crash_interval_, Update(0, count))
         .Times(1)
         .RetiresOnSaturation();
+    ExpectFrequencyFlushCalls();
   }
 
   // Adds active use aggregation counters update expectations that
@@ -107,6 +168,7 @@
     EXPECT_CALL(*user_crash_interval_, Update(_, _))
         .Times(1)
         .RetiresOnSaturation();
+    ExpectFrequencyFlushCalls();
   }
 
   // Adds a metrics library mock expectation that the specified metric
@@ -170,6 +232,12 @@
     dbus_message_unref(msg);
   }
 
+  // Get the frequency counter for the given name.
+  FrequencyCounterMock& GetFrequencyMock(const char* histogram_name) {
+    return *static_cast<FrequencyCounterMock*>(
+        daemon_.frequency_counters_[histogram_name]);
+  }
+
   // The MetricsDaemon under test.
   MetricsDaemon daemon_;
 
@@ -181,14 +249,9 @@
   // update calls are marked as failures. They are pointers so that
   // they can replace the scoped_ptr's allocated by the daemon.
   StrictMock<TaggedCounterMock>* daily_use_;
-  StrictMock<TaggedCounterMock>* kernel_crash_interval_;
-  StrictMock<TaggedCounterMock>* user_crash_interval_;
-  StrictMock<TaggedCounterMock>* unclean_shutdown_interval_;
-
-  StrictMock<FrequencyCounterMock>* kernel_crashes_daily_;
-  StrictMock<FrequencyCounterMock>* user_crashes_daily_;
-  StrictMock<FrequencyCounterMock>* unclean_shutdowns_daily_;
-  StrictMock<FrequencyCounterMock>* any_crashes_daily_;
+  StrictMock<TaggedCounterReporterMock>* kernel_crash_interval_;
+  StrictMock<TaggedCounterReporterMock>* user_crash_interval_;
+  StrictMock<TaggedCounterReporterMock>* unclean_shutdown_interval_;
 };
 
 TEST_F(MetricsDaemonTest, CheckSystemCrash) {
@@ -217,22 +280,6 @@
   MetricsDaemon::ReportDailyUse(&daemon_, /* tag */ 60, /* count */ -5);
 }
 
-TEST_F(MetricsDaemonTest, ReportKernelCrashInterval) {
-  ExpectMetric(MetricsDaemon::kMetricKernelCrashIntervalName, 50,
-               MetricsDaemon::kMetricCrashIntervalMin,
-               MetricsDaemon::kMetricCrashIntervalMax,
-               MetricsDaemon::kMetricCrashIntervalBuckets);
-  MetricsDaemon::ReportKernelCrashInterval(&daemon_, 0, 50);
-}
-
-TEST_F(MetricsDaemonTest, ReportUncleanShutdownInterval) {
-  ExpectMetric(MetricsDaemon::kMetricUncleanShutdownIntervalName, 50,
-               MetricsDaemon::kMetricCrashIntervalMin,
-               MetricsDaemon::kMetricCrashIntervalMax,
-               MetricsDaemon::kMetricCrashIntervalBuckets);
-  MetricsDaemon::ReportUncleanShutdownInterval(&daemon_, 0, 50);
-}
-
 TEST_F(MetricsDaemonTest, LookupNetworkState) {
   EXPECT_EQ(MetricsDaemon::kNetworkStateOnline,
             daemon_.LookupNetworkState("online"));
@@ -268,15 +315,25 @@
   DeleteDBusMessage(msg);
 
   IgnoreActiveUseUpdate();
+  EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesDailyName),
+      Update(1))
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesWeeklyName),
+      Update(1))
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUserCrashesDailyName),
+      Update(1))
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUserCrashesWeeklyName),
+      Update(1))
+      .Times(1)
+      .RetiresOnSaturation();
   EXPECT_CALL(*user_crash_interval_, Flush())
       .Times(1)
       .RetiresOnSaturation();
-  EXPECT_CALL(*user_crashes_daily_, Update(1))
-      .Times(1)
-      .RetiresOnSaturation();
-  EXPECT_CALL(*any_crashes_daily_, Update(1))
-      .Times(1)
-      .RetiresOnSaturation();
   msg = NewDBusSignalString("/",
                             "org.chromium.CrashReporter",
                             "UserCrash",
@@ -410,8 +467,14 @@
   EXPECT_CALL(*kernel_crash_interval_, Flush())
       .Times(1)
       .RetiresOnSaturation();
-  EXPECT_CALL(*kernel_crashes_daily_, Update(1));
-  EXPECT_CALL(*any_crashes_daily_, Update(1));
+  EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesDailyName),
+              Update(1));
+  EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesWeeklyName),
+              Update(1));
+  EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricKernelCrashesDailyName),
+              Update(1));
+  EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricKernelCrashesWeeklyName),
+              Update(1));
   daemon_.ProcessKernelCrash();
 }
 
@@ -420,8 +483,15 @@
   EXPECT_CALL(*unclean_shutdown_interval_, Flush())
       .Times(1)
       .RetiresOnSaturation();
-  EXPECT_CALL(*unclean_shutdowns_daily_, Update(1));
-  EXPECT_CALL(*any_crashes_daily_, Update(1));
+  EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesDailyName),
+              Update(1));
+  EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesWeeklyName),
+              Update(1));
+  EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUncleanShutdownsDailyName),
+              Update(1));
+  EXPECT_CALL(
+      GetFrequencyMock(MetricsDaemon::kMetricUncleanShutdownsWeeklyName),
+      Update(1));
   daemon_.ProcessUncleanShutdown();
 }
 
@@ -430,8 +500,14 @@
   EXPECT_CALL(*user_crash_interval_, Flush())
       .Times(1)
       .RetiresOnSaturation();
-  EXPECT_CALL(*user_crashes_daily_, Update(1));
-  EXPECT_CALL(*any_crashes_daily_, Update(1));
+  EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesDailyName),
+              Update(1));
+  EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesWeeklyName),
+              Update(1));
+  EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUserCrashesDailyName),
+              Update(1));
+  EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUserCrashesWeeklyName),
+              Update(1));
   daemon_.ProcessUserCrash();
 }
 
@@ -514,20 +590,10 @@
   EXPECT_EQ(TestTime(10 * kSecondsPerDay + 1000), daemon_.user_active_last_);
 }
 
-TEST_F(MetricsDaemonTest, ReportUserCrashInterval) {
-  ExpectMetric(MetricsDaemon::kMetricUserCrashIntervalName, 50,
-               MetricsDaemon::kMetricCrashIntervalMin,
-               MetricsDaemon::kMetricCrashIntervalMax,
-               MetricsDaemon::kMetricCrashIntervalBuckets);
-  MetricsDaemon::ReportUserCrashInterval(&daemon_, 0, 50);
-}
-
-TEST_F(MetricsDaemonTest, ReportCrashesDailyFrequency) {
-  ExpectMetric("foobar", 50,
-               MetricsDaemon::kMetricCrashesDailyMin,
-               MetricsDaemon::kMetricCrashesDailyMax,
-               MetricsDaemon::kMetricCrashesDailyBuckets);
-  MetricsDaemon::ReportCrashesDailyFrequency("foobar", &daemon_, 50);
+TEST_F(MetricsDaemonTest, GetHistogramPath) {
+  EXPECT_EQ("/var/log/metrics/Logging.AnyCrashesDaily",
+            daemon_.GetHistogramPath(
+                MetricsDaemon::kMetricAnyCrashesDailyName).value());
 }
 
 int main(int argc, char** argv) {
diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc
index e4087ef..3e3987d 100644
--- a/metrics/metrics_library.cc
+++ b/metrics/metrics_library.cc
@@ -6,6 +6,7 @@
 
 #include <errno.h>
 #include <sys/file.h>
+#include <sys/stat.h>
 
 #include <cstdarg>
 #include <cstdio>
@@ -14,12 +15,14 @@
 #define READ_WRITE_ALL_FILE_FLAGS \
   (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
 
-static const char kAutotestPath[] =
-    "/var/log/metrics/autotest-events";
-static const char kUMAEventsPath[] =
-    "/var/log/metrics/uma-events";
+static const char kAutotestPath[] = "/var/log/metrics/autotest-events";
+static const char kUMAEventsPath[] = "/var/log/metrics/uma-events";
+static const char kConsentFile[] = "/home/chronos/Consent To Send Stats";
 static const int32_t kBufferSize = 1024;
 
+time_t MetricsLibrary::cached_enabled_time_ = 0;
+bool MetricsLibrary::cached_enabled_ = false;
+
 using std::string;
 
 // TODO(sosa@chromium.org) - use Chromium logger instead of stderr
@@ -38,9 +41,24 @@
 }
 
 MetricsLibrary::MetricsLibrary()
-    : uma_events_file_(NULL) {}
+    : uma_events_file_(NULL),
+      consent_file_(kConsentFile) {}
+
+bool MetricsLibrary::AreMetricsEnabled() {
+  static struct stat stat_buffer;
+  time_t this_check_time = time(NULL);
+
+  if (this_check_time != cached_enabled_time_) {
+    cached_enabled_time_ = this_check_time;
+    cached_enabled_ = (stat(consent_file_, &stat_buffer) >= 0);
+  }
+  return cached_enabled_;
+}
 
 bool MetricsLibrary::SendMessageToChrome(int32_t length, const char* message) {
+  if (!AreMetricsEnabled())
+    return true;
+
   int chrome_fd = open(uma_events_file_,
                        O_WRONLY | O_APPEND | O_CREAT,
                        READ_WRITE_ALL_FILE_FLAGS);
diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h
index 76fa451..52da94d 100644
--- a/metrics/metrics_library.h
+++ b/metrics/metrics_library.h
@@ -27,6 +27,9 @@
   // Initializes the library.
   void Init();
 
+  // Returns whether or not metrics collection is enabled.
+  bool AreMetricsEnabled();
+
   // Sends histogram data to Chrome for transport to UMA and returns
   // true on success. This method results in the equivalent of an
   // asynchronous non-blocking RPC to UMA_HISTOGRAM_CUSTOM_COUNTS
@@ -69,6 +72,7 @@
  private:
   friend class CMetricsLibraryTest;
   friend class MetricsLibraryTest;
+  FRIEND_TEST(MetricsLibraryTest, AreMetricsEnabled);
   FRIEND_TEST(MetricsLibraryTest, FormatChromeMessage);
   FRIEND_TEST(MetricsLibraryTest, FormatChromeMessageTooLong);
   FRIEND_TEST(MetricsLibraryTest, SendMessageToChrome);
@@ -89,7 +93,14 @@
   int32_t FormatChromeMessage(int32_t buffer_size, char* buffer,
                               const char* format, ...);
 
+  // Time at which we last checked if metrics were enabled.
+  static time_t cached_enabled_time_;
+
+  // Cached state of whether or not metrics were enabled.
+  static bool cached_enabled_;
+
   const char* uma_events_file_;
+  const char* consent_file_;
 };
 
 #endif  // METRICS_LIBRARY_H_
diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc
index 596abd3..cbf4cce 100644
--- a/metrics/metrics_library_test.cc
+++ b/metrics/metrics_library_test.cc
@@ -12,6 +12,15 @@
 
 static const FilePath kTestUMAEventsFile("test-uma-events");
 
+static const char kTestConsent[] = "test-consent";
+
+static void SetMetricsEnabled(bool enabled) {
+  if (enabled)
+    ASSERT_EQ(1, file_util::WriteFile(FilePath(kTestConsent) , "0", 1));
+  else
+    file_util::Delete(FilePath(kTestConsent), false);
+}
+
 class MetricsLibraryTest : public testing::Test {
  protected:
   virtual void SetUp() {
@@ -19,15 +28,63 @@
     lib_.Init();
     EXPECT_TRUE(NULL != lib_.uma_events_file_);
     lib_.uma_events_file_ = kTestUMAEventsFile.value().c_str();
+    SetMetricsEnabled(true);
+    // Defeat metrics enabled caching between tests.
+    lib_.cached_enabled_time_ = 0;
+    lib_.consent_file_ = kTestConsent;
   }
 
   virtual void TearDown() {
     file_util::Delete(kTestUMAEventsFile, false);
   }
 
+  void VerifyEnabledCacheHit(bool to_value);
+  void VerifyEnabledCacheEviction(bool to_value);
+
   MetricsLibrary lib_;
 };
 
+TEST_F(MetricsLibraryTest, AreMetricsEnabledFalse) {
+  SetMetricsEnabled(false);
+  EXPECT_FALSE(lib_.AreMetricsEnabled());
+}
+
+TEST_F(MetricsLibraryTest, AreMetricsEnabledTrue) {
+  EXPECT_TRUE(lib_.AreMetricsEnabled());
+}
+
+void MetricsLibraryTest::VerifyEnabledCacheHit(bool to_value) {
+  // We might step from one second to the next one time, but not 100
+  // times in a row.
+  for (int i = 0; i < 100; ++i) {
+    lib_.cached_enabled_time_ = 0;
+    SetMetricsEnabled(!to_value);
+    ASSERT_EQ(!to_value, lib_.AreMetricsEnabled());
+    SetMetricsEnabled(to_value);
+    if (lib_.AreMetricsEnabled() == !to_value)
+      return;
+  }
+  ADD_FAILURE() << "Did not see evidence of caching";
+}
+
+void MetricsLibraryTest::VerifyEnabledCacheEviction(bool to_value) {
+  lib_.cached_enabled_time_ = 0;
+  SetMetricsEnabled(!to_value);
+  ASSERT_EQ(!to_value, lib_.AreMetricsEnabled());
+  SetMetricsEnabled(to_value);
+  ASSERT_LT(abs(time(NULL) - lib_.cached_enabled_time_), 5);
+  // Sleep one second (or cheat to be faster).
+  --lib_.cached_enabled_time_;
+  ASSERT_EQ(to_value, lib_.AreMetricsEnabled());
+}
+
+TEST_F(MetricsLibraryTest, AreMetricsEnabledCaching) {
+  VerifyEnabledCacheHit(false);
+  VerifyEnabledCacheHit(true);
+  VerifyEnabledCacheEviction(false);
+  VerifyEnabledCacheEviction(true);
+}
+
 TEST_F(MetricsLibraryTest, FormatChromeMessage) {
   char buf[7];
   const int kLen = 6;
@@ -55,6 +112,12 @@
   EXPECT_EQ(0, memcmp(exp, buf, kLen));
 }
 
+TEST_F(MetricsLibraryTest, SendEnumToUMANotEnabled) {
+  SetMetricsEnabled(false);
+  EXPECT_TRUE(lib_.SendEnumToUMA("Test.EnumMetric", 1, 3));
+  EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile));
+}
+
 TEST_F(MetricsLibraryTest, SendMessageToChrome) {
   EXPECT_TRUE(lib_.SendMessageToChrome(4, "test"));
   EXPECT_TRUE(lib_.SendMessageToChrome(7, "content"));
@@ -84,6 +147,12 @@
   EXPECT_EQ(0, memcmp(exp, buf, kLen));
 }
 
+TEST_F(MetricsLibraryTest, SendToUMANotEnabled) {
+  SetMetricsEnabled(false);
+  EXPECT_TRUE(lib_.SendToUMA("Test.Metric", 2, 1, 100, 50));
+  EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile));
+}
+
 class CMetricsLibraryTest : public testing::Test {
  protected:
   virtual void SetUp() {
@@ -93,6 +162,9 @@
     CMetricsLibraryInit(lib_);
     EXPECT_TRUE(NULL != ml.uma_events_file_);
     ml.uma_events_file_ = kTestUMAEventsFile.value().c_str();
+    SetMetricsEnabled(true);
+    reinterpret_cast<MetricsLibrary*>(lib_)->cached_enabled_time_ = 0;
+    reinterpret_cast<MetricsLibrary*>(lib_)->consent_file_ = kTestConsent;
   }
 
   virtual void TearDown() {
@@ -103,6 +175,15 @@
   CMetricsLibrary lib_;
 };
 
+TEST_F(CMetricsLibraryTest, AreMetricsEnabledFalse) {
+  SetMetricsEnabled(false);
+  EXPECT_FALSE(CMetricsLibraryAreMetricsEnabled(lib_));
+}
+
+TEST_F(CMetricsLibraryTest, AreMetricsEnabledTrue) {
+  EXPECT_TRUE(CMetricsLibraryAreMetricsEnabled(lib_));
+}
+
 TEST_F(CMetricsLibraryTest, SendEnumToUMA) {
   char buf[100];
   const int kLen = 40;