Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / policy / cloud / cloud_policy_invalidator.cc
blob57fdc4c90aa839d2255725d81d55641def7b5e7e
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.
5 #include "chrome/browser/policy/cloud/cloud_policy_invalidator.h"
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/hash.h"
10 #include "base/location.h"
11 #include "base/metrics/histogram.h"
12 #include "base/rand_util.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/time/time.h"
16 #include "base/values.h"
17 #include "chrome/browser/invalidation/invalidation_service.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 "components/policy/core/common/policy_switches.h"
22 #include "policy/policy_constants.h"
23 #include "sync/notifier/object_id_invalidation_map.h"
25 namespace policy {
27 const int CloudPolicyInvalidator::kMissingPayloadDelay = 5;
28 const int CloudPolicyInvalidator::kMaxFetchDelayDefault = 120000;
29 const int CloudPolicyInvalidator::kMaxFetchDelayMin = 1000;
30 const int CloudPolicyInvalidator::kMaxFetchDelayMax = 300000;
32 CloudPolicyInvalidator::CloudPolicyInvalidator(
33 CloudPolicyCore* core,
34 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
35 : state_(UNINITIALIZED),
36 core_(core),
37 task_runner_(task_runner),
38 invalidation_service_(NULL),
39 invalidations_enabled_(false),
40 invalidation_service_enabled_(false),
41 registered_timestamp_(0),
42 invalid_(false),
43 invalidation_version_(0),
44 unknown_version_invalidation_count_(0),
45 weak_factory_(this),
46 max_fetch_delay_(kMaxFetchDelayDefault),
47 policy_hash_value_(0) {
48 DCHECK(core);
49 DCHECK(task_runner.get());
52 CloudPolicyInvalidator::~CloudPolicyInvalidator() {
53 DCHECK(state_ == SHUT_DOWN);
56 void CloudPolicyInvalidator::Initialize(
57 invalidation::InvalidationService* invalidation_service) {
58 DCHECK(state_ == UNINITIALIZED);
59 DCHECK(thread_checker_.CalledOnValidThread());
60 DCHECK(invalidation_service);
61 invalidation_service_ = invalidation_service;
62 state_ = STOPPED;
63 core_->AddObserver(this);
64 if (core_->refresh_scheduler())
65 OnRefreshSchedulerStarted(core_);
68 void CloudPolicyInvalidator::Shutdown() {
69 DCHECK(state_ != SHUT_DOWN);
70 DCHECK(thread_checker_.CalledOnValidThread());
71 if (state_ == STARTED) {
72 if (registered_timestamp_)
73 invalidation_service_->UnregisterInvalidationHandler(this);
74 core_->store()->RemoveObserver(this);
75 weak_factory_.InvalidateWeakPtrs();
77 if (state_ != UNINITIALIZED)
78 core_->RemoveObserver(this);
79 state_ = SHUT_DOWN;
82 void CloudPolicyInvalidator::OnInvalidatorStateChange(
83 syncer::InvalidatorState state) {
84 DCHECK(state_ == STARTED);
85 DCHECK(thread_checker_.CalledOnValidThread());
86 invalidation_service_enabled_ = state == syncer::INVALIDATIONS_ENABLED;
87 UpdateInvalidationsEnabled();
90 void CloudPolicyInvalidator::OnIncomingInvalidation(
91 const syncer::ObjectIdInvalidationMap& invalidation_map) {
92 DCHECK(state_ == STARTED);
93 DCHECK(thread_checker_.CalledOnValidThread());
94 const syncer::SingleObjectInvalidationSet& list =
95 invalidation_map.ForObject(object_id_);
96 if (list.IsEmpty()) {
97 NOTREACHED();
98 return;
101 // Acknowledge all except the invalidation with the highest version.
102 syncer::SingleObjectInvalidationSet::const_reverse_iterator it =
103 list.rbegin();
104 ++it;
105 for ( ; it != list.rend(); ++it) {
106 it->Acknowledge();
109 // Handle the highest version invalidation.
110 HandleInvalidation(list.back());
113 void CloudPolicyInvalidator::OnCoreConnected(CloudPolicyCore* core) {}
115 void CloudPolicyInvalidator::OnRefreshSchedulerStarted(CloudPolicyCore* core) {
116 DCHECK(state_ == STOPPED);
117 DCHECK(thread_checker_.CalledOnValidThread());
118 state_ = STARTED;
119 OnStoreLoaded(core_->store());
120 core_->store()->AddObserver(this);
123 void CloudPolicyInvalidator::OnCoreDisconnecting(CloudPolicyCore* core) {
124 DCHECK(state_ == STARTED || state_ == STOPPED);
125 DCHECK(thread_checker_.CalledOnValidThread());
126 if (state_ == STARTED) {
127 Unregister();
128 core_->store()->RemoveObserver(this);
129 state_ = STOPPED;
133 void CloudPolicyInvalidator::OnStoreLoaded(CloudPolicyStore* store) {
134 DCHECK(state_ == STARTED);
135 DCHECK(thread_checker_.CalledOnValidThread());
136 bool policy_changed = IsPolicyChanged(store->policy());
138 if (registered_timestamp_) {
139 // Update the kMetricPolicyRefresh histogram. In some cases, this object can
140 // be constructed during an OnStoreLoaded callback, which causes
141 // OnStoreLoaded to be called twice at initialization time, so make sure
142 // that the timestamp does not match the timestamp at which registration
143 // occurred. We only measure changes which occur after registration.
144 if (!store->policy() || !store->policy()->has_timestamp() ||
145 store->policy()->timestamp() != registered_timestamp_) {
146 UMA_HISTOGRAM_ENUMERATION(
147 kMetricPolicyRefresh,
148 GetPolicyRefreshMetric(policy_changed),
149 METRIC_POLICY_REFRESH_SIZE);
152 // If the policy was invalid and the version stored matches the latest
153 // invalidation version, acknowledge the latest invalidation.
154 if (invalid_ && store->invalidation_version() == invalidation_version_)
155 AcknowledgeInvalidation();
158 UpdateRegistration(store->policy());
159 UpdateMaxFetchDelay(store->policy_map());
162 void CloudPolicyInvalidator::OnStoreError(CloudPolicyStore* store) {}
164 void CloudPolicyInvalidator::HandleInvalidation(
165 const syncer::Invalidation& invalidation) {
166 // Ignore old invalidations.
167 if (invalid_ &&
168 !invalidation.is_unknown_version() &&
169 invalidation.version() <= invalidation_version_) {
170 return;
173 // If there is still a pending invalidation, acknowledge it, since we only
174 // care about the latest invalidation.
175 if (invalid_)
176 AcknowledgeInvalidation();
178 // Update invalidation state.
179 invalid_ = true;
180 invalidation_.reset(new syncer::Invalidation(invalidation));
182 // When an invalidation with unknown version is received, use negative
183 // numbers based on the number of such invalidations received. This
184 // ensures that the version numbers do not collide with "real" versions
185 // (which are positive) or previous invalidations with unknown version.
186 if (invalidation.is_unknown_version()) {
187 invalidation_version_ = -(++unknown_version_invalidation_count_);
188 } else {
189 invalidation_version_ = invalidation.version();
192 // In order to prevent the cloud policy server from becoming overwhelmed when
193 // a policy with many users is modified, delay for a random period of time
194 // before fetching the policy. Delay for at least 20ms so that if multiple
195 // invalidations are received in quick succession, only one fetch will be
196 // performed.
197 base::TimeDelta delay = base::TimeDelta::FromMilliseconds(
198 base::RandInt(20, max_fetch_delay_));
200 std::string payload;
201 if (!invalidation.is_unknown_version())
202 payload = invalidation.payload();
204 // If there is a payload, the policy can be refreshed at any time, so set
205 // the version and payload on the client immediately. Otherwise, the refresh
206 // must only run after at least kMissingPayloadDelay minutes.
207 if (!payload.empty())
208 core_->client()->SetInvalidationInfo(invalidation_version_, payload);
209 else
210 delay += base::TimeDelta::FromMinutes(kMissingPayloadDelay);
212 // Schedule the policy to be refreshed.
213 task_runner_->PostDelayedTask(
214 FROM_HERE,
215 base::Bind(
216 &CloudPolicyInvalidator::RefreshPolicy,
217 weak_factory_.GetWeakPtr(),
218 payload.empty() /* is_missing_payload */),
219 delay);
221 // Update the kMetricPolicyInvalidations histogram.
222 UMA_HISTOGRAM_BOOLEAN(kMetricPolicyInvalidations, !payload.empty());
225 void CloudPolicyInvalidator::UpdateRegistration(
226 const enterprise_management::PolicyData* policy) {
227 // Create the ObjectId based on the policy data.
228 // If the policy does not specify an the ObjectId, then unregister.
229 if (!policy ||
230 !policy->has_timestamp() ||
231 !policy->has_invalidation_source() ||
232 !policy->has_invalidation_name()) {
233 Unregister();
234 return;
236 invalidation::ObjectId object_id(
237 policy->invalidation_source(),
238 policy->invalidation_name());
240 // If the policy object id in the policy data is different from the currently
241 // registered object id, update the object registration.
242 if (!registered_timestamp_ || !(object_id == object_id_))
243 Register(policy->timestamp(), object_id);
246 void CloudPolicyInvalidator::Register(
247 int64 timestamp,
248 const invalidation::ObjectId& object_id) {
249 // Register this handler with the invalidation service if needed.
250 if (!registered_timestamp_) {
251 OnInvalidatorStateChange(invalidation_service_->GetInvalidatorState());
252 invalidation_service_->RegisterInvalidationHandler(this);
255 // Update internal state.
256 if (invalid_)
257 AcknowledgeInvalidation();
258 registered_timestamp_ = timestamp;
259 object_id_ = object_id;
260 UpdateInvalidationsEnabled();
262 // Update registration with the invalidation service.
263 syncer::ObjectIdSet ids;
264 ids.insert(object_id);
265 invalidation_service_->UpdateRegisteredInvalidationIds(this, ids);
268 void CloudPolicyInvalidator::Unregister() {
269 if (registered_timestamp_) {
270 if (invalid_)
271 AcknowledgeInvalidation();
272 invalidation_service_->UpdateRegisteredInvalidationIds(
273 this,
274 syncer::ObjectIdSet());
275 invalidation_service_->UnregisterInvalidationHandler(this);
276 registered_timestamp_ = 0;
277 UpdateInvalidationsEnabled();
281 void CloudPolicyInvalidator::UpdateMaxFetchDelay(const PolicyMap& policy_map) {
282 int delay;
284 // Try reading the delay from the policy.
285 const base::Value* delay_policy_value =
286 policy_map.GetValue(key::kMaxInvalidationFetchDelay);
287 if (delay_policy_value && delay_policy_value->GetAsInteger(&delay)) {
288 set_max_fetch_delay(delay);
289 return;
292 // Try reading the delay from the command line switch.
293 std::string delay_string =
294 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
295 switches::kCloudPolicyInvalidationDelay);
296 if (base::StringToInt(delay_string, &delay)) {
297 set_max_fetch_delay(delay);
298 return;
301 set_max_fetch_delay(kMaxFetchDelayDefault);
304 void CloudPolicyInvalidator::set_max_fetch_delay(int delay) {
305 if (delay < kMaxFetchDelayMin)
306 max_fetch_delay_ = kMaxFetchDelayMin;
307 else if (delay > kMaxFetchDelayMax)
308 max_fetch_delay_ = kMaxFetchDelayMax;
309 else
310 max_fetch_delay_ = delay;
313 void CloudPolicyInvalidator::UpdateInvalidationsEnabled() {
314 bool invalidations_enabled =
315 invalidation_service_enabled_ && registered_timestamp_;
316 if (invalidations_enabled_ != invalidations_enabled) {
317 invalidations_enabled_ = invalidations_enabled;
318 core_->refresh_scheduler()->SetInvalidationServiceAvailability(
319 invalidations_enabled);
323 void CloudPolicyInvalidator::RefreshPolicy(bool is_missing_payload) {
324 DCHECK(thread_checker_.CalledOnValidThread());
325 // In the missing payload case, the invalidation version has not been set on
326 // the client yet, so set it now that the required time has elapsed.
327 if (is_missing_payload)
328 core_->client()->SetInvalidationInfo(invalidation_version_, std::string());
329 core_->refresh_scheduler()->RefreshSoon();
332 void CloudPolicyInvalidator::AcknowledgeInvalidation() {
333 DCHECK(invalid_);
334 invalid_ = false;
335 core_->client()->SetInvalidationInfo(0, std::string());
336 invalidation_->Acknowledge();
337 invalidation_.reset();
338 // Cancel any scheduled policy refreshes.
339 weak_factory_.InvalidateWeakPtrs();
342 bool CloudPolicyInvalidator::IsPolicyChanged(
343 const enterprise_management::PolicyData* policy) {
344 // Determine if the policy changed by comparing its hash value to the
345 // previous policy's hash value.
346 uint32 new_hash_value = 0;
347 if (policy && policy->has_policy_value())
348 new_hash_value = base::Hash(policy->policy_value());
349 bool changed = new_hash_value != policy_hash_value_;
350 policy_hash_value_ = new_hash_value;
351 return changed;
354 int CloudPolicyInvalidator::GetPolicyRefreshMetric(bool policy_changed) {
355 if (policy_changed) {
356 if (invalid_)
357 return METRIC_POLICY_REFRESH_INVALIDATED_CHANGED;
358 if (invalidations_enabled_)
359 return METRIC_POLICY_REFRESH_CHANGED;
360 return METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS;
362 if (invalid_)
363 return METRIC_POLICY_REFRESH_INVALIDATED_UNCHANGED;
364 return METRIC_POLICY_REFRESH_UNCHANGED;
367 } // namespace policy