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

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

Issue 109743002: Move policy code into components/policy. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: moar fixes Created 7 years 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 | Annotate | Revision Log
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698