Only send _NET_ACTIVE_WINDOW hint if the chromium window is not already active.
[chromium-blink-merge.git] / chromeos / timezone / timezone_resolver.cc
blobbe73382d7bff599b9076c23865b1ae94bd9f775c
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.
5 #include "chromeos/timezone/timezone_resolver.h"
7 #include <math.h>
9 #include <algorithm>
11 #include "base/bind.h"
12 #include "base/callback_helpers.h"
13 #include "base/logging.h"
14 #include "base/power_monitor/power_monitor.h"
15 #include "base/power_monitor/power_observer.h"
16 #include "base/prefs/pref_registry_simple.h"
17 #include "base/prefs/pref_service.h"
18 #include "base/rand_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/time/time.h"
21 #include "chromeos/geolocation/geoposition.h"
22 #include "chromeos/geolocation/simple_geolocation_provider.h"
23 #include "chromeos/timezone/timezone_provider.h"
25 namespace chromeos {
27 namespace {
29 class TZRequest;
31 // Total timezone resolving process timeout.
32 const unsigned int kRefreshTimeZoneTimeoutSeconds = 60;
34 // Initial delay (for the first request).
35 const double kInitialRefreshIntervalSec = 3.0;
37 // Timezone refresh happens at least once each this interval.
38 const double kMaximumRefreshIntervalSec = 6.0 * 3600; // 6 hours
40 // Delay between refresh attempts depends on current number of requests and
41 // this constant.
42 // [interval = kInitialRefreshIntervalMS * (2 ^
43 // (kRefreshIntervalRequestsCountMultiplier * requests_count))]
44 // in seconds.
45 // request_number interval (seconds)
46 // 1 3 (initial, requests_count = 0)
47 // 2 24 (requests_count = 1)
48 // 3 1536 (requests_count = 2)
49 // 4 12288 (requests_count = 3)
50 // 5+ 21600 (maximum)
51 const unsigned int kRefreshIntervalRequestsCountMultiplier = 3;
53 // We should limit request rate on browser start to prevent server overload
54 // on permanent browser crash.
55 // If too little time has passed since previous request, initialize
56 // |requests_count_| with |kRefreshTimeZoneInitialRequestCountOnRateLimit|.
57 const double kRefreshTimeZoneMinimumDelayOnRestartSec =
58 10 * 60.0; // 10 minutes
59 const unsigned int kRefreshTimeZoneInitialRequestCountOnRateLimit = 2;
61 int MaxRequestsCountForInterval(const double interval_seconds) {
62 return log2(interval_seconds / kInitialRefreshIntervalSec) /
63 kRefreshIntervalRequestsCountMultiplier;
66 int IntervalForNextRequest(const int requests) {
67 const base::TimeDelta initial_interval =
68 base::TimeDelta::FromSecondsD(kInitialRefreshIntervalSec);
69 return static_cast<int>(initial_interval.InSecondsF() *
70 (2 << (static_cast<unsigned>(requests) *
71 kRefreshIntervalRequestsCountMultiplier)));
74 } // anonymous namespace
76 const char TimeZoneResolver::kLastTimeZoneRefreshTime[] =
77 "timezone_resolver.last_update_time";
79 // This class periodically refreshes location and timezone.
80 // It should be destroyed to stop refresh.
81 class TimeZoneResolver::TimeZoneResolverImpl : public base::PowerObserver {
82 public:
83 explicit TimeZoneResolverImpl(const TimeZoneResolver* resolver);
85 ~TimeZoneResolverImpl() override;
87 // This is called once after the object is created.
88 void Start();
90 // PowerObserver implementation.
91 void OnResume() override;
93 // (Re)Starts timer.
94 void ScheduleRequest();
96 // Creates new TZRequest.
97 void CreateNewRequest();
99 // Called by TZRequest.
100 SimpleGeolocationProvider* geolocation_provider() {
101 return &geolocation_provider_;
103 TimeZoneProvider* timezone_provider() { return &timezone_provider_; }
105 // Update requests count and last request time.
106 void RecordAttempt();
108 // This is called by TZRequest. Destroys active request and starts a new one.
109 void RequestIsFinished();
111 void ApplyTimeZone(const TimeZoneResponseData* timezone);
113 TimeZoneResolver::DelayNetworkCallClosure delay_network_call() const {
114 return resolver_->delay_network_call();
117 base::WeakPtr<TimeZoneResolver::TimeZoneResolverImpl> AsWeakPtr();
119 private:
120 const TimeZoneResolver* resolver_;
122 // Returns delay to next timezone update request
123 base::TimeDelta CalculateNextInterval();
125 SimpleGeolocationProvider geolocation_provider_;
126 TimeZoneProvider timezone_provider_;
128 base::OneShotTimer<TimeZoneResolver::TimeZoneResolverImpl> refresh_timer_;
130 // Total number of request attempts.
131 int requests_count_;
133 // This is not NULL when update is in progress.
134 scoped_ptr<TZRequest> request_;
136 base::WeakPtrFactory<TimeZoneResolver::TimeZoneResolverImpl>
137 weak_ptr_factory_;
139 DISALLOW_COPY_AND_ASSIGN(TimeZoneResolverImpl);
142 namespace {
144 // This class implements a single timezone refresh attempt.
145 class TZRequest {
146 public:
147 explicit TZRequest(TimeZoneResolver::TimeZoneResolverImpl* resolver)
148 : resolver_(resolver), weak_ptr_factory_(this) {}
150 ~TZRequest();
152 // Starts request after specified delay.
153 void Start();
155 // Called from SimpleGeolocationProvider when location is resolved.
156 void OnLocationResolved(const Geoposition& position,
157 bool server_error,
158 const base::TimeDelta elapsed);
160 // TimeZoneRequest::TimeZoneResponseCallback implementation.
161 void OnTimezoneResolved(scoped_ptr<TimeZoneResponseData> timezone,
162 bool server_error);
164 base::WeakPtr<TZRequest> AsWeakPtr();
166 private:
167 // This is called by network detector when network is available.
168 void StartRequestOnNetworkAvailable();
170 TimeZoneResolver::TimeZoneResolverImpl* const resolver_;
172 base::WeakPtrFactory<TZRequest> weak_ptr_factory_;
174 DISALLOW_COPY_AND_ASSIGN(TZRequest);
177 TZRequest::~TZRequest() {
180 void TZRequest::StartRequestOnNetworkAvailable() {
181 resolver_->RecordAttempt();
182 resolver_->geolocation_provider()->RequestGeolocation(
183 base::TimeDelta::FromSeconds(kRefreshTimeZoneTimeoutSeconds),
184 base::Bind(&TZRequest::OnLocationResolved, AsWeakPtr()));
187 void TZRequest::Start() {
188 // call to chromeos::DelayNetworkCall
189 resolver_->delay_network_call().Run(
190 base::Bind(&TZRequest::StartRequestOnNetworkAvailable, AsWeakPtr()));
193 void TZRequest::OnLocationResolved(const Geoposition& position,
194 bool server_error,
195 const base::TimeDelta elapsed) {
196 base::ScopedClosureRunner on_request_finished(
197 base::Bind(&TimeZoneResolver::TimeZoneResolverImpl::RequestIsFinished,
198 base::Unretained(resolver_)));
200 // Ignore invalid position.
201 if (!position.Valid())
202 return;
204 const base::TimeDelta timeout =
205 base::TimeDelta::FromSeconds(kRefreshTimeZoneTimeoutSeconds);
207 if (elapsed >= timeout) {
208 VLOG(1) << "Refresh TimeZone: got location after timeout ("
209 << elapsed.InSecondsF() << " seconds elapsed). Ignored.";
210 return;
213 resolver_->timezone_provider()->RequestTimezone(
214 position,
215 timeout - elapsed,
216 base::Bind(&TZRequest::OnTimezoneResolved, AsWeakPtr()));
218 // Prevent |on_request_finished| from firing here.
219 base::Closure unused = on_request_finished.Release();
222 void TZRequest::OnTimezoneResolved(scoped_ptr<TimeZoneResponseData> timezone,
223 bool server_error) {
224 base::ScopedClosureRunner on_request_finished(
225 base::Bind(&TimeZoneResolver::TimeZoneResolverImpl::RequestIsFinished,
226 base::Unretained(resolver_)));
228 DCHECK(timezone);
229 VLOG(1) << "Refreshed local timezone={" << timezone->ToStringForDebug()
230 << "}.";
232 if (timezone->status != TimeZoneResponseData::OK) {
233 VLOG(1) << "Refresh TimeZone: failed to resolve timezone.";
234 return;
237 resolver_->ApplyTimeZone(timezone.get());
240 base::WeakPtr<TZRequest> TZRequest::AsWeakPtr() {
241 return weak_ptr_factory_.GetWeakPtr();
244 } // anonymous namespace
246 // ------------------------------------------------------------------------
247 // TimeZoneResolver::TimeZoneResolverImpl implementation.
249 TimeZoneResolver::TimeZoneResolverImpl::TimeZoneResolverImpl(
250 const TimeZoneResolver* resolver)
251 : resolver_(resolver),
252 geolocation_provider_(
253 resolver->context().get(),
254 SimpleGeolocationProvider::DefaultGeolocationProviderURL()),
255 timezone_provider_(resolver->context().get(),
256 DefaultTimezoneProviderURL()),
257 requests_count_(0),
258 weak_ptr_factory_(this) {
259 DCHECK(!resolver_->apply_timezone().is_null());
260 DCHECK(!resolver_->delay_network_call().is_null());
262 base::PowerMonitor* power_monitor = base::PowerMonitor::Get();
263 power_monitor->AddObserver(this);
265 const int64 last_refresh_at_raw =
266 resolver_->local_state()->GetInt64(kLastTimeZoneRefreshTime);
267 const base::Time last_refresh_at =
268 base::Time::FromInternalValue(last_refresh_at_raw);
269 const base::Time next_refresh_not_before =
270 last_refresh_at +
271 base::TimeDelta::FromSecondsD(kRefreshTimeZoneMinimumDelayOnRestartSec);
272 if (next_refresh_not_before > base::Time::Now()) {
273 requests_count_ = kRefreshTimeZoneInitialRequestCountOnRateLimit;
274 VLOG(1) << "TimeZoneResolverImpl(): initialize requests_count_="
275 << requests_count_ << " because of rate limit.";
279 TimeZoneResolver::TimeZoneResolverImpl::~TimeZoneResolverImpl() {
280 base::PowerMonitor* power_monitor = base::PowerMonitor::Get();
281 if (power_monitor)
282 power_monitor->RemoveObserver(this);
285 void TimeZoneResolver::TimeZoneResolverImpl::Start() {
286 // Start() is usually called twice:
287 // - On device boot.
288 // - On user session start.
289 if (request_ || refresh_timer_.IsRunning())
290 return;
292 ScheduleRequest();
295 // Returns delay to next timezone update request
296 base::TimeDelta
297 TimeZoneResolver::TimeZoneResolverImpl::CalculateNextInterval() {
298 // This is initial request, which should be served immediately.
299 if (requests_count_ == 0) {
300 return base::TimeDelta::FromSecondsD(kInitialRefreshIntervalSec);
303 // See comment to kRefreshIntervalRequestsCountMultiplier.
304 if (requests_count_ >=
305 MaxRequestsCountForInterval(kMaximumRefreshIntervalSec)) {
306 return base::TimeDelta::FromSecondsD(kMaximumRefreshIntervalSec);
309 const int base_interval = IntervalForNextRequest(requests_count_);
310 DCHECK_LE(base_interval, kMaximumRefreshIntervalSec);
312 // Add jitter to level request rate.
313 const base::TimeDelta interval(
314 base::TimeDelta::FromSecondsD(base::RandDouble() * 2 * base_interval));
315 VLOG(1) << "TimeZoneResolverImpl::CalculateNextInterval(): interval="
316 << interval.InSecondsF();
317 return interval;
320 void TimeZoneResolver::TimeZoneResolverImpl::OnResume() {
321 requests_count_ = 0;
322 // Refresh timezone immediately.
323 request_.reset();
324 ScheduleRequest();
327 void TimeZoneResolver::TimeZoneResolverImpl::ScheduleRequest() {
328 if (request_)
329 return;
331 // base::OneShotTimer
332 base::TimeDelta interval = CalculateNextInterval();
333 refresh_timer_.Stop();
334 refresh_timer_.Start(
335 FROM_HERE, interval,
336 base::Bind(&TimeZoneResolver::TimeZoneResolverImpl::CreateNewRequest,
337 AsWeakPtr()));
340 void TimeZoneResolver::TimeZoneResolverImpl::CreateNewRequest() {
341 if (request_)
342 return;
344 refresh_timer_.Stop();
346 request_.reset(new TZRequest(this));
347 request_->Start();
350 void TimeZoneResolver::TimeZoneResolverImpl::RecordAttempt() {
351 resolver_->local_state()->SetInt64(kLastTimeZoneRefreshTime,
352 base::Time::Now().ToInternalValue());
353 ++requests_count_;
356 void TimeZoneResolver::TimeZoneResolverImpl::RequestIsFinished() {
357 request_.reset();
358 ScheduleRequest();
361 void TimeZoneResolver::TimeZoneResolverImpl::ApplyTimeZone(
362 const TimeZoneResponseData* timezone) {
363 resolver_->apply_timezone().Run(timezone);
366 base::WeakPtr<TimeZoneResolver::TimeZoneResolverImpl>
367 TimeZoneResolver::TimeZoneResolverImpl::AsWeakPtr() {
368 return weak_ptr_factory_.GetWeakPtr();
371 // ------------------------------------------------------------------------
372 // TimeZoneResolver implementation
374 TimeZoneResolver::TimeZoneResolver(
375 scoped_refptr<net::URLRequestContextGetter> context,
376 const GURL& url,
377 const ApplyTimeZoneCallback& apply_timezone,
378 const DelayNetworkCallClosure& delay_network_call,
379 PrefService* local_state)
380 : context_(context),
381 url_(url),
382 apply_timezone_(apply_timezone),
383 delay_network_call_(delay_network_call),
384 local_state_(local_state) {
385 DCHECK(!apply_timezone.is_null());
388 TimeZoneResolver::~TimeZoneResolver() {
389 Stop();
392 void TimeZoneResolver::Start() {
393 DCHECK(thread_checker_.CalledOnValidThread());
394 if (!implementation_) {
395 implementation_.reset(new TimeZoneResolverImpl(this));
396 implementation_->Start();
400 void TimeZoneResolver::Stop() {
401 DCHECK(thread_checker_.CalledOnValidThread());
402 implementation_.reset();
405 // static
406 int TimeZoneResolver::MaxRequestsCountForIntervalForTesting(
407 const double interval_seconds) {
408 return MaxRequestsCountForInterval(interval_seconds);
411 // static
412 int TimeZoneResolver::IntervalForNextRequestForTesting(const int requests) {
413 return IntervalForNextRequest(requests);
416 // static
417 void TimeZoneResolver::RegisterPrefs(PrefRegistrySimple* registry) {
418 registry->RegisterInt64Pref(kLastTimeZoneRefreshTime, 0);
421 } // namespace chromeos