• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CloudPolicyInvalidator(CloudPolicyCore * core,const scoped_refptr<base::SequencedTaskRunner> & task_runner,scoped_ptr<base::Clock> clock)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 
~CloudPolicyInvalidator()55 CloudPolicyInvalidator::~CloudPolicyInvalidator() {
56   DCHECK(state_ == SHUT_DOWN);
57 }
58 
Initialize(invalidation::InvalidationService * invalidation_service)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 
Shutdown()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 
OnInvalidatorStateChange(syncer::InvalidatorState state)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 
OnIncomingInvalidation(const syncer::ObjectIdInvalidationMap & invalidation_map)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 
GetOwnerName() const116 std::string CloudPolicyInvalidator::GetOwnerName() const { return "Cloud"; }
117 
OnCoreConnected(CloudPolicyCore * core)118 void CloudPolicyInvalidator::OnCoreConnected(CloudPolicyCore* core) {}
119 
OnRefreshSchedulerStarted(CloudPolicyCore * core)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 
OnCoreDisconnecting(CloudPolicyCore * core)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 
OnStoreLoaded(CloudPolicyStore * store)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 
OnStoreError(CloudPolicyStore * store)160 void CloudPolicyInvalidator::OnStoreError(CloudPolicyStore* store) {}
161 
HandleInvalidation(const syncer::Invalidation & invalidation)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 
UpdateRegistration(const enterprise_management::PolicyData * policy)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 
Register(const invalidation::ObjectId & object_id)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 
Unregister()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 
UpdateMaxFetchDelay(const PolicyMap & policy_map)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 
set_max_fetch_delay(int delay)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 
UpdateInvalidationsEnabled()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 
RefreshPolicy(bool is_missing_payload)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 
AcknowledgeInvalidation()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 
IsPolicyChanged(const enterprise_management::PolicyData * policy)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 
IsInvalidationExpired(int64 version)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 
GetPolicyRefreshMetric(bool policy_changed)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 
GetInvalidationMetric(bool is_missing_payload,bool is_expired)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 
GetInvalidationsEnabled()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