Home | History | Annotate | Download | only in cloud
      1 // Copyright 2013 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_invalidator.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/hash.h"
      9 #include "base/location.h"
     10 #include "base/metrics/histogram.h"
     11 #include "base/rand_util.h"
     12 #include "base/sequenced_task_runner.h"
     13 #include "base/time/clock.h"
     14 #include "base/time/time.h"
     15 #include "base/values.h"
     16 #include "components/invalidation/invalidation_service.h"
     17 #include "components/policy/core/common/cloud/cloud_policy_client.h"
     18 #include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h"
     19 #include "components/policy/core/common/cloud/enterprise_metrics.h"
     20 #include "policy/policy_constants.h"
     21 #include "sync/notifier/object_id_invalidation_map.h"
     22 
     23 namespace policy {
     24 
     25 const int CloudPolicyInvalidator::kMissingPayloadDelay = 5;
     26 const int CloudPolicyInvalidator::kMaxFetchDelayDefault = 10000;
     27 const int CloudPolicyInvalidator::kMaxFetchDelayMin = 1000;
     28 const int CloudPolicyInvalidator::kMaxFetchDelayMax = 300000;
     29 const int CloudPolicyInvalidator::kInvalidationGracePeriod = 10;
     30 const int CloudPolicyInvalidator::kUnknownVersionIgnorePeriod = 30;
     31 const int CloudPolicyInvalidator::kMaxInvalidationTimeDelta = 300;
     32 
     33 CloudPolicyInvalidator::CloudPolicyInvalidator(
     34     CloudPolicyCore* core,
     35     const scoped_refptr<base::SequencedTaskRunner>& task_runner,
     36     scoped_ptr<base::Clock> clock)
     37     : state_(UNINITIALIZED),
     38       core_(core),
     39       task_runner_(task_runner),
     40       clock_(clock.Pass()),
     41       invalidation_service_(NULL),
     42       invalidations_enabled_(false),
     43       invalidation_service_enabled_(false),
     44       is_registered_(false),
     45       invalid_(false),
     46       invalidation_version_(0),
     47       unknown_version_invalidation_count_(0),
     48       weak_factory_(this),
     49       max_fetch_delay_(kMaxFetchDelayDefault),
     50       policy_hash_value_(0) {
     51   DCHECK(core);
     52   DCHECK(task_runner.get());
     53 }
     54 
     55 CloudPolicyInvalidator::~CloudPolicyInvalidator() {
     56   DCHECK(state_ == SHUT_DOWN);
     57 }
     58 
     59 void CloudPolicyInvalidator::Initialize(
     60     invalidation::InvalidationService* invalidation_service) {
     61   DCHECK(state_ == UNINITIALIZED);
     62   DCHECK(thread_checker_.CalledOnValidThread());
     63   DCHECK(invalidation_service);
     64   invalidation_service_ = invalidation_service;
     65   state_ = STOPPED;
     66   core_->AddObserver(this);
     67   if (core_->refresh_scheduler())
     68     OnRefreshSchedulerStarted(core_);
     69 }
     70 
     71 void CloudPolicyInvalidator::Shutdown() {
     72   DCHECK(state_ != SHUT_DOWN);
     73   DCHECK(thread_checker_.CalledOnValidThread());
     74   if (state_ == STARTED) {
     75     if (is_registered_)
     76       invalidation_service_->UnregisterInvalidationHandler(this);
     77     core_->store()->RemoveObserver(this);
     78     weak_factory_.InvalidateWeakPtrs();
     79   }
     80   if (state_ != UNINITIALIZED)
     81     core_->RemoveObserver(this);
     82   state_ = SHUT_DOWN;
     83 }
     84 
     85 void CloudPolicyInvalidator::OnInvalidatorStateChange(
     86     syncer::InvalidatorState state) {
     87   DCHECK(state_ == STARTED);
     88   DCHECK(thread_checker_.CalledOnValidThread());
     89   invalidation_service_enabled_ = state == syncer::INVALIDATIONS_ENABLED;
     90   UpdateInvalidationsEnabled();
     91 }
     92 
     93 void CloudPolicyInvalidator::OnIncomingInvalidation(
     94     const syncer::ObjectIdInvalidationMap& invalidation_map) {
     95   DCHECK(state_ == STARTED);
     96   DCHECK(thread_checker_.CalledOnValidThread());
     97   const syncer::SingleObjectInvalidationSet& list =
     98       invalidation_map.ForObject(object_id_);
     99   if (list.IsEmpty()) {
    100     NOTREACHED();
    101     return;
    102   }
    103 
    104   // Acknowledge all except the invalidation with the highest version.
    105   syncer::SingleObjectInvalidationSet::const_reverse_iterator it =
    106       list.rbegin();
    107   ++it;
    108   for ( ; it != list.rend(); ++it) {
    109     it->Acknowledge();
    110   }
    111 
    112   // Handle the highest version invalidation.
    113   HandleInvalidation(list.back());
    114 }
    115 
    116 std::string CloudPolicyInvalidator::GetOwnerName() const { return "Cloud"; }
    117 
    118 void CloudPolicyInvalidator::OnCoreConnected(CloudPolicyCore* core) {}
    119 
    120 void CloudPolicyInvalidator::OnRefreshSchedulerStarted(CloudPolicyCore* core) {
    121   DCHECK(state_ == STOPPED);
    122   DCHECK(thread_checker_.CalledOnValidThread());
    123   state_ = STARTED;
    124   OnStoreLoaded(core_->store());
    125   core_->store()->AddObserver(this);
    126 }
    127 
    128 void CloudPolicyInvalidator::OnCoreDisconnecting(CloudPolicyCore* core) {
    129   DCHECK(state_ == STARTED || state_ == STOPPED);
    130   DCHECK(thread_checker_.CalledOnValidThread());
    131   if (state_ == STARTED) {
    132     Unregister();
    133     core_->store()->RemoveObserver(this);
    134     state_ = STOPPED;
    135   }
    136 }
    137 
    138 void CloudPolicyInvalidator::OnStoreLoaded(CloudPolicyStore* store) {
    139   DCHECK(state_ == STARTED);
    140   DCHECK(thread_checker_.CalledOnValidThread());
    141   bool policy_changed = IsPolicyChanged(store->policy());
    142 
    143   if (is_registered_) {
    144     // Update the kMetricPolicyRefresh histogram.
    145     UMA_HISTOGRAM_ENUMERATION(
    146         kMetricPolicyRefresh,
    147         GetPolicyRefreshMetric(policy_changed),
    148         METRIC_POLICY_REFRESH_SIZE);
    149 
    150     // If the policy was invalid and the version stored matches the latest
    151     // invalidation version, acknowledge the latest invalidation.
    152     if (invalid_ && store->invalidation_version() == invalidation_version_)
    153       AcknowledgeInvalidation();
    154   }
    155 
    156   UpdateRegistration(store->policy());
    157   UpdateMaxFetchDelay(store->policy_map());
    158 }
    159 
    160 void CloudPolicyInvalidator::OnStoreError(CloudPolicyStore* store) {}
    161 
    162 void CloudPolicyInvalidator::HandleInvalidation(
    163     const syncer::Invalidation& invalidation) {
    164   // Ignore old invalidations.
    165   if (invalid_ &&
    166       !invalidation.is_unknown_version() &&
    167       invalidation.version() <= invalidation_version_) {
    168     return;
    169   }
    170 
    171   // If there is still a pending invalidation, acknowledge it, since we only
    172   // care about the latest invalidation.
    173   if (invalid_)
    174     AcknowledgeInvalidation();
    175 
    176   // Get the version and payload from the invalidation.
    177   // When an invalidation with unknown version is received, use negative
    178   // numbers based on the number of such invalidations received. This
    179   // ensures that the version numbers do not collide with "real" versions
    180   // (which are positive) or previous invalidations with unknown version.
    181   int64 version;
    182   std::string payload;
    183   if (invalidation.is_unknown_version()) {
    184     version = -(++unknown_version_invalidation_count_);
    185   } else {
    186     version = invalidation.version();
    187     payload = invalidation.payload();
    188   }
    189 
    190   // Ignore the invalidation if it is expired.
    191   bool is_expired = IsInvalidationExpired(version);
    192   UMA_HISTOGRAM_ENUMERATION(
    193       kMetricPolicyInvalidations,
    194       GetInvalidationMetric(payload.empty(), is_expired),
    195       POLICY_INVALIDATION_TYPE_SIZE);
    196   if (is_expired) {
    197     invalidation.Acknowledge();
    198     return;
    199   }
    200 
    201   // Update invalidation state.
    202   invalid_ = true;
    203   invalidation_.reset(new syncer::Invalidation(invalidation));
    204   invalidation_version_ = version;
    205 
    206   // In order to prevent the cloud policy server from becoming overwhelmed when
    207   // a policy with many users is modified, delay for a random period of time
    208   // before fetching the policy. Delay for at least 20ms so that if multiple
    209   // invalidations are received in quick succession, only one fetch will be
    210   // performed.
    211   base::TimeDelta delay = base::TimeDelta::FromMilliseconds(
    212       base::RandInt(20, max_fetch_delay_));
    213 
    214   // If there is a payload, the policy can be refreshed at any time, so set
    215   // the version and payload on the client immediately. Otherwise, the refresh
    216   // must only run after at least kMissingPayloadDelay minutes.
    217   if (!payload.empty())
    218     core_->client()->SetInvalidationInfo(version, payload);
    219   else
    220     delay += base::TimeDelta::FromMinutes(kMissingPayloadDelay);
    221 
    222   // Schedule the policy to be refreshed.
    223   task_runner_->PostDelayedTask(
    224       FROM_HERE,
    225       base::Bind(
    226           &CloudPolicyInvalidator::RefreshPolicy,
    227           weak_factory_.GetWeakPtr(),
    228           payload.empty() /* is_missing_payload */),
    229       delay);
    230 }
    231 
    232 void CloudPolicyInvalidator::UpdateRegistration(
    233     const enterprise_management::PolicyData* policy) {
    234   // Create the ObjectId based on the policy data.
    235   // If the policy does not specify an the ObjectId, then unregister.
    236   if (!policy ||
    237       !policy->has_invalidation_source() ||
    238       !policy->has_invalidation_name()) {
    239     Unregister();
    240     return;
    241   }
    242   invalidation::ObjectId object_id(
    243       policy->invalidation_source(),
    244       policy->invalidation_name());
    245 
    246   // If the policy object id in the policy data is different from the currently
    247   // registered object id, update the object registration.
    248   if (!is_registered_ || !(object_id == object_id_))
    249     Register(object_id);
    250 }
    251 
    252 void CloudPolicyInvalidator::Register(const invalidation::ObjectId& object_id) {
    253   // Register this handler with the invalidation service if needed.
    254   if (!is_registered_) {
    255     OnInvalidatorStateChange(invalidation_service_->GetInvalidatorState());
    256     invalidation_service_->RegisterInvalidationHandler(this);
    257   }
    258 
    259   // Update internal state.
    260   if (invalid_)
    261     AcknowledgeInvalidation();
    262   is_registered_ = true;
    263   object_id_ = object_id;
    264   UpdateInvalidationsEnabled();
    265 
    266   // Update registration with the invalidation service.
    267   syncer::ObjectIdSet ids;
    268   ids.insert(object_id);
    269   invalidation_service_->UpdateRegisteredInvalidationIds(this, ids);
    270 }
    271 
    272 void CloudPolicyInvalidator::Unregister() {
    273   if (is_registered_) {
    274     if (invalid_)
    275       AcknowledgeInvalidation();
    276     invalidation_service_->UpdateRegisteredInvalidationIds(
    277         this,
    278         syncer::ObjectIdSet());
    279     invalidation_service_->UnregisterInvalidationHandler(this);
    280     is_registered_ = false;
    281     UpdateInvalidationsEnabled();
    282   }
    283 }
    284 
    285 void CloudPolicyInvalidator::UpdateMaxFetchDelay(const PolicyMap& policy_map) {
    286   int delay;
    287 
    288   // Try reading the delay from the policy.
    289   const base::Value* delay_policy_value =
    290       policy_map.GetValue(key::kMaxInvalidationFetchDelay);
    291   if (delay_policy_value && delay_policy_value->GetAsInteger(&delay)) {
    292     set_max_fetch_delay(delay);
    293     return;
    294   }
    295 
    296   set_max_fetch_delay(kMaxFetchDelayDefault);
    297 }
    298 
    299 void CloudPolicyInvalidator::set_max_fetch_delay(int delay) {
    300   if (delay < kMaxFetchDelayMin)
    301     max_fetch_delay_ = kMaxFetchDelayMin;
    302   else if (delay > kMaxFetchDelayMax)
    303     max_fetch_delay_ = kMaxFetchDelayMax;
    304   else
    305     max_fetch_delay_ = delay;
    306 }
    307 
    308 void CloudPolicyInvalidator::UpdateInvalidationsEnabled() {
    309   bool invalidations_enabled = invalidation_service_enabled_ && is_registered_;
    310   if (invalidations_enabled_ != invalidations_enabled) {
    311     invalidations_enabled_ = invalidations_enabled;
    312     if (invalidations_enabled)
    313       invalidations_enabled_time_ = clock_->Now();
    314     core_->refresh_scheduler()->SetInvalidationServiceAvailability(
    315         invalidations_enabled);
    316   }
    317 }
    318 
    319 void CloudPolicyInvalidator::RefreshPolicy(bool is_missing_payload) {
    320   DCHECK(thread_checker_.CalledOnValidThread());
    321   // In the missing payload case, the invalidation version has not been set on
    322   // the client yet, so set it now that the required time has elapsed.
    323   if (is_missing_payload)
    324     core_->client()->SetInvalidationInfo(invalidation_version_, std::string());
    325   core_->refresh_scheduler()->RefreshSoon();
    326 }
    327 
    328 void CloudPolicyInvalidator::AcknowledgeInvalidation() {
    329   DCHECK(invalid_);
    330   invalid_ = false;
    331   core_->client()->SetInvalidationInfo(0, std::string());
    332   invalidation_->Acknowledge();
    333   invalidation_.reset();
    334   // Cancel any scheduled policy refreshes.
    335   weak_factory_.InvalidateWeakPtrs();
    336 }
    337 
    338 bool CloudPolicyInvalidator::IsPolicyChanged(
    339     const enterprise_management::PolicyData* policy) {
    340   // Determine if the policy changed by comparing its hash value to the
    341   // previous policy's hash value.
    342   uint32 new_hash_value = 0;
    343   if (policy && policy->has_policy_value())
    344     new_hash_value = base::Hash(policy->policy_value());
    345   bool changed = new_hash_value != policy_hash_value_;
    346   policy_hash_value_ = new_hash_value;
    347   return changed;
    348 }
    349 
    350 bool CloudPolicyInvalidator::IsInvalidationExpired(int64 version) {
    351   base::Time last_fetch_time = base::Time::UnixEpoch() +
    352       base::TimeDelta::FromMilliseconds(core_->store()->policy()->timestamp());
    353 
    354   // If the version is unknown, consider the invalidation invalid if the
    355   // policy was fetched very recently.
    356   if (version < 0) {
    357     base::TimeDelta elapsed = clock_->Now() - last_fetch_time;
    358     return elapsed.InSeconds() < kUnknownVersionIgnorePeriod;
    359   }
    360 
    361   // The invalidation version is the timestamp in microseconds. If the
    362   // invalidation occurred before the last policy fetch, then the invalidation
    363   // is expired. Time is added to the invalidation to err on the side of not
    364   // expired.
    365   base::Time invalidation_time = base::Time::UnixEpoch() +
    366       base::TimeDelta::FromMicroseconds(version) +
    367       base::TimeDelta::FromSeconds(kMaxInvalidationTimeDelta);
    368   return invalidation_time < last_fetch_time;
    369 }
    370 
    371 int CloudPolicyInvalidator::GetPolicyRefreshMetric(bool policy_changed) {
    372   if (policy_changed) {
    373     if (invalid_)
    374       return METRIC_POLICY_REFRESH_INVALIDATED_CHANGED;
    375     if (GetInvalidationsEnabled())
    376       return METRIC_POLICY_REFRESH_CHANGED;
    377     return METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS;
    378   }
    379   if (invalid_)
    380     return METRIC_POLICY_REFRESH_INVALIDATED_UNCHANGED;
    381   return METRIC_POLICY_REFRESH_UNCHANGED;
    382 }
    383 
    384 int CloudPolicyInvalidator::GetInvalidationMetric(bool is_missing_payload,
    385                                                   bool is_expired) {
    386   if (is_expired) {
    387     if (is_missing_payload)
    388       return POLICY_INVALIDATION_TYPE_NO_PAYLOAD_EXPIRED;
    389     return POLICY_INVALIDATION_TYPE_EXPIRED;
    390   }
    391   if (is_missing_payload)
    392     return POLICY_INVALIDATION_TYPE_NO_PAYLOAD;
    393   return POLICY_INVALIDATION_TYPE_NORMAL;
    394 }
    395 
    396 bool CloudPolicyInvalidator::GetInvalidationsEnabled() {
    397   if (!invalidations_enabled_)
    398     return false;
    399   // If invalidations have been enabled for less than the grace period, then
    400   // consider invalidations to be disabled for metrics reporting.
    401   base::TimeDelta elapsed = clock_->Now() - invalidations_enabled_time_;
    402   return elapsed.InSeconds() >= kInvalidationGracePeriod;
    403 }
    404 
    405 }  // namespace policy
    406