Home | History | Annotate | Download | only in cloud
      1 // Copyright (c) 2012 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 "chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/bind.h"
     10 #include "base/bind_helpers.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "base/metrics/histogram.h"
     13 #include "base/sequenced_task_runner.h"
     14 #include "base/time/default_tick_clock.h"
     15 #include "base/time/tick_clock.h"
     16 #include "chrome/browser/chrome_notification_types.h"
     17 #include "chrome/browser/policy/cloud/cloud_policy_constants.h"
     18 #include "content/public/browser/notification_details.h"
     19 
     20 namespace policy {
     21 
     22 namespace {
     23 
     24 // The maximum rate at which to refresh policies.
     25 const size_t kMaxRefreshesPerHour = 5;
     26 
     27 // The maximum time to wait for the invalidations service to become available
     28 // before starting to issue requests.
     29 // TODO(joaodasilva): set this to a non-zero value once the invalidations
     30 // service is wired to this class and we have a good estimate of how long
     31 // to wait.
     32 const int kWaitForInvalidationsTimeoutSeconds = 0;
     33 
     34 }  // namespace
     35 
     36 #if defined(OS_ANDROID)
     37 
     38 const int64 CloudPolicyRefreshScheduler::kDefaultRefreshDelayMs =
     39     24 * 60 * 60 * 1000;  // 1 day.
     40 const int64 CloudPolicyRefreshScheduler::kUnmanagedRefreshDelayMs =
     41     24 * 60 * 60 * 1000;  // 1 day.
     42 // Delay for periodic refreshes when the invalidations service is available,
     43 // in milliseconds.
     44 // TODO(joaodasilva): increase this value once we're confident that the
     45 // invalidations channel works as expected.
     46 const int64 CloudPolicyRefreshScheduler::kWithInvalidationsRefreshDelayMs =
     47     24 * 60 * 60 * 1000;  // 1 day.
     48 const int64 CloudPolicyRefreshScheduler::kInitialErrorRetryDelayMs =
     49     5 * 60 * 1000;  // 5 minutes.
     50 const int64 CloudPolicyRefreshScheduler::kRefreshDelayMinMs =
     51     30 * 60 * 1000;  // 30 minutes.
     52 const int64 CloudPolicyRefreshScheduler::kRefreshDelayMaxMs =
     53     7 * 24 * 60 * 60 * 1000;  // 1 week.
     54 
     55 #else
     56 
     57 const int64 CloudPolicyRefreshScheduler::kDefaultRefreshDelayMs =
     58     3 * 60 * 60 * 1000;  // 3 hours.
     59 const int64 CloudPolicyRefreshScheduler::kUnmanagedRefreshDelayMs =
     60     24 * 60 * 60 * 1000;  // 1 day.
     61 // Delay for periodic refreshes when the invalidations service is available,
     62 // in milliseconds.
     63 // TODO(joaodasilva): increase this value once we're confident that the
     64 // invalidations channel works as expected.
     65 const int64 CloudPolicyRefreshScheduler::kWithInvalidationsRefreshDelayMs =
     66     24 * 60 * 60 * 1000;  // 1 day.
     67 const int64 CloudPolicyRefreshScheduler::kInitialErrorRetryDelayMs =
     68     5 * 60 * 1000;  // 5 minutes.
     69 const int64 CloudPolicyRefreshScheduler::kRefreshDelayMinMs =
     70     30 * 60 * 1000;  // 30 minutes.
     71 const int64 CloudPolicyRefreshScheduler::kRefreshDelayMaxMs =
     72     24 * 60 * 60 * 1000;  // 1 day.
     73 
     74 #endif
     75 
     76 CloudPolicyRefreshScheduler::CloudPolicyRefreshScheduler(
     77     CloudPolicyClient* client,
     78     CloudPolicyStore* store,
     79     const scoped_refptr<base::SequencedTaskRunner>& task_runner)
     80     : client_(client),
     81       store_(store),
     82       task_runner_(task_runner),
     83       error_retry_delay_ms_(kInitialErrorRetryDelayMs),
     84       refresh_delay_ms_(kDefaultRefreshDelayMs),
     85       rate_limiter_(kMaxRefreshesPerHour,
     86                     base::TimeDelta::FromHours(1),
     87                     base::Bind(&CloudPolicyRefreshScheduler::RefreshNow,
     88                                base::Unretained(this)),
     89                     task_runner_,
     90                     scoped_ptr<base::TickClock>(new base::DefaultTickClock())),
     91       invalidations_available_(false),
     92       creation_time_(base::Time::NowFromSystemTime()) {
     93   client_->AddObserver(this);
     94   store_->AddObserver(this);
     95   net::NetworkChangeNotifier::AddIPAddressObserver(this);
     96 
     97   UpdateLastRefreshFromPolicy();
     98   WaitForInvalidationService();
     99 }
    100 
    101 CloudPolicyRefreshScheduler::~CloudPolicyRefreshScheduler() {
    102   store_->RemoveObserver(this);
    103   client_->RemoveObserver(this);
    104   net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
    105 }
    106 
    107 void CloudPolicyRefreshScheduler::SetRefreshDelay(int64 refresh_delay) {
    108   refresh_delay_ms_ = std::min(std::max(refresh_delay, kRefreshDelayMinMs),
    109                                kRefreshDelayMaxMs);
    110   ScheduleRefresh();
    111 }
    112 
    113 void CloudPolicyRefreshScheduler::RefreshSoon() {
    114   // An external consumer needs a policy update now (e.g. a new extension, or
    115   // the InvalidationService received a policy invalidation), so don't wait
    116   // before fetching anymore.
    117   wait_for_invalidations_timeout_callback_.Cancel();
    118   rate_limiter_.PostRequest();
    119 }
    120 
    121 void CloudPolicyRefreshScheduler::SetInvalidationServiceAvailability(
    122     bool is_available) {
    123   if (!creation_time_.is_null()) {
    124     base::TimeDelta elapsed = base::Time::NowFromSystemTime() - creation_time_;
    125     UMA_HISTOGRAM_MEDIUM_TIMES("Enterprise.PolicyInvalidationsStartupTime",
    126                                elapsed);
    127     creation_time_ = base::Time();
    128   }
    129 
    130   if (is_available == invalidations_available_) {
    131     // No change in state. If we're currently WaitingForInvalidationService
    132     // then the timeout task will eventually execute and trigger a reschedule;
    133     // let the InvalidationService keep retrying until that happens.
    134     return;
    135   }
    136 
    137   wait_for_invalidations_timeout_callback_.Cancel();
    138   invalidations_available_ = is_available;
    139 
    140   if (invalidations_available_) {
    141     ScheduleRefresh();
    142   } else {
    143     // If the invalidation service was previously available but is now offline
    144     // then this may be a temporary failure; give it some time to recover before
    145     // falling back on the polling behavior.
    146     WaitForInvalidationService();
    147   }
    148 }
    149 
    150 void CloudPolicyRefreshScheduler::OnPolicyFetched(CloudPolicyClient* client) {
    151   error_retry_delay_ms_ = kInitialErrorRetryDelayMs;
    152 
    153   // Schedule the next refresh.
    154   last_refresh_ = base::Time::NowFromSystemTime();
    155   ScheduleRefresh();
    156 }
    157 
    158 void CloudPolicyRefreshScheduler::OnRegistrationStateChanged(
    159     CloudPolicyClient* client) {
    160   error_retry_delay_ms_ = kInitialErrorRetryDelayMs;
    161 
    162   // The client might have registered, so trigger an immediate refresh.
    163   RefreshNow();
    164 }
    165 
    166 void CloudPolicyRefreshScheduler::OnClientError(CloudPolicyClient* client) {
    167   // Save the status for below.
    168   DeviceManagementStatus status = client_->status();
    169 
    170   // Schedule an error retry if applicable.
    171   last_refresh_ = base::Time::NowFromSystemTime();
    172   ScheduleRefresh();
    173 
    174   // Update the retry delay.
    175   if (client->is_registered() &&
    176       (status == DM_STATUS_REQUEST_FAILED ||
    177        status == DM_STATUS_TEMPORARY_UNAVAILABLE)) {
    178     error_retry_delay_ms_ = std::min(error_retry_delay_ms_ * 2,
    179                                      refresh_delay_ms_);
    180   } else {
    181     error_retry_delay_ms_ = kInitialErrorRetryDelayMs;
    182   }
    183 }
    184 
    185 void CloudPolicyRefreshScheduler::OnStoreLoaded(CloudPolicyStore* store) {
    186   UpdateLastRefreshFromPolicy();
    187 
    188   // Re-schedule the next refresh in case the is_managed bit changed.
    189   ScheduleRefresh();
    190 }
    191 
    192 void CloudPolicyRefreshScheduler::OnStoreError(CloudPolicyStore* store) {
    193   // If |store_| fails, the is_managed bit that it provides may become stale.
    194   // The best guess in that situation is to assume is_managed didn't change and
    195   // continue using the stale information. Thus, no specific response to a store
    196   // error is required. NB: Changes to is_managed fire OnStoreLoaded().
    197 }
    198 
    199 void CloudPolicyRefreshScheduler::OnIPAddressChanged() {
    200   if (client_->status() == DM_STATUS_REQUEST_FAILED)
    201     RefreshSoon();
    202 }
    203 
    204 void CloudPolicyRefreshScheduler::UpdateLastRefreshFromPolicy() {
    205   if (!last_refresh_.is_null())
    206     return;
    207 
    208   // If the client has already fetched policy, assume that happened recently. If
    209   // that assumption ever breaks, the proper thing to do probably is to move the
    210   // |last_refresh_| bookkeeping to CloudPolicyClient.
    211   if (!client_->responses().empty()) {
    212     last_refresh_ = base::Time::NowFromSystemTime();
    213     return;
    214   }
    215 
    216 #if defined(OS_ANDROID)
    217   // Refreshing on Android:
    218   // - if no user is signed-in then the |client_| is never registered and
    219   //   nothing happens here.
    220   // - if the user is signed-in but isn't enterprise then the |client_| is
    221   //   never registered and nothing happens here.
    222   // - if the user is signed-in but isn't registered for policy yet then the
    223   //   |client_| isn't registered either; the UserPolicySigninService will try
    224   //   to register, and OnRegistrationStateChanged() will be invoked later.
    225   // - if the client is signed-in and has policy then its timestamp is used to
    226   //   determine when to perform the next fetch, which will be once the cached
    227   //   version is considered "old enough".
    228   //
    229   // If there is an old policy cache then a fetch will be performed "soon"; if
    230   // that fetch fails then a retry is attempted after a delay, with exponential
    231   // backoff. If those fetches keep failing then the cached timestamp is *not*
    232   // updated, and another fetch (and subsequent retries) will be attempted
    233   // again on the next startup.
    234   //
    235   // But if the cached policy is considered fresh enough then we try to avoid
    236   // fetching again on startup; the Android logic differs from the desktop in
    237   // this aspect.
    238   if (store_->has_policy() && store_->policy()->has_timestamp()) {
    239     last_refresh_ =
    240         base::Time::UnixEpoch() +
    241         base::TimeDelta::FromMilliseconds(store_->policy()->timestamp());
    242   }
    243 #else
    244   // If there is a cached non-managed response, make sure to only re-query the
    245   // server after kUnmanagedRefreshDelayMs. NB: For existing policy, an
    246   // immediate refresh is intentional.
    247   if (store_->has_policy() && store_->policy()->has_timestamp() &&
    248       !store_->is_managed()) {
    249     last_refresh_ =
    250         base::Time::UnixEpoch() +
    251         base::TimeDelta::FromMilliseconds(store_->policy()->timestamp());
    252   }
    253 #endif
    254 }
    255 
    256 void CloudPolicyRefreshScheduler::RefreshNow() {
    257   last_refresh_ = base::Time();
    258   ScheduleRefresh();
    259 }
    260 
    261 void CloudPolicyRefreshScheduler::ScheduleRefresh() {
    262   // If the client isn't registered, there is nothing to do.
    263   if (!client_->is_registered()) {
    264     refresh_callback_.Cancel();
    265     return;
    266   }
    267 
    268   // Don't schedule anything yet if we're still waiting for the invalidations
    269   // service.
    270   if (WaitingForInvalidationService())
    271     return;
    272 
    273   // If policy invalidations are available then periodic updates are done at
    274   // a much lower rate; otherwise use the |refresh_delay_ms_| value.
    275   int64 refresh_delay_ms =
    276       invalidations_available_ ? kWithInvalidationsRefreshDelayMs
    277                                : refresh_delay_ms_;
    278 
    279   // If there is a registration, go by the client's status. That will tell us
    280   // what the appropriate refresh delay should be.
    281   switch (client_->status()) {
    282     case DM_STATUS_SUCCESS:
    283       if (store_->is_managed())
    284         RefreshAfter(refresh_delay_ms);
    285       else
    286         RefreshAfter(kUnmanagedRefreshDelayMs);
    287       return;
    288     case DM_STATUS_SERVICE_ACTIVATION_PENDING:
    289     case DM_STATUS_SERVICE_POLICY_NOT_FOUND:
    290       RefreshAfter(refresh_delay_ms);
    291       return;
    292     case DM_STATUS_REQUEST_FAILED:
    293     case DM_STATUS_TEMPORARY_UNAVAILABLE:
    294       RefreshAfter(error_retry_delay_ms_);
    295       return;
    296     case DM_STATUS_REQUEST_INVALID:
    297     case DM_STATUS_HTTP_STATUS_ERROR:
    298     case DM_STATUS_RESPONSE_DECODING_ERROR:
    299     case DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED:
    300       RefreshAfter(kUnmanagedRefreshDelayMs);
    301       return;
    302     case DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID:
    303     case DM_STATUS_SERVICE_DEVICE_NOT_FOUND:
    304     case DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER:
    305     case DM_STATUS_SERVICE_DEVICE_ID_CONFLICT:
    306     case DM_STATUS_SERVICE_MISSING_LICENSES:
    307       // Need a re-registration, no use in retrying.
    308       refresh_callback_.Cancel();
    309       return;
    310   }
    311 
    312   NOTREACHED() << "Invalid client status " << client_->status();
    313   RefreshAfter(kUnmanagedRefreshDelayMs);
    314 }
    315 
    316 void CloudPolicyRefreshScheduler::PerformRefresh() {
    317   if (client_->is_registered()) {
    318     // Update |last_refresh_| so another fetch isn't triggered inadvertently.
    319     last_refresh_ = base::Time::NowFromSystemTime();
    320 
    321     // The result of this operation will be reported through a callback, at
    322     // which point the next refresh will be scheduled.
    323     client_->FetchPolicy();
    324     return;
    325   }
    326 
    327   // This should never happen, as the registration change should have been
    328   // handled via OnRegistrationStateChanged().
    329   NOTREACHED();
    330 }
    331 
    332 void CloudPolicyRefreshScheduler::RefreshAfter(int delta_ms) {
    333   base::TimeDelta delta(base::TimeDelta::FromMilliseconds(delta_ms));
    334   refresh_callback_.Cancel();
    335 
    336   // Schedule the callback.
    337   base::TimeDelta delay =
    338       std::max((last_refresh_ + delta) - base::Time::NowFromSystemTime(),
    339                base::TimeDelta());
    340   refresh_callback_.Reset(
    341       base::Bind(&CloudPolicyRefreshScheduler::PerformRefresh,
    342                  base::Unretained(this)));
    343   task_runner_->PostDelayedTask(FROM_HERE, refresh_callback_.callback(), delay);
    344 }
    345 
    346 void CloudPolicyRefreshScheduler::WaitForInvalidationService() {
    347   DCHECK(!WaitingForInvalidationService());
    348   wait_for_invalidations_timeout_callback_.Reset(
    349       base::Bind(
    350           &CloudPolicyRefreshScheduler::OnWaitForInvalidationServiceTimeout,
    351           base::Unretained(this)));
    352   task_runner_->PostDelayedTask(
    353       FROM_HERE,
    354       wait_for_invalidations_timeout_callback_.callback(),
    355       base::TimeDelta::FromSeconds(kWaitForInvalidationsTimeoutSeconds));
    356 }
    357 
    358 void CloudPolicyRefreshScheduler::OnWaitForInvalidationServiceTimeout() {
    359   wait_for_invalidations_timeout_callback_.Cancel();
    360   ScheduleRefresh();
    361 }
    362 
    363 bool CloudPolicyRefreshScheduler::WaitingForInvalidationService() const {
    364   return !wait_for_invalidations_timeout_callback_.IsCancelled();
    365 }
    366 
    367 }  // namespace policy
    368