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