Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(666)

Side by Side Diff: chrome/browser/invalidation/ticl_invalidation_service.cc

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

Powered by Google App Engine
This is Rietveld 408576698