Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / invalidation / ticl_invalidation_service.cc
blobc32714d00ee36613844c939960fbeae9c5ff7999
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.
5 #include "chrome/browser/invalidation/ticl_invalidation_service.h"
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"
23 static const char* kOAuth2Scopes[] = {
24 GaiaConstants::kGoogleTalkOAuth2Scope
27 static const net::BackoffEntry::Policy kRequestAccessTokenBackoffPolicy = {
28 // Number of initial errors (in sequence) to ignore before applying
29 // exponential back-off rules.
32 // Initial delay for exponential back-off in ms.
33 2000,
35 // Factor by which the waiting time will be multiplied.
38 // Fuzzing percentage. ex: 10% will spread requests randomly
39 // between 90%-100% of the calculated time.
40 0.2, // 20%
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.
47 // Time to keep an entry from being discarded even when it
48 // has no significant state, -1 to never discard.
49 -1,
51 // Don't use initial delay unless the last request was an error.
52 false,
55 namespace invalidation {
57 TiclInvalidationService::TiclInvalidationService(
58 SigninManagerBase* signin,
59 ProfileOAuth2TokenService* oauth2_token_service,
60 Profile* profile)
61 : OAuth2TokenService::Consumer("ticl_invalidation"),
62 profile_(profile),
63 signin_manager_(signin),
64 oauth2_token_service_(oauth2_token_service),
65 invalidator_registrar_(new syncer::InvalidatorRegistrar()),
66 request_access_token_backoff_(&kRequestAccessTokenBackoffPolicy) {
69 TiclInvalidationService::~TiclInvalidationService() {
70 DCHECK(CalledOnValidThread());
73 void TiclInvalidationService::Init() {
74 DCHECK(CalledOnValidThread());
76 invalidator_storage_.reset(new InvalidatorStorage(profile_->GetPrefs()));
77 if (invalidator_storage_->GetInvalidatorClientId().empty()) {
78 // This also clears any existing state. We can't reuse old invalidator
79 // state with the new ID anyway.
80 invalidator_storage_->SetInvalidatorClientId(GenerateInvalidatorClientId());
83 if (IsReadyToStart()) {
84 StartInvalidator(PUSH_CLIENT_CHANNEL);
87 notification_registrar_.Add(this,
88 chrome::NOTIFICATION_GOOGLE_SIGNED_OUT,
89 content::Source<Profile>(profile_));
90 oauth2_token_service_->AddObserver(this);
93 void TiclInvalidationService::InitForTest(syncer::Invalidator* invalidator) {
94 // Here we perform the equivalent of Init() and StartInvalidator(), but with
95 // some minor changes to account for the fact that we're injecting the
96 // invalidator.
97 invalidator_.reset(invalidator);
99 invalidator_->RegisterHandler(this);
100 invalidator_->UpdateRegisteredIds(
101 this,
102 invalidator_registrar_->GetAllRegisteredIds());
105 void TiclInvalidationService::RegisterInvalidationHandler(
106 syncer::InvalidationHandler* handler) {
107 DCHECK(CalledOnValidThread());
108 DVLOG(2) << "Registering an invalidation handler";
109 invalidator_registrar_->RegisterHandler(handler);
112 void TiclInvalidationService::UpdateRegisteredInvalidationIds(
113 syncer::InvalidationHandler* handler,
114 const syncer::ObjectIdSet& ids) {
115 DCHECK(CalledOnValidThread());
116 DVLOG(2) << "Registering ids: " << ids.size();
117 invalidator_registrar_->UpdateRegisteredIds(handler, ids);
118 if (invalidator_) {
119 invalidator_->UpdateRegisteredIds(
120 this,
121 invalidator_registrar_->GetAllRegisteredIds());
125 void TiclInvalidationService::UnregisterInvalidationHandler(
126 syncer::InvalidationHandler* handler) {
127 DCHECK(CalledOnValidThread());
128 DVLOG(2) << "Unregistering";
129 invalidator_registrar_->UnregisterHandler(handler);
130 if (invalidator_) {
131 invalidator_->UpdateRegisteredIds(
132 this,
133 invalidator_registrar_->GetAllRegisteredIds());
137 syncer::InvalidatorState TiclInvalidationService::GetInvalidatorState() const {
138 DCHECK(CalledOnValidThread());
139 if (invalidator_) {
140 DVLOG(2) << "GetInvalidatorState returning "
141 << invalidator_->GetInvalidatorState();
142 return invalidator_->GetInvalidatorState();
143 } else {
144 DVLOG(2) << "Invalidator currently stopped";
145 return syncer::TRANSIENT_INVALIDATION_ERROR;
149 std::string TiclInvalidationService::GetInvalidatorClientId() const {
150 DCHECK(CalledOnValidThread());
151 return invalidator_storage_->GetInvalidatorClientId();
154 void TiclInvalidationService::Observe(
155 int type,
156 const content::NotificationSource& source,
157 const content::NotificationDetails& details) {
158 DCHECK(CalledOnValidThread());
159 DCHECK_EQ(type, chrome::NOTIFICATION_GOOGLE_SIGNED_OUT);
160 Logout();
163 void TiclInvalidationService::RequestAccessToken() {
164 // Only one active request at a time.
165 if (access_token_request_ != NULL)
166 return;
167 request_access_token_retry_timer_.Stop();
168 OAuth2TokenService::ScopeSet oauth2_scopes;
169 for (size_t i = 0; i < arraysize(kOAuth2Scopes); i++)
170 oauth2_scopes.insert(kOAuth2Scopes[i]);
171 // Invalidate previous token, otherwise token service will return the same
172 // token again.
173 const std::string& account_id = oauth2_token_service_->GetPrimaryAccountId();
174 oauth2_token_service_->InvalidateToken(account_id,
175 oauth2_scopes,
176 access_token_);
177 access_token_.clear();
178 access_token_request_ = oauth2_token_service_->StartRequest(account_id,
179 oauth2_scopes,
180 this);
183 void TiclInvalidationService::OnGetTokenSuccess(
184 const OAuth2TokenService::Request* request,
185 const std::string& access_token,
186 const base::Time& expiration_time) {
187 DCHECK_EQ(access_token_request_, request);
188 access_token_request_.reset();
189 // Reset backoff time after successful response.
190 request_access_token_backoff_.Reset();
191 access_token_ = access_token;
192 if (!IsStarted() && IsReadyToStart()) {
193 StartInvalidator(PUSH_CLIENT_CHANNEL);
194 } else {
195 UpdateInvalidatorCredentials();
199 void TiclInvalidationService::OnGetTokenFailure(
200 const OAuth2TokenService::Request* request,
201 const GoogleServiceAuthError& error) {
202 DCHECK_EQ(access_token_request_, request);
203 DCHECK_NE(error.state(), GoogleServiceAuthError::NONE);
204 access_token_request_.reset();
205 switch (error.state()) {
206 case GoogleServiceAuthError::CONNECTION_FAILED:
207 case GoogleServiceAuthError::SERVICE_UNAVAILABLE: {
208 // Transient error. Retry after some time.
209 request_access_token_backoff_.InformOfRequest(false);
210 request_access_token_retry_timer_.Start(
211 FROM_HERE,
212 request_access_token_backoff_.GetTimeUntilRelease(),
213 base::Bind(&TiclInvalidationService::RequestAccessToken,
214 base::Unretained(this)));
215 break;
217 case GoogleServiceAuthError::SERVICE_ERROR:
218 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: {
219 invalidator_registrar_->UpdateInvalidatorState(
220 syncer::INVALIDATION_CREDENTIALS_REJECTED);
221 break;
223 default: {
224 // We have no way to notify the user of this. Do nothing.
229 void TiclInvalidationService::OnRefreshTokenAvailable(
230 const std::string& account_id) {
231 if (oauth2_token_service_->GetPrimaryAccountId() == account_id) {
232 if (!IsStarted() && IsReadyToStart()) {
233 StartInvalidator(PUSH_CLIENT_CHANNEL);
238 void TiclInvalidationService::OnRefreshTokenRevoked(
239 const std::string& account_id) {
240 if (oauth2_token_service_->GetPrimaryAccountId() == account_id) {
241 access_token_.clear();
242 if (IsStarted()) {
243 UpdateInvalidatorCredentials();
248 void TiclInvalidationService::OnInvalidatorStateChange(
249 syncer::InvalidatorState state) {
250 if (state == syncer::INVALIDATION_CREDENTIALS_REJECTED) {
251 // This may be due to normal OAuth access token expiration. If so, we must
252 // fetch a new one using our refresh token. Resetting the invalidator's
253 // access token will not reset the invalidator's exponential backoff, so
254 // it's safe to try to update the token every time we receive this signal.
256 // We won't be receiving any invalidations while the refresh is in progress,
257 // we set our state to TRANSIENT_INVALIDATION_ERROR. If the credentials
258 // really are invalid, the refresh request should fail and
259 // OnGetTokenFailure() will put us into a INVALIDATION_CREDENTIALS_REJECTED
260 // state.
261 invalidator_registrar_->UpdateInvalidatorState(
262 syncer::TRANSIENT_INVALIDATION_ERROR);
263 RequestAccessToken();
264 } else {
265 invalidator_registrar_->UpdateInvalidatorState(state);
269 void TiclInvalidationService::OnIncomingInvalidation(
270 const syncer::ObjectIdInvalidationMap& invalidation_map) {
271 invalidator_registrar_->DispatchInvalidationsToHandlers(invalidation_map);
274 void TiclInvalidationService::Shutdown() {
275 DCHECK(CalledOnValidThread());
276 oauth2_token_service_->RemoveObserver(this);
277 if (IsStarted()) {
278 StopInvalidator();
280 invalidator_storage_.reset();
281 invalidator_registrar_.reset();
284 bool TiclInvalidationService::IsReadyToStart() {
285 if (profile_->IsManaged()) {
286 DVLOG(2) << "Not starting TiclInvalidationService: User is managed.";
287 return false;
290 if (signin_manager_->GetAuthenticatedUsername().empty()) {
291 DVLOG(2) << "Not starting TiclInvalidationService: User is not signed in.";
292 return false;
295 if (!oauth2_token_service_) {
296 DVLOG(2)
297 << "Not starting TiclInvalidationService: "
298 << "OAuth2TokenService unavailable.";
299 return false;
302 if (!oauth2_token_service_->RefreshTokenIsAvailable(
303 oauth2_token_service_->GetPrimaryAccountId())) {
304 DVLOG(2)
305 << "Not starting TiclInvalidationServce: Waiting for refresh token.";
306 return false;
309 return true;
312 bool TiclInvalidationService::IsStarted() {
313 return invalidator_.get() != NULL;
316 void TiclInvalidationService::StartInvalidator(
317 InvalidationNetworkChannel network_channel) {
318 DCHECK(CalledOnValidThread());
319 DCHECK(!invalidator_);
320 DCHECK(invalidator_storage_);
321 DCHECK(!invalidator_storage_->GetInvalidatorClientId().empty());
323 if (access_token_.empty()) {
324 DVLOG(1)
325 << "TiclInvalidationService: "
326 << "Deferring start until we have an access token.";
327 RequestAccessToken();
328 return;
331 syncer::NetworkChannelCreator network_channel_creator;
333 switch (network_channel) {
334 case PUSH_CLIENT_CHANNEL: {
335 notifier::NotifierOptions options =
336 ParseNotifierOptions(*CommandLine::ForCurrentProcess());
337 options.request_context_getter = profile_->GetRequestContext();
338 options.auth_mechanism = "X-OAUTH2";
339 DCHECK_EQ(notifier::NOTIFICATION_SERVER, options.notification_method);
340 network_channel_creator =
341 syncer::NonBlockingInvalidator::MakePushClientChannelCreator(options);
342 break;
344 case GCM_NETWORK_CHANNEL: {
345 network_channel_creator =
346 syncer::NonBlockingInvalidator::MakeGCMNetworkChannelCreator();
347 break;
349 default: {
350 NOTREACHED();
351 return;
354 invalidator_.reset(new syncer::NonBlockingInvalidator(
355 network_channel_creator,
356 invalidator_storage_->GetInvalidatorClientId(),
357 invalidator_storage_->GetSavedInvalidations(),
358 invalidator_storage_->GetBootstrapData(),
359 syncer::WeakHandle<syncer::InvalidationStateTracker>(
360 invalidator_storage_->AsWeakPtr()),
361 content::GetUserAgent(GURL()),
362 profile_->GetRequestContext()));
364 UpdateInvalidatorCredentials();
366 invalidator_->RegisterHandler(this);
367 invalidator_->UpdateRegisteredIds(
368 this,
369 invalidator_registrar_->GetAllRegisteredIds());
372 void TiclInvalidationService::UpdateInvalidatorCredentials() {
373 std::string email = signin_manager_->GetAuthenticatedUsername();
375 DCHECK(!email.empty()) << "Expected user to be signed in.";
377 DVLOG(2) << "UpdateCredentials: " << email;
378 invalidator_->UpdateCredentials(email, access_token_);
381 void TiclInvalidationService::StopInvalidator() {
382 DCHECK(invalidator_);
383 invalidator_->UnregisterHandler(this);
384 invalidator_.reset();
387 void TiclInvalidationService::Logout() {
388 access_token_request_.reset();
389 request_access_token_retry_timer_.Stop();
391 if (IsStarted()) {
392 StopInvalidator();
395 // This service always expects to have a valid invalidator storage.
396 // So we must not only clear the old one, but also start a new one.
397 invalidator_storage_->Clear();
398 invalidator_storage_.reset(new InvalidatorStorage(profile_->GetPrefs()));
399 invalidator_storage_->SetInvalidatorClientId(GenerateInvalidatorClientId());
402 } // namespace invalidation