Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(137)

Side by Side Diff: chrome/browser/policy/cloud/cloud_policy_invalidator.cc

Issue 19733003: Implement cloud policy invalidations using the invalidation service framework. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 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/command_line.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/strings/string_number_conversions.h"
14 #include "base/time/time.h"
15 #include "base/values.h"
16 #include "chrome/browser/invalidation/invalidation_service.h"
17 #include "chrome/browser/invalidation/invalidation_service_factory.h"
18 #include "chrome/browser/policy/cloud/enterprise_metrics.h"
19 #include "chrome/common/chrome_switches.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 = 5000;
27 const int CloudPolicyInvalidator::kMaxFetchDelayMin = 1000;
28 const int CloudPolicyInvalidator::kMaxFetchDelayMax = 300000;
29
30 CloudPolicyInvalidator::CloudPolicyInvalidator(
31 CloudPolicyInvalidationHandler* invalidation_handler,
32 CloudPolicyStore* store,
33 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
34 : invalidation_handler_(invalidation_handler),
35 store_(store),
36 task_runner_(task_runner),
37 profile_(NULL),
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 ack_handle_(syncer::AckHandle::InvalidAckHandle()),
46 weak_factory_(this),
47 max_fetch_delay_(kMaxFetchDelayDefault) {
48 DCHECK(invalidation_handler);
49 DCHECK(store);
50 DCHECK(task_runner.get());
51 DCHECK(!IsInitialized());
52 }
53
54 CloudPolicyInvalidator::~CloudPolicyInvalidator() {}
55
56 void CloudPolicyInvalidator::Initialize(Profile* profile) {
57 DCHECK(!IsInitialized());
58 DCHECK(profile);
59 profile_ = profile;
60 Initialize();
61 }
62
63 void CloudPolicyInvalidator::Initialize(
64 invalidation::InvalidationService* invalidation_service) {
65 DCHECK(!IsInitialized());
66 DCHECK(invalidation_service);
67 invalidation_service_ = invalidation_service;
68 Initialize();
69 }
70
71 void CloudPolicyInvalidator::Shutdown() {
72 if (IsInitialized()) {
73 if (registered_timestamp_)
74 invalidation_service_->UnregisterInvalidationHandler(this);
75 store_->RemoveObserver(this);
76 }
77 }
78
79 void CloudPolicyInvalidator::OnInvalidatorStateChange(
80 syncer::InvalidatorState state) {
81 invalidation_service_enabled_ = state == syncer::INVALIDATIONS_ENABLED;
82 UpdateInvalidationsEnabled();
83 }
84
85 void CloudPolicyInvalidator::OnIncomingInvalidation(
86 const syncer::ObjectIdInvalidationMap& invalidation_map) {
87 DCHECK(thread_checker_.CalledOnValidThread());
88 const syncer::ObjectIdInvalidationMap::const_iterator invalidation =
89 invalidation_map.find(object_id_);
90 if (invalidation == invalidation_map.end()) {
91 NOTREACHED();
92 return;
93 }
94 HandleInvalidation(invalidation->second);
95 }
96
97 void CloudPolicyInvalidator::OnStoreLoaded(CloudPolicyStore* store) {
98 DCHECK(IsInitialized());
99 DCHECK(thread_checker_.CalledOnValidThread());
100 if (registered_timestamp_) {
101 // Update the kMetricPolicyRefresh histogram. In some cases, this object can
102 // be constructed during an OnStoreLoaded callback, which causes
103 // OnStoreLoaded to be called twice at initialization time, so make sure
104 // that the timestamp does not match the timestamp at which registration
105 // occurred. We only measure changes which occur after registration.
106 if (!store->policy() || !store->policy()->has_timestamp() ||
107 store->policy()->timestamp() != registered_timestamp_) {
108 UMA_HISTOGRAM_ENUMERATION(
109 kMetricPolicyRefresh,
110 GetPolicyRefreshMetric(),
111 kMetricPolicyRefreshSize);
112 }
113
114 // If the policy was invalid and the version stored matches the latest
115 // invalidation version, acknowledge the latest invalidation.
116 if (invalid_ && store->invalidation_version() == invalidation_version_)
117 AcknowledgeInvalidation();
118 }
119
120 UpdateRegistration(store->policy());
121 UpdateMaxFetchDelay(store->policy_map());
122 }
123
124 void CloudPolicyInvalidator::OnStoreError(CloudPolicyStore* store) {}
125
126 void CloudPolicyInvalidator::Initialize() {
127 OnStoreLoaded(store_);
128 store_->AddObserver(this);
129 }
130
131 bool CloudPolicyInvalidator::IsInitialized() {
132 // Could have been initialized with a profile or invalidation service.
133 return profile_ || invalidation_service_;
134 }
135
136 void CloudPolicyInvalidator::HandleInvalidation(
137 const syncer::Invalidation& invalidation) {
138 // The invalidation service may send an invalidation more than once if there
139 // is a delay in acknowledging it. Duplicate invalidations are ignored.
140 if (invalid_ && ack_handle_.Equals(invalidation.ack_handle))
141 return;
142
143 // If there is still a pending invalidation, acknowledge it, since we only
144 // care about the latest invalidation.
145 if (invalid_)
146 AcknowledgeInvalidation();
147
148 // Update invalidation state.
149 invalid_ = true;
150 ack_handle_ = invalidation.ack_handle;
151 invalidation_version_ = invalidation.version;
152
153 // When an invalidation with unknown version is received, use negative
154 // numbers based on the number of such invalidations received. This
155 // ensures that the version numbers do not collide with "real" versions
156 // (which are positive) or previous invalidations with unknown version.
157 if (invalidation_version_ == syncer::Invalidation::kUnknownVersion)
158 invalidation_version_ = -(++unknown_version_invalidation_count_);
159
160 // In order to prevent the cloud policy server from becoming overwhelmed when
161 // a policy with many users is modified, delay for a random period of time
162 // before fetching the policy. Delay for at least 20ms so that if multiple
163 // invalidations are received in quick succession, only one fetch will be
164 // performed.
165 base::TimeDelta delay = base::TimeDelta::FromMilliseconds(
166 base::RandInt(20, max_fetch_delay_));
167
168 // If there is a payload, the invalidate callback can run at any time, so set
169 // the version and payload on the client immediately. Otherwise, the callback
170 // must only run after at least kMissingPayloadDelay minutes.
171 const std::string& payload = invalidation.payload;
172 if (!invalidation.payload.empty())
173 invalidation_handler_->SetInvalidationInfo(invalidation_version_, payload);
174 else
175 delay += base::TimeDelta::FromMinutes(kMissingPayloadDelay);
176
177 // Schedule the invalidate callback to run.
178 task_runner_->PostDelayedTask(
179 FROM_HERE,
180 base::Bind(
181 &CloudPolicyInvalidator::RunInvalidateCallback,
182 weak_factory_.GetWeakPtr(),
183 payload.empty() /* is_missing_payload */),
184 delay);
185
186 // Update the kMetricPolicyInvalidations histogram.
187 UMA_HISTOGRAM_ENUMERATION(
188 kMetricPolicyInvalidations,
189 payload.empty() ?
190 kMetricPolicyInvalidationsNoPayload :
191 kMetricPolicyInvalidationsPayload,
192 kMetricPolicyInvalidationsSize);
193 }
194
195 void CloudPolicyInvalidator::UpdateRegistration(
196 const enterprise_management::PolicyData* policy) {
197 // Create the ObjectId based on the policy data.
198 // If the policy does not specify an the ObjectId, then unregister.
199 if (!policy ||
200 !policy->has_timestamp() ||
201 !policy->has_invalidation_source() ||
202 !policy->has_invalidation_name()) {
203 Unregister();
204 return;
205 }
206 invalidation::ObjectId object_id(
207 policy->invalidation_source(),
208 policy->invalidation_name());
209
210 // If the policy object id in the policy data is different from the currently
211 // registered object id, update the object registration.
212 if (!registered_timestamp_ || !(object_id == object_id_))
213 Register(policy->timestamp(), object_id);
214 }
215
216 void CloudPolicyInvalidator::Register(
217 int64 timestamp,
218 const invalidation::ObjectId& object_id) {
219 // Get the invalidation service from the profile if needed.
220 if (!invalidation_service_) {
221 DCHECK(profile_);
222 invalidation_service_ =
223 invalidation::InvalidationServiceFactory::GetForProfile(profile_);
224 if (!invalidation_service_)
225 return;
226 }
227
228 // Register this handler with the invalidation service if needed.
229 if (!registered_timestamp_) {
230 OnInvalidatorStateChange(invalidation_service_->GetInvalidatorState());
231 invalidation_service_->RegisterInvalidationHandler(this);
232 }
233
234 // Update internal state.
235 if (invalid_)
236 AcknowledgeInvalidation();
237 registered_timestamp_ = timestamp;
238 object_id_ = object_id;
239 UpdateInvalidationsEnabled();
240
241 // Update registration with the invalidation service.
242 syncer::ObjectIdSet ids;
243 ids.insert(object_id);
244 invalidation_service_->UpdateRegisteredInvalidationIds(this, ids);
245 }
246
247 void CloudPolicyInvalidator::Unregister() {
248 if (registered_timestamp_) {
249 if (invalid_)
250 AcknowledgeInvalidation();
251 invalidation_service_->UpdateRegisteredInvalidationIds(
252 this,
253 syncer::ObjectIdSet());
254 invalidation_service_->UnregisterInvalidationHandler(this);
255 registered_timestamp_ = 0;
256 UpdateInvalidationsEnabled();
257 }
258 }
259
260 void CloudPolicyInvalidator::UpdateMaxFetchDelay(const PolicyMap& policy_map) {
261 int delay;
262
263 // Try reading the delay from the policy.
264 const base::Value* delay_policy_value =
265 policy_map.GetValue(key::kMaxInvalidationFetchDelay);
266 if (delay_policy_value && delay_policy_value->GetAsInteger(&delay)) {
267 set_max_fetch_delay(delay);
268 return;
269 }
270
271 // Try reading the delay from the command line switch.
272 std::string delay_string =
273 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
274 switches::kCloudPolicyInvalidationDelay);
275 if (base::StringToInt(delay_string, &delay)) {
276 set_max_fetch_delay(delay);
277 return;
278 }
279
280 set_max_fetch_delay(kMaxFetchDelayDefault);
281 }
282
283 void CloudPolicyInvalidator::set_max_fetch_delay(int delay) {
284 if (delay < kMaxFetchDelayMin)
285 max_fetch_delay_ = kMaxFetchDelayMin;
286 else if (delay > kMaxFetchDelayMax)
287 max_fetch_delay_ = kMaxFetchDelayMax;
288 else
289 max_fetch_delay_ = delay;
290 }
291
292 void CloudPolicyInvalidator::UpdateInvalidationsEnabled() {
293 bool invalidations_enabled =
294 invalidation_service_enabled_ && registered_timestamp_;
295 if (invalidations_enabled_ != invalidations_enabled) {
296 invalidations_enabled_ = invalidations_enabled;
297 invalidation_handler_->OnInvalidatorStateChanged(invalidations_enabled);
298 }
299 }
300
301 void CloudPolicyInvalidator::RunInvalidateCallback(bool is_missing_payload) {
302 DCHECK(thread_checker_.CalledOnValidThread());
303 // In the missing payload case, the invalidation version has not been set on
304 // the client yet, so set it now that the required time has elapsed.
305 if (is_missing_payload) {
306 invalidation_handler_->SetInvalidationInfo(
307 invalidation_version_,
308 std::string());
309 }
310 invalidation_handler_->InvalidatePolicy();
311 }
312
313 void CloudPolicyInvalidator::AcknowledgeInvalidation() {
314 DCHECK(invalid_);
315 invalid_ = false;
316 invalidation_handler_->SetInvalidationInfo(0, std::string());
317 invalidation_service_->AcknowledgeInvalidation(object_id_, ack_handle_);
318 // Cancel any scheduled invalidate callbacks.
319 weak_factory_.InvalidateWeakPtrs();
320 }
321
322 int CloudPolicyInvalidator::GetPolicyRefreshMetric() {
323 if (store_->policy_changed()) {
324 if (invalid_)
325 return kMetricPolicyRefreshInvalidatedChanged;
326 if (invalidations_enabled_)
327 return kMetricPolicyRefreshChanged;
328 return kMetricPolicyRefreshChangedNoInvalidations;
329 }
330 if (invalid_)
331 return kMetricPolicyRefreshInvalidatedUnchanged;
332 return kMetricPolicyRefreshUnchanged;
333 }
334
335 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698