Chromium Code Reviews| Index: chrome/browser/sync/profile_sync_service.cc |
| diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc |
| index bfea304d422f680c4dc02f746109fd8162f2c7ef..8b9f3cf646cb4ae286d5fd89e33f03c861e4fabe 100644 |
| --- a/chrome/browser/sync/profile_sync_service.cc |
| +++ b/chrome/browser/sync/profile_sync_service.cc |
| @@ -30,6 +30,8 @@ |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/signin/about_signin_internals.h" |
| #include "chrome/browser/signin/about_signin_internals_factory.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/signin_manager_factory.h" |
| #include "chrome/browser/signin/token_service.h" |
| @@ -110,24 +112,16 @@ const char* ProfileSyncService::kDevServerUrl = |
| static const int kSyncClearDataTimeoutInSeconds = 60; // 1 minute. |
| -static const char* kRelevantTokenServices[] = { |
| - GaiaConstants::kSyncService |
| +static const char* kOAuth2Scopes[] = { |
| + GaiaConstants::kChromeSyncOAuth2Scope, |
| + // GoogleTalk scope is needed for notifications |
|
Andrew T Wilson (Slow)
2013/05/31 12:57:28
nit: add period at the end for sentence punctuatio
pavely
2013/06/04 00:49:59
Done.
|
| + GaiaConstants::kGoogleTalkOAuth2Scope |
| }; |
| -static const int kRelevantTokenServicesCount = |
| - arraysize(kRelevantTokenServices); |
| + |
| static const char* kSyncUnrecoverableErrorHistogram = |
| "Sync.UnrecoverableErrors"; |
| -// Helper to check if the given token service is relevant for sync. |
| -static bool IsTokenServiceRelevant(const std::string& service) { |
| - for (int i = 0; i < kRelevantTokenServicesCount; ++i) { |
| - if (service == kRelevantTokenServices[i]) |
| - return true; |
| - } |
| - return false; |
| -} |
| - |
| bool ShouldShowActionOnUI( |
| const syncer::SyncProtocolError& error) { |
| return (error.action != syncer::UNKNOWN_ACTION && |
| @@ -193,11 +187,12 @@ bool ProfileSyncService::IsSyncEnabledAndLoggedIn() { |
| return !GetEffectiveUsername().empty(); |
| } |
| -bool ProfileSyncService::IsSyncTokenAvailable() { |
| - TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); |
| +bool ProfileSyncService::IsOAuthRefreshTokenAvailable() { |
| + ProfileOAuth2TokenService* token_service = |
| + ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); |
| if (!token_service) |
| return false; |
| - return token_service->HasTokenForService(GaiaConstants::kSyncService); |
| + return token_service->RefreshTokenIsAvailable(); |
| } |
| #if defined(OS_ANDROID) |
| bool ProfileSyncService::ShouldEnablePasswordSyncForAndroid() const { |
| @@ -245,6 +240,12 @@ void ProfileSyncService::Initialize() { |
| TrySyncDatatypePrefRecovery(); |
| + if (IsOAuthRefreshTokenAvailable()) { |
| + // Tell token service to pre-request access token. We likely don't need it |
| + // right away but it will be available when we decide to initialize backend. |
| + RequestAccessToken(false, false); |
| + } |
| + |
| TryStart(); |
| } |
| @@ -292,7 +293,7 @@ void ProfileSyncService::TryStart() { |
| // (like ChromeOS) we don't start sync until tokens are loaded, because the |
| // user can be "signed in" on those platforms long before the tokens get |
| // loaded, and we don't want to generate spurious auth errors. |
| - if (!IsSyncTokenAvailable() && |
| + if (!IsOAuthRefreshTokenAvailable() && |
| !(!auto_start_enabled_ && token_service->TokensLoadedFromDB())) { |
| return; |
| } |
| @@ -426,13 +427,11 @@ SyncCredentials ProfileSyncService::GetCredentials() { |
| SyncCredentials credentials; |
| credentials.email = GetEffectiveUsername(); |
| DCHECK(!credentials.email.empty()); |
| - TokenService* service = TokenServiceFactory::GetForProfile(profile_); |
| - if (service->HasTokenForService(GaiaConstants::kSyncService)) { |
| - credentials.sync_token = service->GetTokenForService( |
| - GaiaConstants::kSyncService); |
| + if (!access_token_.empty()) { |
| + credentials.sync_token = access_token_; |
| credentials.sync_token_time = |
| AboutSigninInternalsFactory::GetForProfile(profile_)-> |
| - GetTokenTime(GaiaConstants::kSyncService); |
| + GetTokenTime(GaiaConstants::kGaiaOAuth2LoginRefreshToken); |
| UMA_HISTOGRAM_BOOLEAN("Sync.CredentialsLost", false); |
| } else { |
| // We've lost our sync credentials (crbug.com/121755), so just make up some |
| @@ -514,6 +513,11 @@ void ProfileSyncService::StartUp(StartUpDeferredOption deferred_option) { |
| DCHECK(IsSyncEnabledAndLoggedIn()); |
| + if (access_token_.empty()) { |
| + RequestAccessToken(false, true); |
| + return; |
| + } |
| + |
| if (start_up_time_.is_null()) { |
| start_up_time_ = base::Time::Now(); |
| last_synced_time_ = sync_prefs_.GetLastSyncedTime(); |
| @@ -661,6 +665,27 @@ syncer::InvalidatorState ProfileSyncService::GetInvalidatorState() const { |
| return invalidator_registrar_->GetInvalidatorState(); |
| } |
| +void ProfileSyncService::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(); |
| + access_token_ = access_token; |
| + if (backend_) |
| + backend_->UpdateCredentials(GetCredentials()); |
| + else |
| + TryStart(); |
| +} |
| + |
| +void ProfileSyncService::OnGetTokenFailure( |
| + const OAuth2TokenService::Request* request, |
| + const GoogleServiceAuthError& error) { |
| + DCHECK_EQ(access_token_request_, request); |
| + access_token_request_.reset(); |
| + UpdateAuthErrorState(error); |
| +} |
| + |
| void ProfileSyncService::EmitInvalidationForTest( |
| const invalidation::ObjectId& id, |
| const std::string& payload) { |
| @@ -1087,10 +1112,18 @@ AuthError ConnectionStatusToAuthError( |
| void ProfileSyncService::OnConnectionStatusChange( |
| syncer::ConnectionStatus status) { |
| - const GoogleServiceAuthError auth_error = |
| - ConnectionStatusToAuthError(status); |
| - DVLOG(1) << "Connection status change: " << auth_error.ToString(); |
| - UpdateAuthErrorState(auth_error); |
| + if (status == syncer::CONNECTION_AUTH_ERROR) { |
| + // Sync or Tango server returned error indicating that access token is |
| + // invalid. It could be either expired or access is revoked. Let's request |
| + // another access token and if access is revoked then request for token will |
| + // fail with corresponding error. |
| + RequestAccessToken(true, true); |
| + } else { |
| + const GoogleServiceAuthError auth_error = |
| + ConnectionStatusToAuthError(status); |
| + DVLOG(1) << "Connection status change: " << auth_error.ToString(); |
| + UpdateAuthErrorState(auth_error); |
| + } |
| } |
| void ProfileSyncService::OnStopSyncingPermanently() { |
| @@ -1833,6 +1866,30 @@ void ProfileSyncService::ConsumeCachedPassphraseIfPossible() { |
| SetEncryptionPassphrase(passphrase, IMPLICIT); |
| } |
| +void ProfileSyncService::RequestAccessToken( |
| + bool invalidate_previous_token, |
| + bool invoke_callback) { |
| + // Only one active request at a time. |
| + if (access_token_request_ != NULL) |
| + return; |
| + OAuth2TokenService::ScopeSet oauth2_scopes; |
| + for (size_t i = 0; i < arraysize(kOAuth2Scopes); i++) |
| + oauth2_scopes.insert(kOAuth2Scopes[i]); |
| + OAuth2TokenService* token_service = |
| + ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); |
| + if (invalidate_previous_token) { |
| + // Invalidate previous token, othervise token service will return the same |
|
Andrew T Wilson (Slow)
2013/05/31 12:57:28
nit:othervise->otherwise. Also add a period at the
pavely
2013/06/04 00:49:59
Done.
|
| + // token again |
| + token_service->InvalidateToken(oauth2_scopes, access_token_); |
| + access_token_.clear(); |
| + } |
| + access_token_request_ = token_service->StartRequest(oauth2_scopes, this); |
| + if (!invoke_callback) { |
| + // Deleting request will not cancel RPC but callbacks won't be invoked. |
| + access_token_request_.reset(); |
| + } |
| +} |
| + |
| void ProfileSyncService::SetEncryptionPassphrase(const std::string& passphrase, |
| PassphraseType type) { |
| // This should only be called when the backend has been initialized. |
| @@ -1936,27 +1993,33 @@ void ProfileSyncService::Observe(int type, |
| break; |
| } |
| case chrome::NOTIFICATION_TOKEN_REQUEST_FAILED: { |
| + // TODO(atwilson): sync shouldn't report refresh token request failures. |
| + // TokenService should do that instead. |
| const TokenService::TokenRequestFailedDetails& token_details = |
| *(content::Details<const TokenService::TokenRequestFailedDetails>( |
| details).ptr()); |
| - if (IsTokenServiceRelevant(token_details.service()) && |
| - !IsSyncTokenAvailable()) { |
| - // The additional check around IsSyncTokenAvailable() above prevents us |
| - // sounding the alarm if we actually have a valid token but a refresh |
| - // attempt by TokenService failed for any variety of reasons (e.g. flaky |
| - // network). It's possible the token we do have is also invalid, but in |
| - // that case we should already have (or can expect) an auth error sent |
| - // from the sync backend. |
| + if (token_details.service() == |
| + GaiaConstants::kGaiaOAuth2LoginRefreshToken && |
| + !IsOAuthRefreshTokenAvailable()) { |
| + // The additional check around IsOAuthRefreshTokenAvailable() above |
| + // prevents us sounding the alarm if we actually have a valid token but |
| + // a refresh attempt by TokenService failed for any variety of reasons |
| + // (e.g. flaky network). It's possible the token we do have is also |
| + // invalid, but in that case we should already have (or can expect) an |
| + // auth error sent from the sync backend. |
| AuthError error(AuthError::INVALID_GAIA_CREDENTIALS); |
| UpdateAuthErrorState(error); |
| } |
| break; |
| } |
| case chrome::NOTIFICATION_TOKEN_AVAILABLE: { |
| + // TODO(atwilson): Listen for notifications on OAuth2TokenService |
| + // (crbug.com/243737) |
| const TokenService::TokenAvailableDetails& token_details = |
| *(content::Details<const TokenService::TokenAvailableDetails>( |
| details).ptr()); |
| - if (!IsTokenServiceRelevant(token_details.service())) |
| + if (token_details.service() != |
| + GaiaConstants::kGaiaOAuth2LoginRefreshToken) |
| break; |
| } // Fall through. |
| case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: { |
| @@ -1966,14 +2029,16 @@ void ProfileSyncService::Observe(int type, |
| // not loaded, GetCredentials() will generate invalid credentials to |
| // cause the backend to generate an auth error (crbug.com/121755). |
| if (backend_) |
| - backend_->UpdateCredentials(GetCredentials()); |
| + RequestAccessToken(true, true); |
| else |
| + RequestAccessToken(true, false); |
|
Andrew T Wilson (Slow)
2013/05/31 12:57:28
Doesn't this fetch a token on every startup even i
|
| TryStart(); |
| break; |
| } |
| case chrome::NOTIFICATION_TOKENS_CLEARED: { |
| // GetCredentials() will generate invalid credentials to cause the backend |
| // to generate an auth error. |
| + access_token_.clear(); |
| if (backend_) |
| backend_->UpdateCredentials(GetCredentials()); |
| break; |
| @@ -2110,4 +2175,3 @@ std::string ProfileSyncService::GetEffectiveUsername() { |
| return signin_->GetAuthenticatedUsername(); |
| } |
| - |