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