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.
5 #include "components/invalidation/ticl_invalidation_service.h"
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"
20 static const char* kOAuth2Scopes
[] = {
21 GaiaConstants::kGoogleTalkOAuth2Scope
24 static const net::BackoffEntry::Policy kRequestAccessTokenBackoffPolicy
= {
25 // Number of initial errors (in sequence) to ignore before applying
26 // exponential back-off rules.
29 // Initial delay for exponential back-off in ms.
32 // Factor by which the waiting time will be multiplied.
35 // Fuzzing percentage. ex: 10% will spread requests randomly
36 // between 90%-100% of the calculated time.
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.
44 // Time to keep an entry from being discarded even when it
45 // has no significant state, -1 to never discard.
48 // Don't use initial delay unless the last request was an error.
52 namespace invalidation
{
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
),
71 TiclInvalidationService::~TiclInvalidationService() {
72 DCHECK(CalledOnValidThread());
73 settings_provider_
->RemoveObserver(this);
74 identity_provider_
->RemoveActiveAccountRefreshTokenObserver(this);
75 identity_provider_
->RemoveObserver(this);
81 void TiclInvalidationService::Init(
82 scoped_ptr
<syncer::InvalidationStateTracker
> invalidation_state_tracker
) {
83 DCHECK(CalledOnValidThread());
84 invalidation_state_tracker_
= invalidation_state_tracker
.Pass();
86 if (invalidation_state_tracker_
->GetInvalidatorClientId().empty()) {
87 invalidation_state_tracker_
->ClearAndSetNewClientId(
88 GenerateInvalidatorClientId());
91 UpdateInvalidationNetworkChannel();
92 if (IsReadyToStart()) {
93 StartInvalidator(network_channel_type_
);
96 identity_provider_
->AddObserver(this);
97 identity_provider_
->AddActiveAccountRefreshTokenObserver(this);
98 settings_provider_
->AddObserver(this);
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
107 invalidation_state_tracker_
= invalidation_state_tracker
.Pass();
108 invalidator_
.reset(invalidator
);
110 invalidator_
->RegisterHandler(this);
111 invalidator_
->UpdateRegisteredIds(
113 invalidator_registrar_
->GetAllRegisteredIds());
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());
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
);
131 invalidator_
->UpdateRegisteredIds(
133 invalidator_registrar_
->GetAllRegisteredIds());
135 logger_
.OnUpdateIds(invalidator_registrar_
->GetSanitizedHandlersIdsMap());
138 void TiclInvalidationService::UnregisterInvalidationHandler(
139 syncer::InvalidationHandler
* handler
) {
140 DCHECK(CalledOnValidThread());
141 DVLOG(2) << "Unregistering";
142 invalidator_registrar_
->UnregisterHandler(handler
);
144 invalidator_
->UpdateRegisteredIds(
146 invalidator_registrar_
->GetAllRegisteredIds());
148 logger_
.OnUnregistration(handler
->GetOwnerName());
151 syncer::InvalidatorState
TiclInvalidationService::GetInvalidatorState() const {
152 DCHECK(CalledOnValidThread());
154 DVLOG(2) << "GetInvalidatorState returning "
155 << invalidator_
->GetInvalidatorState();
156 return invalidator_
->GetInvalidatorState();
158 DVLOG(2) << "Invalidator currently stopped";
159 return syncer::TRANSIENT_INVALIDATION_ERROR
;
163 std::string
TiclInvalidationService::GetInvalidatorClientId() const {
164 DCHECK(CalledOnValidThread());
165 return invalidation_state_tracker_
->GetInvalidatorClientId();
168 InvalidationLogger
* TiclInvalidationService::GetInvalidationLogger() {
172 IdentityProvider
* TiclInvalidationService::GetIdentityProvider() {
173 return identity_provider_
.get();
176 void TiclInvalidationService::RequestDetailedStatus(
177 base::Callback
<void(const base::DictionaryValue
&)> return_callback
) const {
179 return_callback
.Run(network_channel_options_
);
180 invalidator_
->RequestDetailedStatus(return_callback
);
184 void TiclInvalidationService::RequestAccessToken() {
185 // Only one active request at a time.
186 if (access_token_request_
!= NULL
)
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
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);
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_
);
214 UpdateInvalidatorCredentials();
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(
231 request_access_token_backoff_
.GetTimeUntilRelease(),
232 base::Bind(&TiclInvalidationService::RequestAccessToken
,
233 base::Unretained(this)));
236 case GoogleServiceAuthError::SERVICE_ERROR
:
237 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS
: {
238 invalidator_registrar_
->UpdateInvalidatorState(
239 syncer::INVALIDATION_CREDENTIALS_REJECTED
);
243 // We have no way to notify the user of this. Do nothing.
248 void TiclInvalidationService::OnRefreshTokenAvailable(
249 const std::string
& account_id
) {
250 if (!IsStarted() && IsReadyToStart())
251 StartInvalidator(network_channel_type_
);
254 void TiclInvalidationService::OnRefreshTokenRevoked(
255 const std::string
& account_id
) {
256 access_token_
.clear();
258 UpdateInvalidatorCredentials();
261 void TiclInvalidationService::OnActiveAccountLogout() {
262 access_token_request_
.reset();
263 request_access_token_retry_timer_
.Stop();
265 if (gcm_invalidation_bridge_
)
266 gcm_invalidation_bridge_
->Unregister();
272 // This service always expects to have a valid invalidation state. Thus, we
273 // must generate a new client ID to replace the existing one. Setting a new
274 // client ID also clears all other state.
275 invalidation_state_tracker_
->
276 ClearAndSetNewClientId(GenerateInvalidatorClientId());
279 void TiclInvalidationService::OnUseGCMChannelChanged() {
280 UpdateInvalidationNetworkChannel();
283 void TiclInvalidationService::OnInvalidatorStateChange(
284 syncer::InvalidatorState state
) {
285 if (state
== syncer::INVALIDATION_CREDENTIALS_REJECTED
) {
286 // This may be due to normal OAuth access token expiration. If so, we must
287 // fetch a new one using our refresh token. Resetting the invalidator's
288 // access token will not reset the invalidator's exponential backoff, so
289 // it's safe to try to update the token every time we receive this signal.
291 // We won't be receiving any invalidations while the refresh is in progress,
292 // we set our state to TRANSIENT_INVALIDATION_ERROR. If the credentials
293 // really are invalid, the refresh request should fail and
294 // OnGetTokenFailure() will put us into a INVALIDATION_CREDENTIALS_REJECTED
296 invalidator_registrar_
->UpdateInvalidatorState(
297 syncer::TRANSIENT_INVALIDATION_ERROR
);
298 RequestAccessToken();
300 invalidator_registrar_
->UpdateInvalidatorState(state
);
302 logger_
.OnStateChange(state
);
305 void TiclInvalidationService::OnIncomingInvalidation(
306 const syncer::ObjectIdInvalidationMap
& invalidation_map
) {
307 invalidator_registrar_
->DispatchInvalidationsToHandlers(invalidation_map
);
309 logger_
.OnInvalidation(invalidation_map
);
312 std::string
TiclInvalidationService::GetOwnerName() const { return "TICL"; }
314 bool TiclInvalidationService::IsReadyToStart() {
315 if (identity_provider_
->GetActiveAccountId().empty()) {
316 DVLOG(2) << "Not starting TiclInvalidationService: User is not signed in.";
320 OAuth2TokenService
* token_service
= identity_provider_
->GetTokenService();
321 if (!token_service
) {
323 << "Not starting TiclInvalidationService: "
324 << "OAuth2TokenService unavailable.";
328 if (!token_service
->RefreshTokenIsAvailable(
329 identity_provider_
->GetActiveAccountId())) {
331 << "Not starting TiclInvalidationServce: Waiting for refresh token.";
338 bool TiclInvalidationService::IsStarted() const {
339 return invalidator_
.get() != NULL
;
342 void TiclInvalidationService::StartInvalidator(
343 InvalidationNetworkChannel network_channel
) {
344 DCHECK(CalledOnValidThread());
345 DCHECK(!invalidator_
);
346 DCHECK(invalidation_state_tracker_
);
347 DCHECK(!invalidation_state_tracker_
->GetInvalidatorClientId().empty());
349 // Request access token for PushClientChannel. GCMNetworkChannel will request
350 // access token before sending message to server.
351 if (network_channel
== PUSH_CLIENT_CHANNEL
&& access_token_
.empty()) {
353 << "TiclInvalidationService: "
354 << "Deferring start until we have an access token.";
355 RequestAccessToken();
359 syncer::NetworkChannelCreator network_channel_creator
;
361 switch (network_channel
) {
362 case PUSH_CLIENT_CHANNEL
: {
363 notifier::NotifierOptions options
=
364 ParseNotifierOptions(*base::CommandLine::ForCurrentProcess());
365 options
.request_context_getter
= request_context_
;
366 options
.auth_mechanism
= "X-OAUTH2";
367 network_channel_options_
.SetString("Options.HostPort",
368 options
.xmpp_host_port
.ToString());
369 network_channel_options_
.SetString("Options.AuthMechanism",
370 options
.auth_mechanism
);
371 DCHECK_EQ(notifier::NOTIFICATION_SERVER
, options
.notification_method
);
372 network_channel_creator
=
373 syncer::NonBlockingInvalidator::MakePushClientChannelCreator(options
);
376 case GCM_NETWORK_CHANNEL
: {
377 gcm_invalidation_bridge_
.reset(new GCMInvalidationBridge(
378 gcm_driver_
, identity_provider_
.get()));
379 network_channel_creator
=
380 syncer::NonBlockingInvalidator::MakeGCMNetworkChannelCreator(
382 gcm_invalidation_bridge_
->CreateDelegate().Pass());
391 UMA_HISTOGRAM_ENUMERATION(
392 "Invalidations.NetworkChannel", network_channel
, NETWORK_CHANNELS_COUNT
);
393 invalidator_
.reset(new syncer::NonBlockingInvalidator(
394 network_channel_creator
,
395 invalidation_state_tracker_
->GetInvalidatorClientId(),
396 invalidation_state_tracker_
->GetSavedInvalidations(),
397 invalidation_state_tracker_
->GetBootstrapData(),
398 invalidation_state_tracker_
.get(),
402 UpdateInvalidatorCredentials();
404 invalidator_
->RegisterHandler(this);
405 invalidator_
->UpdateRegisteredIds(
407 invalidator_registrar_
->GetAllRegisteredIds());
410 void TiclInvalidationService::UpdateInvalidationNetworkChannel() {
411 const InvalidationNetworkChannel network_channel_type
=
412 settings_provider_
->UseGCMChannel() ? GCM_NETWORK_CHANNEL
413 : PUSH_CLIENT_CHANNEL
;
414 if (network_channel_type_
== network_channel_type
)
416 network_channel_type_
= network_channel_type
;
419 StartInvalidator(network_channel_type_
);
423 void TiclInvalidationService::UpdateInvalidatorCredentials() {
424 std::string email
= identity_provider_
->GetActiveAccountId();
426 DCHECK(!email
.empty()) << "Expected user to be signed in.";
428 DVLOG(2) << "UpdateCredentials: " << email
;
429 invalidator_
->UpdateCredentials(email
, access_token_
);
432 void TiclInvalidationService::StopInvalidator() {
433 DCHECK(invalidator_
);
434 gcm_invalidation_bridge_
.reset();
435 invalidator_
->UnregisterHandler(this);
436 invalidator_
.reset();
439 } // namespace invalidation