OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/invalidation/ticl_invalidation_service.h" | 5 #include "chrome/browser/invalidation/ticl_invalidation_service.h" |
6 | 6 |
7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
8 #include "chrome/browser/invalidation/invalidation_service_util.h" | 8 #include "chrome/browser/invalidation/invalidation_service_util.h" |
| 9 #include "chrome/browser/managed_mode/managed_user_service.h" |
9 #include "chrome/browser/profiles/profile.h" | 10 #include "chrome/browser/profiles/profile.h" |
| 11 #include "chrome/browser/signin/profile_oauth2_token_service.h" |
| 12 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" |
10 #include "chrome/browser/signin/signin_manager.h" | 13 #include "chrome/browser/signin/signin_manager.h" |
11 #include "chrome/browser/signin/token_service.h" | |
12 #include "chrome/common/chrome_notification_types.h" | 14 #include "chrome/common/chrome_notification_types.h" |
13 #include "content/public/browser/notification_service.h" | 15 #include "content/public/browser/notification_service.h" |
14 #include "google_apis/gaia/gaia_constants.h" | 16 #include "google_apis/gaia/gaia_constants.h" |
15 #include "sync/notifier/invalidator.h" | 17 #include "sync/notifier/invalidator.h" |
16 #include "sync/notifier/invalidator_state.h" | 18 #include "sync/notifier/invalidator_state.h" |
17 #include "sync/notifier/non_blocking_invalidator.h" | 19 #include "sync/notifier/non_blocking_invalidator.h" |
18 | 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 |
19 namespace invalidation { | 53 namespace invalidation { |
20 | 54 |
21 TiclInvalidationService::TiclInvalidationService(SigninManagerBase* signin, | 55 TiclInvalidationService::TiclInvalidationService( |
22 TokenService* token_service, | 56 SigninManagerBase* signin, |
23 Profile* profile) | 57 TokenService* token_service, |
24 : profile_(profile), | 58 OAuth2TokenService* oauth2_token_service, |
25 signin_manager_(signin), | 59 Profile* profile) |
26 token_service_(token_service), | 60 : profile_(profile), |
27 invalidator_registrar_(new syncer::InvalidatorRegistrar()) { } | 61 signin_manager_(signin), |
| 62 token_service_(token_service), |
| 63 oauth2_token_service_(oauth2_token_service), |
| 64 invalidator_registrar_(new syncer::InvalidatorRegistrar()), |
| 65 request_access_token_backoff_(&kRequestAccessTokenBackoffPolicy) { |
| 66 } |
28 | 67 |
29 TiclInvalidationService::~TiclInvalidationService() { | 68 TiclInvalidationService::~TiclInvalidationService() { |
30 DCHECK(CalledOnValidThread()); | 69 DCHECK(CalledOnValidThread()); |
31 } | 70 } |
32 | 71 |
33 void TiclInvalidationService::Init() { | 72 void TiclInvalidationService::Init() { |
34 DCHECK(CalledOnValidThread()); | 73 DCHECK(CalledOnValidThread()); |
35 | 74 |
36 invalidator_storage_.reset(new InvalidatorStorage(profile_->GetPrefs())); | 75 invalidator_storage_.reset(new InvalidatorStorage(profile_->GetPrefs())); |
37 if (invalidator_storage_->GetInvalidatorClientId().empty()) { | 76 if (invalidator_storage_->GetInvalidatorClientId().empty()) { |
38 // This also clears any existing state. We can't reuse old invalidator | 77 // This also clears any existing state. We can't reuse old invalidator |
39 // state with the new ID anyway. | 78 // state with the new ID anyway. |
40 invalidator_storage_->SetInvalidatorClientId(GenerateInvalidatorClientId()); | 79 invalidator_storage_->SetInvalidatorClientId(GenerateInvalidatorClientId()); |
41 } | 80 } |
42 | 81 |
43 if (IsReadyToStart()) { | 82 if (IsReadyToStart()) { |
44 Start(); | 83 StartInvalidator(); |
45 } | 84 } |
46 | 85 |
47 notification_registrar_.Add(this, | 86 notification_registrar_.Add(this, |
48 chrome::NOTIFICATION_TOKEN_AVAILABLE, | 87 chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, |
| 88 content::Source<Profile>(profile_)); |
| 89 notification_registrar_.Add(this, |
| 90 chrome::NOTIFICATION_TOKEN_LOADING_FINISHED, |
49 content::Source<TokenService>(token_service_)); | 91 content::Source<TokenService>(token_service_)); |
50 notification_registrar_.Add(this, | 92 notification_registrar_.Add(this, |
51 chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, | 93 chrome::NOTIFICATION_TOKENS_CLEARED, |
52 content::Source<Profile>(profile_)); | 94 content::Source<TokenService>(token_service_)); |
53 } | 95 } |
54 | 96 |
55 void TiclInvalidationService::InitForTest(syncer::Invalidator* invalidator) { | 97 void TiclInvalidationService::InitForTest(syncer::Invalidator* invalidator) { |
56 // Here we perform the equivalent of Init() and Start(), but with some minor | 98 // Here we perform the equivalent of Init() and StartInvalidator(), but with |
57 // changes to account for the fact that we're injecting the invalidator. | 99 // some minor changes to account for the fact that we're injecting the |
| 100 // invalidator. |
58 invalidator_.reset(invalidator); | 101 invalidator_.reset(invalidator); |
59 | 102 |
60 invalidator_->RegisterHandler(this); | 103 invalidator_->RegisterHandler(this); |
61 invalidator_->UpdateRegisteredIds( | 104 invalidator_->UpdateRegisteredIds( |
62 this, | 105 this, |
63 invalidator_registrar_->GetAllRegisteredIds()); | 106 invalidator_registrar_->GetAllRegisteredIds()); |
64 } | 107 } |
65 | 108 |
66 void TiclInvalidationService::RegisterInvalidationHandler( | 109 void TiclInvalidationService::RegisterInvalidationHandler( |
67 syncer::InvalidationHandler* handler) { | 110 syncer::InvalidationHandler* handler) { |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
121 return invalidator_storage_->GetInvalidatorClientId(); | 164 return invalidator_storage_->GetInvalidatorClientId(); |
122 } | 165 } |
123 | 166 |
124 void TiclInvalidationService::Observe( | 167 void TiclInvalidationService::Observe( |
125 int type, | 168 int type, |
126 const content::NotificationSource& source, | 169 const content::NotificationSource& source, |
127 const content::NotificationDetails& details) { | 170 const content::NotificationDetails& details) { |
128 DCHECK(CalledOnValidThread()); | 171 DCHECK(CalledOnValidThread()); |
129 | 172 |
130 switch (type) { | 173 switch (type) { |
131 case chrome::NOTIFICATION_TOKEN_AVAILABLE: { | 174 case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: { |
132 const TokenService::TokenAvailableDetails& token_details = | 175 if (!IsStarted() && IsReadyToStart()) { |
133 *(content::Details<const TokenService::TokenAvailableDetails>( | 176 StartInvalidator(); |
134 details).ptr()); | |
135 if (token_details.service() == GaiaConstants::kSyncService) { | |
136 DCHECK(IsReadyToStart()); | |
137 if (!IsStarted()) { | |
138 Start(); | |
139 } else { | |
140 UpdateToken(); | |
141 } | |
142 } | 177 } |
143 break; | 178 break; |
144 } | 179 } |
| 180 case chrome::NOTIFICATION_TOKENS_CLEARED: { |
| 181 access_token_.clear(); |
| 182 if (IsStarted()) { |
| 183 UpdateInvalidatorCredentials(); |
| 184 } |
| 185 break; |
| 186 } |
145 case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT: { | 187 case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT: { |
146 Logout(); | 188 Logout(); |
147 break; | 189 break; |
148 } | 190 } |
149 default: { | 191 default: { |
150 NOTREACHED(); | 192 NOTREACHED(); |
151 } | 193 } |
152 } | 194 } |
153 } | 195 } |
154 | 196 |
| 197 void TiclInvalidationService::RequestAccessToken() { |
| 198 // Only one active request at a time. |
| 199 if (access_token_request_ != NULL) |
| 200 return; |
| 201 request_access_token_retry_timer_.Stop(); |
| 202 OAuth2TokenService::ScopeSet oauth2_scopes; |
| 203 for (size_t i = 0; i < arraysize(kOAuth2Scopes); i++) |
| 204 oauth2_scopes.insert(kOAuth2Scopes[i]); |
| 205 // Invalidate previous token, otherwise token service will return the same |
| 206 // token again. |
| 207 oauth2_token_service_->InvalidateToken(oauth2_scopes, access_token_); |
| 208 access_token_.clear(); |
| 209 access_token_request_ = |
| 210 oauth2_token_service_->StartRequest(oauth2_scopes, this); |
| 211 } |
| 212 |
| 213 void TiclInvalidationService::OnGetTokenSuccess( |
| 214 const OAuth2TokenService::Request* request, |
| 215 const std::string& access_token, |
| 216 const base::Time& expiration_time) { |
| 217 DCHECK_EQ(access_token_request_, request); |
| 218 access_token_request_.reset(); |
| 219 // Reset backoff time after successful response. |
| 220 request_access_token_backoff_.Reset(); |
| 221 access_token_ = access_token; |
| 222 if (!IsStarted() && IsReadyToStart()) { |
| 223 StartInvalidator(); |
| 224 } else { |
| 225 UpdateInvalidatorCredentials(); |
| 226 } |
| 227 } |
| 228 |
| 229 void TiclInvalidationService::OnGetTokenFailure( |
| 230 const OAuth2TokenService::Request* request, |
| 231 const GoogleServiceAuthError& error) { |
| 232 DCHECK_EQ(access_token_request_, request); |
| 233 DCHECK_NE(error.state(), GoogleServiceAuthError::NONE); |
| 234 access_token_request_.reset(); |
| 235 switch (error.state()) { |
| 236 case GoogleServiceAuthError::CONNECTION_FAILED: |
| 237 case GoogleServiceAuthError::SERVICE_UNAVAILABLE: { |
| 238 // Transient error. Retry after some time. |
| 239 request_access_token_backoff_.InformOfRequest(false); |
| 240 request_access_token_retry_timer_.Start( |
| 241 FROM_HERE, |
| 242 request_access_token_backoff_.GetTimeUntilRelease(), |
| 243 base::Bind(&TiclInvalidationService::RequestAccessToken, |
| 244 base::Unretained(this))); |
| 245 break; |
| 246 } |
| 247 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: { |
| 248 // This is a real auth error. |
| 249 invalidator_registrar_->UpdateInvalidatorState( |
| 250 syncer::INVALIDATION_CREDENTIALS_REJECTED); |
| 251 break; |
| 252 } |
| 253 default: { |
| 254 // We have no way to notify the user of this. Do nothing. |
| 255 } |
| 256 } |
| 257 } |
| 258 |
155 void TiclInvalidationService::OnInvalidatorStateChange( | 259 void TiclInvalidationService::OnInvalidatorStateChange( |
156 syncer::InvalidatorState state) { | 260 syncer::InvalidatorState state) { |
157 invalidator_registrar_->UpdateInvalidatorState(state); | 261 invalidator_registrar_->UpdateInvalidatorState(state); |
158 } | 262 } |
159 | 263 |
160 void TiclInvalidationService::OnIncomingInvalidation( | 264 void TiclInvalidationService::OnIncomingInvalidation( |
161 const syncer::ObjectIdInvalidationMap& invalidation_map) { | 265 const syncer::ObjectIdInvalidationMap& invalidation_map) { |
162 invalidator_registrar_->DispatchInvalidationsToHandlers(invalidation_map); | 266 invalidator_registrar_->DispatchInvalidationsToHandlers(invalidation_map); |
163 } | 267 } |
164 | 268 |
165 void TiclInvalidationService::Shutdown() { | 269 void TiclInvalidationService::Shutdown() { |
166 DCHECK(CalledOnValidThread()); | 270 DCHECK(CalledOnValidThread()); |
167 if (IsStarted()) { | 271 if (IsStarted()) { |
168 StopInvalidator(); | 272 StopInvalidator(); |
169 } | 273 } |
170 invalidator_storage_.reset(); | 274 invalidator_storage_.reset(); |
171 invalidator_registrar_.reset(); | 275 invalidator_registrar_.reset(); |
172 } | 276 } |
173 | 277 |
174 bool TiclInvalidationService::IsReadyToStart() { | 278 bool TiclInvalidationService::IsReadyToStart() { |
175 if (signin_manager_->GetAuthenticatedUsername().empty()) { | 279 if (ManagedUserService::ProfileIsManaged(profile_)) { |
176 DVLOG(2) << "Not starting TiclInvalidationService: user is not signed in."; | 280 DVLOG(2) << "Not starting TiclInvalidationService: User is managed."; |
177 return false; | 281 return false; |
178 } | 282 } |
179 | 283 |
180 if (!token_service_) { | 284 if (signin_manager_->GetAuthenticatedUsername().empty()) { |
| 285 DVLOG(2) << "Not starting TiclInvalidationService: User is not signed in."; |
| 286 return false; |
| 287 } |
| 288 |
| 289 if (!oauth2_token_service_) { |
181 DVLOG(2) | 290 DVLOG(2) |
182 << "Not starting TiclInvalidationService: TokenService unavailable."; | 291 << "Not starting TiclInvalidationService: TokenService unavailable."; |
183 return false; | 292 return false; |
184 } | 293 } |
185 | 294 |
186 if (!token_service_->HasTokenForService(GaiaConstants::kSyncService)) { | 295 if (!oauth2_token_service_->RefreshTokenIsAvailable()) { |
187 DVLOG(2) << "Not starting TiclInvalidationService: Sync token unavailable."; | 296 DVLOG(2) |
| 297 << "Not starting TiclInvalidationServce: Waiting for refresh token."; |
188 return false; | 298 return false; |
189 } | 299 } |
190 | 300 |
191 return true; | 301 return true; |
192 } | 302 } |
193 | 303 |
194 bool TiclInvalidationService::IsStarted() { | 304 bool TiclInvalidationService::IsStarted() { |
195 return invalidator_.get() != NULL; | 305 return invalidator_.get() != NULL; |
196 } | 306 } |
197 | 307 |
198 void TiclInvalidationService::Start() { | 308 void TiclInvalidationService::StartInvalidator() { |
199 DCHECK(CalledOnValidThread()); | 309 DCHECK(CalledOnValidThread()); |
200 DCHECK(!invalidator_); | 310 DCHECK(!invalidator_); |
201 DCHECK(invalidator_storage_); | 311 DCHECK(invalidator_storage_); |
202 DCHECK(!invalidator_storage_->GetInvalidatorClientId().empty()); | 312 DCHECK(!invalidator_storage_->GetInvalidatorClientId().empty()); |
203 | 313 |
| 314 if (access_token_.empty()) { |
| 315 DVLOG(1) |
| 316 << "TiclInvalidationService: " |
| 317 << "Deferring start until we have an access token."; |
| 318 RequestAccessToken(); |
| 319 return; |
| 320 } |
| 321 |
204 notifier::NotifierOptions options = | 322 notifier::NotifierOptions options = |
205 ParseNotifierOptions(*CommandLine::ForCurrentProcess()); | 323 ParseNotifierOptions(*CommandLine::ForCurrentProcess()); |
206 options.request_context_getter = profile_->GetRequestContext(); | 324 options.request_context_getter = profile_->GetRequestContext(); |
| 325 options.auth_mechanism = "X-OAUTH2"; |
207 invalidator_.reset(new syncer::NonBlockingInvalidator( | 326 invalidator_.reset(new syncer::NonBlockingInvalidator( |
208 options, | 327 options, |
209 invalidator_storage_->GetInvalidatorClientId(), | 328 invalidator_storage_->GetInvalidatorClientId(), |
210 invalidator_storage_->GetAllInvalidationStates(), | 329 invalidator_storage_->GetAllInvalidationStates(), |
211 invalidator_storage_->GetBootstrapData(), | 330 invalidator_storage_->GetBootstrapData(), |
212 syncer::WeakHandle<syncer::InvalidationStateTracker>( | 331 syncer::WeakHandle<syncer::InvalidationStateTracker>( |
213 invalidator_storage_->AsWeakPtr()), | 332 invalidator_storage_->AsWeakPtr()), |
214 content::GetUserAgent(GURL()))); | 333 content::GetUserAgent(GURL()))); |
215 | 334 |
216 UpdateToken(); | 335 UpdateInvalidatorCredentials(); |
217 | 336 |
218 invalidator_->RegisterHandler(this); | 337 invalidator_->RegisterHandler(this); |
219 invalidator_->UpdateRegisteredIds( | 338 invalidator_->UpdateRegisteredIds( |
220 this, | 339 this, |
221 invalidator_registrar_->GetAllRegisteredIds()); | 340 invalidator_registrar_->GetAllRegisteredIds()); |
222 } | 341 } |
223 | 342 |
224 void TiclInvalidationService::UpdateToken() { | 343 void TiclInvalidationService::UpdateInvalidatorCredentials() { |
225 std::string email = signin_manager_->GetAuthenticatedUsername(); | 344 std::string email = signin_manager_->GetAuthenticatedUsername(); |
| 345 |
226 DCHECK(!email.empty()) << "Expected user to be signed in."; | 346 DCHECK(!email.empty()) << "Expected user to be signed in."; |
227 DCHECK(token_service_->HasTokenForService(GaiaConstants::kSyncService)); | 347 DCHECK(!access_token_.empty()); |
228 | |
229 std::string sync_token = token_service_->GetTokenForService( | |
230 GaiaConstants::kSyncService); | |
231 | 348 |
232 DVLOG(2) << "UpdateCredentials: " << email; | 349 DVLOG(2) << "UpdateCredentials: " << email; |
233 invalidator_->UpdateCredentials(email, sync_token); | 350 invalidator_->UpdateCredentials(email, access_token_); |
234 } | 351 } |
235 | 352 |
236 void TiclInvalidationService::StopInvalidator() { | 353 void TiclInvalidationService::StopInvalidator() { |
237 DCHECK(invalidator_); | 354 DCHECK(invalidator_); |
238 invalidator_->UnregisterHandler(this); | 355 invalidator_->UnregisterHandler(this); |
239 invalidator_.reset(); | 356 invalidator_.reset(); |
240 } | 357 } |
241 | 358 |
242 void TiclInvalidationService::Logout() { | 359 void TiclInvalidationService::Logout() { |
| 360 request_access_token_retry_timer_.Stop(); |
| 361 |
243 StopInvalidator(); | 362 StopInvalidator(); |
244 | 363 |
245 // This service always expects to have a valid invalidator storage. | 364 // This service always expects to have a valid invalidator storage. |
246 // So we must not only clear the old one, but also start a new one. | 365 // So we must not only clear the old one, but also start a new one. |
247 invalidator_storage_->Clear(); | 366 invalidator_storage_->Clear(); |
248 invalidator_storage_.reset(new InvalidatorStorage(profile_->GetPrefs())); | 367 invalidator_storage_.reset(new InvalidatorStorage(profile_->GetPrefs())); |
249 invalidator_storage_->SetInvalidatorClientId(GenerateInvalidatorClientId()); | 368 invalidator_storage_->SetInvalidatorClientId(GenerateInvalidatorClientId()); |
250 } | 369 } |
251 | 370 |
252 } // namespace invalidation | 371 } // namespace invalidation |
OLD | NEW |