gcm_channel_status_syncer.cc revision 1675a649fd7a8b3cb80ffddae2dc181f122353c5
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. 4 5#include "components/gcm_driver/gcm_channel_status_syncer.h" 6 7#include "base/bind.h" 8#include "base/command_line.h" 9#include "base/location.h" 10#include "base/logging.h" 11#include "base/message_loop/message_loop.h" 12#include "base/prefs/pref_registry_simple.h" 13#include "base/prefs/pref_service.h" 14#include "base/rand_util.h" 15#include "base/strings/string_number_conversions.h" 16#include "components/gcm_driver/gcm_channel_status_request.h" 17#include "components/gcm_driver/gcm_driver.h" 18#include "components/pref_registry/pref_registry_syncable.h" 19 20namespace gcm { 21 22namespace { 23 24// The GCM channel's enabled state. 25const char kGCMChannelStatus[] = "gcm.channel_status"; 26 27// The GCM channel's polling interval (in seconds). 28const char kGCMChannelPollIntervalSeconds[] = "gcm.poll_interval"; 29 30// Last time when checking with the GCM channel status server is done. 31const char kGCMChannelLastCheckTime[] = "gcm.check_time"; 32 33// A small delay to avoid sending request at browser startup time for first-time 34// request. 35const int kFirstTimeDelaySeconds = 1 * 60; // 1 minute. 36 37// The fuzzing variation added to the polling delay. 38const int kGCMChannelRequestTimeJitterSeconds = 15 * 60; // 15 minues. 39 40// The minimum poll interval that can be overridden to. 41const int kMinCustomPollIntervalMinutes = 2; 42 43// Custom poll interval could not be used more than the limit below. 44const int kMaxNumberToUseCustomPollInterval = 10; 45 46} // namespace 47 48namespace switches { 49 50// Override the default poll interval for testing purpose. 51const char kCustomPollIntervalMinutes[] = "gcm-channel-poll-interval"; 52 53} // namepsace switches 54 55// static 56void GCMChannelStatusSyncer::RegisterPrefs(PrefRegistrySimple* registry) { 57 registry->RegisterBooleanPref(kGCMChannelStatus, true); 58 registry->RegisterIntegerPref( 59 kGCMChannelPollIntervalSeconds, 60 GCMChannelStatusRequest::default_poll_interval_seconds()); 61 registry->RegisterInt64Pref(kGCMChannelLastCheckTime, 0); 62} 63 64// static 65void GCMChannelStatusSyncer::RegisterProfilePrefs( 66 user_prefs::PrefRegistrySyncable* registry) { 67 registry->RegisterBooleanPref( 68 kGCMChannelStatus, 69 true, 70 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 71 registry->RegisterIntegerPref( 72 kGCMChannelPollIntervalSeconds, 73 GCMChannelStatusRequest::default_poll_interval_seconds(), 74 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 75 registry->RegisterInt64Pref( 76 kGCMChannelLastCheckTime, 77 0, 78 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 79} 80 81// static 82int GCMChannelStatusSyncer::first_time_delay_seconds() { 83 return kFirstTimeDelaySeconds; 84} 85 86GCMChannelStatusSyncer::GCMChannelStatusSyncer( 87 GCMDriver* driver, 88 PrefService* prefs, 89 const std::string& channel_status_request_url, 90 const std::string& user_agent, 91 const scoped_refptr<net::URLRequestContextGetter>& request_context) 92 : driver_(driver), 93 prefs_(prefs), 94 channel_status_request_url_(channel_status_request_url), 95 user_agent_(user_agent), 96 request_context_(request_context), 97 started_(false), 98 gcm_enabled_(true), 99 poll_interval_seconds_( 100 GCMChannelStatusRequest::default_poll_interval_seconds()), 101 custom_poll_interval_use_count_(0), 102 delay_removed_for_testing_(false), 103 weak_ptr_factory_(this) { 104 gcm_enabled_ = prefs_->GetBoolean(kGCMChannelStatus); 105 poll_interval_seconds_ = prefs_->GetInteger(kGCMChannelPollIntervalSeconds); 106 if (poll_interval_seconds_ < 107 GCMChannelStatusRequest::min_poll_interval_seconds()) { 108 poll_interval_seconds_ = 109 GCMChannelStatusRequest::min_poll_interval_seconds(); 110 } 111 if (CommandLine::ForCurrentProcess()->HasSwitch( 112 switches::kCustomPollIntervalMinutes)) { 113 std::string value(CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 114 switches::kCustomPollIntervalMinutes)); 115 int minutes = 0; 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; 121 } 122 } 123 } 124 last_check_time_ = base::Time::FromInternalValue( 125 prefs_->GetInt64(kGCMChannelLastCheckTime)); 126} 127 128GCMChannelStatusSyncer::~GCMChannelStatusSyncer() { 129} 130 131void GCMChannelStatusSyncer::EnsureStarted() { 132 // Bail out if the request is already scheduled or started. 133 if (started_) 134 return; 135 started_ = true; 136 137 ScheduleRequest(); 138} 139 140void GCMChannelStatusSyncer::Stop() { 141 started_ = false; 142 request_.reset(); 143 weak_ptr_factory_.InvalidateWeakPtrs(); 144} 145 146void GCMChannelStatusSyncer::OnRequestCompleted(bool update_received, 147 bool enabled, 148 int poll_interval_seconds) { 149 DCHECK(request_); 150 request_.reset(); 151 152 // Persist the current time as the last request complete time. 153 last_check_time_ = base::Time::Now(); 154 prefs_->SetInt64(kGCMChannelLastCheckTime, 155 last_check_time_.ToInternalValue()); 156 157 if (update_received) { 158 if (gcm_enabled_ != enabled) { 159 gcm_enabled_ = enabled; 160 prefs_->SetBoolean(kGCMChannelStatus, enabled); 161 if (gcm_enabled_) 162 driver_->Enable(); 163 else 164 driver_->Disable(); 165 } 166 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(kGCMChannelPollIntervalSeconds, 174 poll_interval_seconds_); 175 } 176 } 177 } 178 179 // Do not schedule next request if syncer is stopped. 180 if (started_) 181 ScheduleRequest(); 182} 183 184void GCMChannelStatusSyncer::ScheduleRequest() { 185 current_request_delay_interval_ = GetRequestDelayInterval(); 186 base::MessageLoop::current()->PostDelayedTask( 187 FROM_HERE, 188 base::Bind(&GCMChannelStatusSyncer::StartRequest, 189 weak_ptr_factory_.GetWeakPtr()), 190 current_request_delay_interval_); 191 192 if (custom_poll_interval_use_count_) 193 custom_poll_interval_use_count_--; 194} 195 196void GCMChannelStatusSyncer::StartRequest() { 197 DCHECK(!request_); 198 199 request_.reset(new GCMChannelStatusRequest( 200 request_context_, 201 channel_status_request_url_, 202 user_agent_, 203 base::Bind(&GCMChannelStatusSyncer::OnRequestCompleted, 204 weak_ptr_factory_.GetWeakPtr()))); 205 request_->Start(); 206} 207 208base::TimeDelta GCMChannelStatusSyncer::GetRequestDelayInterval() const { 209 // No delay during testing. 210 if (delay_removed_for_testing_) 211 return base::TimeDelta(); 212 213 // Make sure that checking with server occurs at polling interval, regardless 214 // whether the browser restarts. 215 int64 delay_seconds = poll_interval_seconds_ - 216 (base::Time::Now() - last_check_time_).InSeconds(); 217 if (delay_seconds < 0) 218 delay_seconds = 0; 219 220 if (last_check_time_.is_null()) { 221 // For the first-time request, add a small delay to avoid sending request at 222 // browser startup time. 223 DCHECK(!delay_seconds); 224 delay_seconds = kFirstTimeDelaySeconds; 225 } else { 226 // Otherwise, add a fuzzing variation to the delay. 227 // The fuzzing variation is off when the custom interval is used. 228 if (!custom_poll_interval_use_count_) 229 delay_seconds += base::RandInt(0, kGCMChannelRequestTimeJitterSeconds); 230 } 231 232 return base::TimeDelta::FromSeconds(delay_seconds); 233} 234 235} // namespace gcm 236