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/network_time/network_time_tracker.h"
6
7#include "base/basictypes.h"
8#include "base/i18n/time_formatting.h"
9#include "base/logging.h"
10#include "base/prefs/pref_registry_simple.h"
11#include "base/prefs/pref_service.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/time/tick_clock.h"
14#include "components/network_time/network_time_pref_names.h"
15
16namespace network_time {
17
18namespace {
19
20// Clock resolution is platform dependent.
21#if defined(OS_WIN)
22const int64 kTicksResolutionMs = base::Time::kMinLowResolutionThresholdMs;
23#else
24const int64 kTicksResolutionMs = 1;  // Assume 1ms for non-windows platforms.
25#endif
26
27// Number of time measurements performed in a given network time calculation.
28const int kNumTimeMeasurements = 5;
29
30}  // namespace
31
32// static
33void NetworkTimeTracker::RegisterPrefs(PrefRegistrySimple* registry) {
34  registry->RegisterDictionaryPref(prefs::kNetworkTimeMapping,
35                                   new base::DictionaryValue());
36}
37
38NetworkTimeTracker::NetworkTimeTracker(scoped_ptr<base::TickClock> tick_clock,
39                                       PrefService* pref_service)
40    : tick_clock_(tick_clock.Pass()),
41      pref_service_(pref_service),
42      received_network_time_(false) {
43  const base::DictionaryValue* time_mapping =
44      pref_service_->GetDictionary(prefs::kNetworkTimeMapping);
45  double local_time_js = 0;
46  double network_time_js = 0;
47  if (time_mapping->GetDouble("local", &local_time_js) &&
48      time_mapping->GetDouble("network", &network_time_js)) {
49    base::Time local_time_saved = base::Time::FromJsTime(local_time_js);
50    base::Time local_time_now = base::Time::Now();
51    if (local_time_saved > local_time_now ||
52        local_time_now - local_time_saved > base::TimeDelta::FromDays(7)) {
53      // Drop saved mapping if clock skew has changed or the data is too old.
54      pref_service_->ClearPref(prefs::kNetworkTimeMapping);
55    } else {
56      network_time_ = base::Time::FromJsTime(network_time_js) +
57          (local_time_now - local_time_saved);
58      network_time_ticks_ = base::TimeTicks::Now();
59    }
60  }
61}
62
63NetworkTimeTracker::~NetworkTimeTracker() {
64  DCHECK(thread_checker_.CalledOnValidThread());
65}
66
67void NetworkTimeTracker::UpdateNetworkTime(base::Time network_time,
68                                           base::TimeDelta resolution,
69                                           base::TimeDelta latency,
70                                           base::TimeTicks post_time) {
71  DCHECK(thread_checker_.CalledOnValidThread());
72  DVLOG(1) << "Network time updating to "
73           << base::UTF16ToUTF8(
74                  base::TimeFormatFriendlyDateAndTime(network_time));
75  // Update network time on every request to limit dependency on ticks lag.
76  // TODO(mad): Find a heuristic to avoid augmenting the
77  // network_time_uncertainty_ too much by a particularly long latency.
78  // Maybe only update when the the new time either improves in accuracy or
79  // drifts too far from |network_time_|.
80  network_time_ = network_time;
81
82  // Calculate the delay since the network time was received.
83  base::TimeTicks now = tick_clock_->NowTicks();
84  base::TimeDelta task_delay = now - post_time;
85  // Estimate that the time was set midway through the latency time.
86  network_time_ticks_ = now - task_delay - latency / 2;
87
88  // Can't assume a better time than the resolution of the given time
89  // and 5 ticks measurements are involved, each with their own uncertainty.
90  // 1 & 2 are the ones used to compute the latency, 3 is the Now() from when
91  // this task was posted, 4 is the Now() above and 5 will be the Now() used in
92  // GetNetworkTime().
93  network_time_uncertainty_ =
94      resolution + latency + kNumTimeMeasurements *
95      base::TimeDelta::FromMilliseconds(kTicksResolutionMs);
96
97  received_network_time_ = true;
98
99  base::Time network_time_now;
100  if (GetNetworkTime(base::TimeTicks::Now(), &network_time_now, NULL)) {
101    // Update time mapping if tracker received time update from server, i.e.
102    // mapping is accurate.
103    base::Time local_now = base::Time::Now();
104    base::DictionaryValue time_mapping;
105    time_mapping.SetDouble("local", local_now.ToJsTime());
106    time_mapping.SetDouble("network", network_time_now.ToJsTime());
107    pref_service_->Set(prefs::kNetworkTimeMapping, time_mapping);
108  }
109}
110
111bool NetworkTimeTracker::GetNetworkTime(base::TimeTicks time_ticks,
112                                        base::Time* network_time,
113                                        base::TimeDelta* uncertainty) const {
114  DCHECK(thread_checker_.CalledOnValidThread());
115  DCHECK(network_time);
116  if (network_time_.is_null())
117    return false;
118  DCHECK(!network_time_ticks_.is_null());
119  *network_time = network_time_ + (time_ticks - network_time_ticks_);
120  if (uncertainty)
121    *uncertainty = network_time_uncertainty_;
122  return true;
123}
124
125}  // namespace network_time
126