| 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 |