Chromium Code Reviews| Index: chrome/browser/policy/device_management_policy_provider.cc |
| diff --git a/chrome/browser/policy/device_management_policy_provider.cc b/chrome/browser/policy/device_management_policy_provider.cc |
| index 8b9d9b49a7e72af478b224503ad5f434d274cce8..22bbc4c717117f657845a781bf1bfc039d1c0724 100644 |
| --- a/chrome/browser/policy/device_management_policy_provider.cc |
| +++ b/chrome/browser/policy/device_management_policy_provider.cc |
| @@ -12,6 +12,7 @@ |
| #include "chrome/browser/browser_thread.h" |
| #include "chrome/browser/policy/device_management_backend.h" |
| #include "chrome/browser/policy/device_management_policy_cache.h" |
| +#include "chrome/browser/policy/profile_policy_context.h" |
| #include "chrome/browser/policy/proto/device_management_constants.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/common/chrome_paths.h" |
| @@ -23,8 +24,12 @@ namespace policy { |
| namespace em = enterprise_management; |
| -const int64 kPolicyRefreshRateInMilliseconds = 3 * 60 * 60 * 1000; // 3 hours |
| -const int64 kPolicyRefreshMaxEarlierInMilliseconds = 20 * 60 * 1000; // 20 mins |
| +// The maximum ratio in percent of the policy refresh rate we use for fuzzing |
|
danno
2011/01/06 19:16:26
I like random deviation here, too.
Mattias Nissler (ping if slow)
2011/01/07 11:15:33
Done.
|
| +// the policy refresh time instant. |
| +const int kPolicyRefreshFuzzFactorPercent = 10; |
| +// Maximum preemption resulting from fuzzing we are willing to accept. |
| +const int64 kPolicyRefreshFuzzMaxInMilliseconds = 30 * 60 * 1000; |
| + |
| // These are the base values for delays before retrying after an error. They |
| // will be doubled each time they are used. |
| const int64 kPolicyRefreshErrorDelayInMilliseconds = 3 * 1000; // 3 seconds |
| @@ -35,44 +40,25 @@ const int64 kPolicyRefreshUnmanagedDeviceInMilliseconds = 24 * 60 * 60 * 1000; |
| const FilePath::StringType kDeviceTokenFilename = FILE_PATH_LITERAL("Token"); |
| const FilePath::StringType kPolicyFilename = FILE_PATH_LITERAL("Policy"); |
| -// Ensures that the portion of the policy provider implementation that requires |
| -// the IOThread is deferred until the IOThread is fully initialized. The policy |
| -// provider posts this task on the UI thread during its constructor, thereby |
| -// guaranteeing that the code won't get executed until after the UI and IO |
| -// threads are fully constructed. |
| -class DeviceManagementPolicyProvider::InitializeAfterIOThreadExistsTask |
| - : public Task { |
| +// Calls back into the provider to refresh policy. |
| +class DeviceManagementPolicyProvider::RefreshTask : public CancelableTask { |
| public: |
| - explicit InitializeAfterIOThreadExistsTask( |
| - base::WeakPtr<DeviceManagementPolicyProvider> provider) |
| - : provider_(provider) { |
| - } |
| + explicit RefreshTask(DeviceManagementPolicyProvider* provider) |
| + : provider_(provider) {} |
| // Task implementation: |
| virtual void Run() { |
| - DeviceManagementPolicyProvider* provider = provider_.get(); |
| - if (provider) |
| - provider->InitializeAfterIOThreadExists(); |
| + if (provider_) |
| + provider_->RefreshTaskExecute(); |
| } |
| - private: |
| - base::WeakPtr<DeviceManagementPolicyProvider> provider_; |
| -}; |
| - |
| -class DeviceManagementPolicyProvider::RefreshTask : public Task { |
| - public: |
| - explicit RefreshTask(base::WeakPtr<DeviceManagementPolicyProvider> provider) |
| - : provider_(provider) {} |
| - |
| - // Task implementation: |
| - virtual void Run() { |
| - DeviceManagementPolicyProvider* provider = provider_.get(); |
| - if (provider) |
| - provider->RefreshTaskExecute(); |
| + // CancelableTask implementation: |
| + virtual void Cancel() { |
| + provider_ = NULL; |
| } |
| private: |
| - base::WeakPtr<DeviceManagementPolicyProvider> provider_; |
| + DeviceManagementPolicyProvider* provider_; |
| }; |
| DeviceManagementPolicyProvider::DeviceManagementPolicyProvider( |
| @@ -82,36 +68,19 @@ DeviceManagementPolicyProvider::DeviceManagementPolicyProvider( |
| : ConfigurationPolicyProvider(policy_list) { |
| Initialize(backend, |
| profile, |
| - kPolicyRefreshRateInMilliseconds, |
| - kPolicyRefreshMaxEarlierInMilliseconds, |
| + ProfilePolicyContext::kDefaultPolicyRefreshRateInMilliseconds, |
| + kPolicyRefreshFuzzFactorPercent, |
| + kPolicyRefreshFuzzMaxInMilliseconds, |
| kPolicyRefreshErrorDelayInMilliseconds, |
| kDeviceTokenRefreshErrorDelayInMilliseconds, |
| kPolicyRefreshUnmanagedDeviceInMilliseconds); |
| } |
| -DeviceManagementPolicyProvider::DeviceManagementPolicyProvider( |
| - const PolicyDefinitionList* policy_list, |
| - DeviceManagementBackend* backend, |
| - Profile* profile, |
| - int64 policy_refresh_rate_ms, |
| - int64 policy_refresh_max_earlier_ms, |
| - int64 policy_refresh_error_delay_ms, |
| - int64 token_fetch_error_delay_ms, |
| - int64 unmanaged_device_refresh_rate_ms) |
| - : ConfigurationPolicyProvider(policy_list) { |
| - Initialize(backend, |
| - profile, |
| - policy_refresh_rate_ms, |
| - policy_refresh_max_earlier_ms, |
| - policy_refresh_error_delay_ms, |
| - token_fetch_error_delay_ms, |
| - unmanaged_device_refresh_rate_ms); |
| -} |
| - |
| DeviceManagementPolicyProvider::~DeviceManagementPolicyProvider() { |
| FOR_EACH_OBSERVER(ConfigurationPolicyProvider::Observer, |
| observer_list_, |
| OnProviderGoingAway()); |
| + CancelRefreshTask(); |
| } |
| bool DeviceManagementPolicyProvider::Provide( |
| @@ -122,156 +91,135 @@ bool DeviceManagementPolicyProvider::Provide( |
| } |
| bool DeviceManagementPolicyProvider::IsInitializationComplete() const { |
| - return !waiting_for_initial_policies_; |
| + return !cache_->last_policy_refresh_time().is_null(); |
| } |
| void DeviceManagementPolicyProvider::HandlePolicyResponse( |
| const em::DevicePolicyResponse& response) { |
| + DCHECK(TokenAvailable()); |
| if (cache_->SetPolicy(response)) |
| NotifyCloudPolicyUpdate(); |
| - policy_request_pending_ = false; |
| - // Reset the error delay since policy fetching succeeded this time. |
| - policy_refresh_error_delay_ms_ = kPolicyRefreshErrorDelayInMilliseconds; |
| - ScheduleRefreshTask(GetRefreshTaskDelay()); |
| - // Update this provider's internal waiting state, but don't notify anyone |
| - // else yet (that's done by the PrefValueStore that receives the policy). |
| - waiting_for_initial_policies_ = false; |
| + SetState(STATE_POLICY_VALID); |
| } |
| void DeviceManagementPolicyProvider::OnError( |
| DeviceManagementBackend::ErrorCode code) { |
| - policy_request_pending_ = false; |
| + DCHECK(TokenAvailable()); |
| if (code == DeviceManagementBackend::kErrorServiceDeviceNotFound || |
| code == DeviceManagementBackend::kErrorServiceManagementTokenInvalid) { |
| LOG(WARNING) << "The device token was either invalid or unknown to the " |
| << "device manager, re-registering device."; |
| - token_fetcher_->Restart(); |
| + SetState(STATE_TOKEN_RESET); |
| } else if (code == |
| DeviceManagementBackend::kErrorServiceManagementNotSupported) { |
| VLOG(1) << "The device is no longer managed, resetting device token."; |
| - token_fetcher_->Restart(); |
| + SetState(STATE_TOKEN_RESET); |
| } else { |
| LOG(WARNING) << "Could not provide policy from the device manager (error = " |
| << code << "), will retry in " |
| - << (policy_refresh_error_delay_ms_/1000) << " seconds."; |
| - ScheduleRefreshTask(policy_refresh_error_delay_ms_); |
| - policy_refresh_error_delay_ms_ *= 2; |
| - if (policy_refresh_rate_ms_ && |
| - policy_refresh_rate_ms_ < policy_refresh_error_delay_ms_) { |
| - policy_refresh_error_delay_ms_ = policy_refresh_rate_ms_; |
| - } |
| + << (effective_policy_refresh_error_delay_ms_ / 1000) |
| + << " seconds."; |
| + SetState(STATE_POLICY_ERROR); |
| } |
| - StopWaitingForInitialPolicies(); |
| } |
| void DeviceManagementPolicyProvider::OnTokenSuccess() { |
| - if (policy_request_pending_) |
| - return; |
| + DCHECK(!TokenAvailable()); |
| cache_->SetDeviceUnmanaged(false); |
| - SendPolicyRequest(); |
| + SetState(STATE_TOKEN_VALID); |
| } |
| void DeviceManagementPolicyProvider::OnTokenError() { |
| + DCHECK(!TokenAvailable()); |
| LOG(WARNING) << "Could not retrieve device token."; |
| - ScheduleRefreshTask(token_fetch_error_delay_ms_); |
| - token_fetch_error_delay_ms_ *= 2; |
| - if (token_fetch_error_delay_ms_ > policy_refresh_rate_ms_) |
| - token_fetch_error_delay_ms_ = policy_refresh_rate_ms_; |
| - StopWaitingForInitialPolicies(); |
| + SetState(STATE_TOKEN_ERROR); |
| } |
| void DeviceManagementPolicyProvider::OnNotManaged() { |
| + DCHECK(!TokenAvailable()); |
| VLOG(1) << "This device is not managed."; |
| cache_->SetDeviceUnmanaged(true); |
| - ScheduleRefreshTask(unmanaged_device_refresh_rate_ms_); |
| - StopWaitingForInitialPolicies(); |
| + SetState(STATE_UNMANAGED); |
| } |
| -void DeviceManagementPolicyProvider::AddObserver( |
| - ConfigurationPolicyProvider::Observer* observer) { |
| - observer_list_.AddObserver(observer); |
| +void DeviceManagementPolicyProvider::SetRefreshRate( |
| + int64 refresh_rate_milliseconds) { |
|
Jakob Kummerow
2011/01/07 09:48:51
missing line:
policy_refresh_rate_ms_ = refresh_ra
|
| + // Reschedule the refresh task if necessary. |
| + if (state_ == STATE_POLICY_VALID) |
| + SetState(STATE_POLICY_VALID); |
| } |
| -void DeviceManagementPolicyProvider::RemoveObserver( |
| - ConfigurationPolicyProvider::Observer* observer) { |
| - observer_list_.RemoveObserver(observer); |
| -} |
| - |
| -void DeviceManagementPolicyProvider::NotifyCloudPolicyUpdate() { |
| - FOR_EACH_OBSERVER(ConfigurationPolicyProvider::Observer, |
| - observer_list_, |
| - OnUpdatePolicy()); |
| +DeviceManagementPolicyProvider::DeviceManagementPolicyProvider( |
| + const PolicyDefinitionList* policy_list, |
| + DeviceManagementBackend* backend, |
| + Profile* profile, |
| + int64 policy_refresh_rate_ms, |
| + int policy_refresh_fuzz_factor_percent, |
| + int64 policy_refresh_fuzz_max_ms, |
| + int64 policy_refresh_error_delay_ms, |
| + int64 token_fetch_error_delay_ms, |
| + int64 unmanaged_device_refresh_rate_ms) |
| + : ConfigurationPolicyProvider(policy_list) { |
| + Initialize(backend, |
| + profile, |
| + policy_refresh_rate_ms, |
| + policy_refresh_fuzz_factor_percent, |
| + policy_refresh_fuzz_max_ms, |
| + policy_refresh_error_delay_ms, |
| + token_fetch_error_delay_ms, |
| + unmanaged_device_refresh_rate_ms); |
| } |
| void DeviceManagementPolicyProvider::Initialize( |
| DeviceManagementBackend* backend, |
| Profile* profile, |
| int64 policy_refresh_rate_ms, |
| - int64 policy_refresh_max_earlier_ms, |
| + int policy_refresh_fuzz_factor_percent, |
| + int64 policy_refresh_fuzz_max_ms, |
| int64 policy_refresh_error_delay_ms, |
| int64 token_fetch_error_delay_ms, |
| int64 unmanaged_device_refresh_rate_ms) { |
| + DCHECK(profile); |
| backend_.reset(backend); |
| profile_ = profile; |
| storage_dir_ = GetOrCreateDeviceManagementDir(profile_->GetPath()); |
| - policy_request_pending_ = false; |
| - refresh_task_pending_ = false; |
| - waiting_for_initial_policies_ = true; |
| + state_ = STATE_INITIALIZING; |
| + refresh_task_ = NULL; |
| policy_refresh_rate_ms_ = policy_refresh_rate_ms; |
| - policy_refresh_max_earlier_ms_ = policy_refresh_max_earlier_ms; |
| + policy_refresh_fuzz_factor_percent_ = policy_refresh_fuzz_factor_percent; |
| + policy_refresh_fuzz_max_ms_ = policy_refresh_fuzz_max_ms; |
| policy_refresh_error_delay_ms_ = policy_refresh_error_delay_ms; |
| + effective_policy_refresh_error_delay_ms_ = policy_refresh_error_delay_ms; |
| token_fetch_error_delay_ms_ = token_fetch_error_delay_ms; |
| + effective_token_fetch_error_delay_ms_ = token_fetch_error_delay_ms; |
| unmanaged_device_refresh_rate_ms_ = unmanaged_device_refresh_rate_ms; |
| const FilePath policy_path = storage_dir_.Append(kPolicyFilename); |
| cache_.reset(new DeviceManagementPolicyCache(policy_path)); |
| cache_->LoadPolicyFromFile(); |
| + SetDeviceTokenFetcher(new DeviceTokenFetcher(backend_.get(), profile, |
| + GetTokenPath())); |
| + |
| if (cache_->is_device_unmanaged()) { |
| // This is a non-first login on an unmanaged device. |
| - waiting_for_initial_policies_ = false; |
| - // Defer token_fetcher_ initialization until this device should ask for |
| - // a device token again. |
| - base::Time unmanaged_timestamp = cache_->last_policy_refresh_time(); |
| - int64 delay = unmanaged_device_refresh_rate_ms_ - |
| - (base::Time::NowFromSystemTime().ToInternalValue() - |
| - unmanaged_timestamp.ToInternalValue()); |
| - if (delay < 0) |
| - delay = 0; |
| - BrowserThread::PostDelayedTask( |
| - BrowserThread::UI, FROM_HERE, |
| - new InitializeAfterIOThreadExistsTask(AsWeakPtr()), |
| - delay); |
| + SetState(STATE_UNMANAGED); |
| } else { |
| - if (file_util::PathExists( |
| - storage_dir_.Append(kDeviceTokenFilename))) { |
| - // This is a non-first login on a managed device. |
| - waiting_for_initial_policies_ = false; |
| - } |
| - // Defer initialization that requires the IOThread until after the IOThread |
| - // has been initialized. |
| - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| - new InitializeAfterIOThreadExistsTask(AsWeakPtr())); |
| + SetState(STATE_INITIALIZING); |
| } |
| } |
| -void DeviceManagementPolicyProvider::InitializeAfterIOThreadExists() { |
| - if (profile_) { |
| - if (!token_fetcher_) { |
| - token_fetcher_ = new DeviceTokenFetcher( |
| - backend_.get(), profile_, GetTokenPath()); |
| - } |
| - registrar_.Init(token_fetcher_); |
| - registrar_.AddObserver(this); |
| - token_fetcher_->StartFetching(); |
| - } |
| +void DeviceManagementPolicyProvider::AddObserver( |
| + ConfigurationPolicyProvider::Observer* observer) { |
| + observer_list_.AddObserver(observer); |
| } |
| -void DeviceManagementPolicyProvider::SendPolicyRequest() { |
| - if (policy_request_pending_) |
| - return; |
| +void DeviceManagementPolicyProvider::RemoveObserver( |
| + ConfigurationPolicyProvider::Observer* observer) { |
| + observer_list_.RemoveObserver(observer); |
| +} |
| - policy_request_pending_ = true; |
| +void DeviceManagementPolicyProvider::SendPolicyRequest() { |
| em::DevicePolicyRequest policy_request; |
| policy_request.set_policy_scope(kChromePolicyScope); |
| em::DevicePolicySettingRequest* setting = |
| @@ -284,40 +232,39 @@ void DeviceManagementPolicyProvider::SendPolicyRequest() { |
| } |
| void DeviceManagementPolicyProvider::RefreshTaskExecute() { |
| - DCHECK(refresh_task_pending_); |
| - refresh_task_pending_ = false; |
| - // If there is no valid device token, the token_fetcher_ apparently failed, |
| - // so it must be restarted. |
| - if (!token_fetcher_->IsTokenValid()) { |
| - if (token_fetcher_->IsTokenPending()) { |
| - NOTREACHED(); |
| + DCHECK(refresh_task_); |
| + refresh_task_ = NULL; |
| + |
| + switch (state_) { |
| + case STATE_INITIALIZING: |
| + token_fetcher_->StartFetching(); |
| + return; |
| + case STATE_TOKEN_VALID: |
| + case STATE_POLICY_VALID: |
| + case STATE_POLICY_ERROR: |
| + SendPolicyRequest(); |
| + return; |
| + case STATE_UNMANAGED: |
| + case STATE_TOKEN_ERROR: |
| + case STATE_TOKEN_RESET: |
| + token_fetcher_->Restart(); |
| return; |
| - } |
| - token_fetcher_->Restart(); |
| - return; |
| } |
| - // If there is a device token, just refresh policies. |
| - SendPolicyRequest(); |
| + |
| + NOTREACHED() << "Unhandled state"; |
| } |
| -void DeviceManagementPolicyProvider::ScheduleRefreshTask( |
| - int64 delay_in_milliseconds) { |
| - // This check is simply a safeguard, the situation currently cannot happen. |
| - if (refresh_task_pending_) { |
| - NOTREACHED(); |
| - return; |
| +void DeviceManagementPolicyProvider::CancelRefreshTask() { |
| + if (refresh_task_) { |
| + refresh_task_->Cancel(); |
| + refresh_task_ = NULL; |
| } |
| - refresh_task_pending_ = true; |
| - BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE, |
| - new RefreshTask(AsWeakPtr()), |
| - delay_in_milliseconds); |
| } |
| -int64 DeviceManagementPolicyProvider::GetRefreshTaskDelay() { |
| - int64 delay = policy_refresh_rate_ms_; |
| - if (policy_refresh_max_earlier_ms_) |
| - delay -= base::RandGenerator(policy_refresh_max_earlier_ms_); |
| - return delay; |
| +void DeviceManagementPolicyProvider::NotifyCloudPolicyUpdate() { |
| + FOR_EACH_OBSERVER(ConfigurationPolicyProvider::Observer, |
| + observer_list_, |
| + OnUpdatePolicy()); |
| } |
| FilePath DeviceManagementPolicyProvider::GetTokenPath() { |
| @@ -326,14 +273,85 @@ FilePath DeviceManagementPolicyProvider::GetTokenPath() { |
| void DeviceManagementPolicyProvider::SetDeviceTokenFetcher( |
| DeviceTokenFetcher* token_fetcher) { |
| - DCHECK(!token_fetcher_); |
| + registrar_.Init(token_fetcher); |
| + registrar_.AddObserver(this); |
| token_fetcher_ = token_fetcher; |
| } |
| -void DeviceManagementPolicyProvider::StopWaitingForInitialPolicies() { |
| - waiting_for_initial_policies_ = false; |
| - // Notify observers that initial policy fetch is complete. |
| - NotifyCloudPolicyUpdate(); |
| +void DeviceManagementPolicyProvider::SetState( |
| + DeviceManagementPolicyProvider::ProviderState new_state) { |
| + if (!IsInitializationComplete() && |
|
danno
2011/01/06 19:16:26
I am confused here. If !IsInitializationComplete y
Mattias Nissler (ping if slow)
2011/01/07 11:15:33
The idea is to notify observers once we switch to
|
| + state_ != STATE_INITIALIZING && |
| + state_ != STATE_TOKEN_VALID) { |
| + // Notify observers that initial policy fetch is complete. |
| + NotifyCloudPolicyUpdate(); |
|
danno
2011/01/06 19:16:26
I found the old code much more readable, it was cl
Mattias Nissler (ping if slow)
2011/01/07 11:15:33
I disagree. We now have a central point that detec
|
| + } |
| + state_ = new_state; |
| + |
| + base::Time now(base::Time::NowFromSystemTime()); |
| + base::Time refresh_at; |
| + base::Time last_refresh(cache_->last_policy_refresh_time()); |
| + if (last_refresh.is_null()) |
| + last_refresh = now; |
| + |
| + // Determine when to take the next step. |
| + switch (state_) { |
| + case STATE_INITIALIZING: |
| + refresh_at = now; |
| + break; |
| + case STATE_TOKEN_VALID: |
| + cache_->SetDeviceUnmanaged(false); |
| + effective_token_fetch_error_delay_ms_ = token_fetch_error_delay_ms_; |
| + refresh_at = now; |
| + break; |
| + case STATE_TOKEN_RESET: |
| + refresh_at = now; |
| + break; |
| + case STATE_UNMANAGED: |
| + refresh_at = last_refresh + |
| + base::TimeDelta::FromMilliseconds(unmanaged_device_refresh_rate_ms_); |
| + break; |
| + case STATE_POLICY_VALID: |
| + effective_policy_refresh_error_delay_ms_ = policy_refresh_error_delay_ms_; |
| + refresh_at = |
| + last_refresh + base::TimeDelta::FromMilliseconds(GetRefreshDelay()); |
| + break; |
| + case STATE_TOKEN_ERROR: |
| + refresh_at = now + base::TimeDelta::FromMilliseconds( |
| + effective_token_fetch_error_delay_ms_); |
| + effective_token_fetch_error_delay_ms_ *= 2; |
| + if (effective_token_fetch_error_delay_ms_ > policy_refresh_rate_ms_) |
| + effective_token_fetch_error_delay_ms_ = policy_refresh_rate_ms_; |
| + break; |
| + case STATE_POLICY_ERROR: |
| + refresh_at = now + base::TimeDelta::FromMilliseconds( |
| + effective_policy_refresh_error_delay_ms_); |
| + effective_policy_refresh_error_delay_ms_ *= 2; |
| + if (effective_policy_refresh_error_delay_ms_ > policy_refresh_rate_ms_) |
| + effective_policy_refresh_error_delay_ms_ = policy_refresh_rate_ms_; |
| + break; |
| + } |
| + |
| + // Update the refresh task. |
| + CancelRefreshTask(); |
| + if (!refresh_at.is_null()) { |
| + refresh_task_ = new RefreshTask(this); |
| + int64 delay = std::max<int64>((refresh_at - now).InMilliseconds(), 0); |
| + BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE, refresh_task_, |
| + delay); |
| + } |
| +} |
| + |
| +int64 DeviceManagementPolicyProvider::GetRefreshDelay() { |
| + int64 fuzz = policy_refresh_fuzz_factor_percent_ * policy_refresh_rate_ms_; |
| + fuzz = std::min(fuzz, policy_refresh_fuzz_max_ms_); |
| + return policy_refresh_rate_ms_ - base::RandGenerator(fuzz + 1); |
| +} |
| + |
| +bool DeviceManagementPolicyProvider::TokenAvailable() const { |
| + return state_ == STATE_TOKEN_VALID || |
| + state_ == STATE_POLICY_VALID || |
| + state_ == STATE_POLICY_ERROR; |
| } |
| // static |