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/non_blocking_invalidator.h"
13 #include "google_apis/gaia/gaia_constants.h"
14 #include "net/url_request/url_request_context_getter.h"
15 #include "sync/internal_api/public/base/invalidator_state.h"
16 #include "sync/notifier/invalidation_util.h"
17 #include "sync/notifier/invalidator.h"
18 #include "sync/notifier/object_id_invalidation_map.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
TiclInvalidationService(const std::string & user_agent,scoped_ptr<IdentityProvider> identity_provider,scoped_ptr<TiclSettingsProvider> settings_provider,gcm::GCMDriver * gcm_driver,const scoped_refptr<net::URLRequestContextGetter> & request_context)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_(PUSH_CLIENT_CHANNEL),
67 gcm_driver_(gcm_driver),
68 request_context_(request_context),
69 logger_() {}
70
~TiclInvalidationService()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
Init(scoped_ptr<syncer::InvalidationStateTracker> invalidation_state_tracker)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
InitForTest(scoped_ptr<syncer::InvalidationStateTracker> invalidation_state_tracker,syncer::Invalidator * invalidator)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
RegisterInvalidationHandler(syncer::InvalidationHandler * handler)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
UpdateRegisteredInvalidationIds(syncer::InvalidationHandler * handler,const syncer::ObjectIdSet & ids)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
UnregisterInvalidationHandler(syncer::InvalidationHandler * handler)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
GetInvalidatorState() const151 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
GetInvalidatorClientId() const163 std::string TiclInvalidationService::GetInvalidatorClientId() const {
164 DCHECK(CalledOnValidThread());
165 return invalidation_state_tracker_->GetInvalidatorClientId();
166 }
167
GetInvalidationLogger()168 InvalidationLogger* TiclInvalidationService::GetInvalidationLogger() {
169 return &logger_;
170 }
171
GetIdentityProvider()172 IdentityProvider* TiclInvalidationService::GetIdentityProvider() {
173 return identity_provider_.get();
174 }
175
RequestDetailedStatus(base::Callback<void (const base::DictionaryValue &)> return_callback) const176 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
RequestAccessToken()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
OnGetTokenSuccess(const OAuth2TokenService::Request * request,const std::string & access_token,const base::Time & expiration_time)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
OnGetTokenFailure(const OAuth2TokenService::Request * request,const GoogleServiceAuthError & error)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
OnRefreshTokenAvailable(const std::string & account_id)248 void TiclInvalidationService::OnRefreshTokenAvailable(
249 const std::string& account_id) {
250 if (!IsStarted() && IsReadyToStart())
251 StartInvalidator(network_channel_type_);
252 }
253
OnRefreshTokenRevoked(const std::string & account_id)254 void TiclInvalidationService::OnRefreshTokenRevoked(
255 const std::string& account_id) {
256 access_token_.clear();
257 if (IsStarted())
258 UpdateInvalidatorCredentials();
259 }
260
OnActiveAccountLogout()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
OnUseGCMChannelChanged()276 void TiclInvalidationService::OnUseGCMChannelChanged() {
277 UpdateInvalidationNetworkChannel();
278 }
279
OnInvalidatorStateChange(syncer::InvalidatorState state)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
OnIncomingInvalidation(const syncer::ObjectIdInvalidationMap & invalidation_map)302 void TiclInvalidationService::OnIncomingInvalidation(
303 const syncer::ObjectIdInvalidationMap& invalidation_map) {
304 invalidator_registrar_->DispatchInvalidationsToHandlers(invalidation_map);
305
306 logger_.OnInvalidation(invalidation_map);
307 }
308
GetOwnerName() const309 std::string TiclInvalidationService::GetOwnerName() const { return "TICL"; }
310
IsReadyToStart()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
IsStarted() const335 bool TiclInvalidationService::IsStarted() const {
336 return invalidator_.get() != NULL;
337 }
338
StartInvalidator(InvalidationNetworkChannel network_channel)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
UpdateInvalidationNetworkChannel()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
UpdateInvalidatorCredentials()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
StopInvalidator()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