OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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_refresh_scheduler.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/bind_helpers.h" | |
11 #include "base/command_line.h" | |
12 #include "base/memory/scoped_ptr.h" | |
13 #include "base/metrics/histogram.h" | |
14 #include "base/sequenced_task_runner.h" | |
15 #include "base/time/default_tick_clock.h" | |
16 #include "base/time/tick_clock.h" | |
17 #include "chrome/browser/policy/cloud/cloud_policy_constants.h" | |
18 #include "components/policy/core/common/policy_switches.h" | |
19 | |
20 namespace policy { | |
21 | |
22 namespace { | |
23 | |
24 // The maximum rate at which to refresh policies. | |
25 const size_t kMaxRefreshesPerHour = 5; | |
26 | |
27 // The maximum time to wait for the invalidations service to become available | |
28 // before starting to issue requests. | |
29 const int kWaitForInvalidationsTimeoutSeconds = 5; | |
30 | |
31 } // namespace | |
32 | |
33 #if defined(OS_ANDROID) | |
34 | |
35 const int64 CloudPolicyRefreshScheduler::kDefaultRefreshDelayMs = | |
36 24 * 60 * 60 * 1000; // 1 day. | |
37 const int64 CloudPolicyRefreshScheduler::kUnmanagedRefreshDelayMs = | |
38 24 * 60 * 60 * 1000; // 1 day. | |
39 // Delay for periodic refreshes when the invalidations service is available, | |
40 // in milliseconds. | |
41 // TODO(joaodasilva): increase this value once we're confident that the | |
42 // invalidations channel works as expected. | |
43 const int64 CloudPolicyRefreshScheduler::kWithInvalidationsRefreshDelayMs = | |
44 24 * 60 * 60 * 1000; // 1 day. | |
45 const int64 CloudPolicyRefreshScheduler::kInitialErrorRetryDelayMs = | |
46 5 * 60 * 1000; // 5 minutes. | |
47 const int64 CloudPolicyRefreshScheduler::kRefreshDelayMinMs = | |
48 30 * 60 * 1000; // 30 minutes. | |
49 const int64 CloudPolicyRefreshScheduler::kRefreshDelayMaxMs = | |
50 7 * 24 * 60 * 60 * 1000; // 1 week. | |
51 | |
52 #else | |
53 | |
54 const int64 CloudPolicyRefreshScheduler::kDefaultRefreshDelayMs = | |
55 3 * 60 * 60 * 1000; // 3 hours. | |
56 const int64 CloudPolicyRefreshScheduler::kUnmanagedRefreshDelayMs = | |
57 24 * 60 * 60 * 1000; // 1 day. | |
58 // Delay for periodic refreshes when the invalidations service is available, | |
59 // in milliseconds. | |
60 // TODO(joaodasilva): increase this value once we're confident that the | |
61 // invalidations channel works as expected. | |
62 const int64 CloudPolicyRefreshScheduler::kWithInvalidationsRefreshDelayMs = | |
63 3 * 60 * 60 * 1000; // 3 hours. | |
64 const int64 CloudPolicyRefreshScheduler::kInitialErrorRetryDelayMs = | |
65 5 * 60 * 1000; // 5 minutes. | |
66 const int64 CloudPolicyRefreshScheduler::kRefreshDelayMinMs = | |
67 30 * 60 * 1000; // 30 minutes. | |
68 const int64 CloudPolicyRefreshScheduler::kRefreshDelayMaxMs = | |
69 24 * 60 * 60 * 1000; // 1 day. | |
70 | |
71 #endif | |
72 | |
73 CloudPolicyRefreshScheduler::CloudPolicyRefreshScheduler( | |
74 CloudPolicyClient* client, | |
75 CloudPolicyStore* store, | |
76 const scoped_refptr<base::SequencedTaskRunner>& task_runner) | |
77 : client_(client), | |
78 store_(store), | |
79 task_runner_(task_runner), | |
80 error_retry_delay_ms_(kInitialErrorRetryDelayMs), | |
81 refresh_delay_ms_(kDefaultRefreshDelayMs), | |
82 rate_limiter_(kMaxRefreshesPerHour, | |
83 base::TimeDelta::FromHours(1), | |
84 base::Bind(&CloudPolicyRefreshScheduler::RefreshNow, | |
85 base::Unretained(this)), | |
86 task_runner_, | |
87 scoped_ptr<base::TickClock>(new base::DefaultTickClock())), | |
88 invalidations_available_(false), | |
89 creation_time_(base::Time::NowFromSystemTime()) { | |
90 client_->AddObserver(this); | |
91 store_->AddObserver(this); | |
92 net::NetworkChangeNotifier::AddIPAddressObserver(this); | |
93 | |
94 UpdateLastRefreshFromPolicy(); | |
95 | |
96 // Give some time for the invalidation service to become available before the | |
97 // first refresh if there is already policy present. | |
98 if (store->has_policy()) | |
99 WaitForInvalidationService(); | |
100 else | |
101 ScheduleRefresh(); | |
102 } | |
103 | |
104 CloudPolicyRefreshScheduler::~CloudPolicyRefreshScheduler() { | |
105 store_->RemoveObserver(this); | |
106 client_->RemoveObserver(this); | |
107 net::NetworkChangeNotifier::RemoveIPAddressObserver(this); | |
108 } | |
109 | |
110 void CloudPolicyRefreshScheduler::SetRefreshDelay(int64 refresh_delay) { | |
111 refresh_delay_ms_ = std::min(std::max(refresh_delay, kRefreshDelayMinMs), | |
112 kRefreshDelayMaxMs); | |
113 ScheduleRefresh(); | |
114 } | |
115 | |
116 void CloudPolicyRefreshScheduler::RefreshSoon() { | |
117 // An external consumer needs a policy update now (e.g. a new extension, or | |
118 // the InvalidationService received a policy invalidation), so don't wait | |
119 // before fetching anymore. | |
120 wait_for_invalidations_timeout_callback_.Cancel(); | |
121 rate_limiter_.PostRequest(); | |
122 } | |
123 | |
124 void CloudPolicyRefreshScheduler::SetInvalidationServiceAvailability( | |
125 bool is_available) { | |
126 if (!creation_time_.is_null()) { | |
127 base::TimeDelta elapsed = base::Time::NowFromSystemTime() - creation_time_; | |
128 UMA_HISTOGRAM_MEDIUM_TIMES("Enterprise.PolicyInvalidationsStartupTime", | |
129 elapsed); | |
130 creation_time_ = base::Time(); | |
131 } | |
132 | |
133 if (is_available == invalidations_available_) { | |
134 // No change in state. If we're currently WaitingForInvalidationService | |
135 // then the timeout task will eventually execute and trigger a reschedule; | |
136 // let the InvalidationService keep retrying until that happens. | |
137 return; | |
138 } | |
139 | |
140 wait_for_invalidations_timeout_callback_.Cancel(); | |
141 invalidations_available_ = is_available; | |
142 | |
143 // Schedule a refresh since the refresh delay has been updated; however, allow | |
144 // some time for the invalidation service to update. If it is now online, the | |
145 // wait allows pending invalidations to be delivered. If it is now offline, | |
146 // then the wait allows for the service to recover from transient failure | |
147 // before falling back on the polling behavior. | |
148 WaitForInvalidationService(); | |
149 } | |
150 | |
151 void CloudPolicyRefreshScheduler::OnPolicyFetched(CloudPolicyClient* client) { | |
152 error_retry_delay_ms_ = kInitialErrorRetryDelayMs; | |
153 | |
154 // Schedule the next refresh. | |
155 last_refresh_ = base::Time::NowFromSystemTime(); | |
156 ScheduleRefresh(); | |
157 } | |
158 | |
159 void CloudPolicyRefreshScheduler::OnRegistrationStateChanged( | |
160 CloudPolicyClient* client) { | |
161 error_retry_delay_ms_ = kInitialErrorRetryDelayMs; | |
162 | |
163 // The client might have registered, so trigger an immediate refresh. | |
164 RefreshNow(); | |
165 } | |
166 | |
167 void CloudPolicyRefreshScheduler::OnClientError(CloudPolicyClient* client) { | |
168 // Save the status for below. | |
169 DeviceManagementStatus status = client_->status(); | |
170 | |
171 // Schedule an error retry if applicable. | |
172 last_refresh_ = base::Time::NowFromSystemTime(); | |
173 ScheduleRefresh(); | |
174 | |
175 // Update the retry delay. | |
176 if (client->is_registered() && | |
177 (status == DM_STATUS_REQUEST_FAILED || | |
178 status == DM_STATUS_TEMPORARY_UNAVAILABLE)) { | |
179 error_retry_delay_ms_ = std::min(error_retry_delay_ms_ * 2, | |
180 refresh_delay_ms_); | |
181 } else { | |
182 error_retry_delay_ms_ = kInitialErrorRetryDelayMs; | |
183 } | |
184 } | |
185 | |
186 void CloudPolicyRefreshScheduler::OnStoreLoaded(CloudPolicyStore* store) { | |
187 UpdateLastRefreshFromPolicy(); | |
188 | |
189 // Re-schedule the next refresh in case the is_managed bit changed. | |
190 ScheduleRefresh(); | |
191 } | |
192 | |
193 void CloudPolicyRefreshScheduler::OnStoreError(CloudPolicyStore* store) { | |
194 // If |store_| fails, the is_managed bit that it provides may become stale. | |
195 // The best guess in that situation is to assume is_managed didn't change and | |
196 // continue using the stale information. Thus, no specific response to a store | |
197 // error is required. NB: Changes to is_managed fire OnStoreLoaded(). | |
198 } | |
199 | |
200 void CloudPolicyRefreshScheduler::OnIPAddressChanged() { | |
201 if (client_->status() == DM_STATUS_REQUEST_FAILED) | |
202 RefreshSoon(); | |
203 } | |
204 | |
205 void CloudPolicyRefreshScheduler::UpdateLastRefreshFromPolicy() { | |
206 if (!last_refresh_.is_null()) | |
207 return; | |
208 | |
209 // If the client has already fetched policy, assume that happened recently. If | |
210 // that assumption ever breaks, the proper thing to do probably is to move the | |
211 // |last_refresh_| bookkeeping to CloudPolicyClient. | |
212 if (!client_->responses().empty()) { | |
213 last_refresh_ = base::Time::NowFromSystemTime(); | |
214 return; | |
215 } | |
216 | |
217 #if defined(OS_ANDROID) | |
218 // Refreshing on Android: | |
219 // - if no user is signed-in then the |client_| is never registered and | |
220 // nothing happens here. | |
221 // - if the user is signed-in but isn't enterprise then the |client_| is | |
222 // never registered and nothing happens here. | |
223 // - if the user is signed-in but isn't registered for policy yet then the | |
224 // |client_| isn't registered either; the UserPolicySigninService will try | |
225 // to register, and OnRegistrationStateChanged() will be invoked later. | |
226 // - if the client is signed-in and has policy then its timestamp is used to | |
227 // determine when to perform the next fetch, which will be once the cached | |
228 // version is considered "old enough". | |
229 // | |
230 // If there is an old policy cache then a fetch will be performed "soon"; if | |
231 // that fetch fails then a retry is attempted after a delay, with exponential | |
232 // backoff. If those fetches keep failing then the cached timestamp is *not* | |
233 // updated, and another fetch (and subsequent retries) will be attempted | |
234 // again on the next startup. | |
235 // | |
236 // But if the cached policy is considered fresh enough then we try to avoid | |
237 // fetching again on startup; the Android logic differs from the desktop in | |
238 // this aspect. | |
239 if (store_->has_policy() && store_->policy()->has_timestamp()) { | |
240 last_refresh_ = | |
241 base::Time::UnixEpoch() + | |
242 base::TimeDelta::FromMilliseconds(store_->policy()->timestamp()); | |
243 } | |
244 #else | |
245 // If there is a cached non-managed response, make sure to only re-query the | |
246 // server after kUnmanagedRefreshDelayMs. NB: For existing policy, an | |
247 // immediate refresh is intentional. | |
248 if (store_->has_policy() && store_->policy()->has_timestamp() && | |
249 !store_->is_managed()) { | |
250 last_refresh_ = | |
251 base::Time::UnixEpoch() + | |
252 base::TimeDelta::FromMilliseconds(store_->policy()->timestamp()); | |
253 } | |
254 #endif | |
255 } | |
256 | |
257 void CloudPolicyRefreshScheduler::RefreshNow() { | |
258 last_refresh_ = base::Time(); | |
259 ScheduleRefresh(); | |
260 } | |
261 | |
262 void CloudPolicyRefreshScheduler::ScheduleRefresh() { | |
263 // If the client isn't registered, there is nothing to do. | |
264 if (!client_->is_registered()) { | |
265 refresh_callback_.Cancel(); | |
266 return; | |
267 } | |
268 | |
269 // Don't schedule anything yet if we're still waiting for the invalidations | |
270 // service. | |
271 if (WaitingForInvalidationService()) | |
272 return; | |
273 | |
274 // If policy invalidations are available then periodic updates are done at | |
275 // a much lower rate; otherwise use the |refresh_delay_ms_| value. | |
276 int64 refresh_delay_ms = | |
277 invalidations_available_ ? kWithInvalidationsRefreshDelayMs | |
278 : refresh_delay_ms_; | |
279 | |
280 // If there is a registration, go by the client's status. That will tell us | |
281 // what the appropriate refresh delay should be. | |
282 switch (client_->status()) { | |
283 case DM_STATUS_SUCCESS: | |
284 if (store_->is_managed()) | |
285 RefreshAfter(refresh_delay_ms); | |
286 else | |
287 RefreshAfter(kUnmanagedRefreshDelayMs); | |
288 return; | |
289 case DM_STATUS_SERVICE_ACTIVATION_PENDING: | |
290 case DM_STATUS_SERVICE_POLICY_NOT_FOUND: | |
291 RefreshAfter(refresh_delay_ms); | |
292 return; | |
293 case DM_STATUS_REQUEST_FAILED: | |
294 case DM_STATUS_TEMPORARY_UNAVAILABLE: | |
295 RefreshAfter(error_retry_delay_ms_); | |
296 return; | |
297 case DM_STATUS_REQUEST_INVALID: | |
298 case DM_STATUS_HTTP_STATUS_ERROR: | |
299 case DM_STATUS_RESPONSE_DECODING_ERROR: | |
300 case DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED: | |
301 RefreshAfter(kUnmanagedRefreshDelayMs); | |
302 return; | |
303 case DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID: | |
304 case DM_STATUS_SERVICE_DEVICE_NOT_FOUND: | |
305 case DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER: | |
306 case DM_STATUS_SERVICE_DEVICE_ID_CONFLICT: | |
307 case DM_STATUS_SERVICE_MISSING_LICENSES: | |
308 // Need a re-registration, no use in retrying. | |
309 refresh_callback_.Cancel(); | |
310 return; | |
311 } | |
312 | |
313 NOTREACHED() << "Invalid client status " << client_->status(); | |
314 RefreshAfter(kUnmanagedRefreshDelayMs); | |
315 } | |
316 | |
317 void CloudPolicyRefreshScheduler::PerformRefresh() { | |
318 if (client_->is_registered()) { | |
319 // Update |last_refresh_| so another fetch isn't triggered inadvertently. | |
320 last_refresh_ = base::Time::NowFromSystemTime(); | |
321 | |
322 // The result of this operation will be reported through a callback, at | |
323 // which point the next refresh will be scheduled. | |
324 client_->FetchPolicy(); | |
325 return; | |
326 } | |
327 | |
328 // This should never happen, as the registration change should have been | |
329 // handled via OnRegistrationStateChanged(). | |
330 NOTREACHED(); | |
331 } | |
332 | |
333 void CloudPolicyRefreshScheduler::RefreshAfter(int delta_ms) { | |
334 base::TimeDelta delta(base::TimeDelta::FromMilliseconds(delta_ms)); | |
335 refresh_callback_.Cancel(); | |
336 | |
337 // Schedule the callback. | |
338 base::TimeDelta delay = | |
339 std::max((last_refresh_ + delta) - base::Time::NowFromSystemTime(), | |
340 base::TimeDelta()); | |
341 refresh_callback_.Reset( | |
342 base::Bind(&CloudPolicyRefreshScheduler::PerformRefresh, | |
343 base::Unretained(this))); | |
344 task_runner_->PostDelayedTask(FROM_HERE, refresh_callback_.callback(), delay); | |
345 } | |
346 | |
347 void CloudPolicyRefreshScheduler::WaitForInvalidationService() { | |
348 DCHECK(!WaitingForInvalidationService()); | |
349 wait_for_invalidations_timeout_callback_.Reset( | |
350 base::Bind( | |
351 &CloudPolicyRefreshScheduler::OnWaitForInvalidationServiceTimeout, | |
352 base::Unretained(this))); | |
353 base::TimeDelta delay = | |
354 base::TimeDelta::FromSeconds(kWaitForInvalidationsTimeoutSeconds); | |
355 // Do not wait for the invalidation service if the feature is disabled. | |
356 if (CommandLine::ForCurrentProcess()->HasSwitch( | |
357 switches::kDisableCloudPolicyPush)) { | |
358 delay = base::TimeDelta(); | |
359 } | |
360 task_runner_->PostDelayedTask( | |
361 FROM_HERE, | |
362 wait_for_invalidations_timeout_callback_.callback(), | |
363 delay); | |
364 } | |
365 | |
366 void CloudPolicyRefreshScheduler::OnWaitForInvalidationServiceTimeout() { | |
367 wait_for_invalidations_timeout_callback_.Cancel(); | |
368 ScheduleRefresh(); | |
369 } | |
370 | |
371 bool CloudPolicyRefreshScheduler::WaitingForInvalidationService() const { | |
372 return !wait_for_invalidations_timeout_callback_.IsCancelled(); | |
373 } | |
374 | |
375 } // namespace policy | |
OLD | NEW |