• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 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 "chrome/browser/invalidation/ticl_invalidation_service.h"
6 
7 #include "base/command_line.h"
8 #include "base/metrics/histogram.h"
9 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/invalidation/invalidation_service_util.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/signin/about_signin_internals.h"
13 #include "chrome/browser/signin/about_signin_internals_factory.h"
14 #include "chrome/browser/signin/profile_oauth2_token_service.h"
15 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
16 #include "chrome/browser/signin/signin_manager.h"
17 #include "content/public/browser/notification_service.h"
18 #include "google_apis/gaia/gaia_constants.h"
19 #include "sync/notifier/invalidator.h"
20 #include "sync/notifier/invalidator_state.h"
21 #include "sync/notifier/non_blocking_invalidator.h"
22 
23 static const char* kOAuth2Scopes[] = {
24   GaiaConstants::kGoogleTalkOAuth2Scope
25 };
26 
27 static const net::BackoffEntry::Policy kRequestAccessTokenBackoffPolicy = {
28   // Number of initial errors (in sequence) to ignore before applying
29   // exponential back-off rules.
30   0,
31 
32   // Initial delay for exponential back-off in ms.
33   2000,
34 
35   // Factor by which the waiting time will be multiplied.
36   2,
37 
38   // Fuzzing percentage. ex: 10% will spread requests randomly
39   // between 90%-100% of the calculated time.
40   0.2, // 20%
41 
42   // Maximum amount of time we are willing to delay our request in ms.
43   // TODO(pavely): crbug.com/246686 ProfileSyncService should retry
44   // RequestAccessToken on connection state change after backoff
45   1000 * 3600 * 4, // 4 hours.
46 
47   // Time to keep an entry from being discarded even when it
48   // has no significant state, -1 to never discard.
49   -1,
50 
51   // Don't use initial delay unless the last request was an error.
52   false,
53 };
54 
55 namespace invalidation {
56 
TiclInvalidationService(SigninManagerBase * signin,ProfileOAuth2TokenService * oauth2_token_service,Profile * profile)57 TiclInvalidationService::TiclInvalidationService(
58     SigninManagerBase* signin,
59     ProfileOAuth2TokenService* oauth2_token_service,
60     Profile* profile)
61     : profile_(profile),
62       signin_manager_(signin),
63       oauth2_token_service_(oauth2_token_service),
64       invalidator_registrar_(new syncer::InvalidatorRegistrar()),
65       request_access_token_backoff_(&kRequestAccessTokenBackoffPolicy) {
66 }
67 
~TiclInvalidationService()68 TiclInvalidationService::~TiclInvalidationService() {
69   DCHECK(CalledOnValidThread());
70 }
71 
Init()72 void TiclInvalidationService::Init() {
73   DCHECK(CalledOnValidThread());
74 
75   invalidator_storage_.reset(new InvalidatorStorage(profile_->GetPrefs()));
76   if (invalidator_storage_->GetInvalidatorClientId().empty()) {
77     // This also clears any existing state.  We can't reuse old invalidator
78     // state with the new ID anyway.
79     invalidator_storage_->SetInvalidatorClientId(GenerateInvalidatorClientId());
80   }
81 
82   if (IsReadyToStart()) {
83     StartInvalidator();
84   }
85 
86   notification_registrar_.Add(this,
87                               chrome::NOTIFICATION_GOOGLE_SIGNED_OUT,
88                               content::Source<Profile>(profile_));
89   oauth2_token_service_->AddObserver(this);
90 }
91 
InitForTest(syncer::Invalidator * invalidator)92 void TiclInvalidationService::InitForTest(syncer::Invalidator* invalidator) {
93   // Here we perform the equivalent of Init() and StartInvalidator(), but with
94   // some minor changes to account for the fact that we're injecting the
95   // invalidator.
96   invalidator_.reset(invalidator);
97 
98   invalidator_->RegisterHandler(this);
99   invalidator_->UpdateRegisteredIds(
100       this,
101       invalidator_registrar_->GetAllRegisteredIds());
102 }
103 
RegisterInvalidationHandler(syncer::InvalidationHandler * handler)104 void TiclInvalidationService::RegisterInvalidationHandler(
105     syncer::InvalidationHandler* handler) {
106   DCHECK(CalledOnValidThread());
107   DVLOG(2) << "Registering an invalidation handler";
108   invalidator_registrar_->RegisterHandler(handler);
109 }
110 
UpdateRegisteredInvalidationIds(syncer::InvalidationHandler * handler,const syncer::ObjectIdSet & ids)111 void TiclInvalidationService::UpdateRegisteredInvalidationIds(
112     syncer::InvalidationHandler* handler,
113     const syncer::ObjectIdSet& ids) {
114   DCHECK(CalledOnValidThread());
115   DVLOG(2) << "Registering ids: " << ids.size();
116   invalidator_registrar_->UpdateRegisteredIds(handler, ids);
117   if (invalidator_) {
118     invalidator_->UpdateRegisteredIds(
119         this,
120         invalidator_registrar_->GetAllRegisteredIds());
121   }
122 }
123 
UnregisterInvalidationHandler(syncer::InvalidationHandler * handler)124 void TiclInvalidationService::UnregisterInvalidationHandler(
125     syncer::InvalidationHandler* handler) {
126   DCHECK(CalledOnValidThread());
127   DVLOG(2) << "Unregistering";
128   invalidator_registrar_->UnregisterHandler(handler);
129   if (invalidator_) {
130     invalidator_->UpdateRegisteredIds(
131         this,
132         invalidator_registrar_->GetAllRegisteredIds());
133   }
134 }
135 
GetInvalidatorState() const136 syncer::InvalidatorState TiclInvalidationService::GetInvalidatorState() const {
137   DCHECK(CalledOnValidThread());
138   if (invalidator_) {
139     DVLOG(2) << "GetInvalidatorState returning "
140         << invalidator_->GetInvalidatorState();
141     return invalidator_->GetInvalidatorState();
142   } else {
143     DVLOG(2) << "Invalidator currently stopped";
144     return syncer::TRANSIENT_INVALIDATION_ERROR;
145   }
146 }
147 
GetInvalidatorClientId() const148 std::string TiclInvalidationService::GetInvalidatorClientId() const {
149   DCHECK(CalledOnValidThread());
150   return invalidator_storage_->GetInvalidatorClientId();
151 }
152 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)153 void TiclInvalidationService::Observe(
154     int type,
155     const content::NotificationSource& source,
156     const content::NotificationDetails& details) {
157   DCHECK(CalledOnValidThread());
158   DCHECK_EQ(type, chrome::NOTIFICATION_GOOGLE_SIGNED_OUT);
159   Logout();
160 }
161 
RequestAccessToken()162 void TiclInvalidationService::RequestAccessToken() {
163   // Only one active request at a time.
164   if (access_token_request_ != NULL)
165     return;
166   request_access_token_retry_timer_.Stop();
167   OAuth2TokenService::ScopeSet oauth2_scopes;
168   for (size_t i = 0; i < arraysize(kOAuth2Scopes); i++)
169     oauth2_scopes.insert(kOAuth2Scopes[i]);
170   // Invalidate previous token, otherwise token service will return the same
171   // token again.
172   const std::string& account_id = oauth2_token_service_->GetPrimaryAccountId();
173   oauth2_token_service_->InvalidateToken(account_id,
174                                          oauth2_scopes,
175                                          access_token_);
176   access_token_.clear();
177   access_token_request_ = oauth2_token_service_->StartRequest(account_id,
178                                                               oauth2_scopes,
179                                                               this);
180 }
181 
OnGetTokenSuccess(const OAuth2TokenService::Request * request,const std::string & access_token,const base::Time & expiration_time)182 void TiclInvalidationService::OnGetTokenSuccess(
183     const OAuth2TokenService::Request* request,
184     const std::string& access_token,
185     const base::Time& expiration_time) {
186   DCHECK_EQ(access_token_request_, request);
187   access_token_request_.reset();
188   // Reset backoff time after successful response.
189   request_access_token_backoff_.Reset();
190   access_token_ = access_token;
191   if (!IsStarted() && IsReadyToStart()) {
192     StartInvalidator();
193   } else {
194     UpdateInvalidatorCredentials();
195   }
196 }
197 
OnGetTokenFailure(const OAuth2TokenService::Request * request,const GoogleServiceAuthError & error)198 void TiclInvalidationService::OnGetTokenFailure(
199     const OAuth2TokenService::Request* request,
200     const GoogleServiceAuthError& error) {
201   DCHECK_EQ(access_token_request_, request);
202   DCHECK_NE(error.state(), GoogleServiceAuthError::NONE);
203   access_token_request_.reset();
204   switch (error.state()) {
205     case GoogleServiceAuthError::CONNECTION_FAILED:
206     case GoogleServiceAuthError::SERVICE_UNAVAILABLE: {
207       // Transient error. Retry after some time.
208       request_access_token_backoff_.InformOfRequest(false);
209       request_access_token_retry_timer_.Start(
210             FROM_HERE,
211             request_access_token_backoff_.GetTimeUntilRelease(),
212             base::Bind(&TiclInvalidationService::RequestAccessToken,
213                        base::Unretained(this)));
214       break;
215     }
216     case GoogleServiceAuthError::SERVICE_ERROR:
217     case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: {
218       // This is a real auth error.
219       // Report time since token was issued for invalid credentials error.
220       base::Time auth_token_time =
221           AboutSigninInternalsFactory::GetForProfile(profile_)->
222               GetTokenTime(GaiaConstants::kGaiaOAuth2LoginRefreshToken);
223       if (!auth_token_time.is_null()) {
224         base::TimeDelta age = base::Time::Now() - auth_token_time;
225         if (age < base::TimeDelta::FromHours(1)) {
226           UMA_HISTOGRAM_CUSTOM_TIMES(
227               "Sync.AuthInvalidationRejectedTokenAgeShort",
228               age,
229               base::TimeDelta::FromSeconds(1),
230               base::TimeDelta::FromHours(1),
231               50);
232         }
233         UMA_HISTOGRAM_COUNTS("Sync.AuthInvalidationRejectedTokenAgeLong",
234                              age.InDays());
235       }
236       invalidator_registrar_->UpdateInvalidatorState(
237           syncer::INVALIDATION_CREDENTIALS_REJECTED);
238       break;
239     }
240     default: {
241       // We have no way to notify the user of this.  Do nothing.
242     }
243   }
244 }
245 
OnRefreshTokenAvailable(const std::string & account_id)246 void TiclInvalidationService::OnRefreshTokenAvailable(
247     const std::string& account_id) {
248   if (oauth2_token_service_->GetPrimaryAccountId() == account_id) {
249     if (!IsStarted() && IsReadyToStart()) {
250       StartInvalidator();
251     }
252   }
253 }
254 
OnRefreshTokenRevoked(const std::string & account_id)255 void TiclInvalidationService::OnRefreshTokenRevoked(
256     const std::string& account_id) {
257   if (oauth2_token_service_->GetPrimaryAccountId() == account_id) {
258     access_token_.clear();
259     if (IsStarted()) {
260       UpdateInvalidatorCredentials();
261     }
262   }
263 }
264 
OnInvalidatorStateChange(syncer::InvalidatorState state)265 void TiclInvalidationService::OnInvalidatorStateChange(
266     syncer::InvalidatorState state) {
267   if (state == syncer::INVALIDATION_CREDENTIALS_REJECTED) {
268     // This may be due to normal OAuth access token expiration.  If so, we must
269     // fetch a new one using our refresh token.  Resetting the invalidator's
270     // access token will not reset the invalidator's exponential backoff, so
271     // it's safe to try to update the token every time we receive this signal.
272     //
273     // We won't be receiving any invalidations while the refresh is in progress,
274     // we set our state to TRANSIENT_INVALIDATION_ERROR.  If the credentials
275     // really are invalid, the refresh request should fail and
276     // OnGetTokenFailure() will put us into a INVALIDATION_CREDENTIALS_REJECTED
277     // state.
278     invalidator_registrar_->UpdateInvalidatorState(
279         syncer::TRANSIENT_INVALIDATION_ERROR);
280     RequestAccessToken();
281   } else {
282     invalidator_registrar_->UpdateInvalidatorState(state);
283   }
284 }
285 
OnIncomingInvalidation(const syncer::ObjectIdInvalidationMap & invalidation_map)286 void TiclInvalidationService::OnIncomingInvalidation(
287     const syncer::ObjectIdInvalidationMap& invalidation_map) {
288   invalidator_registrar_->DispatchInvalidationsToHandlers(invalidation_map);
289 }
290 
Shutdown()291 void TiclInvalidationService::Shutdown() {
292   DCHECK(CalledOnValidThread());
293   oauth2_token_service_->RemoveObserver(this);
294   if (IsStarted()) {
295     StopInvalidator();
296   }
297   invalidator_storage_.reset();
298   invalidator_registrar_.reset();
299 }
300 
IsReadyToStart()301 bool TiclInvalidationService::IsReadyToStart() {
302   if (profile_->IsManaged()) {
303     DVLOG(2) << "Not starting TiclInvalidationService: User is managed.";
304     return false;
305   }
306 
307   if (signin_manager_->GetAuthenticatedUsername().empty()) {
308     DVLOG(2) << "Not starting TiclInvalidationService: User is not signed in.";
309     return false;
310   }
311 
312   if (!oauth2_token_service_) {
313     DVLOG(2)
314         << "Not starting TiclInvalidationService: "
315         << "OAuth2TokenService unavailable.";
316     return false;
317   }
318 
319   if (!oauth2_token_service_->RefreshTokenIsAvailable(
320           oauth2_token_service_->GetPrimaryAccountId())) {
321     DVLOG(2)
322         << "Not starting TiclInvalidationServce: Waiting for refresh token.";
323     return false;
324   }
325 
326   return true;
327 }
328 
IsStarted()329 bool TiclInvalidationService::IsStarted() {
330   return invalidator_.get() != NULL;
331 }
332 
StartInvalidator()333 void TiclInvalidationService::StartInvalidator() {
334   DCHECK(CalledOnValidThread());
335   DCHECK(!invalidator_);
336   DCHECK(invalidator_storage_);
337   DCHECK(!invalidator_storage_->GetInvalidatorClientId().empty());
338 
339   if (access_token_.empty()) {
340     DVLOG(1)
341         << "TiclInvalidationService: "
342         << "Deferring start until we have an access token.";
343     RequestAccessToken();
344     return;
345   }
346 
347   notifier::NotifierOptions options =
348       ParseNotifierOptions(*CommandLine::ForCurrentProcess());
349   options.request_context_getter = profile_->GetRequestContext();
350   options.auth_mechanism = "X-OAUTH2";
351   invalidator_.reset(new syncer::NonBlockingInvalidator(
352           options,
353           invalidator_storage_->GetInvalidatorClientId(),
354           invalidator_storage_->GetSavedInvalidations(),
355           invalidator_storage_->GetBootstrapData(),
356           syncer::WeakHandle<syncer::InvalidationStateTracker>(
357               invalidator_storage_->AsWeakPtr()),
358           content::GetUserAgent(GURL())));
359 
360   UpdateInvalidatorCredentials();
361 
362   invalidator_->RegisterHandler(this);
363   invalidator_->UpdateRegisteredIds(
364       this,
365       invalidator_registrar_->GetAllRegisteredIds());
366 }
367 
UpdateInvalidatorCredentials()368 void TiclInvalidationService::UpdateInvalidatorCredentials() {
369   std::string email = signin_manager_->GetAuthenticatedUsername();
370 
371   DCHECK(!email.empty()) << "Expected user to be signed in.";
372 
373   DVLOG(2) << "UpdateCredentials: " << email;
374   invalidator_->UpdateCredentials(email, access_token_);
375 }
376 
StopInvalidator()377 void TiclInvalidationService::StopInvalidator() {
378   DCHECK(invalidator_);
379   invalidator_->UnregisterHandler(this);
380   invalidator_.reset();
381 }
382 
Logout()383 void TiclInvalidationService::Logout() {
384   access_token_request_.reset();
385   request_access_token_retry_timer_.Stop();
386 
387   if (IsStarted()) {
388     StopInvalidator();
389   }
390 
391   // This service always expects to have a valid invalidator storage.
392   // So we must not only clear the old one, but also start a new one.
393   invalidator_storage_->Clear();
394   invalidator_storage_.reset(new InvalidatorStorage(profile_->GetPrefs()));
395   invalidator_storage_->SetInvalidatorClientId(GenerateInvalidatorClientId());
396 }
397 
398 }  // namespace invalidation
399