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"
8 #include "base/command_line.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"
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
),
37 task_runner_(task_runner
),
38 invalidation_service_(NULL
),
39 invalidations_enabled_(false),
40 invalidation_service_enabled_(false),
41 registered_timestamp_(0),
43 invalidation_version_(0),
44 unknown_version_invalidation_count_(0),
46 max_fetch_delay_(kMaxFetchDelayDefault
),
47 policy_hash_value_(0) {
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
;
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);
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_
);
101 // Acknowledge all except the invalidation with the highest version.
102 syncer::SingleObjectInvalidationSet::const_reverse_iterator it
=
105 for ( ; it
!= list
.rend(); ++it
) {
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());
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
) {
128 core_
->store()->RemoveObserver(this);
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.
168 !invalidation
.is_unknown_version() &&
169 invalidation
.version() <= invalidation_version_
) {
173 // If there is still a pending invalidation, acknowledge it, since we only
174 // care about the latest invalidation.
176 AcknowledgeInvalidation();
178 // Update invalidation state.
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_
);
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
197 base::TimeDelta delay
= base::TimeDelta::FromMilliseconds(
198 base::RandInt(20, max_fetch_delay_
));
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
);
210 delay
+= base::TimeDelta::FromMinutes(kMissingPayloadDelay
);
212 // Schedule the policy to be refreshed.
213 task_runner_
->PostDelayedTask(
216 &CloudPolicyInvalidator::RefreshPolicy
,
217 weak_factory_
.GetWeakPtr(),
218 payload
.empty() /* is_missing_payload */),
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.
230 !policy
->has_timestamp() ||
231 !policy
->has_invalidation_source() ||
232 !policy
->has_invalidation_name()) {
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(
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.
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_
) {
271 AcknowledgeInvalidation();
272 invalidation_service_
->UpdateRegisteredInvalidationIds(
274 syncer::ObjectIdSet());
275 invalidation_service_
->UnregisterInvalidationHandler(this);
276 registered_timestamp_
= 0;
277 UpdateInvalidationsEnabled();
281 void CloudPolicyInvalidator::UpdateMaxFetchDelay(const PolicyMap
& policy_map
) {
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
);
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
);
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
;
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() {
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
;
354 int CloudPolicyInvalidator::GetPolicyRefreshMetric(bool policy_changed
) {
355 if (policy_changed
) {
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
;
363 return METRIC_POLICY_REFRESH_INVALIDATED_UNCHANGED
;
364 return METRIC_POLICY_REFRESH_UNCHANGED
;
367 } // namespace policy