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 "components/gcm_driver/gcm_channel_status_syncer.h"
8 #include "base/command_line.h"
9 #include "base/location.h"
10 #include "base/logging.h"
11 #include "base/prefs/pref_registry_simple.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/rand_util.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "components/gcm_driver/gcm_channel_status_request.h"
18 #include "components/gcm_driver/gcm_driver.h"
19 #include "components/pref_registry/pref_registry_syncable.h"
25 // A small delay to avoid sending request at browser startup time for first-time
27 const int kFirstTimeDelaySeconds
= 1 * 60; // 1 minute.
29 // The fuzzing variation added to the polling delay.
30 const int kGCMChannelRequestTimeJitterSeconds
= 15 * 60; // 15 minues.
32 // The minimum poll interval that can be overridden to.
33 const int kMinCustomPollIntervalMinutes
= 2;
35 // Custom poll interval could not be used more than the limit below.
36 const int kMaxNumberToUseCustomPollInterval
= 10;
42 // The GCM channel's enabled state.
43 const char kGCMChannelStatus
[] = "gcm.channel_status";
45 // The GCM channel's polling interval (in seconds).
46 const char kGCMChannelPollIntervalSeconds
[] = "gcm.poll_interval";
48 // Last time when checking with the GCM channel status server is done.
49 const char kGCMChannelLastCheckTime
[] = "gcm.check_time";
55 // Override the default poll interval for testing purpose.
56 const char kCustomPollIntervalMinutes
[] = "gcm-channel-poll-interval";
58 } // namepsace switches
61 void GCMChannelStatusSyncer::RegisterPrefs(PrefRegistrySimple
* registry
) {
62 registry
->RegisterBooleanPref(prefs::kGCMChannelStatus
, true);
63 registry
->RegisterIntegerPref(
64 prefs::kGCMChannelPollIntervalSeconds
,
65 GCMChannelStatusRequest::default_poll_interval_seconds());
66 registry
->RegisterInt64Pref(prefs::kGCMChannelLastCheckTime
, 0);
70 void GCMChannelStatusSyncer::RegisterProfilePrefs(
71 user_prefs::PrefRegistrySyncable
* registry
) {
72 registry
->RegisterBooleanPref(prefs::kGCMChannelStatus
, true);
73 registry
->RegisterIntegerPref(
74 prefs::kGCMChannelPollIntervalSeconds
,
75 GCMChannelStatusRequest::default_poll_interval_seconds());
76 registry
->RegisterInt64Pref(prefs::kGCMChannelLastCheckTime
, 0);
80 int GCMChannelStatusSyncer::first_time_delay_seconds() {
81 return kFirstTimeDelaySeconds
;
84 GCMChannelStatusSyncer::GCMChannelStatusSyncer(
87 const std::string
& channel_status_request_url
,
88 const std::string
& user_agent
,
89 const scoped_refptr
<net::URLRequestContextGetter
>& request_context
)
92 channel_status_request_url_(channel_status_request_url
),
93 user_agent_(user_agent
),
94 request_context_(request_context
),
97 poll_interval_seconds_(
98 GCMChannelStatusRequest::default_poll_interval_seconds()),
99 custom_poll_interval_use_count_(0),
100 delay_removed_for_testing_(false),
101 weak_ptr_factory_(this) {
102 gcm_enabled_
= prefs_
->GetBoolean(prefs::kGCMChannelStatus
);
103 poll_interval_seconds_
= prefs_
->GetInteger(
104 prefs::kGCMChannelPollIntervalSeconds
);
105 if (poll_interval_seconds_
<
106 GCMChannelStatusRequest::min_poll_interval_seconds()) {
107 poll_interval_seconds_
=
108 GCMChannelStatusRequest::min_poll_interval_seconds();
110 const base::CommandLine
& command_line
=
111 *base::CommandLine::ForCurrentProcess();
112 if (command_line
.HasSwitch(switches::kCustomPollIntervalMinutes
)) {
113 std::string
value(command_line
.GetSwitchValueASCII(
114 switches::kCustomPollIntervalMinutes
));
116 if (base::StringToInt(value
, &minutes
)) {
117 DCHECK_GE(minutes
, kMinCustomPollIntervalMinutes
);
118 if (minutes
>= kMinCustomPollIntervalMinutes
) {
119 poll_interval_seconds_
= minutes
* 60;
120 custom_poll_interval_use_count_
= kMaxNumberToUseCustomPollInterval
;
124 last_check_time_
= base::Time::FromInternalValue(
125 prefs_
->GetInt64(prefs::kGCMChannelLastCheckTime
));
128 GCMChannelStatusSyncer::~GCMChannelStatusSyncer() {
131 void GCMChannelStatusSyncer::EnsureStarted() {
132 // Bail out if the request is already scheduled or started.
140 void GCMChannelStatusSyncer::Stop() {
143 weak_ptr_factory_
.InvalidateWeakPtrs();
146 void GCMChannelStatusSyncer::OnRequestCompleted(bool update_received
,
148 int poll_interval_seconds
) {
152 // Persist the current time as the last request complete time.
153 last_check_time_
= base::Time::Now();
154 prefs_
->SetInt64(prefs::kGCMChannelLastCheckTime
,
155 last_check_time_
.ToInternalValue());
157 if (update_received
) {
158 if (gcm_enabled_
!= enabled
) {
159 gcm_enabled_
= enabled
;
160 prefs_
->SetBoolean(prefs::kGCMChannelStatus
, enabled
);
167 // Skip updating poll interval if the custom one is still in effect.
168 if (!custom_poll_interval_use_count_
) {
169 DCHECK_GE(poll_interval_seconds
,
170 GCMChannelStatusRequest::min_poll_interval_seconds());
171 if (poll_interval_seconds_
!= poll_interval_seconds
) {
172 poll_interval_seconds_
= poll_interval_seconds
;
173 prefs_
->SetInteger(prefs::kGCMChannelPollIntervalSeconds
,
174 poll_interval_seconds_
);
179 // Do not schedule next request if syncer is stopped.
184 void GCMChannelStatusSyncer::ScheduleRequest() {
185 current_request_delay_interval_
= GetRequestDelayInterval();
186 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
187 FROM_HERE
, base::Bind(&GCMChannelStatusSyncer::StartRequest
,
188 weak_ptr_factory_
.GetWeakPtr()),
189 current_request_delay_interval_
);
191 if (custom_poll_interval_use_count_
)
192 custom_poll_interval_use_count_
--;
195 void GCMChannelStatusSyncer::StartRequest() {
198 request_
.reset(new GCMChannelStatusRequest(
200 channel_status_request_url_
,
202 base::Bind(&GCMChannelStatusSyncer::OnRequestCompleted
,
203 weak_ptr_factory_
.GetWeakPtr())));
207 base::TimeDelta
GCMChannelStatusSyncer::GetRequestDelayInterval() const {
208 // No delay during testing.
209 if (delay_removed_for_testing_
)
210 return base::TimeDelta();
212 // Make sure that checking with server occurs at polling interval, regardless
213 // whether the browser restarts.
214 int64 delay_seconds
= poll_interval_seconds_
-
215 (base::Time::Now() - last_check_time_
).InSeconds();
216 if (delay_seconds
< 0)
219 if (last_check_time_
.is_null()) {
220 // For the first-time request, add a small delay to avoid sending request at
221 // browser startup time.
222 DCHECK(!delay_seconds
);
223 delay_seconds
= kFirstTimeDelaySeconds
;
225 // Otherwise, add a fuzzing variation to the delay.
226 // The fuzzing variation is off when the custom interval is used.
227 if (!custom_poll_interval_use_count_
)
228 delay_seconds
+= base::RandInt(0, kGCMChannelRequestTimeJitterSeconds
);
231 return base::TimeDelta::FromSeconds(delay_seconds
);