blob: da011dfaf33218e603ada371fc0cb8721734df55 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/policy/cloud_policy_refresh_scheduler.h"
#include <algorithm>
#include "base/task_runner.h"
#include "chrome/browser/policy/cloud_policy_constants.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/common/chrome_notification_types.h"
#include "content/public/browser/notification_details.h"
namespace policy {
const int64 CloudPolicyRefreshScheduler::kUnmanagedRefreshDelayMs =
24 * 60 * 60 * 1000; // 1 day.
const int64 CloudPolicyRefreshScheduler::kInitialErrorRetryDelayMs =
5 * 60 * 1000; // 5 minutes.
const int64 CloudPolicyRefreshScheduler::kRefreshDelayMinMs =
30 * 60 * 1000; // 30 minutes.
const int64 CloudPolicyRefreshScheduler::kRefreshDelayMaxMs =
24 * 60 * 60 * 1000; // 1 day.
CloudPolicyRefreshScheduler::CloudPolicyRefreshScheduler(
CloudPolicyClient* client,
CloudPolicyStore* store,
PrefService* prefs,
const std::string& refresh_pref,
const scoped_refptr<base::TaskRunner>& task_runner)
: client_(client),
store_(store),
task_runner_(task_runner),
error_retry_delay_ms_(kInitialErrorRetryDelayMs) {
client_->AddObserver(this);
store_->AddObserver(this);
net::NetworkChangeNotifier::AddIPAddressObserver(this);
refresh_delay_.Init(refresh_pref.c_str(), prefs, this);
UpdateLastRefreshFromPolicy();
ScheduleRefresh();
}
CloudPolicyRefreshScheduler::~CloudPolicyRefreshScheduler() {
store_->RemoveObserver(this);
client_->RemoveObserver(this);
net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
}
void CloudPolicyRefreshScheduler::OnPolicyFetched(CloudPolicyClient* client) {
error_retry_delay_ms_ = kInitialErrorRetryDelayMs;
// Schedule the next refresh.
last_refresh_ = base::Time::NowFromSystemTime();
ScheduleRefresh();
}
void CloudPolicyRefreshScheduler::OnRegistrationStateChanged(
CloudPolicyClient* client) {
error_retry_delay_ms_ = kInitialErrorRetryDelayMs;
// The client might have registered, so trigger an immediate refresh.
last_refresh_ = base::Time();
ScheduleRefresh();
}
void CloudPolicyRefreshScheduler::OnClientError(CloudPolicyClient* client) {
// Save the status for below.
DeviceManagementStatus status = client_->status();
// Schedule an error retry if applicable.
last_refresh_ = base::Time::NowFromSystemTime();
ScheduleRefresh();
// Update the retry delay.
if (client->is_registered() &&
(status == DM_STATUS_REQUEST_FAILED ||
status == DM_STATUS_TEMPORARY_UNAVAILABLE)) {
error_retry_delay_ms_ = std::min(error_retry_delay_ms_ * 2,
GetRefreshDelay());
} else {
error_retry_delay_ms_ = kInitialErrorRetryDelayMs;
}
}
void CloudPolicyRefreshScheduler::OnStoreLoaded(CloudPolicyStore* store) {
UpdateLastRefreshFromPolicy();
// Re-schedule the next refresh in case the is_managed bit changed.
ScheduleRefresh();
}
void CloudPolicyRefreshScheduler::OnStoreError(CloudPolicyStore* store) {
// If |store_| fails, the is_managed bit that it provides may become stale.
// The best guess in that situation is to assume is_managed didn't change and
// continue using the stale information. Thus, no specific response to a store
// error is required. NB: Changes to is_managed fire OnStoreLoaded().
}
void CloudPolicyRefreshScheduler::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_EQ(chrome::NOTIFICATION_PREF_CHANGED, type);
DCHECK_EQ(refresh_delay_.GetPrefName(),
*content::Details<std::string>(details).ptr());
ScheduleRefresh();
}
void CloudPolicyRefreshScheduler::OnIPAddressChanged() {
if (client_->status() == DM_STATUS_REQUEST_FAILED)
RefreshAfter(0);
}
void CloudPolicyRefreshScheduler::UpdateLastRefreshFromPolicy() {
if (!last_refresh_.is_null())
return;
// If the client has already fetched policy, assume that happened recently. If
// that assumption ever breaks, the proper thing to do probably is to move the
// |last_refresh_| bookkeeping to CloudPolicyClient.
if (client_->policy()) {
last_refresh_ = base::Time::NowFromSystemTime();
return;
}
// If there is a cached non-managed response, make sure to only re-query the
// server after kUnmanagedRefreshDelayMs. NB: For existing policy, an
// immediate refresh is intentional.
if (store_->has_policy() && !store_->is_managed()) {
last_refresh_ =
base::Time::UnixEpoch() +
base::TimeDelta::FromMilliseconds(store_->policy()->timestamp());
}
}
void CloudPolicyRefreshScheduler::ScheduleRefresh() {
// If the client isn't registered, there is nothing to do.
if (!client_->is_registered()) {
refresh_callback_.Cancel();
return;
}
// If there is a registration, go by the client's status. That will tell us
// what the appropriate refresh delay should be.
switch (client_->status()) {
case DM_STATUS_SUCCESS:
if (store_->is_managed())
RefreshAfter(GetRefreshDelay());
else
RefreshAfter(kUnmanagedRefreshDelayMs);
return;
case DM_STATUS_SERVICE_ACTIVATION_PENDING:
case DM_STATUS_SERVICE_POLICY_NOT_FOUND:
RefreshAfter(GetRefreshDelay());
return;
case DM_STATUS_REQUEST_FAILED:
case DM_STATUS_TEMPORARY_UNAVAILABLE:
RefreshAfter(error_retry_delay_ms_);
return;
case DM_STATUS_REQUEST_INVALID:
case DM_STATUS_HTTP_STATUS_ERROR:
case DM_STATUS_RESPONSE_DECODING_ERROR:
case DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED:
RefreshAfter(kUnmanagedRefreshDelayMs);
return;
case DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID:
case DM_STATUS_SERVICE_DEVICE_NOT_FOUND:
case DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER:
case DM_STATUS_SERVICE_DEVICE_ID_CONFLICT:
case DM_STATUS_MISSING_LICENSES:
// Need a re-registration, no use in retrying.
return;
}
NOTREACHED() << "Invalid client status " << client_->status();
RefreshAfter(kUnmanagedRefreshDelayMs);
}
void CloudPolicyRefreshScheduler::PerformRefresh() {
if (client_->is_registered()) {
// Update |last_refresh_| so another fetch isn't triggered inadvertently.
last_refresh_ = base::Time::NowFromSystemTime();
// The result of this operation will be reported through a callback, at
// which point the next refresh will be scheduled.
client_->FetchPolicy();
return;
}
// This should never happen, as the registration change should have been
// handled via OnRegistrationStateChanged().
NOTREACHED();
}
void CloudPolicyRefreshScheduler::RefreshAfter(int delta_ms) {
base::TimeDelta delta(base::TimeDelta::FromMilliseconds(delta_ms));
refresh_callback_.Cancel();
// Schedule the callback.
base::TimeDelta delay =
std::max((last_refresh_ + delta) - base::Time::NowFromSystemTime(),
base::TimeDelta());
refresh_callback_.Reset(
base::Bind(&CloudPolicyRefreshScheduler::PerformRefresh,
base::Unretained(this)));
task_runner_->PostDelayedTask(FROM_HERE, refresh_callback_.callback(), delay);
}
int64 CloudPolicyRefreshScheduler::GetRefreshDelay() {
return std::min(std::max<int64>(refresh_delay_.GetValue(),
kRefreshDelayMinMs),
kRefreshDelayMaxMs);
}
} // namespace policy