blob: 443c90d57b8207f63390f8860cf8784b30ba594c [file] [log] [blame]
jianli2dc910b02014-09-19 02:42:461// 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/gcm_driver/gcm_channel_status_syncer.h"
6
7#include "base/bind.h"
8#include "base/location.h"
9#include "base/logging.h"
10#include "base/message_loop/message_loop.h"
11#include "base/prefs/pref_registry_simple.h"
12#include "base/prefs/pref_service.h"
13#include "base/rand_util.h"
14#include "components/gcm_driver/gcm_channel_status_request.h"
15#include "components/gcm_driver/gcm_driver.h"
16#include "components/pref_registry/pref_registry_syncable.h"
17
18namespace gcm {
19
20namespace {
21
22// The GCM channel's enabled state.
23const char kGCMChannelStatus[] = "gcm.channel_status";
24
25// The GCM channel's polling interval (in seconds).
26const char kGCMChannelPollIntervalSeconds[] = "gcm.poll_interval";
27
28// Last time when checking with the GCM channel status server is done.
29const char kGCMChannelLastCheckTime[] = "gcm.check_time";
30
31// A small delay to avoid sending request at browser startup time for first-time
32// request.
33const int kFirstTimeDelaySeconds = 1 * 60; // 1 minute.
34
35// The fuzzing variation added to the polling delay.
36const int kGCMChannelRequestTimeJitterSeconds = 15 * 60; // 15 minues.
37
38} // namespace
39
40// static
41void GCMChannelStatusSyncer::RegisterPrefs(PrefRegistrySimple* registry) {
42 registry->RegisterBooleanPref(kGCMChannelStatus, true);
43 registry->RegisterIntegerPref(
44 kGCMChannelPollIntervalSeconds,
45 GCMChannelStatusRequest::default_poll_interval_seconds());
46 registry->RegisterInt64Pref(kGCMChannelLastCheckTime, 0);
47}
48
49// static
50void GCMChannelStatusSyncer::RegisterProfilePrefs(
51 user_prefs::PrefRegistrySyncable* registry) {
52 registry->RegisterBooleanPref(
53 kGCMChannelStatus,
54 true,
55 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
56 registry->RegisterIntegerPref(
57 kGCMChannelPollIntervalSeconds,
58 GCMChannelStatusRequest::default_poll_interval_seconds(),
59 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
60 registry->RegisterInt64Pref(
61 kGCMChannelLastCheckTime,
62 0,
63 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
64}
65
66// static
67int GCMChannelStatusSyncer::first_time_delay_seconds() {
68 return kFirstTimeDelaySeconds;
69}
70
71GCMChannelStatusSyncer::GCMChannelStatusSyncer(
72 GCMDriver* driver,
73 PrefService* prefs,
fgorski3f6b84cd2014-10-10 21:04:1874 const std::string& channel_status_request_url,
75 const std::string& user_agent,
jianli2dc910b02014-09-19 02:42:4676 const scoped_refptr<net::URLRequestContextGetter>& request_context)
77 : driver_(driver),
78 prefs_(prefs),
fgorski3f6b84cd2014-10-10 21:04:1879 channel_status_request_url_(channel_status_request_url),
80 user_agent_(user_agent),
jianli2dc910b02014-09-19 02:42:4681 request_context_(request_context),
82 gcm_enabled_(true),
83 poll_interval_seconds_(
84 GCMChannelStatusRequest::default_poll_interval_seconds()),
85 delay_removed_for_testing_(false),
86 weak_ptr_factory_(this) {
87 gcm_enabled_ = prefs_->GetBoolean(kGCMChannelStatus);
88 poll_interval_seconds_ = prefs_->GetInteger(kGCMChannelPollIntervalSeconds);
89 if (poll_interval_seconds_ <
90 GCMChannelStatusRequest::min_poll_interval_seconds()) {
91 poll_interval_seconds_ =
92 GCMChannelStatusRequest::min_poll_interval_seconds();
93 }
94 last_check_time_ = base::Time::FromInternalValue(
95 prefs_->GetInt64(kGCMChannelLastCheckTime));
96}
97
98GCMChannelStatusSyncer::~GCMChannelStatusSyncer() {
99}
100
101void GCMChannelStatusSyncer::EnsureStarted() {
102 // Bail out if the request is already scheduled or started.
103 if (weak_ptr_factory_.HasWeakPtrs() || request_)
104 return;
105
106 ScheduleRequest();
107}
108
109void GCMChannelStatusSyncer::Stop() {
110 request_.reset();
111 weak_ptr_factory_.InvalidateWeakPtrs();
112}
113
114void GCMChannelStatusSyncer::OnRequestCompleted(bool enabled,
115 int poll_interval_seconds) {
116 DCHECK(request_);
117 request_.reset();
118
119 // Persist the current time as the last request complete time.
120 last_check_time_ = base::Time::Now();
121 prefs_->SetInt64(kGCMChannelLastCheckTime,
122 last_check_time_.ToInternalValue());
123
124 if (gcm_enabled_ != enabled) {
125 gcm_enabled_ = enabled;
126 prefs_->SetBoolean(kGCMChannelStatus, enabled);
127 if (gcm_enabled_)
128 driver_->Enable();
129 else
130 driver_->Disable();
131 }
132
133 DCHECK_GE(poll_interval_seconds,
134 GCMChannelStatusRequest::min_poll_interval_seconds());
135 if (poll_interval_seconds_ != poll_interval_seconds) {
136 poll_interval_seconds_ = poll_interval_seconds;
137 prefs_->SetInteger(kGCMChannelPollIntervalSeconds, poll_interval_seconds_);
138 }
139
140 ScheduleRequest();
141}
142
143void GCMChannelStatusSyncer::ScheduleRequest() {
144 current_request_delay_interval_ = GetRequestDelayInterval();
145 base::MessageLoop::current()->PostDelayedTask(
146 FROM_HERE,
147 base::Bind(&GCMChannelStatusSyncer::StartRequest,
148 weak_ptr_factory_.GetWeakPtr()),
149 current_request_delay_interval_);
150}
151
152void GCMChannelStatusSyncer::StartRequest() {
153 DCHECK(!request_);
154
155 request_.reset(new GCMChannelStatusRequest(
156 request_context_,
fgorski3f6b84cd2014-10-10 21:04:18157 channel_status_request_url_,
158 user_agent_,
jianli2dc910b02014-09-19 02:42:46159 base::Bind(&GCMChannelStatusSyncer::OnRequestCompleted,
160 weak_ptr_factory_.GetWeakPtr())));
161 request_->Start();
162}
163
164base::TimeDelta GCMChannelStatusSyncer::GetRequestDelayInterval() const {
165 // No delay during testing.
166 if (delay_removed_for_testing_)
167 return base::TimeDelta();
168
169 // Make sure that checking with server occurs at polling interval, regardless
170 // whether the browser restarts.
171 int64 delay_seconds = poll_interval_seconds_ -
172 (base::Time::Now() - last_check_time_).InSeconds();
173 if (delay_seconds < 0)
174 delay_seconds = 0;
175
176 if (last_check_time_.is_null()) {
177 // For the first-time request, add a small delay to avoid sending request at
178 // browser startup time.
179 DCHECK(!delay_seconds);
180 delay_seconds = kFirstTimeDelaySeconds;
181 } else {
182 // Otherwise, add a fuzzing variation to the delay.
183 delay_seconds += base::RandInt(0, kGCMChannelRequestTimeJitterSeconds);
184 }
185
186 return base::TimeDelta::FromSeconds(delay_seconds);
187}
188
189} // namespace gcm