Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/invalidation/ticl_invalidation_service.h" | 5 #include "chrome/browser/invalidation/ticl_invalidation_service.h" |
| 6 | 6 |
| 7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
| 8 #include "chrome/browser/invalidation/invalidation_service_util.h" | 8 #include "chrome/browser/invalidation/invalidation_service_util.h" |
| 9 #include "chrome/browser/profiles/profile.h" | 9 #include "chrome/browser/profiles/profile.h" |
| 10 #include "chrome/browser/signin/profile_oauth2_token_service.h" | |
| 11 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" | |
| 10 #include "chrome/browser/signin/signin_manager.h" | 12 #include "chrome/browser/signin/signin_manager.h" |
| 11 #include "chrome/browser/signin/token_service.h" | |
| 12 #include "chrome/common/chrome_notification_types.h" | 13 #include "chrome/common/chrome_notification_types.h" |
| 13 #include "content/public/browser/notification_service.h" | 14 #include "content/public/browser/notification_service.h" |
| 14 #include "google_apis/gaia/gaia_constants.h" | 15 #include "google_apis/gaia/gaia_constants.h" |
| 15 #include "sync/notifier/invalidator.h" | 16 #include "sync/notifier/invalidator.h" |
| 16 #include "sync/notifier/invalidator_state.h" | 17 #include "sync/notifier/invalidator_state.h" |
| 17 #include "sync/notifier/non_blocking_invalidator.h" | 18 #include "sync/notifier/non_blocking_invalidator.h" |
| 18 | 19 |
| 20 static const char* kOAuth2Scopes[] = { | |
| 21 GaiaConstants::kGoogleTalkOAuth2Scope | |
| 22 }; | |
| 23 | |
| 24 static const net::BackoffEntry::Policy kRequestAccessTokenBackoffPolicy = { | |
| 25 // Number of initial errors (in sequence) to ignore before applying | |
| 26 // exponential back-off rules. | |
| 27 0, | |
| 28 | |
| 29 // Initial delay for exponential back-off in ms. | |
| 30 2000, | |
| 31 | |
| 32 // Factor by which the waiting time will be multiplied. | |
| 33 2, | |
| 34 | |
| 35 // Fuzzing percentage. ex: 10% will spread requests randomly | |
| 36 // between 90%-100% of the calculated time. | |
| 37 0.2, // 20% | |
| 38 | |
| 39 // Maximum amount of time we are willing to delay our request in ms. | |
| 40 // TODO(pavely): crbug.com/246686 ProfileSyncService should retry | |
| 41 // RequestAccessToken on connection state change after backoff | |
| 42 1000 * 3600 * 4, // 4 hours. | |
| 43 | |
| 44 // Time to keep an entry from being discarded even when it | |
| 45 // has no significant state, -1 to never discard. | |
| 46 -1, | |
| 47 | |
| 48 // Don't use initial delay unless the last request was an error. | |
| 49 false, | |
| 50 }; | |
| 51 | |
| 19 namespace invalidation { | 52 namespace invalidation { |
| 20 | 53 |
| 21 TiclInvalidationService::TiclInvalidationService(SigninManagerBase* signin, | 54 TiclInvalidationService::TiclInvalidationService( |
| 22 TokenService* token_service, | 55 SigninManagerBase* signin, |
| 23 Profile* profile) | 56 TokenService* token_service, |
| 24 : profile_(profile), | 57 OAuth2TokenService* oauth2_token_service, |
| 25 signin_manager_(signin), | 58 Profile* profile) |
| 26 token_service_(token_service), | 59 : profile_(profile), |
| 27 invalidator_registrar_(new syncer::InvalidatorRegistrar()) { } | 60 signin_manager_(signin), |
| 61 token_service_(token_service), | |
| 62 oauth2_token_service_(oauth2_token_service), | |
| 63 invalidator_registrar_(new syncer::InvalidatorRegistrar()), | |
| 64 request_access_token_backoff_(&kRequestAccessTokenBackoffPolicy) { | |
| 65 } | |
| 28 | 66 |
| 29 TiclInvalidationService::~TiclInvalidationService() { | 67 TiclInvalidationService::~TiclInvalidationService() { |
| 30 DCHECK(CalledOnValidThread()); | 68 DCHECK(CalledOnValidThread()); |
| 31 } | 69 } |
| 32 | 70 |
| 33 void TiclInvalidationService::Init() { | 71 void TiclInvalidationService::Init() { |
| 34 DCHECK(CalledOnValidThread()); | 72 DCHECK(CalledOnValidThread()); |
| 35 | 73 |
| 36 invalidator_storage_.reset(new InvalidatorStorage(profile_->GetPrefs())); | 74 invalidator_storage_.reset(new InvalidatorStorage(profile_->GetPrefs())); |
| 37 if (invalidator_storage_->GetInvalidatorClientId().empty()) { | 75 if (invalidator_storage_->GetInvalidatorClientId().empty()) { |
| 38 // This also clears any existing state. We can't reuse old invalidator | 76 // This also clears any existing state. We can't reuse old invalidator |
| 39 // state with the new ID anyway. | 77 // state with the new ID anyway. |
| 40 invalidator_storage_->SetInvalidatorClientId(GenerateInvalidatorClientId()); | 78 invalidator_storage_->SetInvalidatorClientId(GenerateInvalidatorClientId()); |
| 41 } | 79 } |
| 42 | 80 |
| 43 if (IsReadyToStart()) { | 81 if (IsReadyToStart()) { |
| 44 Start(); | 82 StartInvalidator(); |
| 45 } | 83 } |
| 46 | 84 |
| 47 notification_registrar_.Add(this, | 85 notification_registrar_.Add(this, |
| 48 chrome::NOTIFICATION_TOKEN_AVAILABLE, | 86 chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, |
| 87 content::Source<Profile>(profile_)); | |
| 88 notification_registrar_.Add(this, | |
| 89 chrome::NOTIFICATION_TOKEN_LOADING_FINISHED, | |
| 49 content::Source<TokenService>(token_service_)); | 90 content::Source<TokenService>(token_service_)); |
| 50 notification_registrar_.Add(this, | 91 notification_registrar_.Add(this, |
| 51 chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, | 92 chrome::NOTIFICATION_TOKENS_CLEARED, |
| 52 content::Source<Profile>(profile_)); | 93 content::Source<TokenService>(token_service_)); |
| 53 } | 94 } |
| 54 | 95 |
| 55 void TiclInvalidationService::InitForTest(syncer::Invalidator* invalidator) { | 96 void TiclInvalidationService::InitForTest(syncer::Invalidator* invalidator) { |
| 56 // Here we perform the equivalent of Init() and Start(), but with some minor | 97 // Here we perform the equivalent of Init() and StartInvalidator(), but with |
| 57 // changes to account for the fact that we're injecting the invalidator. | 98 // some minor changes to account for the fact that we're injecting the |
| 99 // invalidator. | |
| 58 invalidator_.reset(invalidator); | 100 invalidator_.reset(invalidator); |
| 59 | 101 |
| 60 invalidator_->RegisterHandler(this); | 102 invalidator_->RegisterHandler(this); |
| 61 invalidator_->UpdateRegisteredIds( | 103 invalidator_->UpdateRegisteredIds( |
| 62 this, | 104 this, |
| 63 invalidator_registrar_->GetAllRegisteredIds()); | 105 invalidator_registrar_->GetAllRegisteredIds()); |
| 64 } | 106 } |
| 65 | 107 |
| 66 void TiclInvalidationService::RegisterInvalidationHandler( | 108 void TiclInvalidationService::RegisterInvalidationHandler( |
| 67 syncer::InvalidationHandler* handler) { | 109 syncer::InvalidationHandler* handler) { |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 121 return invalidator_storage_->GetInvalidatorClientId(); | 163 return invalidator_storage_->GetInvalidatorClientId(); |
| 122 } | 164 } |
| 123 | 165 |
| 124 void TiclInvalidationService::Observe( | 166 void TiclInvalidationService::Observe( |
| 125 int type, | 167 int type, |
| 126 const content::NotificationSource& source, | 168 const content::NotificationSource& source, |
| 127 const content::NotificationDetails& details) { | 169 const content::NotificationDetails& details) { |
| 128 DCHECK(CalledOnValidThread()); | 170 DCHECK(CalledOnValidThread()); |
| 129 | 171 |
| 130 switch (type) { | 172 switch (type) { |
| 131 case chrome::NOTIFICATION_TOKEN_AVAILABLE: { | 173 case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: { |
| 132 const TokenService::TokenAvailableDetails& token_details = | 174 if (!IsStarted() && IsReadyToStart()) { |
| 133 *(content::Details<const TokenService::TokenAvailableDetails>( | 175 StartInvalidator(); |
| 134 details).ptr()); | |
| 135 if (token_details.service() == GaiaConstants::kSyncService) { | |
| 136 DCHECK(IsReadyToStart()); | |
| 137 if (!IsStarted()) { | |
| 138 Start(); | |
| 139 } else { | |
| 140 UpdateToken(); | |
| 141 } | |
| 142 } | 176 } |
| 143 break; | 177 break; |
| 144 } | 178 } |
| 179 case chrome::NOTIFICATION_TOKENS_CLEARED: { | |
| 180 access_token_.clear(); | |
| 181 if (IsStarted()) { | |
| 182 UpdateInvalidatorCredentials(); | |
| 183 } | |
| 184 break; | |
| 185 } | |
| 145 case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT: { | 186 case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT: { |
| 146 Logout(); | 187 Logout(); |
| 147 break; | 188 break; |
| 148 } | 189 } |
| 149 default: { | 190 default: { |
| 150 NOTREACHED(); | 191 NOTREACHED(); |
| 151 } | 192 } |
| 152 } | 193 } |
| 153 } | 194 } |
| 154 | 195 |
| 196 void TiclInvalidationService::RequestAccessToken() { | |
| 197 // Only one active request at a time. | |
| 198 if (access_token_request_ != NULL) | |
| 199 return; | |
| 200 request_access_token_retry_timer_.Stop(); | |
| 201 OAuth2TokenService::ScopeSet oauth2_scopes; | |
| 202 for (size_t i = 0; i < arraysize(kOAuth2Scopes); i++) | |
| 203 oauth2_scopes.insert(kOAuth2Scopes[i]); | |
| 204 OAuth2TokenService* token_service = | |
| 205 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); | |
| 206 // Invalidate previous token, otherwise token service will return the same | |
| 207 // token again. | |
| 208 token_service->InvalidateToken(oauth2_scopes, access_token_); | |
| 209 access_token_.clear(); | |
| 210 access_token_request_ = token_service->StartRequest(oauth2_scopes, this); | |
| 211 } | |
| 212 | |
| 213 void TiclInvalidationService::OnGetTokenSuccess( | |
| 214 const OAuth2TokenService::Request* request, | |
| 215 const std::string& access_token, | |
| 216 const base::Time& expiration_time) { | |
| 217 DCHECK_EQ(access_token_request_, request); | |
| 218 access_token_request_.reset(); | |
| 219 // Reset backoff time after successful response. | |
| 220 request_access_token_backoff_.Reset(); | |
| 221 access_token_ = access_token; | |
| 222 if (!IsStarted()) { | |
| 223 StartInvalidator(); | |
| 224 } else { | |
| 225 UpdateInvalidatorCredentials(); | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 void TiclInvalidationService::OnGetTokenFailure( | |
| 230 const OAuth2TokenService::Request* request, | |
| 231 const GoogleServiceAuthError& error) { | |
| 232 DCHECK_EQ(access_token_request_, request); | |
| 233 DCHECK_NE(error.state(), GoogleServiceAuthError::NONE); | |
| 234 access_token_request_.reset(); | |
| 235 switch (error.state()) { | |
| 236 case GoogleServiceAuthError::CONNECTION_FAILED: | |
| 237 case GoogleServiceAuthError::SERVICE_UNAVAILABLE: { | |
| 238 // Transient error. Retry after some time. | |
| 239 request_access_token_backoff_.InformOfRequest(false); | |
| 240 request_access_token_retry_timer_.Start( | |
| 241 FROM_HERE, | |
| 242 request_access_token_backoff_.GetTimeUntilRelease(), | |
| 243 base::Bind(&TiclInvalidationService::RequestAccessToken, | |
| 244 base::Unretained(this))); | |
| 245 break; | |
| 246 } | |
| 247 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: { | |
| 248 // Now *this* is a real auth error. | |
| 249 invalidator_registrar_->UpdateInvalidatorState( | |
| 250 syncer::INVALIDATION_CREDENTIALS_REJECTED); | |
| 251 break; | |
| 252 } | |
| 253 default: { | |
| 254 // We have no way to notify the user of this. Do nothing. | |
| 255 } | |
| 256 } | |
| 257 } | |
| 258 | |
| 155 void TiclInvalidationService::OnInvalidatorStateChange( | 259 void TiclInvalidationService::OnInvalidatorStateChange( |
| 156 syncer::InvalidatorState state) { | 260 syncer::InvalidatorState state) { |
| 157 invalidator_registrar_->UpdateInvalidatorState(state); | 261 if (state == syncer::INVALIDATION_CREDENTIALS_REJECTED) { |
|
rlarocque
2013/06/14 23:48:27
I decided against modifying SyncManager's invalida
| |
| 262 // Don't report this as an auth error right away. It's possible that we | |
| 263 // just need to refresh our access token. If that fails, then we can update | |
| 264 // the user-facing state. | |
| 265 RequestAccessToken(); | |
| 266 } else { | |
| 267 invalidator_registrar_->UpdateInvalidatorState(state); | |
| 268 } | |
| 158 } | 269 } |
| 159 | 270 |
| 160 void TiclInvalidationService::OnIncomingInvalidation( | 271 void TiclInvalidationService::OnIncomingInvalidation( |
| 161 const syncer::ObjectIdInvalidationMap& invalidation_map) { | 272 const syncer::ObjectIdInvalidationMap& invalidation_map) { |
| 162 invalidator_registrar_->DispatchInvalidationsToHandlers(invalidation_map); | 273 invalidator_registrar_->DispatchInvalidationsToHandlers(invalidation_map); |
| 163 } | 274 } |
| 164 | 275 |
| 165 void TiclInvalidationService::Shutdown() { | 276 void TiclInvalidationService::Shutdown() { |
| 166 DCHECK(CalledOnValidThread()); | 277 DCHECK(CalledOnValidThread()); |
| 167 if (IsStarted()) { | 278 if (IsStarted()) { |
| 168 StopInvalidator(); | 279 StopInvalidator(); |
| 169 } | 280 } |
| 170 invalidator_storage_.reset(); | 281 invalidator_storage_.reset(); |
| 171 invalidator_registrar_.reset(); | 282 invalidator_registrar_.reset(); |
| 172 } | 283 } |
| 173 | 284 |
| 174 bool TiclInvalidationService::IsReadyToStart() { | 285 bool TiclInvalidationService::IsReadyToStart() { |
| 175 if (signin_manager_->GetAuthenticatedUsername().empty()) { | 286 if (signin_manager_->GetAuthenticatedUsername().empty()) { |
| 176 DVLOG(2) << "Not starting TiclInvalidationService: user is not signed in."; | 287 DVLOG(2) << "Not starting TiclInvalidationService: user is not signed in."; |
| 177 return false; | 288 return false; |
| 178 } | 289 } |
| 179 | 290 |
| 180 if (!token_service_) { | 291 if (!oauth2_token_service_) { |
| 181 DVLOG(2) | 292 DVLOG(2) |
| 182 << "Not starting TiclInvalidationService: TokenService unavailable."; | 293 << "Not starting TiclInvalidationService: TokenService unavailable."; |
| 183 return false; | 294 return false; |
| 184 } | 295 } |
| 185 | 296 |
| 186 if (!token_service_->HasTokenForService(GaiaConstants::kSyncService)) { | 297 if (!oauth2_token_service_->RefreshTokenIsAvailable()) { |
| 187 DVLOG(2) << "Not starting TiclInvalidationService: Sync token unavailable."; | 298 DVLOG(2) |
| 299 << "Not starting TiclInvalidationServce: Waiting for refresh token."; | |
| 188 return false; | 300 return false; |
| 189 } | 301 } |
| 190 | 302 |
| 191 return true; | 303 return true; |
| 192 } | 304 } |
| 193 | 305 |
| 194 bool TiclInvalidationService::IsStarted() { | 306 bool TiclInvalidationService::IsStarted() { |
| 195 return invalidator_.get() != NULL; | 307 return invalidator_.get() != NULL; |
| 196 } | 308 } |
| 197 | 309 |
| 198 void TiclInvalidationService::Start() { | 310 void TiclInvalidationService::StartInvalidator() { |
| 199 DCHECK(CalledOnValidThread()); | 311 DCHECK(CalledOnValidThread()); |
| 200 DCHECK(!invalidator_); | 312 DCHECK(!invalidator_); |
| 201 DCHECK(invalidator_storage_); | 313 DCHECK(invalidator_storage_); |
| 202 DCHECK(!invalidator_storage_->GetInvalidatorClientId().empty()); | 314 DCHECK(!invalidator_storage_->GetInvalidatorClientId().empty()); |
| 203 | 315 |
| 316 if (access_token_.empty()) { | |
| 317 DVLOG(1) | |
| 318 << "TiclInvalidationService: " | |
| 319 << "Deferring start until we have an access token."; | |
| 320 RequestAccessToken(); | |
| 321 return; | |
| 322 } | |
| 323 | |
| 204 notifier::NotifierOptions options = | 324 notifier::NotifierOptions options = |
| 205 ParseNotifierOptions(*CommandLine::ForCurrentProcess()); | 325 ParseNotifierOptions(*CommandLine::ForCurrentProcess()); |
| 206 options.request_context_getter = profile_->GetRequestContext(); | 326 options.request_context_getter = profile_->GetRequestContext(); |
| 327 options.auth_mechanism = "X-OAUTH2"; | |
| 207 invalidator_.reset(new syncer::NonBlockingInvalidator( | 328 invalidator_.reset(new syncer::NonBlockingInvalidator( |
| 208 options, | 329 options, |
| 209 invalidator_storage_->GetInvalidatorClientId(), | 330 invalidator_storage_->GetInvalidatorClientId(), |
| 210 invalidator_storage_->GetAllInvalidationStates(), | 331 invalidator_storage_->GetAllInvalidationStates(), |
| 211 invalidator_storage_->GetBootstrapData(), | 332 invalidator_storage_->GetBootstrapData(), |
| 212 syncer::WeakHandle<syncer::InvalidationStateTracker>( | 333 syncer::WeakHandle<syncer::InvalidationStateTracker>( |
| 213 invalidator_storage_->AsWeakPtr()), | 334 invalidator_storage_->AsWeakPtr()), |
| 214 content::GetUserAgent(GURL()))); | 335 content::GetUserAgent(GURL()))); |
| 215 | 336 |
| 216 UpdateToken(); | 337 UpdateInvalidatorCredentials(); |
| 217 | 338 |
| 218 invalidator_->RegisterHandler(this); | 339 invalidator_->RegisterHandler(this); |
| 219 invalidator_->UpdateRegisteredIds( | 340 invalidator_->UpdateRegisteredIds( |
| 220 this, | 341 this, |
| 221 invalidator_registrar_->GetAllRegisteredIds()); | 342 invalidator_registrar_->GetAllRegisteredIds()); |
| 222 } | 343 } |
| 223 | 344 |
| 224 void TiclInvalidationService::UpdateToken() { | 345 void TiclInvalidationService::UpdateInvalidatorCredentials() { |
| 225 std::string email = signin_manager_->GetAuthenticatedUsername(); | 346 std::string email = signin_manager_->GetAuthenticatedUsername(); |
| 347 | |
| 226 DCHECK(!email.empty()) << "Expected user to be signed in."; | 348 DCHECK(!email.empty()) << "Expected user to be signed in."; |
| 227 DCHECK(token_service_->HasTokenForService(GaiaConstants::kSyncService)); | 349 DCHECK(!access_token_.empty()); |
| 228 | |
| 229 std::string sync_token = token_service_->GetTokenForService( | |
| 230 GaiaConstants::kSyncService); | |
| 231 | 350 |
| 232 DVLOG(2) << "UpdateCredentials: " << email; | 351 DVLOG(2) << "UpdateCredentials: " << email; |
| 233 invalidator_->UpdateCredentials(email, sync_token); | 352 invalidator_->UpdateCredentials(email, access_token_); |
| 234 } | 353 } |
| 235 | 354 |
| 236 void TiclInvalidationService::StopInvalidator() { | 355 void TiclInvalidationService::StopInvalidator() { |
| 237 DCHECK(invalidator_); | 356 DCHECK(invalidator_); |
| 238 invalidator_->UnregisterHandler(this); | 357 invalidator_->UnregisterHandler(this); |
| 239 invalidator_.reset(); | 358 invalidator_.reset(); |
| 240 } | 359 } |
| 241 | 360 |
| 242 void TiclInvalidationService::Logout() { | 361 void TiclInvalidationService::Logout() { |
| 362 request_access_token_retry_timer_.Stop(); | |
| 363 | |
| 243 StopInvalidator(); | 364 StopInvalidator(); |
| 244 | 365 |
| 245 // This service always expects to have a valid invalidator storage. | 366 // This service always expects to have a valid invalidator storage. |
| 246 // So we must not only clear the old one, but also start a new one. | 367 // So we must not only clear the old one, but also start a new one. |
| 247 invalidator_storage_->Clear(); | 368 invalidator_storage_->Clear(); |
| 248 invalidator_storage_.reset(new InvalidatorStorage(profile_->GetPrefs())); | 369 invalidator_storage_.reset(new InvalidatorStorage(profile_->GetPrefs())); |
| 249 invalidator_storage_->SetInvalidatorClientId(GenerateInvalidatorClientId()); | 370 invalidator_storage_->SetInvalidatorClientId(GenerateInvalidatorClientId()); |
| 250 } | 371 } |
| 251 | 372 |
| 252 } // namespace invalidation | 373 } // namespace invalidation |
| OLD | NEW |