Chromium Code Reviews| Index: chrome/browser/invalidation/ticl_invalidation_service.cc |
| diff --git a/chrome/browser/invalidation/ticl_invalidation_service.cc b/chrome/browser/invalidation/ticl_invalidation_service.cc |
| index bfee8f0ac885fec779b042367d9575090ebe8d14..e73ae280c1c6fe74d9157b04e371596dd4c3f905 100644 |
| --- a/chrome/browser/invalidation/ticl_invalidation_service.cc |
| +++ b/chrome/browser/invalidation/ticl_invalidation_service.cc |
| @@ -7,8 +7,9 @@ |
| #include "base/command_line.h" |
| #include "chrome/browser/invalidation/invalidation_service_util.h" |
| #include "chrome/browser/profiles/profile.h" |
| +#include "chrome/browser/signin/profile_oauth2_token_service.h" |
| +#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" |
| #include "chrome/browser/signin/signin_manager.h" |
| -#include "chrome/browser/signin/token_service.h" |
| #include "chrome/common/chrome_notification_types.h" |
| #include "content/public/browser/notification_service.h" |
| #include "google_apis/gaia/gaia_constants.h" |
| @@ -16,15 +17,52 @@ |
| #include "sync/notifier/invalidator_state.h" |
| #include "sync/notifier/non_blocking_invalidator.h" |
| +static const char* kOAuth2Scopes[] = { |
| + GaiaConstants::kGoogleTalkOAuth2Scope |
| +}; |
| + |
| +static const net::BackoffEntry::Policy kRequestAccessTokenBackoffPolicy = { |
| + // Number of initial errors (in sequence) to ignore before applying |
| + // exponential back-off rules. |
| + 0, |
| + |
| + // Initial delay for exponential back-off in ms. |
| + 2000, |
| + |
| + // Factor by which the waiting time will be multiplied. |
| + 2, |
| + |
| + // Fuzzing percentage. ex: 10% will spread requests randomly |
| + // between 90%-100% of the calculated time. |
| + 0.2, // 20% |
| + |
| + // Maximum amount of time we are willing to delay our request in ms. |
| + // TODO(pavely): crbug.com/246686 ProfileSyncService should retry |
| + // RequestAccessToken on connection state change after backoff |
| + 1000 * 3600 * 4, // 4 hours. |
| + |
| + // Time to keep an entry from being discarded even when it |
| + // has no significant state, -1 to never discard. |
| + -1, |
| + |
| + // Don't use initial delay unless the last request was an error. |
| + false, |
| +}; |
| + |
| namespace invalidation { |
| -TiclInvalidationService::TiclInvalidationService(SigninManagerBase* signin, |
| - TokenService* token_service, |
| - Profile* profile) |
| - : profile_(profile), |
| - signin_manager_(signin), |
| - token_service_(token_service), |
| - invalidator_registrar_(new syncer::InvalidatorRegistrar()) { } |
| +TiclInvalidationService::TiclInvalidationService( |
| + SigninManagerBase* signin, |
| + TokenService* token_service, |
| + OAuth2TokenService* oauth2_token_service, |
| + Profile* profile) |
| + : profile_(profile), |
| + signin_manager_(signin), |
| + token_service_(token_service), |
| + oauth2_token_service_(oauth2_token_service), |
| + invalidator_registrar_(new syncer::InvalidatorRegistrar()), |
| + request_access_token_backoff_(&kRequestAccessTokenBackoffPolicy) { |
| +} |
| TiclInvalidationService::~TiclInvalidationService() { |
| DCHECK(CalledOnValidThread()); |
| @@ -41,20 +79,24 @@ void TiclInvalidationService::Init() { |
| } |
| if (IsReadyToStart()) { |
| - Start(); |
| + StartInvalidator(); |
| } |
| notification_registrar_.Add(this, |
| - chrome::NOTIFICATION_TOKEN_AVAILABLE, |
| - content::Source<TokenService>(token_service_)); |
| - notification_registrar_.Add(this, |
| chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, |
| content::Source<Profile>(profile_)); |
| + notification_registrar_.Add(this, |
| + chrome::NOTIFICATION_TOKEN_LOADING_FINISHED, |
| + content::Source<TokenService>(token_service_)); |
| + notification_registrar_.Add(this, |
| + chrome::NOTIFICATION_TOKENS_CLEARED, |
| + content::Source<TokenService>(token_service_)); |
| } |
| void TiclInvalidationService::InitForTest(syncer::Invalidator* invalidator) { |
| - // Here we perform the equivalent of Init() and Start(), but with some minor |
| - // changes to account for the fact that we're injecting the invalidator. |
| + // Here we perform the equivalent of Init() and StartInvalidator(), but with |
| + // some minor changes to account for the fact that we're injecting the |
| + // invalidator. |
| invalidator_.reset(invalidator); |
| invalidator_->RegisterHandler(this); |
| @@ -128,17 +170,16 @@ void TiclInvalidationService::Observe( |
| DCHECK(CalledOnValidThread()); |
| switch (type) { |
| - case chrome::NOTIFICATION_TOKEN_AVAILABLE: { |
| - const TokenService::TokenAvailableDetails& token_details = |
| - *(content::Details<const TokenService::TokenAvailableDetails>( |
| - details).ptr()); |
| - if (token_details.service() == GaiaConstants::kSyncService) { |
| - DCHECK(IsReadyToStart()); |
| - if (!IsStarted()) { |
| - Start(); |
| - } else { |
| - UpdateToken(); |
| - } |
| + case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: { |
| + if (!IsStarted() && IsReadyToStart()) { |
| + StartInvalidator(); |
| + } |
| + break; |
| + } |
| + case chrome::NOTIFICATION_TOKENS_CLEARED: { |
| + access_token_.clear(); |
| + if (IsStarted()) { |
| + UpdateInvalidatorCredentials(); |
| } |
| break; |
| } |
| @@ -152,9 +193,79 @@ void TiclInvalidationService::Observe( |
| } |
| } |
| +void TiclInvalidationService::RequestAccessToken() { |
| + // Only one active request at a time. |
| + if (access_token_request_ != NULL) |
| + return; |
| + request_access_token_retry_timer_.Stop(); |
| + OAuth2TokenService::ScopeSet oauth2_scopes; |
| + for (size_t i = 0; i < arraysize(kOAuth2Scopes); i++) |
| + oauth2_scopes.insert(kOAuth2Scopes[i]); |
| + OAuth2TokenService* token_service = |
| + ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); |
| + // Invalidate previous token, otherwise token service will return the same |
| + // token again. |
| + token_service->InvalidateToken(oauth2_scopes, access_token_); |
| + access_token_.clear(); |
| + access_token_request_ = token_service->StartRequest(oauth2_scopes, this); |
| +} |
| + |
| +void TiclInvalidationService::OnGetTokenSuccess( |
| + const OAuth2TokenService::Request* request, |
| + const std::string& access_token, |
| + const base::Time& expiration_time) { |
| + DCHECK_EQ(access_token_request_, request); |
| + access_token_request_.reset(); |
| + // Reset backoff time after successful response. |
| + request_access_token_backoff_.Reset(); |
| + access_token_ = access_token; |
| + if (!IsStarted()) { |
| + StartInvalidator(); |
| + } else { |
| + UpdateInvalidatorCredentials(); |
| + } |
| +} |
| + |
| +void TiclInvalidationService::OnGetTokenFailure( |
| + const OAuth2TokenService::Request* request, |
| + const GoogleServiceAuthError& error) { |
| + DCHECK_EQ(access_token_request_, request); |
| + DCHECK_NE(error.state(), GoogleServiceAuthError::NONE); |
| + access_token_request_.reset(); |
| + switch (error.state()) { |
| + case GoogleServiceAuthError::CONNECTION_FAILED: |
| + case GoogleServiceAuthError::SERVICE_UNAVAILABLE: { |
| + // Transient error. Retry after some time. |
| + request_access_token_backoff_.InformOfRequest(false); |
| + request_access_token_retry_timer_.Start( |
| + FROM_HERE, |
| + request_access_token_backoff_.GetTimeUntilRelease(), |
| + base::Bind(&TiclInvalidationService::RequestAccessToken, |
| + base::Unretained(this))); |
| + break; |
| + } |
| + case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: { |
| + // Now *this* is a real auth error. |
| + invalidator_registrar_->UpdateInvalidatorState( |
| + syncer::INVALIDATION_CREDENTIALS_REJECTED); |
| + break; |
| + } |
| + default: { |
| + // We have no way to notify the user of this. Do nothing. |
| + } |
| + } |
| +} |
| + |
| void TiclInvalidationService::OnInvalidatorStateChange( |
| syncer::InvalidatorState state) { |
| - invalidator_registrar_->UpdateInvalidatorState(state); |
| + if (state == syncer::INVALIDATION_CREDENTIALS_REJECTED) { |
|
rlarocque
2013/06/14 23:48:27
I decided against modifying SyncManager's invalida
|
| + // Don't report this as an auth error right away. It's possible that we |
| + // just need to refresh our access token. If that fails, then we can update |
| + // the user-facing state. |
| + RequestAccessToken(); |
| + } else { |
| + invalidator_registrar_->UpdateInvalidatorState(state); |
| + } |
| } |
| void TiclInvalidationService::OnIncomingInvalidation( |
| @@ -177,14 +288,15 @@ bool TiclInvalidationService::IsReadyToStart() { |
| return false; |
| } |
| - if (!token_service_) { |
| + if (!oauth2_token_service_) { |
| DVLOG(2) |
| << "Not starting TiclInvalidationService: TokenService unavailable."; |
| return false; |
| } |
| - if (!token_service_->HasTokenForService(GaiaConstants::kSyncService)) { |
| - DVLOG(2) << "Not starting TiclInvalidationService: Sync token unavailable."; |
| + if (!oauth2_token_service_->RefreshTokenIsAvailable()) { |
| + DVLOG(2) |
| + << "Not starting TiclInvalidationServce: Waiting for refresh token."; |
| return false; |
| } |
| @@ -195,15 +307,24 @@ bool TiclInvalidationService::IsStarted() { |
| return invalidator_.get() != NULL; |
| } |
| -void TiclInvalidationService::Start() { |
| +void TiclInvalidationService::StartInvalidator() { |
| DCHECK(CalledOnValidThread()); |
| DCHECK(!invalidator_); |
| DCHECK(invalidator_storage_); |
| DCHECK(!invalidator_storage_->GetInvalidatorClientId().empty()); |
| + if (access_token_.empty()) { |
| + DVLOG(1) |
| + << "TiclInvalidationService: " |
| + << "Deferring start until we have an access token."; |
| + RequestAccessToken(); |
| + return; |
| + } |
| + |
| notifier::NotifierOptions options = |
| ParseNotifierOptions(*CommandLine::ForCurrentProcess()); |
| options.request_context_getter = profile_->GetRequestContext(); |
| + options.auth_mechanism = "X-OAUTH2"; |
| invalidator_.reset(new syncer::NonBlockingInvalidator( |
| options, |
| invalidator_storage_->GetInvalidatorClientId(), |
| @@ -213,7 +334,7 @@ void TiclInvalidationService::Start() { |
| invalidator_storage_->AsWeakPtr()), |
| content::GetUserAgent(GURL()))); |
| - UpdateToken(); |
| + UpdateInvalidatorCredentials(); |
| invalidator_->RegisterHandler(this); |
| invalidator_->UpdateRegisteredIds( |
| @@ -221,16 +342,14 @@ void TiclInvalidationService::Start() { |
| invalidator_registrar_->GetAllRegisteredIds()); |
| } |
| -void TiclInvalidationService::UpdateToken() { |
| +void TiclInvalidationService::UpdateInvalidatorCredentials() { |
| std::string email = signin_manager_->GetAuthenticatedUsername(); |
| - DCHECK(!email.empty()) << "Expected user to be signed in."; |
| - DCHECK(token_service_->HasTokenForService(GaiaConstants::kSyncService)); |
| - std::string sync_token = token_service_->GetTokenForService( |
| - GaiaConstants::kSyncService); |
| + DCHECK(!email.empty()) << "Expected user to be signed in."; |
| + DCHECK(!access_token_.empty()); |
| DVLOG(2) << "UpdateCredentials: " << email; |
| - invalidator_->UpdateCredentials(email, sync_token); |
| + invalidator_->UpdateCredentials(email, access_token_); |
| } |
| void TiclInvalidationService::StopInvalidator() { |
| @@ -240,6 +359,8 @@ void TiclInvalidationService::StopInvalidator() { |
| } |
| void TiclInvalidationService::Logout() { |
| + request_access_token_retry_timer_.Stop(); |
| + |
| StopInvalidator(); |
| // This service always expects to have a valid invalidator storage. |