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

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

Powered by Google App Engine
This is Rietveld 408576698