OLD | NEW |
---|---|
(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 | |
OLD | NEW |