• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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