Chromium Code Reviews| Index: chrome/browser/policy/device_token_fetcher.cc |
| diff --git a/chrome/browser/policy/device_token_fetcher.cc b/chrome/browser/policy/device_token_fetcher.cc |
| index 3f977c1a25ccfdbc754e1f8d841cda5ccd9bb9fc..26899dfd8ad8492d1e23c567811f6c3cb4223df2 100644 |
| --- a/chrome/browser/policy/device_token_fetcher.cc |
| +++ b/chrome/browser/policy/device_token_fetcher.cc |
| @@ -4,337 +4,175 @@ |
| #include "chrome/browser/policy/device_token_fetcher.h" |
| -#include "base/file_util.h" |
| -#include "base/path_service.h" |
| -#include "base/singleton.h" |
| -#include "base/string_util.h" |
| -#include "chrome/browser/net/gaia/token_service.h" |
| +#include "base/message_loop.h" |
| +#include "chrome/browser/policy/cloud_policy_cache.h" |
| +#include "chrome/browser/policy/device_management_service.h" |
| #include "chrome/browser/policy/proto/device_management_local.pb.h" |
| -#include "chrome/browser/profiles/profile.h" |
| -#include "chrome/common/chrome_paths.h" |
| -#include "chrome/common/guid.h" |
| -#include "chrome/common/net/gaia/gaia_constants.h" |
| -#include "chrome/common/notification_details.h" |
| -#include "chrome/common/notification_service.h" |
| -#include "chrome/common/notification_source.h" |
| -#include "chrome/common/notification_type.h" |
| - |
| -#if defined(OS_CHROMEOS) |
| -#include "chrome/browser/chromeos/login/user_manager.h" |
| -#else |
| -#include "chrome/browser/browser_signin.h" |
| -#endif |
| namespace { |
| -// Domain names that are known not to be managed. |
| -// We don't register the device when such a user logs in. |
| -const char* kNonManagedDomains[] = { |
| - "@googlemail.com", |
| - "@gmail.com" |
| -}; |
| +// Retry after 3 seconds (with exponential backoff) after token fetch errors. |
| +const int64 kTokenFetchErrorDelayMilliseconds = 3 * 1000; |
| +// For unmanaged devices, check once per day whether they're still unmanaged. |
| +const int64 kUnmanagedDeviceRefreshRateMilliseconds = 24 * 60 * 60 * 1000; |
| -// Checks the domain part of the given username against the list of known |
| -// non-managed domain names. Returns false if |username| is empty or its |
| -// in a domain known not to be managed. |
| -bool CanBeInManagedDomain(const std::string& username) { |
| - if (username.empty()) { |
| - // This means incognito user in case of ChromiumOS and |
| - // no logged-in user in case of Chromium (SigninService). |
| - return false; |
| - } |
| - for (size_t i = 0; i < arraysize(kNonManagedDomains); i++) { |
| - if (EndsWith(username, kNonManagedDomains[i], true)) { |
| - return false; |
| - } |
| - } |
| - return true; |
| } |
| -} // namespace |
| - |
| namespace policy { |
| namespace em = enterprise_management; |
| -DeviceTokenFetcher::ObserverRegistrar::ObserverRegistrar() {} |
| - |
| -DeviceTokenFetcher::ObserverRegistrar::~ObserverRegistrar() { |
| - RemoveAll(); |
| +DeviceTokenFetcher::DeviceTokenFetcher( |
| + DeviceManagementService* service, |
| + CloudPolicyCache* cache) |
| + : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { |
| + Initialize(service, |
| + cache, |
| + kTokenFetchErrorDelayMilliseconds, |
| + kUnmanagedDeviceRefreshRateMilliseconds); |
| } |
| -void DeviceTokenFetcher::ObserverRegistrar::Init( |
| - DeviceTokenFetcher* token_fetcher) { |
| - RemoveAll(); |
| - token_fetcher_ = token_fetcher; |
| +DeviceTokenFetcher::DeviceTokenFetcher( |
| + DeviceManagementService* service, |
| + CloudPolicyCache* cache, |
| + int64 token_fetch_error_delay_ms, |
| + int64 unmanaged_device_refresh_rate_ms) |
| + : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { |
| + Initialize(service, |
| + cache, |
| + token_fetch_error_delay_ms, |
| + unmanaged_device_refresh_rate_ms); |
| } |
| -void DeviceTokenFetcher::ObserverRegistrar::AddObserver( |
| - DeviceTokenFetcher::Observer* observer) { |
| - observers_.push_back(observer); |
| - token_fetcher_->AddObserver(observer); |
| +DeviceTokenFetcher::~DeviceTokenFetcher() { |
| + CancelDelayedWork(); |
| } |
| -void DeviceTokenFetcher::ObserverRegistrar::RemoveAll() { |
| - for (std::vector<DeviceTokenFetcher::Observer*>::iterator it = |
| - observers_.begin(); it != observers_.end(); ++it) { |
| - token_fetcher_->RemoveObserver(*it); |
| - } |
| - observers_.clear(); |
| +void DeviceTokenFetcher::Reset() { |
| + SetState(STATE_INACTIVE); |
| + backend_.reset(); |
| } |
| -DeviceTokenFetcher::DeviceTokenFetcher( |
| - DeviceManagementBackend* backend, |
| - Profile* profile, |
| - const FilePath& token_path) |
| - : profile_(profile), |
| - token_path_(token_path), |
| - backend_(backend), |
| - state_(kStateNotStarted), |
| - device_token_load_complete_event_(true, false) { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - |
| - TokenService* token_service = profile_->GetTokenService(); |
| - auth_token_ = token_service->GetTokenForService( |
| - GaiaConstants::kDeviceManagementService); |
| +void DeviceTokenFetcher::FetchToken(const std::string& auth_token, |
| + const std::string& device_id) { |
| + // Construct a new backend, which will discard any previous requests. |
|
danno
2011/02/04 16:01:33
You should validate that the state machine is in t
Jakob Kummerow
2011/02/14 13:50:34
Done.
|
| + backend_.reset(service_->CreateBackend()); |
| + auth_token_ = auth_token; |
| + device_id_ = device_id; |
| - registrar_.Add(this, |
| - NotificationType::TOKEN_AVAILABLE, |
| - Source<TokenService>(token_service)); |
| - // Register for the event of user login. The device management token won't |
| - // be fetched until we know the domain of the currently logged in user. |
| -#if defined(OS_CHROMEOS) |
| - registrar_.Add(this, |
| - NotificationType::LOGIN_USER_CHANGED, |
| - NotificationService::AllSources()); |
| -#else |
| - registrar_.Add(this, |
| - NotificationType::GOOGLE_SIGNIN_SUCCESSFUL, |
| - Source<Profile>(profile_)); |
| -#endif |
| + em::DeviceRegisterRequest request; |
| + backend_->ProcessRegisterRequest(auth_token, device_id, request, this); |
| } |
| -DeviceTokenFetcher::~DeviceTokenFetcher() {} |
| +const std::string& DeviceTokenFetcher::GetDeviceToken() { |
| + return device_token_; |
| +} |
| -void DeviceTokenFetcher::Observe(NotificationType type, |
| - const NotificationSource& source, |
| - const NotificationDetails& details) { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - if (type == NotificationType::TOKEN_AVAILABLE) { |
| - if (Source<TokenService>(source).ptr() == profile_->GetTokenService()) { |
| - const TokenService::TokenAvailableDetails* token_details = |
| - Details<const TokenService::TokenAvailableDetails>(details).ptr(); |
| - if (token_details->service() == GaiaConstants::kDeviceManagementService) { |
| - if (!HasAuthToken()) { |
| - auth_token_ = token_details->token(); |
| - SendServerRequestIfPossible(); |
| - } |
| - } |
| - } |
| -#if defined(OS_CHROMEOS) |
| - } else if (type == NotificationType::LOGIN_USER_CHANGED) { |
| - SendServerRequestIfPossible(); |
| -#else |
| - } else if (type == NotificationType::GOOGLE_SIGNIN_SUCCESSFUL) { |
| - if (profile_ == Source<Profile>(source).ptr()) { |
| - SendServerRequestIfPossible(); |
| - } |
| -#endif |
| - } else { |
| - NOTREACHED(); |
| - } |
| +void DeviceTokenFetcher::AddObserver(DeviceTokenFetcher::Observer* observer) { |
| + observer_list_.AddObserver(observer); |
| } |
| -std::string DeviceTokenFetcher::GetCurrentUser() { |
| -#if defined(OS_CHROMEOS) |
| - return chromeos::UserManager::Get()->logged_in_user().email(); |
| -#else |
| - return profile_->GetBrowserSignin()->GetSignedInUsername(); |
| -#endif |
| +void DeviceTokenFetcher::RemoveObserver( |
| + DeviceTokenFetcher::Observer* observer) { |
| + observer_list_.RemoveObserver(observer); |
| } |
| void DeviceTokenFetcher::HandleRegisterResponse( |
| const em::DeviceRegisterResponse& response) { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - DCHECK_EQ(kStateRequestingDeviceTokenFromServer, state_); |
| if (response.has_device_management_token()) { |
| device_token_ = response.device_management_token(); |
| - BrowserThread::PostTask( |
| - BrowserThread::FILE, |
| - FROM_HERE, |
| - NewRunnableFunction(&WriteDeviceTokenToDisk, |
| - token_path_, |
| - device_token_, |
| - device_id_)); |
| - SetState(kStateHasDeviceToken); |
| + SetState(STATE_TOKEN_AVAILABLE); |
| } else { |
| NOTREACHED(); |
| - SetState(kStateFailure); |
| + SetState(STATE_ERROR); |
| } |
| } |
| void DeviceTokenFetcher::OnError(DeviceManagementBackend::ErrorCode code) { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - // For privacy reasons, delete all identifying data when this device is not |
| - // managed. |
| if (code == DeviceManagementBackend::kErrorServiceManagementNotSupported) { |
| - device_token_ = std::string(); |
| - device_id_ = std::string(); |
| - BrowserThread::PostTask( |
| - BrowserThread::FILE, |
| - FROM_HERE, |
| - // The Windows compiler needs explicit template instantiation. |
| - NewRunnableFunction<bool(*)(const FilePath&, bool), FilePath, bool>( |
| - &file_util::Delete, token_path_, false)); |
| - SetState(kStateNotManaged); |
| - return; |
| + cache_->SetDeviceUnmanaged(); |
| + SetState(STATE_UNMANAGED); |
| } |
| - SetState(kStateFailure); |
| + SetState(STATE_ERROR); |
| } |
| -void DeviceTokenFetcher::Restart() { |
| - // Complain if there's currently an asynchronous operation going on. |
| - DCHECK(state_ == kStateNotStarted || |
| - state_ == kStateHasDeviceToken || |
| - state_ == kStateFailure || |
| - state_ == kStateNotManaged); |
| - device_token_.clear(); |
| - device_token_load_complete_event_.Reset(); |
| - MakeReadyToRequestDeviceToken(); |
| -} |
| +void DeviceTokenFetcher::Initialize(DeviceManagementService* service, |
| + CloudPolicyCache* cache, |
| + int64 token_fetch_error_delay_ms, |
| + int64 unmanaged_device_refresh_rate_ms) { |
| + service_ = service; |
| + cache_ = cache; |
| + 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; |
| + state_ = STATE_INACTIVE; |
| + delayed_work_task_ = NULL; |
| -void DeviceTokenFetcher::StartFetching() { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - if (state_ == kStateNotStarted) { |
| - SetState(kStateLoadDeviceTokenFromDisk); |
| - // The file calls for loading the persisted token must be deferred to the |
| - // FILE thread. |
| - BrowserThread::PostTask( |
| - BrowserThread::FILE, |
| - FROM_HERE, |
| - NewRunnableMethod(this, |
| - &DeviceTokenFetcher::AttemptTokenLoadFromDisk)); |
| - } |
| -} |
| - |
| -void DeviceTokenFetcher::AttemptTokenLoadFromDisk() { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| - if (file_util::PathExists(token_path_)) { |
| - std::string data; |
| - em::DeviceCredentials device_credentials; |
| - if (file_util::ReadFileToString(token_path_, &data) && |
| - device_credentials.ParseFromArray(data.c_str(), data.size())) { |
| - device_token_ = device_credentials.device_token(); |
| - device_id_ = device_credentials.device_id(); |
| - if (!device_token_.empty() && !device_id_.empty()) { |
| - BrowserThread::PostTask( |
| - BrowserThread::UI, |
| - FROM_HERE, |
| - NewRunnableMethod(this, |
| - &DeviceTokenFetcher::SetState, |
| - kStateHasDeviceToken)); |
| - return; |
| - } |
| - } |
| - } |
| - |
| - BrowserThread::PostTask( |
| - BrowserThread::UI, |
| - FROM_HERE, |
| - NewRunnableMethod(this, |
| - &DeviceTokenFetcher::MakeReadyToRequestDeviceToken)); |
| -} |
| - |
| -void DeviceTokenFetcher::MakeReadyToRequestDeviceToken() { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - SetState(kStateReadyToRequestDeviceTokenFromServer); |
| - SendServerRequestIfPossible(); |
| -} |
| - |
| -void DeviceTokenFetcher::SendServerRequestIfPossible() { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - std::string username = GetCurrentUser(); |
| - if (state_ == kStateReadyToRequestDeviceTokenFromServer |
| - && HasAuthToken() |
| - && backend_ |
| - && !username.empty()) { |
| - if (CanBeInManagedDomain(username)) { |
| - em::DeviceRegisterRequest register_request; |
| - SetState(kStateRequestingDeviceTokenFromServer); |
| - backend_->ProcessRegisterRequest(auth_token_, |
| - GetDeviceID(), |
| - register_request, |
| - this); |
| - } else { |
| - SetState(kStateNotManaged); |
| - } |
| - } |
| -} |
| - |
| -bool DeviceTokenFetcher::IsTokenPending() { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - return !device_token_load_complete_event_.IsSignaled(); |
| -} |
| - |
| -std::string DeviceTokenFetcher::GetDeviceToken() { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - device_token_load_complete_event_.Wait(); |
| - return device_token_; |
| -} |
| - |
| -std::string DeviceTokenFetcher::GetDeviceID() { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - // As long as access to this is only allowed from the UI thread, no explicit |
| - // locking is necessary to prevent the ID from being generated twice. |
| - if (device_id_.empty()) |
| - device_id_ = GenerateNewDeviceID(); |
| - return device_id_; |
| + if (cache_->is_device_unmanaged()) |
| + SetState(STATE_UNMANAGED); |
| } |
| void DeviceTokenFetcher::SetState(FetcherState state) { |
|
danno
2011/02/04 16:01:33
Please include state diagram in header documentati
Jakob Kummerow
2011/02/14 13:50:34
Done.
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - if (state_ == state) |
| - return; |
| state_ = state; |
| - if (state == kStateFailure) { |
| - device_token_load_complete_event_.Signal(); |
| - NotifyTokenError(); |
| - } else if (state == kStateNotManaged) { |
| - device_token_load_complete_event_.Signal(); |
| - NotifyNotManaged(); |
| - } else if (state == kStateHasDeviceToken) { |
| - device_token_load_complete_event_.Signal(); |
| - NotifyTokenSuccess(); |
| + if (state_ != STATE_ERROR) |
| + effective_token_fetch_error_delay_ms_ = token_fetch_error_delay_ms_; |
| + |
| + base::Time delayed_work_at; |
| + switch (state_) { |
| + case STATE_INACTIVE: |
| + device_token_.clear(); |
| + auth_token_.clear(); |
| + device_id_.clear(); |
| + break; |
| + case STATE_TOKEN_AVAILABLE: |
| + FOR_EACH_OBSERVER(Observer, observer_list_, OnTokenAvailable()); |
| + break; |
| + case STATE_UNMANAGED: |
| + delayed_work_at = cache_->last_policy_refresh_time() + |
| + base::TimeDelta::FromMilliseconds(unmanaged_device_refresh_rate_ms_); |
| + break; |
| + case STATE_ERROR: |
| + delayed_work_at = cache_->last_policy_refresh_time() + |
|
Jakob Kummerow
2011/02/07 16:00:09
I think this should be s/cache_->last_policy_refre
Jakob Kummerow
2011/02/14 13:50:34
Done.
|
| + base::TimeDelta::FromMilliseconds( |
| + effective_token_fetch_error_delay_ms_); |
| + effective_token_fetch_error_delay_ms_ *= 2; |
| + break; |
| } |
| -} |
| -void DeviceTokenFetcher::GetDeviceTokenPath(FilePath* token_path) const { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - *token_path = token_path_; |
| -} |
| - |
| -bool DeviceTokenFetcher::IsTokenValid() const { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - return state_ == kStateHasDeviceToken; |
| + CancelDelayedWork(); |
| + if (!delayed_work_at.is_null()) { |
| + base::Time now(base::Time::Now()); |
| + int64 delay = std::max<int64>((delayed_work_at - now).InMilliseconds(), 0); |
| + delayed_work_task_ = |
| + method_factory_.NewRunnableMethod(&DeviceTokenFetcher::DoDelayedWork); |
| + MessageLoop::current()->PostDelayedTask(FROM_HERE, delayed_work_task_, |
| + delay); |
| + } |
| } |
| -// static |
| -void DeviceTokenFetcher::WriteDeviceTokenToDisk( |
| - const FilePath& path, |
| - const std::string& device_token, |
| - const std::string& device_id) { |
| - em::DeviceCredentials device_credentials; |
| - device_credentials.set_device_token(device_token); |
| - device_credentials.set_device_id(device_id); |
| - std::string data; |
| - bool no_error = device_credentials.SerializeToString(&data); |
| - DCHECK(no_error); |
| - file_util::WriteFile(path, data.c_str(), data.length()); |
| +void DeviceTokenFetcher::DoDelayedWork() { |
|
danno
2011/02/04 16:01:33
Please include state diagram in header documentati
Jakob Kummerow
2011/02/14 13:50:34
Done.
|
| + DCHECK(delayed_work_task_); |
| + delayed_work_task_ = NULL; |
| + |
| + switch (state_) { |
| + case STATE_INACTIVE: |
| + case STATE_TOKEN_AVAILABLE: |
| + break; |
| + case STATE_UNMANAGED: |
| + case STATE_ERROR: |
| + if (!auth_token_.empty() && !device_id_.empty()) |
| + FetchToken(auth_token_, device_id_); |
| + else |
| + NOTREACHED(); |
| + break; |
| + } |
| } |
| -// static |
| -std::string DeviceTokenFetcher::GenerateNewDeviceID() { |
| - return guid::GenerateGUID(); |
| +void DeviceTokenFetcher::CancelDelayedWork() { |
| + if (delayed_work_task_) { |
| + delayed_work_task_->Cancel(); |
| + delayed_work_task_ = NULL; |
| + } |
| } |
| } // namespace policy |