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