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"
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"
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
42 // [interval = kInitialRefreshIntervalMS * (2 ^
43 // (kRefreshIntervalRequestsCountMultiplier * requests_count))]
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)
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
{
83 explicit TimeZoneResolverImpl(const TimeZoneResolver
* resolver
);
85 ~TimeZoneResolverImpl() override
;
87 // This is called once after the object is created.
90 // PowerObserver implementation.
91 void OnResume() override
;
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();
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.
133 // This is not NULL when update is in progress.
134 scoped_ptr
<TZRequest
> request_
;
136 base::WeakPtrFactory
<TimeZoneResolver::TimeZoneResolverImpl
>
139 DISALLOW_COPY_AND_ASSIGN(TimeZoneResolverImpl
);
144 // This class implements a single timezone refresh attempt.
147 explicit TZRequest(TimeZoneResolver::TimeZoneResolverImpl
* resolver
)
148 : resolver_(resolver
), weak_ptr_factory_(this) {}
152 // Starts request after specified delay.
155 // Called from SimpleGeolocationProvider when location is resolved.
156 void OnLocationResolved(const Geoposition
& position
,
158 const base::TimeDelta elapsed
);
160 // TimeZoneRequest::TimeZoneResponseCallback implementation.
161 void OnTimezoneResolved(scoped_ptr
<TimeZoneResponseData
> timezone
,
164 base::WeakPtr
<TZRequest
> AsWeakPtr();
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
,
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())
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.";
213 resolver_
->timezone_provider()->RequestTimezone(
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
,
224 base::ScopedClosureRunner
on_request_finished(
225 base::Bind(&TimeZoneResolver::TimeZoneResolverImpl::RequestIsFinished
,
226 base::Unretained(resolver_
)));
229 VLOG(1) << "Refreshed local timezone={" << timezone
->ToStringForDebug()
232 if (timezone
->status
!= TimeZoneResponseData::OK
) {
233 VLOG(1) << "Refresh TimeZone: failed to resolve timezone.";
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()),
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
=
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();
282 power_monitor
->RemoveObserver(this);
285 void TimeZoneResolver::TimeZoneResolverImpl::Start() {
286 // Start() is usually called twice:
288 // - On user session start.
289 if (request_
|| refresh_timer_
.IsRunning())
295 // Returns delay to next timezone update request
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();
320 void TimeZoneResolver::TimeZoneResolverImpl::OnResume() {
322 // Refresh timezone immediately.
327 void TimeZoneResolver::TimeZoneResolverImpl::ScheduleRequest() {
331 // base::OneShotTimer
332 base::TimeDelta interval
= CalculateNextInterval();
333 refresh_timer_
.Stop();
334 refresh_timer_
.Start(
336 base::Bind(&TimeZoneResolver::TimeZoneResolverImpl::CreateNewRequest
,
340 void TimeZoneResolver::TimeZoneResolverImpl::CreateNewRequest() {
344 refresh_timer_
.Stop();
346 request_
.reset(new TZRequest(this));
350 void TimeZoneResolver::TimeZoneResolverImpl::RecordAttempt() {
351 resolver_
->local_state()->SetInt64(kLastTimeZoneRefreshTime
,
352 base::Time::Now().ToInternalValue());
356 void TimeZoneResolver::TimeZoneResolverImpl::RequestIsFinished() {
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
,
377 const ApplyTimeZoneCallback
& apply_timezone
,
378 const DelayNetworkCallClosure
& delay_network_call
,
379 PrefService
* local_state
)
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() {
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();
406 int TimeZoneResolver::MaxRequestsCountForIntervalForTesting(
407 const double interval_seconds
) {
408 return MaxRequestsCountForInterval(interval_seconds
);
412 int TimeZoneResolver::IntervalForNextRequestForTesting(const int requests
) {
413 return IntervalForNextRequest(requests
);
417 void TimeZoneResolver::RegisterPrefs(PrefRegistrySimple
* registry
) {
418 registry
->RegisterInt64Pref(kLastTimeZoneRefreshTime
, 0);
421 } // namespace chromeos