1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "components/invalidation/ticl_invalidation_service.h" 6 7 #include "base/command_line.h" 8 #include "base/metrics/histogram.h" 9 #include "components/gcm_driver/gcm_driver.h" 10 #include "components/invalidation/gcm_invalidation_bridge.h" 11 #include "components/invalidation/invalidation_service_util.h" 12 #include "components/invalidation/invalidation_util.h" 13 #include "components/invalidation/invalidator.h" 14 #include "components/invalidation/invalidator_state.h" 15 #include "components/invalidation/non_blocking_invalidator.h" 16 #include "components/invalidation/object_id_invalidation_map.h" 17 #include "google_apis/gaia/gaia_constants.h" 18 #include "net/url_request/url_request_context_getter.h" 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 52 namespace invalidation { 53 54 TiclInvalidationService::TiclInvalidationService( 55 const std::string& user_agent, 56 scoped_ptr<IdentityProvider> identity_provider, 57 scoped_ptr<TiclSettingsProvider> settings_provider, 58 gcm::GCMDriver* gcm_driver, 59 const scoped_refptr<net::URLRequestContextGetter>& request_context) 60 : OAuth2TokenService::Consumer("ticl_invalidation"), 61 user_agent_(user_agent), 62 identity_provider_(identity_provider.Pass()), 63 settings_provider_(settings_provider.Pass()), 64 invalidator_registrar_(new syncer::InvalidatorRegistrar()), 65 request_access_token_backoff_(&kRequestAccessTokenBackoffPolicy), 66 network_channel_type_(GCM_NETWORK_CHANNEL), 67 gcm_driver_(gcm_driver), 68 request_context_(request_context), 69 logger_() {} 70 71 TiclInvalidationService::~TiclInvalidationService() { 72 DCHECK(CalledOnValidThread()); 73 settings_provider_->RemoveObserver(this); 74 identity_provider_->RemoveActiveAccountRefreshTokenObserver(this); 75 identity_provider_->RemoveObserver(this); 76 if (IsStarted()) { 77 StopInvalidator(); 78 } 79 } 80 81 void TiclInvalidationService::Init( 82 scoped_ptr<syncer::InvalidationStateTracker> invalidation_state_tracker) { 83 DCHECK(CalledOnValidThread()); 84 invalidation_state_tracker_ = invalidation_state_tracker.Pass(); 85 86 if (invalidation_state_tracker_->GetInvalidatorClientId().empty()) { 87 invalidation_state_tracker_->ClearAndSetNewClientId( 88 GenerateInvalidatorClientId()); 89 } 90 91 UpdateInvalidationNetworkChannel(); 92 if (IsReadyToStart()) { 93 StartInvalidator(network_channel_type_); 94 } 95 96 identity_provider_->AddObserver(this); 97 identity_provider_->AddActiveAccountRefreshTokenObserver(this); 98 settings_provider_->AddObserver(this); 99 } 100 101 void TiclInvalidationService::InitForTest( 102 scoped_ptr<syncer::InvalidationStateTracker> invalidation_state_tracker, 103 syncer::Invalidator* invalidator) { 104 // Here we perform the equivalent of Init() and StartInvalidator(), but with 105 // some minor changes to account for the fact that we're injecting the 106 // invalidator. 107 invalidation_state_tracker_ = invalidation_state_tracker.Pass(); 108 invalidator_.reset(invalidator); 109 110 invalidator_->RegisterHandler(this); 111 invalidator_->UpdateRegisteredIds( 112 this, 113 invalidator_registrar_->GetAllRegisteredIds()); 114 } 115 116 void TiclInvalidationService::RegisterInvalidationHandler( 117 syncer::InvalidationHandler* handler) { 118 DCHECK(CalledOnValidThread()); 119 DVLOG(2) << "Registering an invalidation handler"; 120 invalidator_registrar_->RegisterHandler(handler); 121 logger_.OnRegistration(handler->GetOwnerName()); 122 } 123 124 void TiclInvalidationService::UpdateRegisteredInvalidationIds( 125 syncer::InvalidationHandler* handler, 126 const syncer::ObjectIdSet& ids) { 127 DCHECK(CalledOnValidThread()); 128 DVLOG(2) << "Registering ids: " << ids.size(); 129 invalidator_registrar_->UpdateRegisteredIds(handler, ids); 130 if (invalidator_) { 131 invalidator_->UpdateRegisteredIds( 132 this, 133 invalidator_registrar_->GetAllRegisteredIds()); 134 } 135 logger_.OnUpdateIds(invalidator_registrar_->GetSanitizedHandlersIdsMap()); 136 } 137 138 void TiclInvalidationService::UnregisterInvalidationHandler( 139 syncer::InvalidationHandler* handler) { 140 DCHECK(CalledOnValidThread()); 141 DVLOG(2) << "Unregistering"; 142 invalidator_registrar_->UnregisterHandler(handler); 143 if (invalidator_) { 144 invalidator_->UpdateRegisteredIds( 145 this, 146 invalidator_registrar_->GetAllRegisteredIds()); 147 } 148 logger_.OnUnregistration(handler->GetOwnerName()); 149 } 150 151 syncer::InvalidatorState TiclInvalidationService::GetInvalidatorState() const { 152 DCHECK(CalledOnValidThread()); 153 if (invalidator_) { 154 DVLOG(2) << "GetInvalidatorState returning " 155 << invalidator_->GetInvalidatorState(); 156 return invalidator_->GetInvalidatorState(); 157 } else { 158 DVLOG(2) << "Invalidator currently stopped"; 159 return syncer::TRANSIENT_INVALIDATION_ERROR; 160 } 161 } 162 163 std::string TiclInvalidationService::GetInvalidatorClientId() const { 164 DCHECK(CalledOnValidThread()); 165 return invalidation_state_tracker_->GetInvalidatorClientId(); 166 } 167 168 InvalidationLogger* TiclInvalidationService::GetInvalidationLogger() { 169 return &logger_; 170 } 171 172 IdentityProvider* TiclInvalidationService::GetIdentityProvider() { 173 return identity_provider_.get(); 174 } 175 176 void TiclInvalidationService::RequestDetailedStatus( 177 base::Callback<void(const base::DictionaryValue&)> return_callback) const { 178 if (IsStarted()) { 179 return_callback.Run(network_channel_options_); 180 invalidator_->RequestDetailedStatus(return_callback); 181 } 182 } 183 184 void TiclInvalidationService::RequestAccessToken() { 185 // Only one active request at a time. 186 if (access_token_request_ != NULL) 187 return; 188 request_access_token_retry_timer_.Stop(); 189 OAuth2TokenService::ScopeSet oauth2_scopes; 190 for (size_t i = 0; i < arraysize(kOAuth2Scopes); i++) 191 oauth2_scopes.insert(kOAuth2Scopes[i]); 192 // Invalidate previous token, otherwise token service will return the same 193 // token again. 194 const std::string& account_id = identity_provider_->GetActiveAccountId(); 195 OAuth2TokenService* token_service = identity_provider_->GetTokenService(); 196 token_service->InvalidateToken(account_id, oauth2_scopes, access_token_); 197 access_token_.clear(); 198 access_token_request_ = 199 token_service->StartRequest(account_id, oauth2_scopes, this); 200 } 201 202 void TiclInvalidationService::OnGetTokenSuccess( 203 const OAuth2TokenService::Request* request, 204 const std::string& access_token, 205 const base::Time& expiration_time) { 206 DCHECK_EQ(access_token_request_, request); 207 access_token_request_.reset(); 208 // Reset backoff time after successful response. 209 request_access_token_backoff_.Reset(); 210 access_token_ = access_token; 211 if (!IsStarted() && IsReadyToStart()) { 212 StartInvalidator(network_channel_type_); 213 } else { 214 UpdateInvalidatorCredentials(); 215 } 216 } 217 218 void TiclInvalidationService::OnGetTokenFailure( 219 const OAuth2TokenService::Request* request, 220 const GoogleServiceAuthError& error) { 221 DCHECK_EQ(access_token_request_, request); 222 DCHECK_NE(error.state(), GoogleServiceAuthError::NONE); 223 access_token_request_.reset(); 224 switch (error.state()) { 225 case GoogleServiceAuthError::CONNECTION_FAILED: 226 case GoogleServiceAuthError::SERVICE_UNAVAILABLE: { 227 // Transient error. Retry after some time. 228 request_access_token_backoff_.InformOfRequest(false); 229 request_access_token_retry_timer_.Start( 230 FROM_HERE, 231 request_access_token_backoff_.GetTimeUntilRelease(), 232 base::Bind(&TiclInvalidationService::RequestAccessToken, 233 base::Unretained(this))); 234 break; 235 } 236 case GoogleServiceAuthError::SERVICE_ERROR: 237 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: { 238 invalidator_registrar_->UpdateInvalidatorState( 239 syncer::INVALIDATION_CREDENTIALS_REJECTED); 240 break; 241 } 242 default: { 243 // We have no way to notify the user of this. Do nothing. 244 } 245 } 246 } 247 248 void TiclInvalidationService::OnRefreshTokenAvailable( 249 const std::string& account_id) { 250 if (!IsStarted() && IsReadyToStart()) 251 StartInvalidator(network_channel_type_); 252 } 253 254 void TiclInvalidationService::OnRefreshTokenRevoked( 255 const std::string& account_id) { 256 access_token_.clear(); 257 if (IsStarted()) 258 UpdateInvalidatorCredentials(); 259 } 260 261 void TiclInvalidationService::OnActiveAccountLogout() { 262 access_token_request_.reset(); 263 request_access_token_retry_timer_.Stop(); 264 265 if (IsStarted()) { 266 StopInvalidator(); 267 } 268 269 // This service always expects to have a valid invalidation state. Thus, we 270 // must generate a new client ID to replace the existing one. Setting a new 271 // client ID also clears all other state. 272 invalidation_state_tracker_-> 273 ClearAndSetNewClientId(GenerateInvalidatorClientId()); 274 } 275 276 void TiclInvalidationService::OnUseGCMChannelChanged() { 277 UpdateInvalidationNetworkChannel(); 278 } 279 280 void TiclInvalidationService::OnInvalidatorStateChange( 281 syncer::InvalidatorState state) { 282 if (state == syncer::INVALIDATION_CREDENTIALS_REJECTED) { 283 // This may be due to normal OAuth access token expiration. If so, we must 284 // fetch a new one using our refresh token. Resetting the invalidator's 285 // access token will not reset the invalidator's exponential backoff, so 286 // it's safe to try to update the token every time we receive this signal. 287 // 288 // We won't be receiving any invalidations while the refresh is in progress, 289 // we set our state to TRANSIENT_INVALIDATION_ERROR. If the credentials 290 // really are invalid, the refresh request should fail and 291 // OnGetTokenFailure() will put us into a INVALIDATION_CREDENTIALS_REJECTED 292 // state. 293 invalidator_registrar_->UpdateInvalidatorState( 294 syncer::TRANSIENT_INVALIDATION_ERROR); 295 RequestAccessToken(); 296 } else { 297 invalidator_registrar_->UpdateInvalidatorState(state); 298 } 299 logger_.OnStateChange(state); 300 } 301 302 void TiclInvalidationService::OnIncomingInvalidation( 303 const syncer::ObjectIdInvalidationMap& invalidation_map) { 304 invalidator_registrar_->DispatchInvalidationsToHandlers(invalidation_map); 305 306 logger_.OnInvalidation(invalidation_map); 307 } 308 309 std::string TiclInvalidationService::GetOwnerName() const { return "TICL"; } 310 311 bool TiclInvalidationService::IsReadyToStart() { 312 if (identity_provider_->GetActiveAccountId().empty()) { 313 DVLOG(2) << "Not starting TiclInvalidationService: User is not signed in."; 314 return false; 315 } 316 317 OAuth2TokenService* token_service = identity_provider_->GetTokenService(); 318 if (!token_service) { 319 DVLOG(2) 320 << "Not starting TiclInvalidationService: " 321 << "OAuth2TokenService unavailable."; 322 return false; 323 } 324 325 if (!token_service->RefreshTokenIsAvailable( 326 identity_provider_->GetActiveAccountId())) { 327 DVLOG(2) 328 << "Not starting TiclInvalidationServce: Waiting for refresh token."; 329 return false; 330 } 331 332 return true; 333 } 334 335 bool TiclInvalidationService::IsStarted() const { 336 return invalidator_.get() != NULL; 337 } 338 339 void TiclInvalidationService::StartInvalidator( 340 InvalidationNetworkChannel network_channel) { 341 DCHECK(CalledOnValidThread()); 342 DCHECK(!invalidator_); 343 DCHECK(invalidation_state_tracker_); 344 DCHECK(!invalidation_state_tracker_->GetInvalidatorClientId().empty()); 345 346 // Request access token for PushClientChannel. GCMNetworkChannel will request 347 // access token before sending message to server. 348 if (network_channel == PUSH_CLIENT_CHANNEL && access_token_.empty()) { 349 DVLOG(1) 350 << "TiclInvalidationService: " 351 << "Deferring start until we have an access token."; 352 RequestAccessToken(); 353 return; 354 } 355 356 syncer::NetworkChannelCreator network_channel_creator; 357 358 switch (network_channel) { 359 case PUSH_CLIENT_CHANNEL: { 360 notifier::NotifierOptions options = 361 ParseNotifierOptions(*CommandLine::ForCurrentProcess()); 362 options.request_context_getter = request_context_; 363 options.auth_mechanism = "X-OAUTH2"; 364 network_channel_options_.SetString("Options.HostPort", 365 options.xmpp_host_port.ToString()); 366 network_channel_options_.SetString("Options.AuthMechanism", 367 options.auth_mechanism); 368 DCHECK_EQ(notifier::NOTIFICATION_SERVER, options.notification_method); 369 network_channel_creator = 370 syncer::NonBlockingInvalidator::MakePushClientChannelCreator(options); 371 break; 372 } 373 case GCM_NETWORK_CHANNEL: { 374 gcm_invalidation_bridge_.reset(new GCMInvalidationBridge( 375 gcm_driver_, identity_provider_.get())); 376 network_channel_creator = 377 syncer::NonBlockingInvalidator::MakeGCMNetworkChannelCreator( 378 request_context_, 379 gcm_invalidation_bridge_->CreateDelegate().Pass()); 380 break; 381 } 382 default: { 383 NOTREACHED(); 384 return; 385 } 386 } 387 388 UMA_HISTOGRAM_ENUMERATION( 389 "Invalidations.NetworkChannel", network_channel, NETWORK_CHANNELS_COUNT); 390 invalidator_.reset(new syncer::NonBlockingInvalidator( 391 network_channel_creator, 392 invalidation_state_tracker_->GetInvalidatorClientId(), 393 invalidation_state_tracker_->GetSavedInvalidations(), 394 invalidation_state_tracker_->GetBootstrapData(), 395 invalidation_state_tracker_.get(), 396 user_agent_, 397 request_context_)); 398 399 UpdateInvalidatorCredentials(); 400 401 invalidator_->RegisterHandler(this); 402 invalidator_->UpdateRegisteredIds( 403 this, 404 invalidator_registrar_->GetAllRegisteredIds()); 405 } 406 407 void TiclInvalidationService::UpdateInvalidationNetworkChannel() { 408 const InvalidationNetworkChannel network_channel_type = 409 settings_provider_->UseGCMChannel() ? GCM_NETWORK_CHANNEL 410 : PUSH_CLIENT_CHANNEL; 411 if (network_channel_type_ == network_channel_type) 412 return; 413 network_channel_type_ = network_channel_type; 414 if (IsStarted()) { 415 StopInvalidator(); 416 StartInvalidator(network_channel_type_); 417 } 418 } 419 420 void TiclInvalidationService::UpdateInvalidatorCredentials() { 421 std::string email = identity_provider_->GetActiveAccountId(); 422 423 DCHECK(!email.empty()) << "Expected user to be signed in."; 424 425 DVLOG(2) << "UpdateCredentials: " << email; 426 invalidator_->UpdateCredentials(email, access_token_); 427 } 428 429 void TiclInvalidationService::StopInvalidator() { 430 DCHECK(invalidator_); 431 gcm_invalidation_bridge_.reset(); 432 invalidator_->UnregisterHandler(this); 433 invalidator_.reset(); 434 } 435 436 } // namespace invalidation 437