OLD | NEW |
| (Empty) |
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_macros.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 CHECK(invalidator_->UpdateRegisteredIds( | |
112 this, invalidator_registrar_->GetAllRegisteredIds())); | |
113 } | |
114 | |
115 void TiclInvalidationService::RegisterInvalidationHandler( | |
116 syncer::InvalidationHandler* handler) { | |
117 DCHECK(CalledOnValidThread()); | |
118 DVLOG(2) << "Registering an invalidation handler"; | |
119 invalidator_registrar_->RegisterHandler(handler); | |
120 logger_.OnRegistration(handler->GetOwnerName()); | |
121 } | |
122 | |
123 bool TiclInvalidationService::UpdateRegisteredInvalidationIds( | |
124 syncer::InvalidationHandler* handler, | |
125 const syncer::ObjectIdSet& ids) { | |
126 DCHECK(CalledOnValidThread()); | |
127 DVLOG(2) << "Registering ids: " << ids.size(); | |
128 if (!invalidator_registrar_->UpdateRegisteredIds(handler, ids)) | |
129 return false; | |
130 if (invalidator_) { | |
131 CHECK(invalidator_->UpdateRegisteredIds( | |
132 this, invalidator_registrar_->GetAllRegisteredIds())); | |
133 } | |
134 logger_.OnUpdateIds(invalidator_registrar_->GetSanitizedHandlersIdsMap()); | |
135 return true; | |
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 CHECK(invalidator_->UpdateRegisteredIds( | |
145 this, invalidator_registrar_->GetAllRegisteredIds())); | |
146 } | |
147 logger_.OnUnregistration(handler->GetOwnerName()); | |
148 } | |
149 | |
150 syncer::InvalidatorState TiclInvalidationService::GetInvalidatorState() const { | |
151 DCHECK(CalledOnValidThread()); | |
152 if (invalidator_) { | |
153 DVLOG(2) << "GetInvalidatorState returning " | |
154 << invalidator_->GetInvalidatorState(); | |
155 return invalidator_->GetInvalidatorState(); | |
156 } else { | |
157 DVLOG(2) << "Invalidator currently stopped"; | |
158 return syncer::TRANSIENT_INVALIDATION_ERROR; | |
159 } | |
160 } | |
161 | |
162 std::string TiclInvalidationService::GetInvalidatorClientId() const { | |
163 DCHECK(CalledOnValidThread()); | |
164 return invalidation_state_tracker_->GetInvalidatorClientId(); | |
165 } | |
166 | |
167 InvalidationLogger* TiclInvalidationService::GetInvalidationLogger() { | |
168 return &logger_; | |
169 } | |
170 | |
171 IdentityProvider* TiclInvalidationService::GetIdentityProvider() { | |
172 return identity_provider_.get(); | |
173 } | |
174 | |
175 void TiclInvalidationService::RequestDetailedStatus( | |
176 base::Callback<void(const base::DictionaryValue&)> return_callback) const { | |
177 if (IsStarted()) { | |
178 return_callback.Run(network_channel_options_); | |
179 invalidator_->RequestDetailedStatus(return_callback); | |
180 } | |
181 } | |
182 | |
183 void TiclInvalidationService::RequestAccessToken() { | |
184 // Only one active request at a time. | |
185 if (access_token_request_ != NULL) | |
186 return; | |
187 request_access_token_retry_timer_.Stop(); | |
188 OAuth2TokenService::ScopeSet oauth2_scopes; | |
189 for (size_t i = 0; i < arraysize(kOAuth2Scopes); i++) | |
190 oauth2_scopes.insert(kOAuth2Scopes[i]); | |
191 // Invalidate previous token, otherwise token service will return the same | |
192 // token again. | |
193 const std::string& account_id = identity_provider_->GetActiveAccountId(); | |
194 OAuth2TokenService* token_service = identity_provider_->GetTokenService(); | |
195 token_service->InvalidateToken(account_id, oauth2_scopes, access_token_); | |
196 access_token_.clear(); | |
197 access_token_request_ = | |
198 token_service->StartRequest(account_id, oauth2_scopes, this); | |
199 } | |
200 | |
201 void TiclInvalidationService::OnGetTokenSuccess( | |
202 const OAuth2TokenService::Request* request, | |
203 const std::string& access_token, | |
204 const base::Time& expiration_time) { | |
205 DCHECK_EQ(access_token_request_, request); | |
206 access_token_request_.reset(); | |
207 // Reset backoff time after successful response. | |
208 request_access_token_backoff_.Reset(); | |
209 access_token_ = access_token; | |
210 if (!IsStarted() && IsReadyToStart()) { | |
211 StartInvalidator(network_channel_type_); | |
212 } else { | |
213 UpdateInvalidatorCredentials(); | |
214 } | |
215 } | |
216 | |
217 void TiclInvalidationService::OnGetTokenFailure( | |
218 const OAuth2TokenService::Request* request, | |
219 const GoogleServiceAuthError& error) { | |
220 DCHECK_EQ(access_token_request_, request); | |
221 DCHECK_NE(error.state(), GoogleServiceAuthError::NONE); | |
222 access_token_request_.reset(); | |
223 switch (error.state()) { | |
224 case GoogleServiceAuthError::CONNECTION_FAILED: | |
225 case GoogleServiceAuthError::SERVICE_UNAVAILABLE: { | |
226 // Transient error. Retry after some time. | |
227 request_access_token_backoff_.InformOfRequest(false); | |
228 request_access_token_retry_timer_.Start( | |
229 FROM_HERE, | |
230 request_access_token_backoff_.GetTimeUntilRelease(), | |
231 base::Bind(&TiclInvalidationService::RequestAccessToken, | |
232 base::Unretained(this))); | |
233 break; | |
234 } | |
235 case GoogleServiceAuthError::SERVICE_ERROR: | |
236 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: { | |
237 invalidator_registrar_->UpdateInvalidatorState( | |
238 syncer::INVALIDATION_CREDENTIALS_REJECTED); | |
239 break; | |
240 } | |
241 default: { | |
242 // We have no way to notify the user of this. Do nothing. | |
243 } | |
244 } | |
245 } | |
246 | |
247 void TiclInvalidationService::OnRefreshTokenAvailable( | |
248 const std::string& account_id) { | |
249 if (!IsStarted() && IsReadyToStart()) | |
250 StartInvalidator(network_channel_type_); | |
251 } | |
252 | |
253 void TiclInvalidationService::OnRefreshTokenRevoked( | |
254 const std::string& account_id) { | |
255 access_token_.clear(); | |
256 if (IsStarted()) | |
257 UpdateInvalidatorCredentials(); | |
258 } | |
259 | |
260 void TiclInvalidationService::OnActiveAccountLogout() { | |
261 access_token_request_.reset(); | |
262 request_access_token_retry_timer_.Stop(); | |
263 | |
264 if (gcm_invalidation_bridge_) | |
265 gcm_invalidation_bridge_->Unregister(); | |
266 | |
267 if (IsStarted()) { | |
268 StopInvalidator(); | |
269 } | |
270 | |
271 // This service always expects to have a valid invalidation state. Thus, we | |
272 // must generate a new client ID to replace the existing one. Setting a new | |
273 // client ID also clears all other state. | |
274 invalidation_state_tracker_-> | |
275 ClearAndSetNewClientId(GenerateInvalidatorClientId()); | |
276 } | |
277 | |
278 void TiclInvalidationService::OnUseGCMChannelChanged() { | |
279 UpdateInvalidationNetworkChannel(); | |
280 } | |
281 | |
282 void TiclInvalidationService::OnInvalidatorStateChange( | |
283 syncer::InvalidatorState state) { | |
284 if (state == syncer::INVALIDATION_CREDENTIALS_REJECTED) { | |
285 // This may be due to normal OAuth access token expiration. If so, we must | |
286 // fetch a new one using our refresh token. Resetting the invalidator's | |
287 // access token will not reset the invalidator's exponential backoff, so | |
288 // it's safe to try to update the token every time we receive this signal. | |
289 // | |
290 // We won't be receiving any invalidations while the refresh is in progress, | |
291 // we set our state to TRANSIENT_INVALIDATION_ERROR. If the credentials | |
292 // really are invalid, the refresh request should fail and | |
293 // OnGetTokenFailure() will put us into a INVALIDATION_CREDENTIALS_REJECTED | |
294 // state. | |
295 invalidator_registrar_->UpdateInvalidatorState( | |
296 syncer::TRANSIENT_INVALIDATION_ERROR); | |
297 RequestAccessToken(); | |
298 } else { | |
299 invalidator_registrar_->UpdateInvalidatorState(state); | |
300 } | |
301 logger_.OnStateChange(state); | |
302 } | |
303 | |
304 void TiclInvalidationService::OnIncomingInvalidation( | |
305 const syncer::ObjectIdInvalidationMap& invalidation_map) { | |
306 invalidator_registrar_->DispatchInvalidationsToHandlers(invalidation_map); | |
307 | |
308 logger_.OnInvalidation(invalidation_map); | |
309 } | |
310 | |
311 std::string TiclInvalidationService::GetOwnerName() const { return "TICL"; } | |
312 | |
313 bool TiclInvalidationService::IsReadyToStart() { | |
314 if (identity_provider_->GetActiveAccountId().empty()) { | |
315 DVLOG(2) << "Not starting TiclInvalidationService: User is not signed in."; | |
316 return false; | |
317 } | |
318 | |
319 OAuth2TokenService* token_service = identity_provider_->GetTokenService(); | |
320 if (!token_service) { | |
321 DVLOG(2) | |
322 << "Not starting TiclInvalidationService: " | |
323 << "OAuth2TokenService unavailable."; | |
324 return false; | |
325 } | |
326 | |
327 if (!token_service->RefreshTokenIsAvailable( | |
328 identity_provider_->GetActiveAccountId())) { | |
329 DVLOG(2) | |
330 << "Not starting TiclInvalidationServce: Waiting for refresh token."; | |
331 return false; | |
332 } | |
333 | |
334 return true; | |
335 } | |
336 | |
337 bool TiclInvalidationService::IsStarted() const { | |
338 return invalidator_.get() != NULL; | |
339 } | |
340 | |
341 void TiclInvalidationService::StartInvalidator( | |
342 InvalidationNetworkChannel network_channel) { | |
343 DCHECK(CalledOnValidThread()); | |
344 DCHECK(!invalidator_); | |
345 DCHECK(invalidation_state_tracker_); | |
346 DCHECK(!invalidation_state_tracker_->GetInvalidatorClientId().empty()); | |
347 | |
348 // Request access token for PushClientChannel. GCMNetworkChannel will request | |
349 // access token before sending message to server. | |
350 if (network_channel == PUSH_CLIENT_CHANNEL && access_token_.empty()) { | |
351 DVLOG(1) | |
352 << "TiclInvalidationService: " | |
353 << "Deferring start until we have an access token."; | |
354 RequestAccessToken(); | |
355 return; | |
356 } | |
357 | |
358 syncer::NetworkChannelCreator network_channel_creator; | |
359 | |
360 switch (network_channel) { | |
361 case PUSH_CLIENT_CHANNEL: { | |
362 notifier::NotifierOptions options = | |
363 ParseNotifierOptions(*base::CommandLine::ForCurrentProcess()); | |
364 options.request_context_getter = request_context_; | |
365 options.auth_mechanism = "X-OAUTH2"; | |
366 network_channel_options_.SetString("Options.HostPort", | |
367 options.xmpp_host_port.ToString()); | |
368 network_channel_options_.SetString("Options.AuthMechanism", | |
369 options.auth_mechanism); | |
370 DCHECK_EQ(notifier::NOTIFICATION_SERVER, options.notification_method); | |
371 network_channel_creator = | |
372 syncer::NonBlockingInvalidator::MakePushClientChannelCreator(options); | |
373 break; | |
374 } | |
375 case GCM_NETWORK_CHANNEL: { | |
376 gcm_invalidation_bridge_.reset(new GCMInvalidationBridge( | |
377 gcm_driver_, identity_provider_.get())); | |
378 network_channel_creator = | |
379 syncer::NonBlockingInvalidator::MakeGCMNetworkChannelCreator( | |
380 request_context_, | |
381 gcm_invalidation_bridge_->CreateDelegate().Pass()); | |
382 break; | |
383 } | |
384 default: { | |
385 NOTREACHED(); | |
386 return; | |
387 } | |
388 } | |
389 | |
390 UMA_HISTOGRAM_ENUMERATION( | |
391 "Invalidations.NetworkChannel", network_channel, NETWORK_CHANNELS_COUNT); | |
392 invalidator_.reset(new syncer::NonBlockingInvalidator( | |
393 network_channel_creator, | |
394 invalidation_state_tracker_->GetInvalidatorClientId(), | |
395 invalidation_state_tracker_->GetSavedInvalidations(), | |
396 invalidation_state_tracker_->GetBootstrapData(), | |
397 invalidation_state_tracker_.get(), | |
398 user_agent_, | |
399 request_context_)); | |
400 | |
401 UpdateInvalidatorCredentials(); | |
402 | |
403 invalidator_->RegisterHandler(this); | |
404 CHECK(invalidator_->UpdateRegisteredIds( | |
405 this, invalidator_registrar_->GetAllRegisteredIds())); | |
406 } | |
407 | |
408 void TiclInvalidationService::UpdateInvalidationNetworkChannel() { | |
409 const InvalidationNetworkChannel network_channel_type = | |
410 settings_provider_->UseGCMChannel() ? GCM_NETWORK_CHANNEL | |
411 : PUSH_CLIENT_CHANNEL; | |
412 if (network_channel_type_ == network_channel_type) | |
413 return; | |
414 network_channel_type_ = network_channel_type; | |
415 if (IsStarted()) { | |
416 StopInvalidator(); | |
417 StartInvalidator(network_channel_type_); | |
418 } | |
419 } | |
420 | |
421 void TiclInvalidationService::UpdateInvalidatorCredentials() { | |
422 std::string email = identity_provider_->GetActiveAccountId(); | |
423 | |
424 DCHECK(!email.empty()) << "Expected user to be signed in."; | |
425 | |
426 DVLOG(2) << "UpdateCredentials: " << email; | |
427 invalidator_->UpdateCredentials(email, access_token_); | |
428 } | |
429 | |
430 void TiclInvalidationService::StopInvalidator() { | |
431 DCHECK(invalidator_); | |
432 gcm_invalidation_bridge_.reset(); | |
433 invalidator_->UnregisterHandler(this); | |
434 invalidator_.reset(); | |
435 } | |
436 | |
437 } // namespace invalidation | |
OLD | NEW |