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

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, 5 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/command_line.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/metrics/histogram.h"
10 #include "base/rand_util.h"
11 #include "base/sequenced_task_runner.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/time/time.h"
14 #include "base/values.h"
15 #include "chrome/browser/invalidation/invalidation_service.h"
16 #include "chrome/browser/policy/cloud/cloud_policy_client.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 CloudPolicyClient* client,
33 CloudPolicyStore* store,
34 const scoped_refptr<base::SequencedTaskRunner>& task_runner,
35 const base::Closure& invalidate)
36 : invalidation_service_(invalidation_service),
37 client_(client),
38 store_(store),
39 task_runner_(task_runner),
40 invalidate_(invalidate),
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(client);
50 DCHECK(store);
51 DCHECK(task_runner.get());
52
53 invalidation_service->RegisterInvalidationHandler(this);
54 OnStoreLoaded(store);
55 store->AddObserver(this);
56 }
57
58 CloudPolicyInvalidator::~CloudPolicyInvalidator() {
59 invalidation_service_->UnregisterInvalidationHandler(this);
60 store_->RemoveObserver(this);
61 }
62
63 void CloudPolicyInvalidator::Unregister() {
64 if (invalid_)
65 AcknowledgeInvalidation();
66 invalidation_service_->UpdateRegisteredInvalidationIds(
67 this,
68 syncer::ObjectIdSet());
69 registered_timestamp_ = 0;
70 }
71
72 void CloudPolicyInvalidator::OnInvalidatorStateChange(
73 syncer::InvalidatorState state) {}
74
75 void CloudPolicyInvalidator::OnIncomingInvalidation(
76 const syncer::ObjectIdInvalidationMap& invalidation_map) {
77 DCHECK(thread_checker_.CalledOnValidThread());
78 const syncer::ObjectIdInvalidationMap::const_iterator invalidation =
79 invalidation_map.find(object_id_);
80 if (invalidation == invalidation_map.end()) {
81 NOTREACHED();
82 return;
83 }
84 HandleInvalidation(invalidation->second);
85 }
86
87 void CloudPolicyInvalidator::OnStoreLoaded(CloudPolicyStore* store) {
88 DCHECK(thread_checker_.CalledOnValidThread());
89 if (registered_timestamp_) {
90 // Update the kMetricPolicyRefresh histogram. In some cases, this object can
91 // be constructed during an OnStoreLoaded callback, which causes
92 // OnStoreLoaded to be called twice at initialization time, so make sure
93 // that the timestamp does not match the timestamp at which registration
94 // occurred. We only measure changes which occur after registration.
95 if (!store->policy() || !store->policy()->has_timestamp() ||
96 store->policy()->timestamp() != registered_timestamp_) {
97 UMA_HISTOGRAM_ENUMERATION(
98 kMetricPolicyRefresh,
99 GetPolicyRefreshMetric(),
100 kMetricPolicyRefreshSize);
101 }
102
103 // If the policy was invalid and the version stored matches the latest
104 // invalidation version, acknowledge the latest invalidation.
105 if (invalid_ && store->invalidation_version() == invalidation_version_)
106 AcknowledgeInvalidation();
107 }
108
109 UpdateRegistration(store->policy());
110 UpdateMaxFetchDelay(store->policy_map());
111 }
112
113 void CloudPolicyInvalidator::OnStoreError(CloudPolicyStore* store) {}
114
115 void CloudPolicyInvalidator::HandleInvalidation(
rlarocque 2013/07/23 17:43:08 The invalidations framework currently repeatedly r
Steve Condie 2013/07/24 01:42:04 Thanks for pointing this out. My solution is to ig
116 const syncer::Invalidation& invalidation) {
117 // If there is still a pending invalidation, acknowledge it, since we only
118 // care about the latest invalidation.
119 if (invalid_)
120 AcknowledgeInvalidation();
rlarocque 2013/07/23 17:43:08 For example, this probably doesn't work so well if
Steve Condie 2013/07/24 01:42:04 Agreed, this was erroneous in that case.
121
122 // Update invalidation state.
123 invalid_ = true;
124 ack_handle_ = invalidation.ack_handle;
125 invalidation_version_ = invalidation.version;
126
127 // When an invalidation with unknown version is received, use negative
128 // numbers based on the number of such invalidations received. This
129 // ensures that the version numbers do not collide with "real" versions
130 // (which are positive) or previous invalidations with unknown version.
131 if (invalidation_version_ == syncer::Invalidation::kUnknownVersion)
132 invalidation_version_ = -(++unknown_version_invalidation_count_);
133
134 // In order to prevent the cloud policy server from becoming overwhelmed when
135 // a policy with many users is modified, delay for a random period of time
136 // before fetching the policy. Delay for at least 20ms so that if multiple
137 // invalidations are received in quick succession, only one fetch will be
138 // performed.
139 base::TimeDelta delay = base::TimeDelta::FromMilliseconds(
140 base::RandInt(20, max_fetch_delay_));
141
142 // If there is a payload, the invalidate callback can run at any time, so set
143 // the version and payload on the client immediately. Otherwise, the callback
144 // must only run after at least kMissingPayloadDelay minutes.
145 const std::string& payload = invalidation.payload;
146 if (!invalidation.payload.empty())
147 client_->SetInvalidationInfo(invalidation_version_, payload);
148 else
149 delay += base::TimeDelta::FromMinutes(kMissingPayloadDelay);
150
151 // Schedule the invalidate callback to run.
152 task_runner_->PostDelayedTask(
rlarocque 2013/07/23 17:43:08 Would you be interested in cancelling any existing
Steve Condie 2013/07/24 01:42:04 The previous tasks are cancelled by virtue of call
153 FROM_HERE,
154 base::Bind(
155 &CloudPolicyInvalidator::RunInvalidateCallback,
156 weak_factory_.GetWeakPtr(),
157 payload.empty() /* is_missing_payload */),
158 delay);
159
160 // Update the kMetricPolicyInvalidations histogram.
161 UMA_HISTOGRAM_ENUMERATION(
162 kMetricPolicyInvalidations,
163 payload.empty() ?
164 kMetricPolicyInvalidationsNoPayload :
165 kMetricPolicyInvalidationsPayload,
166 kMetricPolicyInvalidationsSize);
167 }
168
169 void CloudPolicyInvalidator::UpdateRegistration(
170 const enterprise_management::PolicyData* policy) {
171 // Create the ObjectId based on the policy data.
172 // If the policy does not specify an the ObjectId, then unregister.
173 if (!policy ||
174 !policy->has_timestamp() ||
175 !policy->has_invalidation_source() ||
176 !policy->has_invalidation_name()) {
177 if (registered_timestamp_)
178 Unregister();
179 return;
180 }
181 invalidation::ObjectId object_id(
182 policy->invalidation_source()/*1025*/,
183 policy->invalidation_name()/*"UENUPOL"*/);
184
185 // If the policy object id in the policy data is different from the currently
186 // registered object id, update the object registration.
187 if (!registered_timestamp_ || !(object_id == object_id_)) {
188 if (invalid_)
189 AcknowledgeInvalidation();
190 registered_timestamp_ = policy->timestamp();
191 object_id_ = object_id;
192
193 syncer::ObjectIdSet ids;
194 ids.insert(object_id);
195 invalidation_service_->UpdateRegisteredInvalidationIds(this, ids);
196 }
197 }
198
199 void CloudPolicyInvalidator::UpdateMaxFetchDelay(const PolicyMap& policy_map) {
200 int delay;
201
202 // Try reading the delay from the policy.
203 const base::Value* delay_policy_value =
204 policy_map.GetValue(key::kMaxInvalidationFetchDelay);
205 if (delay_policy_value && delay_policy_value->GetAsInteger(&delay)) {
206 set_max_fetch_delay(delay);
207 return;
208 }
209
210 // Try reading the delay from the command line switch.
211 std::string delay_string =
212 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
213 switches::kCloudPolicyInvalidationDelay);
214 if (base::StringToInt(delay_string, &delay)) {
215 set_max_fetch_delay(delay);
216 return;
217 }
218
219 max_fetch_delay_ = kMaxFetchDelayDefault;
220 }
221
222 void CloudPolicyInvalidator::set_max_fetch_delay(int delay) {
223 if (delay < kMaxFetchDelayMin)
224 max_fetch_delay_ = kMaxFetchDelayMin;
225 else if (delay > kMaxFetchDelayMax)
226 max_fetch_delay_ = kMaxFetchDelayMax;
227 else
228 max_fetch_delay_ = delay;
229 }
230
231 void CloudPolicyInvalidator::RunInvalidateCallback(bool is_missing_payload) {
232 DCHECK(thread_checker_.CalledOnValidThread());
233 // In the missing payload case, the invalidation version has not been set on
234 // the client yet, so set it now that the required time has elapsed.
235 if (is_missing_payload)
236 client_->SetInvalidationInfo(invalidation_version_, std::string());
237 invalidate_.Run();
rlarocque 2013/07/23 17:43:08 From where does AcknowledgeInvalidation() get call
Steve Condie 2013/07/24 01:42:04 The OnStoreLoaded method. That method is invoked a
238 }
239
240 void CloudPolicyInvalidator::AcknowledgeInvalidation() {
241 DCHECK(invalid_);
242 invalid_ = false;
243 client_->SetInvalidationInfo(0, std::string());
244 invalidation_service_->AcknowledgeInvalidation(object_id_, ack_handle_);
245 // Cancel any scheduled invalidate callbacks.
246 weak_factory_.InvalidateWeakPtrs();
rlarocque 2013/07/23 17:43:08 That's a neat trick. I had no idea that Invalidat
247 }
248
249 int CloudPolicyInvalidator::GetPolicyRefreshMetric() {
250 if (store_->policy_changed()) {
251 if (invalid_)
252 return kMetricPolicyRefreshInvalidatedChanged;
253 return kMetricPolicyRefreshChanged;
254 }
255 if (invalid_)
256 return kMetricPolicyRefreshInvalidatedUnchanged;
257 return kMetricPolicyRefreshUnchanged;
258 }
259
260 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698