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

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: Rebase 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 settings_provider_->RemoveObserver(this);
73 identity_provider_->RemoveActiveAccountRefreshTokenObserver(this);
74 identity_provider_->RemoveObserver(this);
75 if (IsStarted()) {
76 StopInvalidator();
77 }
78 }
79
80 void TiclInvalidationService::Init(
81 scoped_ptr<syncer::InvalidationStateTracker> invalidation_state_tracker) {
82 DCHECK(CalledOnValidThread());
83 invalidation_state_tracker_ = invalidation_state_tracker.Pass();
84
85 if (invalidation_state_tracker_->GetInvalidatorClientId().empty()) {
86 invalidation_state_tracker_->ClearAndSetNewClientId(
87 GenerateInvalidatorClientId());
88 }
89
90 UpdateInvalidationNetworkChannel();
91 if (IsReadyToStart()) {
92 StartInvalidator(network_channel_type_);
93 }
94
95 identity_provider_->AddObserver(this);
96 identity_provider_->AddActiveAccountRefreshTokenObserver(this);
97 settings_provider_->AddObserver(this);
98 }
99
100 void TiclInvalidationService::InitForTest(
101 scoped_ptr<syncer::InvalidationStateTracker> invalidation_state_tracker,
102 syncer::Invalidator* invalidator) {
103 // Here we perform the equivalent of Init() and StartInvalidator(), but with
104 // some minor changes to account for the fact that we're injecting the
105 // invalidator.
106 invalidation_state_tracker_ = invalidation_state_tracker.Pass();
107 invalidator_.reset(invalidator);
108
109 invalidator_->RegisterHandler(this);
110 invalidator_->UpdateRegisteredIds(
111 this,
112 invalidator_registrar_->GetAllRegisteredIds());
113 }
114
115 void TiclInvalidationService::RegisterInvalidationHandler(
116 syncer::InvalidationHandler* handler) {
117 DCHECK(CalledOnValidThread());
118 DVLOG(2) << "Registering an invalidation handler";
119 invalidator_registrar_->RegisterHandler(handler);
120 logger_.OnRegistration(handler->GetOwnerName());
121 }
122
123 void TiclInvalidationService::UpdateRegisteredInvalidationIds(
124 syncer::InvalidationHandler* handler,
125 const syncer::ObjectIdSet& ids) {
126 DCHECK(CalledOnValidThread());
127 DVLOG(2) << "Registering ids: " << ids.size();
128 invalidator_registrar_->UpdateRegisteredIds(handler, ids);
129 if (invalidator_) {
130 invalidator_->UpdateRegisteredIds(
131 this,
132 invalidator_registrar_->GetAllRegisteredIds());
133 }
134 logger_.OnUpdateIds(invalidator_registrar_->GetSanitizedHandlersIdsMap());
135 }
136
137 void TiclInvalidationService::UnregisterInvalidationHandler(
138 syncer::InvalidationHandler* handler) {
139 DCHECK(CalledOnValidThread());
140 DVLOG(2) << "Unregistering";
141 invalidator_registrar_->UnregisterHandler(handler);
142 if (invalidator_) {
143 invalidator_->UpdateRegisteredIds(
144 this,
145 invalidator_registrar_->GetAllRegisteredIds());
146 }
147 logger_.OnUnregistration(handler->GetOwnerName());
148 }
149
150 syncer::InvalidatorState TiclInvalidationService::GetInvalidatorState() const {
151 DCHECK(CalledOnValidThread());
152 if (invalidator_) {
153 DVLOG(2) << "GetInvalidatorState returning "
154 << invalidator_->GetInvalidatorState();
155 return invalidator_->GetInvalidatorState();
156 } else {
157 DVLOG(2) << "Invalidator currently stopped";
158 return syncer::TRANSIENT_INVALIDATION_ERROR;
159 }
160 }
161
162 std::string TiclInvalidationService::GetInvalidatorClientId() const {
163 DCHECK(CalledOnValidThread());
164 return invalidation_state_tracker_->GetInvalidatorClientId();
165 }
166
167 InvalidationLogger* TiclInvalidationService::GetInvalidationLogger() {
168 return &logger_;
169 }
170
171 IdentityProvider* TiclInvalidationService::GetIdentityProvider() {
172 return identity_provider_.get();
173 }
174
175 void TiclInvalidationService::RequestDetailedStatus(
176 base::Callback<void(const base::DictionaryValue&)> return_callback) const {
177 if (IsStarted()) {
178 return_callback.Run(network_channel_options_);
179 invalidator_->RequestDetailedStatus(return_callback);
180 }
181 }
182
183 void TiclInvalidationService::RequestAccessToken() {
184 // Only one active request at a time.
185 if (access_token_request_ != NULL)
186 return;
187 request_access_token_retry_timer_.Stop();
188 OAuth2TokenService::ScopeSet oauth2_scopes;
189 for (size_t i = 0; i < arraysize(kOAuth2Scopes); i++)
190 oauth2_scopes.insert(kOAuth2Scopes[i]);
191 // Invalidate previous token, otherwise token service will return the same
192 // token again.
193 const std::string& account_id = identity_provider_->GetActiveAccountId();
194 OAuth2TokenService* token_service = identity_provider_->GetTokenService();
195 token_service->InvalidateToken(account_id, oauth2_scopes, access_token_);
196 access_token_.clear();
197 access_token_request_ =
198 token_service->StartRequest(account_id, oauth2_scopes, this);
199 }
200
201 void TiclInvalidationService::OnGetTokenSuccess(
202 const OAuth2TokenService::Request* request,
203 const std::string& access_token,
204 const base::Time& expiration_time) {
205 DCHECK_EQ(access_token_request_, request);
206 access_token_request_.reset();
207 // Reset backoff time after successful response.
208 request_access_token_backoff_.Reset();
209 access_token_ = access_token;
210 if (!IsStarted() && IsReadyToStart()) {
211 StartInvalidator(network_channel_type_);
212 } else {
213 UpdateInvalidatorCredentials();
214 }
215 }
216
217 void TiclInvalidationService::OnGetTokenFailure(
218 const OAuth2TokenService::Request* request,
219 const GoogleServiceAuthError& error) {
220 DCHECK_EQ(access_token_request_, request);
221 DCHECK_NE(error.state(), GoogleServiceAuthError::NONE);
222 access_token_request_.reset();
223 switch (error.state()) {
224 case GoogleServiceAuthError::CONNECTION_FAILED:
225 case GoogleServiceAuthError::SERVICE_UNAVAILABLE: {
226 // Transient error. Retry after some time.
227 request_access_token_backoff_.InformOfRequest(false);
228 request_access_token_retry_timer_.Start(
229 FROM_HERE,
230 request_access_token_backoff_.GetTimeUntilRelease(),
231 base::Bind(&TiclInvalidationService::RequestAccessToken,
232 base::Unretained(this)));
233 break;
234 }
235 case GoogleServiceAuthError::SERVICE_ERROR:
236 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: {
237 invalidator_registrar_->UpdateInvalidatorState(
238 syncer::INVALIDATION_CREDENTIALS_REJECTED);
239 break;
240 }
241 default: {
242 // We have no way to notify the user of this. Do nothing.
243 }
244 }
245 }
246
247 void TiclInvalidationService::OnRefreshTokenAvailable(
248 const std::string& account_id) {
249 if (!IsStarted() && IsReadyToStart())
250 StartInvalidator(network_channel_type_);
251 }
252
253 void TiclInvalidationService::OnRefreshTokenRevoked(
254 const std::string& account_id) {
255 access_token_.clear();
256 if (IsStarted())
257 UpdateInvalidatorCredentials();
258 }
259
260 void TiclInvalidationService::OnActiveAccountLogout() {
261 access_token_request_.reset();
262 request_access_token_retry_timer_.Stop();
263
264 if (IsStarted()) {
265 StopInvalidator();
266 }
267
268 // This service always expects to have a valid invalidation state. Thus, we
269 // must generate a new client ID to replace the existing one. Setting a new
270 // client ID also clears all other state.
271 invalidation_state_tracker_->
272 ClearAndSetNewClientId(GenerateInvalidatorClientId());
273 }
274
275 void TiclInvalidationService::OnUseGCMChannelChanged() {
276 UpdateInvalidationNetworkChannel();
277 }
278
279 void TiclInvalidationService::OnInvalidatorStateChange(
280 syncer::InvalidatorState state) {
281 if (state == syncer::INVALIDATION_CREDENTIALS_REJECTED) {
282 // This may be due to normal OAuth access token expiration. If so, we must
283 // fetch a new one using our refresh token. Resetting the invalidator's
284 // access token will not reset the invalidator's exponential backoff, so
285 // it's safe to try to update the token every time we receive this signal.
286 //
287 // We won't be receiving any invalidations while the refresh is in progress,
288 // we set our state to TRANSIENT_INVALIDATION_ERROR. If the credentials
289 // really are invalid, the refresh request should fail and
290 // OnGetTokenFailure() will put us into a INVALIDATION_CREDENTIALS_REJECTED
291 // state.
292 invalidator_registrar_->UpdateInvalidatorState(
293 syncer::TRANSIENT_INVALIDATION_ERROR);
294 RequestAccessToken();
295 } else {
296 invalidator_registrar_->UpdateInvalidatorState(state);
297 }
298 logger_.OnStateChange(state);
299 }
300
301 void TiclInvalidationService::OnIncomingInvalidation(
302 const syncer::ObjectIdInvalidationMap& invalidation_map) {
303 invalidator_registrar_->DispatchInvalidationsToHandlers(invalidation_map);
304
305 logger_.OnInvalidation(invalidation_map);
306 }
307
308 std::string TiclInvalidationService::GetOwnerName() const { return "TICL"; }
309
310 bool TiclInvalidationService::IsReadyToStart() {
311 if (identity_provider_->GetActiveAccountId().empty()) {
312 DVLOG(2) << "Not starting TiclInvalidationService: User is not signed in.";
313 return false;
314 }
315
316 OAuth2TokenService* token_service = identity_provider_->GetTokenService();
317 if (!token_service) {
318 DVLOG(2)
319 << "Not starting TiclInvalidationService: "
320 << "OAuth2TokenService unavailable.";
321 return false;
322 }
323
324 if (!token_service->RefreshTokenIsAvailable(
325 identity_provider_->GetActiveAccountId())) {
326 DVLOG(2)
327 << "Not starting TiclInvalidationServce: Waiting for refresh token.";
328 return false;
329 }
330
331 return true;
332 }
333
334 bool TiclInvalidationService::IsStarted() const {
335 return invalidator_.get() != NULL;
336 }
337
338 void TiclInvalidationService::StartInvalidator(
339 InvalidationNetworkChannel network_channel) {
340 DCHECK(CalledOnValidThread());
341 DCHECK(!invalidator_);
342 DCHECK(invalidation_state_tracker_);
343 DCHECK(!invalidation_state_tracker_->GetInvalidatorClientId().empty());
344
345 // Request access token for PushClientChannel. GCMNetworkChannel will request
346 // access token before sending message to server.
347 if (network_channel == PUSH_CLIENT_CHANNEL && access_token_.empty()) {
348 DVLOG(1)
349 << "TiclInvalidationService: "
350 << "Deferring start until we have an access token.";
351 RequestAccessToken();
352 return;
353 }
354
355 syncer::NetworkChannelCreator network_channel_creator;
356
357 switch (network_channel) {
358 case PUSH_CLIENT_CHANNEL: {
359 notifier::NotifierOptions options =
360 ParseNotifierOptions(*CommandLine::ForCurrentProcess());
361 options.request_context_getter = request_context_;
362 options.auth_mechanism = "X-OAUTH2";
363 network_channel_options_.SetString("Options.HostPort",
364 options.xmpp_host_port.ToString());
365 network_channel_options_.SetString("Options.AuthMechanism",
366 options.auth_mechanism);
367 DCHECK_EQ(notifier::NOTIFICATION_SERVER, options.notification_method);
368 network_channel_creator =
369 syncer::NonBlockingInvalidator::MakePushClientChannelCreator(options);
370 break;
371 }
372 case GCM_NETWORK_CHANNEL: {
373 gcm_invalidation_bridge_.reset(new GCMInvalidationBridge(
374 gcm_driver_, identity_provider_.get()));
375 network_channel_creator =
376 syncer::NonBlockingInvalidator::MakeGCMNetworkChannelCreator(
377 request_context_,
378 gcm_invalidation_bridge_->CreateDelegate().Pass());
379 break;
380 }
381 default: {
382 NOTREACHED();
383 return;
384 }
385 }
386
387 UMA_HISTOGRAM_ENUMERATION(
388 "Invalidations.NetworkChannel", network_channel, NETWORK_CHANNELS_COUNT);
389 invalidator_.reset(new syncer::NonBlockingInvalidator(
390 network_channel_creator,
391 invalidation_state_tracker_->GetInvalidatorClientId(),
392 invalidation_state_tracker_->GetSavedInvalidations(),
393 invalidation_state_tracker_->GetBootstrapData(),
394 invalidation_state_tracker_.get(),
395 GetUserAgent(),
396 request_context_));
397
398 UpdateInvalidatorCredentials();
399
400 invalidator_->RegisterHandler(this);
401 invalidator_->UpdateRegisteredIds(
402 this,
403 invalidator_registrar_->GetAllRegisteredIds());
404 }
405
406 void TiclInvalidationService::UpdateInvalidationNetworkChannel() {
407 const InvalidationNetworkChannel network_channel_type =
408 settings_provider_->UseGCMChannel() ? GCM_NETWORK_CHANNEL
409 : PUSH_CLIENT_CHANNEL;
410 if (network_channel_type_ == network_channel_type)
411 return;
412 network_channel_type_ = network_channel_type;
413 if (IsStarted()) {
414 StopInvalidator();
415 StartInvalidator(network_channel_type_);
416 }
417 }
418
419 void TiclInvalidationService::UpdateInvalidatorCredentials() {
420 std::string email = identity_provider_->GetActiveAccountId();
421
422 DCHECK(!email.empty()) << "Expected user to be signed in.";
423
424 DVLOG(2) << "UpdateCredentials: " << email;
425 invalidator_->UpdateCredentials(email, access_token_);
426 }
427
428 void TiclInvalidationService::StopInvalidator() {
429 DCHECK(invalidator_);
430 gcm_invalidation_bridge_.reset();
431 invalidator_->UnregisterHandler(this);
432 invalidator_.reset();
433 }
434
435 } // namespace invalidation
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698