| // Copyright 2014 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/network_time/network_time_tracker.h" |
| |
| #include <stdint.h> |
| #include <utility> |
| |
| #include "base/i18n/time_formatting.h" |
| #include "base/logging.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time/tick_clock.h" |
| #include "build/build_config.h" |
| #include "components/network_time/network_time_pref_names.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| |
| namespace network_time { |
| |
| namespace { |
| |
| // Number of time measurements performed in a given network time calculation. |
| const uint32_t kNumTimeMeasurements = 7; |
| |
| // Amount of divergence allowed between wall clock and tick clock. |
| const uint32_t kClockDivergenceSeconds = 60; |
| |
| // Maximum time lapse before deserialized data are considered stale. |
| const uint32_t kSerializedDataMaxAgeDays = 7; |
| |
| // Name of a pref that stores the wall clock time, via |ToJsTime|. |
| const char kPrefTime[] = "local"; |
| |
| // Name of a pref that stores the tick clock time, via |ToInternalValue|. |
| const char kPrefTicks[] = "ticks"; |
| |
| // Name of a pref that stores the time uncertainty, via |ToInternalValue|. |
| const char kPrefUncertainty[] = "uncertainty"; |
| |
| // Name of a pref that stores the network time via |ToJsTime|. |
| const char kPrefNetworkTime[] = "network"; |
| |
| } // namespace |
| |
| // static |
| void NetworkTimeTracker::RegisterPrefs(PrefRegistrySimple* registry) { |
| registry->RegisterDictionaryPref(prefs::kNetworkTimeMapping, |
| new base::DictionaryValue()); |
| } |
| |
| NetworkTimeTracker::NetworkTimeTracker(scoped_ptr<base::Clock> clock, |
| scoped_ptr<base::TickClock> tick_clock, |
| PrefService* pref_service) |
| : clock_(std::move(clock)), |
| tick_clock_(std::move(tick_clock)), |
| pref_service_(pref_service) { |
| const base::DictionaryValue* time_mapping = |
| pref_service_->GetDictionary(prefs::kNetworkTimeMapping); |
| double time_js = 0; |
| double ticks_js = 0; |
| double network_time_js = 0; |
| double uncertainty_js = 0; |
| if (time_mapping->GetDouble(kPrefTime, &time_js) && |
| time_mapping->GetDouble(kPrefTicks, &ticks_js) && |
| time_mapping->GetDouble(kPrefUncertainty, &uncertainty_js) && |
| time_mapping->GetDouble(kPrefNetworkTime, &network_time_js)) { |
| time_at_last_measurement_ = base::Time::FromJsTime(time_js); |
| ticks_at_last_measurement_ = base::TimeTicks::FromInternalValue( |
| static_cast<int64_t>(ticks_js)); |
| network_time_uncertainty_ = base::TimeDelta::FromInternalValue( |
| static_cast<int64_t>(uncertainty_js)); |
| network_time_at_last_measurement_ = base::Time::FromJsTime(network_time_js); |
| } |
| base::Time now = clock_->Now(); |
| if (ticks_at_last_measurement_ > tick_clock_->NowTicks() || |
| time_at_last_measurement_ > now || |
| now - time_at_last_measurement_ > |
| base::TimeDelta::FromDays(kSerializedDataMaxAgeDays)) { |
| // Drop saved mapping if either clock has run backward, or the data are too |
| // old. |
| pref_service_->ClearPref(prefs::kNetworkTimeMapping); |
| network_time_at_last_measurement_ = base::Time(); // Reset. |
| } |
| } |
| |
| NetworkTimeTracker::~NetworkTimeTracker() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| } |
| |
| void NetworkTimeTracker::UpdateNetworkTime(base::Time network_time, |
| base::TimeDelta resolution, |
| base::TimeDelta latency, |
| base::TimeTicks post_time) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DVLOG(1) << "Network time updating to " |
| << base::UTF16ToUTF8( |
| base::TimeFormatFriendlyDateAndTime(network_time)); |
| // Update network time on every request to limit dependency on ticks lag. |
| // TODO(mad): Find a heuristic to avoid augmenting the |
| // network_time_uncertainty_ too much by a particularly long latency. |
| // Maybe only update when the the new time either improves in accuracy or |
| // drifts too far from |network_time_at_last_measurement_|. |
| network_time_at_last_measurement_ = network_time; |
| |
| // Calculate the delay since the network time was received. |
| base::TimeTicks now_ticks = tick_clock_->NowTicks(); |
| base::TimeDelta task_delay = now_ticks - post_time; |
| DCHECK_GE(task_delay.InMilliseconds(), 0); |
| DCHECK_GE(latency.InMilliseconds(), 0); |
| // Estimate that the time was set midway through the latency time. |
| base::TimeDelta offset = task_delay + latency / 2; |
| ticks_at_last_measurement_ = now_ticks - offset; |
| time_at_last_measurement_ = clock_->Now() - offset; |
| |
| // Can't assume a better time than the resolution of the given time and the |
| // ticks measurements involved, each with their own uncertainty. 1 & 2 are |
| // the ones used to compute the latency, 3 is the Now() from when this task |
| // was posted, 4 and 5 are the Now() and NowTicks() above, and 6 and 7 will be |
| // the Now() and NowTicks() in GetNetworkTime(). |
| network_time_uncertainty_ = |
| resolution + latency + kNumTimeMeasurements * |
| base::TimeDelta::FromMilliseconds(kTicksResolutionMs); |
| |
| base::DictionaryValue time_mapping; |
| time_mapping.SetDouble(kPrefTime, time_at_last_measurement_.ToJsTime()); |
| time_mapping.SetDouble(kPrefTicks, static_cast<double>( |
| ticks_at_last_measurement_.ToInternalValue())); |
| time_mapping.SetDouble(kPrefUncertainty, static_cast<double>( |
| network_time_uncertainty_.ToInternalValue())); |
| time_mapping.SetDouble(kPrefNetworkTime, |
| network_time_at_last_measurement_.ToJsTime()); |
| pref_service_->Set(prefs::kNetworkTimeMapping, time_mapping); |
| } |
| |
| bool NetworkTimeTracker::GetNetworkTime(base::Time* network_time, |
| base::TimeDelta* uncertainty) const { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(network_time); |
| if (network_time_at_last_measurement_.is_null()) { |
| return false; |
| } |
| DCHECK(!ticks_at_last_measurement_.is_null()); |
| DCHECK(!time_at_last_measurement_.is_null()); |
| base::TimeDelta tick_delta = |
| tick_clock_->NowTicks() - ticks_at_last_measurement_; |
| base::TimeDelta time_delta = clock_->Now() - time_at_last_measurement_; |
| if (time_delta.InMilliseconds() < 0) { // Has wall clock run backward? |
| DVLOG(1) << "Discarding network time due to wall clock running backward"; |
| network_time_at_last_measurement_ = base::Time(); |
| return false; |
| } |
| // Now we know that both |tick_delta| and |time_delta| are positive. |
| base::TimeDelta divergence = (tick_delta - time_delta).magnitude(); |
| if (divergence > base::TimeDelta::FromSeconds(kClockDivergenceSeconds)) { |
| // Most likely either the machine has suspended, or the wall clock has been |
| // reset. |
| DVLOG(1) << "Discarding network time due to clocks diverging"; |
| network_time_at_last_measurement_ = base::Time(); |
| return false; |
| } |
| *network_time = network_time_at_last_measurement_ + tick_delta; |
| if (uncertainty) { |
| *uncertainty = network_time_uncertainty_ + divergence; |
| } |
| return true; |
| } |
| |
| } // namespace network_time |