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.
5 #include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h"
10 #include "base/bind_helpers.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/metrics/histogram.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/time/default_tick_clock.h"
15 #include "base/time/tick_clock.h"
16 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
22 // The maximum rate at which to refresh policies.
23 const size_t kMaxRefreshesPerHour = 5;
25 // The maximum time to wait for the invalidations service to become available
26 // before starting to issue requests.
27 const int kWaitForInvalidationsTimeoutSeconds = 5;
31 #if defined(OS_ANDROID) || defined(OS_IOS)
33 const int64 CloudPolicyRefreshScheduler::kDefaultRefreshDelayMs =
34 24 * 60 * 60 * 1000; // 1 day.
35 const int64 CloudPolicyRefreshScheduler::kUnmanagedRefreshDelayMs =
36 24 * 60 * 60 * 1000; // 1 day.
37 // Delay for periodic refreshes when the invalidations service is available,
39 // TODO(joaodasilva): increase this value once we're confident that the
40 // invalidations channel works as expected.
41 const int64 CloudPolicyRefreshScheduler::kWithInvalidationsRefreshDelayMs =
42 24 * 60 * 60 * 1000; // 1 day.
43 const int64 CloudPolicyRefreshScheduler::kInitialErrorRetryDelayMs =
44 5 * 60 * 1000; // 5 minutes.
45 const int64 CloudPolicyRefreshScheduler::kRefreshDelayMinMs =
46 30 * 60 * 1000; // 30 minutes.
47 const int64 CloudPolicyRefreshScheduler::kRefreshDelayMaxMs =
48 7 * 24 * 60 * 60 * 1000; // 1 week.
52 const int64 CloudPolicyRefreshScheduler::kDefaultRefreshDelayMs =
53 3 * 60 * 60 * 1000; // 3 hours.
54 const int64 CloudPolicyRefreshScheduler::kUnmanagedRefreshDelayMs =
55 24 * 60 * 60 * 1000; // 1 day.
56 // Delay for periodic refreshes when the invalidations service is available,
58 // TODO(joaodasilva): increase this value once we're confident that the
59 // invalidations channel works as expected.
60 const int64 CloudPolicyRefreshScheduler::kWithInvalidationsRefreshDelayMs =
61 3 * 60 * 60 * 1000; // 3 hours.
62 const int64 CloudPolicyRefreshScheduler::kInitialErrorRetryDelayMs =
63 5 * 60 * 1000; // 5 minutes.
64 const int64 CloudPolicyRefreshScheduler::kRefreshDelayMinMs =
65 30 * 60 * 1000; // 30 minutes.
66 const int64 CloudPolicyRefreshScheduler::kRefreshDelayMaxMs =
67 24 * 60 * 60 * 1000; // 1 day.
71 CloudPolicyRefreshScheduler::CloudPolicyRefreshScheduler(
72 CloudPolicyClient* client,
73 CloudPolicyStore* store,
74 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
77 task_runner_(task_runner),
78 error_retry_delay_ms_(kInitialErrorRetryDelayMs),
79 refresh_delay_ms_(kDefaultRefreshDelayMs),
80 rate_limiter_(kMaxRefreshesPerHour,
81 base::TimeDelta::FromHours(1),
82 base::Bind(&CloudPolicyRefreshScheduler::RefreshNow,
83 base::Unretained(this)),
85 scoped_ptr<base::TickClock>(new base::DefaultTickClock())),
86 invalidations_available_(false),
87 creation_time_(base::Time::NowFromSystemTime()) {
88 client_->AddObserver(this);
89 store_->AddObserver(this);
90 net::NetworkChangeNotifier::AddIPAddressObserver(this);
92 UpdateLastRefreshFromPolicy();
94 // Give some time for the invalidation service to become available before the
95 // first refresh if there is already policy present.
96 if (store->has_policy())
97 WaitForInvalidationService();
102 CloudPolicyRefreshScheduler::~CloudPolicyRefreshScheduler() {
103 store_->RemoveObserver(this);
104 client_->RemoveObserver(this);
105 net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
108 void CloudPolicyRefreshScheduler::SetRefreshDelay(int64 refresh_delay) {
109 refresh_delay_ms_ = std::min(std::max(refresh_delay, kRefreshDelayMinMs),
114 void CloudPolicyRefreshScheduler::RefreshSoon() {
115 // An external consumer needs a policy update now (e.g. a new extension, or
116 // the InvalidationService received a policy invalidation), so don't wait
117 // before fetching anymore.
118 wait_for_invalidations_timeout_callback_.Cancel();
119 rate_limiter_.PostRequest();
122 void CloudPolicyRefreshScheduler::SetInvalidationServiceAvailability(
124 if (!creation_time_.is_null()) {
125 base::TimeDelta elapsed = base::Time::NowFromSystemTime() - creation_time_;
126 UMA_HISTOGRAM_MEDIUM_TIMES("Enterprise.PolicyInvalidationsStartupTime",
128 creation_time_ = base::Time();
131 if (is_available == invalidations_available_) {
132 // No change in state. If we're currently WaitingForInvalidationService
133 // then the timeout task will eventually execute and trigger a reschedule;
134 // let the InvalidationService keep retrying until that happens.
138 wait_for_invalidations_timeout_callback_.Cancel();
139 invalidations_available_ = is_available;
141 // Schedule a refresh since the refresh delay has been updated; however, allow
142 // some time for the invalidation service to update. If it is now online, the
143 // wait allows pending invalidations to be delivered. If it is now offline,
144 // then the wait allows for the service to recover from transient failure
145 // before falling back on the polling behavior.
146 WaitForInvalidationService();
149 void CloudPolicyRefreshScheduler::OnPolicyFetched(CloudPolicyClient* client) {
150 error_retry_delay_ms_ = kInitialErrorRetryDelayMs;
152 // Schedule the next refresh.
153 last_refresh_ = base::Time::NowFromSystemTime();
157 void CloudPolicyRefreshScheduler::OnRegistrationStateChanged(
158 CloudPolicyClient* client) {
159 error_retry_delay_ms_ = kInitialErrorRetryDelayMs;
161 // The client might have registered, so trigger an immediate refresh.
165 void CloudPolicyRefreshScheduler::OnClientError(CloudPolicyClient* client) {
166 // Save the status for below.
167 DeviceManagementStatus status = client_->status();
169 // Schedule an error retry if applicable.
170 last_refresh_ = base::Time::NowFromSystemTime();
173 // Update the retry delay.
174 if (client->is_registered() &&
175 (status == DM_STATUS_REQUEST_FAILED ||
176 status == DM_STATUS_TEMPORARY_UNAVAILABLE)) {
177 error_retry_delay_ms_ = std::min(error_retry_delay_ms_ * 2,
180 error_retry_delay_ms_ = kInitialErrorRetryDelayMs;
184 void CloudPolicyRefreshScheduler::OnStoreLoaded(CloudPolicyStore* store) {
185 UpdateLastRefreshFromPolicy();
187 // Re-schedule the next refresh in case the is_managed bit changed.
191 void CloudPolicyRefreshScheduler::OnStoreError(CloudPolicyStore* store) {
192 // If |store_| fails, the is_managed bit that it provides may become stale.
193 // The best guess in that situation is to assume is_managed didn't change and
194 // continue using the stale information. Thus, no specific response to a store
195 // error is required. NB: Changes to is_managed fire OnStoreLoaded().
198 void CloudPolicyRefreshScheduler::OnIPAddressChanged() {
199 if (client_->status() == DM_STATUS_REQUEST_FAILED)
203 void CloudPolicyRefreshScheduler::UpdateLastRefreshFromPolicy() {
204 if (!last_refresh_.is_null())
207 // If the client has already fetched policy, assume that happened recently. If
208 // that assumption ever breaks, the proper thing to do probably is to move the
209 // |last_refresh_| bookkeeping to CloudPolicyClient.
210 if (!client_->responses().empty()) {
211 last_refresh_ = base::Time::NowFromSystemTime();
215 #if defined(OS_ANDROID) || defined(OS_IOS)
216 // Refreshing on mobile platforms:
217 // - if no user is signed-in then the |client_| is never registered and
218 // nothing happens here.
219 // - if the user is signed-in but isn't enterprise then the |client_| is
220 // never registered and nothing happens here.
221 // - if the user is signed-in but isn't registered for policy yet then the
222 // |client_| isn't registered either; the UserPolicySigninService will try
223 // to register, and OnRegistrationStateChanged() will be invoked later.
224 // - if the client is signed-in and has policy then its timestamp is used to
225 // determine when to perform the next fetch, which will be once the cached
226 // version is considered "old enough".
228 // If there is an old policy cache then a fetch will be performed "soon"; if
229 // that fetch fails then a retry is attempted after a delay, with exponential
230 // backoff. If those fetches keep failing then the cached timestamp is *not*
231 // updated, and another fetch (and subsequent retries) will be attempted
232 // again on the next startup.
234 // But if the cached policy is considered fresh enough then we try to avoid
235 // fetching again on startup; the Android logic differs from the desktop in
237 if (store_->has_policy() && store_->policy()->has_timestamp()) {
239 base::Time::UnixEpoch() +
240 base::TimeDelta::FromMilliseconds(store_->policy()->timestamp());
243 // If there is a cached non-managed response, make sure to only re-query the
244 // server after kUnmanagedRefreshDelayMs. NB: For existing policy, an
245 // immediate refresh is intentional.
246 if (store_->has_policy() && store_->policy()->has_timestamp() &&
247 !store_->is_managed()) {
249 base::Time::UnixEpoch() +
250 base::TimeDelta::FromMilliseconds(store_->policy()->timestamp());
255 void CloudPolicyRefreshScheduler::RefreshNow() {
256 last_refresh_ = base::Time();
260 void CloudPolicyRefreshScheduler::ScheduleRefresh() {
261 // If the client isn't registered, there is nothing to do.
262 if (!client_->is_registered()) {
263 refresh_callback_.Cancel();
267 // Don't schedule anything yet if we're still waiting for the invalidations
269 if (WaitingForInvalidationService())
272 // If policy invalidations are available then periodic updates are done at
273 // a much lower rate; otherwise use the |refresh_delay_ms_| value.
274 int64 refresh_delay_ms =
275 invalidations_available_ ? kWithInvalidationsRefreshDelayMs
278 // If there is a registration, go by the client's status. That will tell us
279 // what the appropriate refresh delay should be.
280 switch (client_->status()) {
281 case DM_STATUS_SUCCESS:
282 if (store_->is_managed())
283 RefreshAfter(refresh_delay_ms);
285 RefreshAfter(kUnmanagedRefreshDelayMs);
287 case DM_STATUS_SERVICE_ACTIVATION_PENDING:
288 case DM_STATUS_SERVICE_POLICY_NOT_FOUND:
289 RefreshAfter(refresh_delay_ms);
291 case DM_STATUS_REQUEST_FAILED:
292 case DM_STATUS_TEMPORARY_UNAVAILABLE:
293 RefreshAfter(error_retry_delay_ms_);
295 case DM_STATUS_REQUEST_INVALID:
296 case DM_STATUS_HTTP_STATUS_ERROR:
297 case DM_STATUS_RESPONSE_DECODING_ERROR:
298 case DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED:
299 RefreshAfter(kUnmanagedRefreshDelayMs);
301 case DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID:
302 case DM_STATUS_SERVICE_DEVICE_NOT_FOUND:
303 case DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER:
304 case DM_STATUS_SERVICE_DEVICE_ID_CONFLICT:
305 case DM_STATUS_SERVICE_MISSING_LICENSES:
306 case DM_STATUS_SERVICE_DEPROVISIONED:
307 case DM_STATUS_SERVICE_DOMAIN_MISMATCH:
308 // Need a re-registration, no use in retrying.
309 refresh_callback_.Cancel();
313 NOTREACHED() << "Invalid client status " << client_->status();
314 RefreshAfter(kUnmanagedRefreshDelayMs);
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();
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();
328 // This should never happen, as the registration change should have been
329 // handled via OnRegistrationStateChanged().
333 void CloudPolicyRefreshScheduler::RefreshAfter(int delta_ms) {
334 base::TimeDelta delta(base::TimeDelta::FromMilliseconds(delta_ms));
335 refresh_callback_.Cancel();
337 // Schedule the callback.
338 base::TimeDelta delay =
339 std::max((last_refresh_ + delta) - base::Time::NowFromSystemTime(),
341 refresh_callback_.Reset(
342 base::Bind(&CloudPolicyRefreshScheduler::PerformRefresh,
343 base::Unretained(this)));
344 task_runner_->PostDelayedTask(FROM_HERE, refresh_callback_.callback(), delay);
347 void CloudPolicyRefreshScheduler::WaitForInvalidationService() {
348 DCHECK(!WaitingForInvalidationService());
349 wait_for_invalidations_timeout_callback_.Reset(
351 &CloudPolicyRefreshScheduler::OnWaitForInvalidationServiceTimeout,
352 base::Unretained(this)));
353 base::TimeDelta delay =
354 base::TimeDelta::FromSeconds(kWaitForInvalidationsTimeoutSeconds);
355 task_runner_->PostDelayedTask(
357 wait_for_invalidations_timeout_callback_.callback(),
361 void CloudPolicyRefreshScheduler::OnWaitForInvalidationServiceTimeout() {
362 wait_for_invalidations_timeout_callback_.Cancel();
366 bool CloudPolicyRefreshScheduler::WaitingForInvalidationService() const {
367 return !wait_for_invalidations_timeout_callback_.IsCancelled();
370 } // namespace policy