No longer trigger signin errors on connection_failed for sync.
[chromium-blink-merge.git] / chrome / browser / signin / signin_tracker.cc
blobcb3312057e4d61f09b545dde33e94bde039b8ca5
1 // Copyright (c) 2012 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/signin/signin_tracker.h"
7 #include "chrome/browser/profiles/profile.h"
8 #include "chrome/browser/signin/signin_manager.h"
9 #include "chrome/browser/signin/signin_manager_factory.h"
10 #include "chrome/browser/signin/token_service.h"
11 #include "chrome/browser/signin/token_service_factory.h"
12 #include "chrome/browser/sync/profile_sync_service.h"
13 #include "chrome/browser/sync/profile_sync_service_factory.h"
14 #include "chrome/common/chrome_notification_types.h"
15 #include "content/public/browser/notification_details.h"
16 #include "content/public/browser/notification_source.h"
17 #include "google_apis/gaia/gaia_constants.h"
19 static const char* kSignedInServices[] = {
20 GaiaConstants::kSyncService,
21 GaiaConstants::kGaiaOAuth2LoginRefreshToken
23 static const int kNumSignedInServices =
24 arraysize(kSignedInServices);
26 // Helper to check if the given token service is relevant for sync.
27 SigninTracker::SigninTracker(Profile* profile, Observer* observer)
28 : state_(WAITING_FOR_GAIA_VALIDATION),
29 profile_(profile),
30 observer_(observer),
31 credentials_valid_(false) {
32 Initialize();
35 SigninTracker::SigninTracker(Profile* profile,
36 Observer* observer,
37 LoginState state)
38 : state_(state),
39 profile_(profile),
40 observer_(observer),
41 credentials_valid_(false) {
42 Initialize();
45 SigninTracker::~SigninTracker() {
46 ProfileSyncService* service =
47 ProfileSyncServiceFactory::GetForProfile(profile_);
48 if (service)
49 service->RemoveObserver(this);
52 void SigninTracker::Initialize() {
53 DCHECK(observer_);
54 // Register for notifications from the SigninManager.
55 registrar_.Add(this,
56 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL,
57 content::Source<Profile>(profile_));
58 registrar_.Add(this,
59 chrome::NOTIFICATION_GOOGLE_SIGNIN_FAILED,
60 content::Source<Profile>(profile_));
61 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
62 registrar_.Add(this,
63 chrome::NOTIFICATION_TOKEN_AVAILABLE,
64 content::Source<TokenService>(token_service));
65 registrar_.Add(this,
66 chrome::NOTIFICATION_TOKEN_REQUEST_FAILED,
67 content::Source<TokenService>(token_service));
69 // Also listen for notifications from the various signed in services (only
70 // sync for now).
71 ProfileSyncService* service =
72 ProfileSyncServiceFactory::GetForProfile(profile_);
73 if (service)
74 service->AddObserver(this);
76 if (state_ == SERVICES_INITIALIZING)
77 HandleServiceStateChange();
80 void SigninTracker::Observe(int type,
81 const content::NotificationSource& source,
82 const content::NotificationDetails& details) {
83 // We should not get more than one of these notifications.
84 switch (type) {
85 case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL:
86 DCHECK_EQ(state_, WAITING_FOR_GAIA_VALIDATION);
87 state_ = SERVICES_INITIALIZING;
88 observer_->GaiaCredentialsValid();
89 // If our services are already signed in, see if it's possible to
90 // transition to the SIGNIN_COMPLETE state.
91 if (AreServicesSignedIn(profile_))
92 HandleServiceStateChange();
93 break;
94 case chrome::NOTIFICATION_GOOGLE_SIGNIN_FAILED: {
95 DCHECK_EQ(state_, WAITING_FOR_GAIA_VALIDATION);
96 const GoogleServiceAuthError& error =
97 *(content::Details<const GoogleServiceAuthError>(details).ptr());
98 observer_->SigninFailed(error);
99 break;
101 case chrome::NOTIFICATION_TOKEN_AVAILABLE:
102 // A new token is available - check to see if we're all signed in now.
103 HandleServiceStateChange();
104 break;
105 case chrome::NOTIFICATION_TOKEN_REQUEST_FAILED:
106 if (state_ == SERVICES_INITIALIZING) {
107 const TokenService::TokenRequestFailedDetails& token_details =
108 *(content::Details<const TokenService::TokenRequestFailedDetails>(
109 details).ptr());
110 for (int i = 0; i < kNumSignedInServices; ++i) {
111 if (token_details.service() == kSignedInServices[i]) {
112 // We got an error loading one of our tokens, so notify our
113 // observer.
114 state_ = WAITING_FOR_GAIA_VALIDATION;
115 observer_->SigninFailed(token_details.error());
119 break;
120 default:
121 NOTREACHED();
125 // Called when the ProfileSyncService state changes.
126 void SigninTracker::OnStateChanged() {
127 HandleServiceStateChange();
130 void SigninTracker::HandleServiceStateChange() {
131 if (state_ != SERVICES_INITIALIZING) {
132 // Ignore service updates until after our GAIA credentials are validated.
133 return;
136 GoogleServiceAuthError error(GoogleServiceAuthError::NONE);
137 state_ = GetSigninState(profile_, &error);
138 switch (state_) {
139 case WAITING_FOR_GAIA_VALIDATION:
140 observer_->SigninFailed(error);
141 break;
142 case SIGNIN_COMPLETE:
143 observer_->SigninSuccess();
144 break;
145 default:
146 // State has not changed, nothing to do.
147 DCHECK_EQ(state_, SERVICES_INITIALIZING);
148 break;
152 // static
153 bool SigninTracker::AreServiceTokensLoaded(Profile* profile) {
154 // See if we have all of the tokens required.
155 TokenService* token_service = TokenServiceFactory::GetForProfile(profile);
156 for (int i = 0; i < kNumSignedInServices; ++i) {
157 if (!token_service->HasTokenForService(kSignedInServices[i])) {
158 // Don't have a token for one of our signed-in services.
159 return false;
162 return true;
165 // static
166 bool SigninTracker::AreServicesSignedIn(Profile* profile) {
167 if (!AreServiceTokensLoaded(profile))
168 return false;
169 // Don't care about the sync state if sync is disabled by policy.
170 if (!profile->IsSyncAccessible())
171 return true;
172 ProfileSyncService* service =
173 ProfileSyncServiceFactory::GetForProfile(profile);
174 // Check the sync service state - we ignore CONNECTION_FAILED errors here
175 // because they are transient and do not signify a failure of the signin
176 // process.
177 return (service->IsSyncEnabledAndLoggedIn() &&
178 service->IsSyncTokenAvailable() &&
179 (service->GetAuthError().state() == GoogleServiceAuthError::NONE ||
180 service->GetAuthError().state() ==
181 GoogleServiceAuthError::CONNECTION_FAILED) &&
182 !service->HasUnrecoverableError());
185 // static
186 SigninTracker::LoginState SigninTracker::GetSigninState(
187 Profile* profile,
188 GoogleServiceAuthError* error) {
189 SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile);
190 if (signin->GetAuthenticatedUsername().empty()) {
191 // User is signed out, trigger a signin failure.
192 if (error)
193 *error = GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED);
194 return WAITING_FOR_GAIA_VALIDATION;
197 // Wait until all of our services are logged in. For now this just means sync.
198 // Long term, we should separate out service auth failures from the signin
199 // process, but for the current UI flow we'll validate service signin status
200 // also.
201 ProfileSyncService* service = profile->IsSyncAccessible() ?
202 ProfileSyncServiceFactory::GetForProfile(profile) : NULL;
203 if (service && service->waiting_for_auth()) {
204 // Still waiting for an auth token to come in so stay in the INITIALIZING
205 // state (we do this to avoid triggering an early signin error in the case
206 // where there's a previous auth error in the sync service that hasn't
207 // been cleared yet).
208 return SERVICES_INITIALIZING;
211 // If we haven't loaded all our service tokens yet, just exit (we'll be called
212 // again when another token is loaded, or will transition to SigninFailed if
213 // the loading fails).
214 if (!AreServiceTokensLoaded(profile))
215 return SERVICES_INITIALIZING;
216 if (!AreServicesSignedIn(profile)) {
217 if (error)
218 *error = signin->signin_global_error()->GetLastAuthError();
219 return WAITING_FOR_GAIA_VALIDATION;
222 if (!service || service->sync_initialized())
223 return SIGNIN_COMPLETE;
225 return SERVICES_INITIALIZING;