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 "components/policy/core/common/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 "components/policy/core/common/cloud/cloud_policy_constants.h"
     15 
     16 namespace policy {
     17 
     18 #if defined(OS_ANDROID) || defined(OS_IOS)
     19 
     20 const int64 CloudPolicyRefreshScheduler::kDefaultRefreshDelayMs =
     21     24 * 60 * 60 * 1000;  // 1 day.
     22 const int64 CloudPolicyRefreshScheduler::kUnmanagedRefreshDelayMs =
     23     24 * 60 * 60 * 1000;  // 1 day.
     24 // Delay for periodic refreshes when the invalidations service is available,
     25 // in milliseconds.
     26 // TODO(joaodasilva): increase this value once we're confident that the
     27 // invalidations channel works as expected.
     28 const int64 CloudPolicyRefreshScheduler::kWithInvalidationsRefreshDelayMs =
     29     24 * 60 * 60 * 1000;  // 1 day.
     30 const int64 CloudPolicyRefreshScheduler::kInitialErrorRetryDelayMs =
     31     5 * 60 * 1000;  // 5 minutes.
     32 const int64 CloudPolicyRefreshScheduler::kRefreshDelayMinMs =
     33     30 * 60 * 1000;  // 30 minutes.
     34 const int64 CloudPolicyRefreshScheduler::kRefreshDelayMaxMs =
     35     7 * 24 * 60 * 60 * 1000;  // 1 week.
     36 
     37 #else
     38 
     39 const int64 CloudPolicyRefreshScheduler::kDefaultRefreshDelayMs =
     40     3 * 60 * 60 * 1000;  // 3 hours.
     41 const int64 CloudPolicyRefreshScheduler::kUnmanagedRefreshDelayMs =
     42     24 * 60 * 60 * 1000;  // 1 day.
     43 // Delay for periodic refreshes when the invalidations service is available,
     44 // in milliseconds.
     45 // TODO(joaodasilva): increase this value once we're confident that the
     46 // invalidations channel works as expected.
     47 const int64 CloudPolicyRefreshScheduler::kWithInvalidationsRefreshDelayMs =
     48     3 * 60 * 60 * 1000;  // 3 hours.
     49 const int64 CloudPolicyRefreshScheduler::kInitialErrorRetryDelayMs =
     50     5 * 60 * 1000;  // 5 minutes.
     51 const int64 CloudPolicyRefreshScheduler::kRefreshDelayMinMs =
     52     30 * 60 * 1000;  // 30 minutes.
     53 const int64 CloudPolicyRefreshScheduler::kRefreshDelayMaxMs =
     54     24 * 60 * 60 * 1000;  // 1 day.
     55 
     56 #endif
     57 
     58 CloudPolicyRefreshScheduler::CloudPolicyRefreshScheduler(
     59     CloudPolicyClient* client,
     60     CloudPolicyStore* store,
     61     const scoped_refptr<base::SequencedTaskRunner>& task_runner)
     62     : client_(client),
     63       store_(store),
     64       task_runner_(task_runner),
     65       error_retry_delay_ms_(kInitialErrorRetryDelayMs),
     66       refresh_delay_ms_(kDefaultRefreshDelayMs),
     67       invalidations_available_(false),
     68       creation_time_(base::Time::NowFromSystemTime()) {
     69   client_->AddObserver(this);
     70   store_->AddObserver(this);
     71   net::NetworkChangeNotifier::AddIPAddressObserver(this);
     72 
     73   UpdateLastRefreshFromPolicy();
     74   ScheduleRefresh();
     75 }
     76 
     77 CloudPolicyRefreshScheduler::~CloudPolicyRefreshScheduler() {
     78   store_->RemoveObserver(this);
     79   client_->RemoveObserver(this);
     80   net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
     81 }
     82 
     83 void CloudPolicyRefreshScheduler::SetRefreshDelay(int64 refresh_delay) {
     84   refresh_delay_ms_ = std::min(std::max(refresh_delay, kRefreshDelayMinMs),
     85                                kRefreshDelayMaxMs);
     86   ScheduleRefresh();
     87 }
     88 
     89 void CloudPolicyRefreshScheduler::RefreshSoon() {
     90   RefreshNow();
     91 }
     92 
     93 void CloudPolicyRefreshScheduler::SetInvalidationServiceAvailability(
     94     bool is_available) {
     95   if (!creation_time_.is_null()) {
     96     base::TimeDelta elapsed = base::Time::NowFromSystemTime() - creation_time_;
     97     UMA_HISTOGRAM_MEDIUM_TIMES("Enterprise.PolicyInvalidationsStartupTime",
     98                                elapsed);
     99     creation_time_ = base::Time();
    100   }
    101 
    102   if (is_available == invalidations_available_) {
    103     // No change in state.
    104     return;
    105   }
    106 
    107   invalidations_available_ = is_available;
    108 
    109   // Schedule a refresh since the refresh delay has been updated.
    110   ScheduleRefresh();
    111 }
    112 
    113 void CloudPolicyRefreshScheduler::OnPolicyFetched(CloudPolicyClient* client) {
    114   error_retry_delay_ms_ = kInitialErrorRetryDelayMs;
    115 
    116   // Schedule the next refresh.
    117   last_refresh_ = base::Time::NowFromSystemTime();
    118   ScheduleRefresh();
    119 }
    120 
    121 void CloudPolicyRefreshScheduler::OnRegistrationStateChanged(
    122     CloudPolicyClient* client) {
    123   error_retry_delay_ms_ = kInitialErrorRetryDelayMs;
    124 
    125   // The client might have registered, so trigger an immediate refresh.
    126   RefreshNow();
    127 }
    128 
    129 void CloudPolicyRefreshScheduler::OnClientError(CloudPolicyClient* client) {
    130   // Save the status for below.
    131   DeviceManagementStatus status = client_->status();
    132 
    133   // Schedule an error retry if applicable.
    134   last_refresh_ = base::Time::NowFromSystemTime();
    135   ScheduleRefresh();
    136 
    137   // Update the retry delay.
    138   if (client->is_registered() &&
    139       (status == DM_STATUS_REQUEST_FAILED ||
    140        status == DM_STATUS_TEMPORARY_UNAVAILABLE)) {
    141     error_retry_delay_ms_ = std::min(error_retry_delay_ms_ * 2,
    142                                      refresh_delay_ms_);
    143   } else {
    144     error_retry_delay_ms_ = kInitialErrorRetryDelayMs;
    145   }
    146 }
    147 
    148 void CloudPolicyRefreshScheduler::OnStoreLoaded(CloudPolicyStore* store) {
    149   UpdateLastRefreshFromPolicy();
    150 
    151   // Re-schedule the next refresh in case the is_managed bit changed.
    152   ScheduleRefresh();
    153 }
    154 
    155 void CloudPolicyRefreshScheduler::OnStoreError(CloudPolicyStore* store) {
    156   // If |store_| fails, the is_managed bit that it provides may become stale.
    157   // The best guess in that situation is to assume is_managed didn't change and
    158   // continue using the stale information. Thus, no specific response to a store
    159   // error is required. NB: Changes to is_managed fire OnStoreLoaded().
    160 }
    161 
    162 void CloudPolicyRefreshScheduler::OnIPAddressChanged() {
    163   if (client_->status() == DM_STATUS_REQUEST_FAILED)
    164     RefreshSoon();
    165 }
    166 
    167 void CloudPolicyRefreshScheduler::UpdateLastRefreshFromPolicy() {
    168   if (!last_refresh_.is_null())
    169     return;
    170 
    171   // If the client has already fetched policy, assume that happened recently. If
    172   // that assumption ever breaks, the proper thing to do probably is to move the
    173   // |last_refresh_| bookkeeping to CloudPolicyClient.
    174   if (!client_->responses().empty()) {
    175     last_refresh_ = base::Time::NowFromSystemTime();
    176     return;
    177   }
    178 
    179 #if defined(OS_ANDROID) || defined(OS_IOS)
    180   // Refreshing on mobile platforms:
    181   // - if no user is signed-in then the |client_| is never registered and
    182   //   nothing happens here.
    183   // - if the user is signed-in but isn't enterprise then the |client_| is
    184   //   never registered and nothing happens here.
    185   // - if the user is signed-in but isn't registered for policy yet then the
    186   //   |client_| isn't registered either; the UserPolicySigninService will try
    187   //   to register, and OnRegistrationStateChanged() will be invoked later.
    188   // - if the client is signed-in and has policy then its timestamp is used to
    189   //   determine when to perform the next fetch, which will be once the cached
    190   //   version is considered "old enough".
    191   //
    192   // If there is an old policy cache then a fetch will be performed "soon"; if
    193   // that fetch fails then a retry is attempted after a delay, with exponential
    194   // backoff. If those fetches keep failing then the cached timestamp is *not*
    195   // updated, and another fetch (and subsequent retries) will be attempted
    196   // again on the next startup.
    197   //
    198   // But if the cached policy is considered fresh enough then we try to avoid
    199   // fetching again on startup; the Android logic differs from the desktop in
    200   // this aspect.
    201   if (store_->has_policy() && store_->policy()->has_timestamp()) {
    202     last_refresh_ =
    203         base::Time::UnixEpoch() +
    204         base::TimeDelta::FromMilliseconds(store_->policy()->timestamp());
    205   }
    206 #else
    207   // If there is a cached non-managed response, make sure to only re-query the
    208   // server after kUnmanagedRefreshDelayMs. NB: For existing policy, an
    209   // immediate refresh is intentional.
    210   if (store_->has_policy() && store_->policy()->has_timestamp() &&
    211       !store_->is_managed()) {
    212     last_refresh_ =
    213         base::Time::UnixEpoch() +
    214         base::TimeDelta::FromMilliseconds(store_->policy()->timestamp());
    215   }
    216 #endif
    217 }
    218 
    219 void CloudPolicyRefreshScheduler::RefreshNow() {
    220   last_refresh_ = base::Time();
    221   ScheduleRefresh();
    222 }
    223 
    224 void CloudPolicyRefreshScheduler::ScheduleRefresh() {
    225   // If the client isn't registered, there is nothing to do.
    226   if (!client_->is_registered()) {
    227     refresh_callback_.Cancel();
    228     return;
    229   }
    230 
    231   // If policy invalidations are available then periodic updates are done at
    232   // a much lower rate; otherwise use the |refresh_delay_ms_| value.
    233   int64 refresh_delay_ms =
    234       invalidations_available_ ? kWithInvalidationsRefreshDelayMs
    235                                : refresh_delay_ms_;
    236 
    237   // If there is a registration, go by the client's status. That will tell us
    238   // what the appropriate refresh delay should be.
    239   switch (client_->status()) {
    240     case DM_STATUS_SUCCESS:
    241       if (store_->is_managed())
    242         RefreshAfter(refresh_delay_ms);
    243       else
    244         RefreshAfter(kUnmanagedRefreshDelayMs);
    245       return;
    246     case DM_STATUS_SERVICE_ACTIVATION_PENDING:
    247     case DM_STATUS_SERVICE_POLICY_NOT_FOUND:
    248       RefreshAfter(refresh_delay_ms);
    249       return;
    250     case DM_STATUS_REQUEST_FAILED:
    251     case DM_STATUS_TEMPORARY_UNAVAILABLE:
    252       RefreshAfter(error_retry_delay_ms_);
    253       return;
    254     case DM_STATUS_REQUEST_INVALID:
    255     case DM_STATUS_HTTP_STATUS_ERROR:
    256     case DM_STATUS_RESPONSE_DECODING_ERROR:
    257     case DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED:
    258       RefreshAfter(kUnmanagedRefreshDelayMs);
    259       return;
    260     case DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID:
    261     case DM_STATUS_SERVICE_DEVICE_NOT_FOUND:
    262     case DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER:
    263     case DM_STATUS_SERVICE_DEVICE_ID_CONFLICT:
    264     case DM_STATUS_SERVICE_MISSING_LICENSES:
    265     case DM_STATUS_SERVICE_DEPROVISIONED:
    266     case DM_STATUS_SERVICE_DOMAIN_MISMATCH:
    267       // Need a re-registration, no use in retrying.
    268       refresh_callback_.Cancel();
    269       return;
    270   }
    271 
    272   NOTREACHED() << "Invalid client status " << client_->status();
    273   RefreshAfter(kUnmanagedRefreshDelayMs);
    274 }
    275 
    276 void CloudPolicyRefreshScheduler::PerformRefresh() {
    277   if (client_->is_registered()) {
    278     // Update |last_refresh_| so another fetch isn't triggered inadvertently.
    279     last_refresh_ = base::Time::NowFromSystemTime();
    280 
    281     // The result of this operation will be reported through a callback, at
    282     // which point the next refresh will be scheduled.
    283     client_->FetchPolicy();
    284     return;
    285   }
    286 
    287   // This should never happen, as the registration change should have been
    288   // handled via OnRegistrationStateChanged().
    289   NOTREACHED();
    290 }
    291 
    292 void CloudPolicyRefreshScheduler::RefreshAfter(int delta_ms) {
    293   base::TimeDelta delta(base::TimeDelta::FromMilliseconds(delta_ms));
    294   refresh_callback_.Cancel();
    295 
    296   // Schedule the callback.
    297   base::TimeDelta delay =
    298       std::max((last_refresh_ + delta) - base::Time::NowFromSystemTime(),
    299                base::TimeDelta());
    300   refresh_callback_.Reset(
    301       base::Bind(&CloudPolicyRefreshScheduler::PerformRefresh,
    302                  base::Unretained(this)));
    303   task_runner_->PostDelayedTask(FROM_HERE, refresh_callback_.callback(), delay);
    304 }
    305 
    306 }  // namespace policy
    307