1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/metrics/daily_event.h"
6
7#include "base/i18n/time_formatting.h"
8#include "base/metrics/histogram.h"
9#include "base/prefs/pref_registry_simple.h"
10#include "base/prefs/pref_service.h"
11
12namespace metrics {
13
14namespace {
15
16enum IntervalType {
17  FIRST_RUN,
18  DAY_ELAPSED,
19  CLOCK_CHANGED,
20  NUM_INTERVAL_TYPES
21};
22
23void RecordIntervalTypeHistogram(const std::string& histogram_name,
24                                 IntervalType type) {
25  base::Histogram::FactoryGet(
26      histogram_name,
27      1,
28      NUM_INTERVAL_TYPES,
29      NUM_INTERVAL_TYPES + 1,
30      base::HistogramBase::kUmaTargetedHistogramFlag)->Add(type);
31}
32
33}  // namespace
34
35DailyEvent::Observer::Observer() {
36}
37
38DailyEvent::Observer::~Observer() {
39}
40
41DailyEvent::DailyEvent(PrefService* pref_service,
42                       const char* pref_name,
43                       const std::string& histogram_name)
44    : pref_service_(pref_service),
45      pref_name_(pref_name),
46      histogram_name_(histogram_name) {
47}
48
49DailyEvent::~DailyEvent() {
50}
51
52// static
53void DailyEvent::RegisterPref(PrefRegistrySimple* registry,
54                              const char* pref_name) {
55  registry->RegisterInt64Pref(pref_name, base::Time().ToInternalValue());
56}
57
58void DailyEvent::AddObserver(scoped_ptr<DailyEvent::Observer> observer) {
59  DVLOG(2) << "DailyEvent observer added.";
60  DCHECK(last_fired_.is_null());
61  observers_.push_back(observer.release());
62}
63
64void DailyEvent::CheckInterval() {
65  base::Time now = base::Time::Now();
66  if (last_fired_.is_null()) {
67    // The first time we call CheckInterval, we read the time stored in prefs.
68    last_fired_ = base::Time::FromInternalValue(
69        pref_service_->GetInt64(pref_name_));
70    DVLOG(1) << "DailyEvent time loaded: "
71             << base::TimeFormatShortDateAndTime(last_fired_);
72    if (last_fired_.is_null()) {
73      DVLOG(1) << "DailyEvent first run.";
74      RecordIntervalTypeHistogram(pref_name_, FIRST_RUN);
75      OnInterval(now);
76      return;
77    }
78  }
79  int days_elapsed = (now - last_fired_).InDays();
80  if (days_elapsed >= 1) {
81    DVLOG(1) << "DailyEvent day elapsed.";
82    RecordIntervalTypeHistogram(pref_name_, DAY_ELAPSED);
83    OnInterval(now);
84  } else if (days_elapsed <= -1) {
85    // The "last fired" time is more than a day in the future, so the clock
86    // must have been changed.
87    DVLOG(1) << "DailyEvent clock change detected.";
88    RecordIntervalTypeHistogram(pref_name_, CLOCK_CHANGED);
89    OnInterval(now);
90  }
91}
92
93void DailyEvent::OnInterval(base::Time now) {
94  DCHECK(!now.is_null());
95  last_fired_ = now;
96  pref_service_->SetInt64(pref_name_, last_fired_.ToInternalValue());
97
98  // Notify all observers
99  for (ScopedVector<DailyEvent::Observer>::iterator it = observers_.begin();
100       it != observers_.end();
101       ++it) {
102    (*it)->OnDailyEvent();
103  }
104}
105
106}  // namespace metrics
107