Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / invalidation / ticl_invalidation_service.cc
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/invalidation/gcm_invalidation_bridge.h"
10 #include "chrome/browser/invalidation/invalidation_service_util.h"
11 #include "chrome/browser/services/gcm/gcm_service.h"
12 #include "chrome/common/chrome_content_client.h"
13 #include "google_apis/gaia/gaia_constants.h"
14 #include "net/url_request/url_request_context_getter.h"
15 #include "sync/notifier/gcm_network_channel_delegate.h"
16 #include "sync/notifier/invalidation_util.h"
17 #include "sync/notifier/invalidator.h"
18 #include "sync/notifier/invalidator_state.h"
19 #include "sync/notifier/non_blocking_invalidator.h"
20 #include "sync/notifier/object_id_invalidation_map.h"
21
22 static const char* kOAuth2Scopes[] = {
23   GaiaConstants::kGoogleTalkOAuth2Scope
24 };
25
26 static const net::BackoffEntry::Policy kRequestAccessTokenBackoffPolicy = {
27   // Number of initial errors (in sequence) to ignore before applying
28   // exponential back-off rules.
29   0,
30
31   // Initial delay for exponential back-off in ms.
32   2000,
33
34   // Factor by which the waiting time will be multiplied.
35   2,
36
37   // Fuzzing percentage. ex: 10% will spread requests randomly
38   // between 90%-100% of the calculated time.
39   0.2, // 20%
40
41   // Maximum amount of time we are willing to delay our request in ms.
42   // TODO(pavely): crbug.com/246686 ProfileSyncService should retry
43   // RequestAccessToken on connection state change after backoff
44   1000 * 3600 * 4, // 4 hours.
45
46   // Time to keep an entry from being discarded even when it
47   // has no significant state, -1 to never discard.
48   -1,
49
50   // Don't use initial delay unless the last request was an error.
51   false,
52 };
53
54 namespace invalidation {
55
56 TiclInvalidationService::TiclInvalidationService(
57     scoped_ptr<IdentityProvider> identity_provider,
58     scoped_ptr<TiclSettingsProvider> settings_provider,
59     gcm::GCMService* gcm_service,
60     const scoped_refptr<net::URLRequestContextGetter>& request_context)
61     : OAuth2TokenService::Consumer("ticl_invalidation"),
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_service_(gcm_service),
68       request_context_(request_context),
69       logger_() {}
70
71 TiclInvalidationService::~TiclInvalidationService() {
72   DCHECK(CalledOnValidThread());
73 }
74
75 void TiclInvalidationService::Init(
76     scoped_ptr<syncer::InvalidationStateTracker> invalidation_state_tracker) {
77   DCHECK(CalledOnValidThread());
78   invalidation_state_tracker_ = invalidation_state_tracker.Pass();
79
80   if (invalidation_state_tracker_->GetInvalidatorClientId().empty()) {
81     invalidation_state_tracker_->ClearAndSetNewClientId(
82         GenerateInvalidatorClientId());
83   }
84
85   UpdateInvalidationNetworkChannel();
86   UMA_HISTOGRAM_ENUMERATION("Invalidations.NetworkChannel",
87                             network_channel_type_,
88                             NETWORK_CHANNELS_COUNT);
89
90   if (IsReadyToStart()) {
91     StartInvalidator(network_channel_type_);
92   }
93
94   identity_provider_->AddObserver(this);
95   identity_provider_->AddActiveAccountRefreshTokenObserver(this);
96   settings_provider_->AddObserver(this);
97 }
98
99 void TiclInvalidationService::InitForTest(
100     scoped_ptr<syncer::InvalidationStateTracker> invalidation_state_tracker,
101     syncer::Invalidator* invalidator) {
102   // Here we perform the equivalent of Init() and StartInvalidator(), but with
103   // some minor changes to account for the fact that we're injecting the
104   // invalidator.
105   invalidation_state_tracker_ = invalidation_state_tracker.Pass();
106   invalidator_.reset(invalidator);
107
108   invalidator_->RegisterHandler(this);
109   invalidator_->UpdateRegisteredIds(
110       this,
111       invalidator_registrar_->GetAllRegisteredIds());
112 }
113
114 void TiclInvalidationService::RegisterInvalidationHandler(
115     syncer::InvalidationHandler* handler) {
116   DCHECK(CalledOnValidThread());
117   DVLOG(2) << "Registering an invalidation handler";
118   invalidator_registrar_->RegisterHandler(handler);
119   logger_.OnRegistration(handler->GetOwnerName());
120 }
121
122 void TiclInvalidationService::UpdateRegisteredInvalidationIds(
123     syncer::InvalidationHandler* handler,
124     const syncer::ObjectIdSet& ids) {
125   DCHECK(CalledOnValidThread());
126   DVLOG(2) << "Registering ids: " << ids.size();
127   invalidator_registrar_->UpdateRegisteredIds(handler, ids);
128   if (invalidator_) {
129     invalidator_->UpdateRegisteredIds(
130         this,
131         invalidator_registrar_->GetAllRegisteredIds());
132   }
133   logger_.OnUpdateIds(invalidator_registrar_->GetSanitizedHandlersIdsMap());
134 }
135
136 void TiclInvalidationService::UnregisterInvalidationHandler(
137     syncer::InvalidationHandler* handler) {
138   DCHECK(CalledOnValidThread());
139   DVLOG(2) << "Unregistering";
140   invalidator_registrar_->UnregisterHandler(handler);
141   if (invalidator_) {
142     invalidator_->UpdateRegisteredIds(
143         this,
144         invalidator_registrar_->GetAllRegisteredIds());
145   }
146   logger_.OnUnregistration(handler->GetOwnerName());
147 }
148
149 syncer::InvalidatorState TiclInvalidationService::GetInvalidatorState() const {
150   DCHECK(CalledOnValidThread());
151   if (invalidator_) {
152     DVLOG(2) << "GetInvalidatorState returning "
153         << invalidator_->GetInvalidatorState();
154     return invalidator_->GetInvalidatorState();
155   } else {
156     DVLOG(2) << "Invalidator currently stopped";
157     return syncer::TRANSIENT_INVALIDATION_ERROR;
158   }
159 }
160
161 std::string TiclInvalidationService::GetInvalidatorClientId() const {
162   DCHECK(CalledOnValidThread());
163   return invalidation_state_tracker_->GetInvalidatorClientId();
164 }
165
166 InvalidationLogger* TiclInvalidationService::GetInvalidationLogger() {
167   return &logger_;
168 }
169
170 IdentityProvider* TiclInvalidationService::GetIdentityProvider() {
171   return identity_provider_.get();
172 }
173
174 void TiclInvalidationService::RequestDetailedStatus(
175     base::Callback<void(const base::DictionaryValue&)> return_callback) const {
176   if (IsStarted()) {
177     return_callback.Run(network_channel_options_);
178     invalidator_->RequestDetailedStatus(return_callback);
179   }
180 }
181
182 void TiclInvalidationService::RequestAccessToken() {
183   // Only one active request at a time.
184   if (access_token_request_ != NULL)
185     return;
186   request_access_token_retry_timer_.Stop();
187   OAuth2TokenService::ScopeSet oauth2_scopes;
188   for (size_t i = 0; i < arraysize(kOAuth2Scopes); i++)
189     oauth2_scopes.insert(kOAuth2Scopes[i]);
190   // Invalidate previous token, otherwise token service will return the same
191   // token again.
192   const std::string& account_id = identity_provider_->GetActiveAccountId();
193   OAuth2TokenService* token_service = identity_provider_->GetTokenService();
194   token_service->InvalidateToken(account_id, oauth2_scopes, access_token_);
195   access_token_.clear();
196   access_token_request_ =
197       token_service->StartRequest(account_id, oauth2_scopes, this);
198 }
199
200 void TiclInvalidationService::OnGetTokenSuccess(
201     const OAuth2TokenService::Request* request,
202     const std::string& access_token,
203     const base::Time& expiration_time) {
204   DCHECK_EQ(access_token_request_, request);
205   access_token_request_.reset();
206   // Reset backoff time after successful response.
207   request_access_token_backoff_.Reset();
208   access_token_ = access_token;
209   if (!IsStarted() && IsReadyToStart()) {
210     StartInvalidator(network_channel_type_);
211   } else {
212     UpdateInvalidatorCredentials();
213   }
214 }
215
216 void TiclInvalidationService::OnGetTokenFailure(
217     const OAuth2TokenService::Request* request,
218     const GoogleServiceAuthError& error) {
219   DCHECK_EQ(access_token_request_, request);
220   DCHECK_NE(error.state(), GoogleServiceAuthError::NONE);
221   access_token_request_.reset();
222   switch (error.state()) {
223     case GoogleServiceAuthError::CONNECTION_FAILED:
224     case GoogleServiceAuthError::SERVICE_UNAVAILABLE: {
225       // Transient error. Retry after some time.
226       request_access_token_backoff_.InformOfRequest(false);
227       request_access_token_retry_timer_.Start(
228             FROM_HERE,
229             request_access_token_backoff_.GetTimeUntilRelease(),
230             base::Bind(&TiclInvalidationService::RequestAccessToken,
231                        base::Unretained(this)));
232       break;
233     }
234     case GoogleServiceAuthError::SERVICE_ERROR:
235     case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: {
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
246 void TiclInvalidationService::OnRefreshTokenAvailable(
247     const std::string& account_id) {
248   if (!IsStarted() && IsReadyToStart())
249     StartInvalidator(network_channel_type_);
250 }
251
252 void TiclInvalidationService::OnRefreshTokenRevoked(
253     const std::string& account_id) {
254   access_token_.clear();
255   if (IsStarted())
256     UpdateInvalidatorCredentials();
257 }
258
259 void TiclInvalidationService::OnActiveAccountLogout() {
260   access_token_request_.reset();
261   request_access_token_retry_timer_.Stop();
262
263   if (IsStarted()) {
264     StopInvalidator();
265   }
266
267   // This service always expects to have a valid invalidation state. Thus, we
268   // must generate a new client ID to replace the existing one. Setting a new
269   // client ID also clears all other state.
270   invalidation_state_tracker_->
271       ClearAndSetNewClientId(GenerateInvalidatorClientId());
272 }
273
274 void TiclInvalidationService::OnUseGCMChannelChanged() {
275   UpdateInvalidationNetworkChannel();
276 }
277
278 void TiclInvalidationService::OnInvalidatorStateChange(
279     syncer::InvalidatorState state) {
280   if (state == syncer::INVALIDATION_CREDENTIALS_REJECTED) {
281     // This may be due to normal OAuth access token expiration.  If so, we must
282     // fetch a new one using our refresh token.  Resetting the invalidator's
283     // access token will not reset the invalidator's exponential backoff, so
284     // it's safe to try to update the token every time we receive this signal.
285     //
286     // We won't be receiving any invalidations while the refresh is in progress,
287     // we set our state to TRANSIENT_INVALIDATION_ERROR.  If the credentials
288     // really are invalid, the refresh request should fail and
289     // OnGetTokenFailure() will put us into a INVALIDATION_CREDENTIALS_REJECTED
290     // state.
291     invalidator_registrar_->UpdateInvalidatorState(
292         syncer::TRANSIENT_INVALIDATION_ERROR);
293     RequestAccessToken();
294   } else {
295     invalidator_registrar_->UpdateInvalidatorState(state);
296   }
297   logger_.OnStateChange(state);
298 }
299
300 void TiclInvalidationService::OnIncomingInvalidation(
301     const syncer::ObjectIdInvalidationMap& invalidation_map) {
302   invalidator_registrar_->DispatchInvalidationsToHandlers(invalidation_map);
303
304   logger_.OnInvalidation(invalidation_map);
305 }
306
307 std::string TiclInvalidationService::GetOwnerName() const { return "TICL"; }
308
309 void TiclInvalidationService::Shutdown() {
310   DCHECK(CalledOnValidThread());
311   settings_provider_->RemoveObserver(this);
312   identity_provider_->RemoveActiveAccountRefreshTokenObserver(this);
313   identity_provider_->RemoveObserver(this);
314   if (IsStarted()) {
315     StopInvalidator();
316   }
317   invalidation_state_tracker_.reset();
318   invalidator_registrar_.reset();
319 }
320
321 bool TiclInvalidationService::IsReadyToStart() {
322   if (identity_provider_->GetActiveAccountId().empty()) {
323     DVLOG(2) << "Not starting TiclInvalidationService: User is not signed in.";
324     return false;
325   }
326
327   OAuth2TokenService* token_service = identity_provider_->GetTokenService();
328   if (!token_service) {
329     DVLOG(2)
330         << "Not starting TiclInvalidationService: "
331         << "OAuth2TokenService unavailable.";
332     return false;
333   }
334
335   if (!token_service->RefreshTokenIsAvailable(
336           identity_provider_->GetActiveAccountId())) {
337     DVLOG(2)
338         << "Not starting TiclInvalidationServce: Waiting for refresh token.";
339     return false;
340   }
341
342   return true;
343 }
344
345 bool TiclInvalidationService::IsStarted() const {
346   return invalidator_.get() != NULL;
347 }
348
349 void TiclInvalidationService::StartInvalidator(
350     InvalidationNetworkChannel network_channel) {
351   DCHECK(CalledOnValidThread());
352   DCHECK(!invalidator_);
353   DCHECK(invalidation_state_tracker_);
354   DCHECK(!invalidation_state_tracker_->GetInvalidatorClientId().empty());
355
356   // Request access token for PushClientChannel. GCMNetworkChannel will request
357   // access token before sending message to server.
358   if (network_channel == PUSH_CLIENT_CHANNEL && access_token_.empty()) {
359     DVLOG(1)
360         << "TiclInvalidationService: "
361         << "Deferring start until we have an access token.";
362     RequestAccessToken();
363     return;
364   }
365
366   syncer::NetworkChannelCreator network_channel_creator;
367
368   switch (network_channel) {
369     case PUSH_CLIENT_CHANNEL: {
370       notifier::NotifierOptions options =
371           ParseNotifierOptions(*CommandLine::ForCurrentProcess());
372       options.request_context_getter = request_context_;
373       options.auth_mechanism = "X-OAUTH2";
374       network_channel_options_.SetString("Options.HostPort",
375                                          options.xmpp_host_port.ToString());
376       network_channel_options_.SetString("Options.AuthMechanism",
377                                          options.auth_mechanism);
378       DCHECK_EQ(notifier::NOTIFICATION_SERVER, options.notification_method);
379       network_channel_creator =
380           syncer::NonBlockingInvalidator::MakePushClientChannelCreator(options);
381       break;
382     }
383     case GCM_NETWORK_CHANNEL: {
384       gcm_invalidation_bridge_.reset(new GCMInvalidationBridge(
385           gcm_service_, identity_provider_.get()));
386       network_channel_creator =
387           syncer::NonBlockingInvalidator::MakeGCMNetworkChannelCreator(
388               request_context_,
389               gcm_invalidation_bridge_->CreateDelegate().Pass());
390       break;
391     }
392     default: {
393       NOTREACHED();
394       return;
395     }
396   }
397   invalidator_.reset(new syncer::NonBlockingInvalidator(
398           network_channel_creator,
399           invalidation_state_tracker_->GetInvalidatorClientId(),
400           invalidation_state_tracker_->GetSavedInvalidations(),
401           invalidation_state_tracker_->GetBootstrapData(),
402           invalidation_state_tracker_.get(),
403           GetUserAgent(),
404           request_context_));
405
406   UpdateInvalidatorCredentials();
407
408   invalidator_->RegisterHandler(this);
409   invalidator_->UpdateRegisteredIds(
410       this,
411       invalidator_registrar_->GetAllRegisteredIds());
412 }
413
414 void TiclInvalidationService::UpdateInvalidationNetworkChannel() {
415   const InvalidationNetworkChannel network_channel_type =
416       settings_provider_->UseGCMChannel() ? GCM_NETWORK_CHANNEL
417                                           : PUSH_CLIENT_CHANNEL;
418   if (network_channel_type_ == network_channel_type)
419     return;
420   network_channel_type_ = network_channel_type;
421   if (IsStarted()) {
422     StopInvalidator();
423     StartInvalidator(network_channel_type_);
424   }
425 }
426
427 void TiclInvalidationService::UpdateInvalidatorCredentials() {
428   std::string email = identity_provider_->GetActiveAccountId();
429
430   DCHECK(!email.empty()) << "Expected user to be signed in.";
431
432   DVLOG(2) << "UpdateCredentials: " << email;
433   invalidator_->UpdateCredentials(email, access_token_);
434 }
435
436 void TiclInvalidationService::StopInvalidator() {
437   DCHECK(invalidator_);
438   gcm_invalidation_bridge_.reset();
439   invalidator_->UnregisterHandler(this);
440   invalidator_.reset();
441 }
442
443 }  // namespace invalidation