Home | History | Annotate | Download | only in gcm_driver
      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 
     20 namespace gcm {
     21 
     22 namespace {
     23 
     24 // The GCM channel's enabled state.
     25 const char kGCMChannelStatus[] = "gcm.channel_status";
     26 
     27 // The GCM channel's polling interval (in seconds).
     28 const char kGCMChannelPollIntervalSeconds[] = "gcm.poll_interval";
     29 
     30 // Last time when checking with the GCM channel status server is done.
     31 const char kGCMChannelLastCheckTime[] = "gcm.check_time";
     32 
     33 // A small delay to avoid sending request at browser startup time for first-time
     34 // request.
     35 const int kFirstTimeDelaySeconds = 1 * 60;  // 1 minute.
     36 
     37 // The fuzzing variation added to the polling delay.
     38 const int kGCMChannelRequestTimeJitterSeconds = 15 * 60;  // 15 minues.
     39 
     40 // The minimum poll interval that can be overridden to.
     41 const int kMinCustomPollIntervalMinutes = 2;
     42 
     43 // Custom poll interval could not be used more than the limit below.
     44 const int kMaxNumberToUseCustomPollInterval = 10;
     45 
     46 }  // namespace
     47 
     48 namespace switches {
     49 
     50 // Override the default poll interval for testing purpose.
     51 const char kCustomPollIntervalMinutes[] = "gcm-channel-poll-interval";
     52 
     53 }  // namepsace switches
     54 
     55 // static
     56 void 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
     65 void 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
     82 int GCMChannelStatusSyncer::first_time_delay_seconds() {
     83   return kFirstTimeDelaySeconds;
     84 }
     85 
     86 GCMChannelStatusSyncer::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 
    128 GCMChannelStatusSyncer::~GCMChannelStatusSyncer() {
    129 }
    130 
    131 void 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 
    140 void GCMChannelStatusSyncer::Stop() {
    141   started_ = false;
    142   request_.reset();
    143   weak_ptr_factory_.InvalidateWeakPtrs();
    144 }
    145 
    146 void 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 
    184 void 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 
    196 void 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 
    208 base::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