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..4d724f5a8d01f331fc582062e3dd5f4c647a91fc 100644 |
--- a/chrome/browser/invalidation/ticl_invalidation_service.cc |
+++ b/chrome/browser/invalidation/ticl_invalidation_service.cc |
@@ -6,9 +6,11 @@ |
#include "base/command_line.h" |
#include "chrome/browser/invalidation/invalidation_service_util.h" |
+#include "chrome/browser/managed_mode/managed_user_service.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 +18,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 +80,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 +171,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,6 +194,68 @@ 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]); |
+ // Invalidate previous token, otherwise token service will return the same |
+ // token again. |
+ oauth2_token_service_->InvalidateToken(oauth2_scopes, access_token_); |
+ access_token_.clear(); |
+ access_token_request_ = |
+ oauth2_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() && IsReadyToStart()) { |
+ 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: { |
+ // 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); |
@@ -172,19 +276,25 @@ void TiclInvalidationService::Shutdown() { |
} |
bool TiclInvalidationService::IsReadyToStart() { |
+ if (ManagedUserService::ProfileIsManaged(profile_)) { |
+ DVLOG(2) << "Not starting TiclInvalidationService: User is managed."; |
+ return false; |
+ } |
+ |
if (signin_manager_->GetAuthenticatedUsername().empty()) { |
- DVLOG(2) << "Not starting TiclInvalidationService: user is not signed in."; |
+ DVLOG(2) << "Not starting TiclInvalidationService: User is not signed in."; |
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 +305,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 +332,7 @@ void TiclInvalidationService::Start() { |
invalidator_storage_->AsWeakPtr()), |
content::GetUserAgent(GURL()))); |
- UpdateToken(); |
+ UpdateInvalidatorCredentials(); |
invalidator_->RegisterHandler(this); |
invalidator_->UpdateRegisteredIds( |
@@ -221,16 +340,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 +357,8 @@ void TiclInvalidationService::StopInvalidator() { |
} |
void TiclInvalidationService::Logout() { |
+ request_access_token_retry_timer_.Stop(); |
+ |
StopInvalidator(); |
// This service always expects to have a valid invalidator storage. |