1 // Copyright 2014 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 "google_apis/gaia/account_tracker.h"
7 #include "base/logging.h"
8 #include "base/stl_util.h"
9 #include "base/trace_event/trace_event.h"
10 #include "net/url_request/url_request_context_getter.h"
14 AccountTracker::AccountTracker(
15 IdentityProvider
* identity_provider
,
16 net::URLRequestContextGetter
* request_context_getter
)
17 : identity_provider_(identity_provider
),
18 request_context_getter_(request_context_getter
),
19 shutdown_called_(false) {
20 identity_provider_
->AddObserver(this);
21 identity_provider_
->GetTokenService()->AddObserver(this);
24 AccountTracker::~AccountTracker() {
25 DCHECK(shutdown_called_
);
28 void AccountTracker::Shutdown() {
29 shutdown_called_
= true;
30 STLDeleteValues(&user_info_requests_
);
31 identity_provider_
->GetTokenService()->RemoveObserver(this);
32 identity_provider_
->RemoveObserver(this);
35 bool AccountTracker::IsAllUserInfoFetched() const {
36 return user_info_requests_
.empty();
39 void AccountTracker::AddObserver(Observer
* observer
) {
40 observer_list_
.AddObserver(observer
);
43 void AccountTracker::RemoveObserver(Observer
* observer
) {
44 observer_list_
.RemoveObserver(observer
);
47 std::vector
<AccountIds
> AccountTracker::GetAccounts() const {
48 const std::string active_account_id
=
49 identity_provider_
->GetActiveAccountId();
50 std::vector
<AccountIds
> accounts
;
52 for (std::map
<std::string
, AccountState
>::const_iterator it
=
54 it
!= accounts_
.end();
56 const AccountState
& state
= it
->second
;
57 bool is_visible
= state
.is_signed_in
&& !state
.ids
.gaia
.empty();
59 if (it
->first
== active_account_id
) {
61 accounts
.insert(accounts
.begin(), state
.ids
);
63 return std::vector
<AccountIds
>();
65 } else if (is_visible
) {
66 accounts
.push_back(state
.ids
);
72 AccountIds
AccountTracker::FindAccountIdsByGaiaId(const std::string
& gaia_id
) {
73 for (std::map
<std::string
, AccountState
>::const_iterator it
=
75 it
!= accounts_
.end();
77 const AccountState
& state
= it
->second
;
78 if (state
.ids
.gaia
== gaia_id
) {
86 void AccountTracker::OnRefreshTokenAvailable(const std::string
& account_id
) {
87 TRACE_EVENT1("identity",
88 "AccountTracker::OnRefreshTokenAvailable",
92 // Ignore refresh tokens if there is no active account ID at all.
93 if (identity_provider_
->GetActiveAccountId().empty())
96 DVLOG(1) << "AVAILABLE " << account_id
;
97 UpdateSignInState(account_id
, true);
100 void AccountTracker::OnRefreshTokenRevoked(const std::string
& account_id
) {
101 TRACE_EVENT1("identity",
102 "AccountTracker::OnRefreshTokenRevoked",
106 DVLOG(1) << "REVOKED " << account_id
;
107 UpdateSignInState(account_id
, false);
110 void AccountTracker::OnActiveAccountLogin() {
111 TRACE_EVENT0("identity", "AccountTracker::OnActiveAccountLogin");
113 std::vector
<std::string
> accounts
=
114 identity_provider_
->GetTokenService()->GetAccounts();
116 DVLOG(1) << "LOGIN " << accounts
.size() << " accounts available.";
118 for (std::vector
<std::string
>::const_iterator it
= accounts
.begin();
119 it
!= accounts
.end();
121 OnRefreshTokenAvailable(*it
);
125 void AccountTracker::OnActiveAccountLogout() {
126 TRACE_EVENT0("identity", "AccountTracker::OnActiveAccountLogout");
127 DVLOG(1) << "LOGOUT";
128 StopTrackingAllAccounts();
131 void AccountTracker::SetAccountStateForTest(AccountIds ids
, bool is_signed_in
) {
132 accounts_
[ids
.account_key
].ids
= ids
;
133 accounts_
[ids
.account_key
].is_signed_in
= is_signed_in
;
135 DVLOG(1) << "SetAccountStateForTest " << ids
.account_key
<< ":"
139 for (std::map
<std::string
, AccountState
>::const_iterator it
=
141 it
!= accounts_
.end();
143 DVLOG(1) << it
->first
<< ":" << it
->second
.is_signed_in
;
148 void AccountTracker::NotifyAccountAdded(const AccountState
& account
) {
149 DCHECK(!account
.ids
.gaia
.empty());
151 Observer
, observer_list_
, OnAccountAdded(account
.ids
));
154 void AccountTracker::NotifyAccountRemoved(const AccountState
& account
) {
155 DCHECK(!account
.ids
.gaia
.empty());
157 Observer
, observer_list_
, OnAccountRemoved(account
.ids
));
160 void AccountTracker::NotifySignInChanged(const AccountState
& account
) {
161 DCHECK(!account
.ids
.gaia
.empty());
162 FOR_EACH_OBSERVER(Observer
,
164 OnAccountSignInChanged(account
.ids
, account
.is_signed_in
));
167 void AccountTracker::UpdateSignInState(const std::string account_key
,
169 StartTrackingAccount(account_key
);
170 AccountState
& account
= accounts_
[account_key
];
171 bool needs_gaia_id
= account
.ids
.gaia
.empty();
172 bool was_signed_in
= account
.is_signed_in
;
173 account
.is_signed_in
= is_signed_in
;
175 if (needs_gaia_id
&& is_signed_in
)
176 StartFetchingUserInfo(account_key
);
178 if (!needs_gaia_id
&& (was_signed_in
!= is_signed_in
))
179 NotifySignInChanged(account
);
182 void AccountTracker::StartTrackingAccount(const std::string account_key
) {
183 if (!ContainsKey(accounts_
, account_key
)) {
184 DVLOG(1) << "StartTracking " << account_key
;
185 AccountState account_state
;
186 account_state
.ids
.account_key
= account_key
;
187 account_state
.ids
.email
= account_key
;
188 account_state
.is_signed_in
= false;
189 accounts_
.insert(make_pair(account_key
, account_state
));
193 void AccountTracker::StopTrackingAccount(const std::string account_key
) {
194 DVLOG(1) << "StopTracking " << account_key
;
195 if (ContainsKey(accounts_
, account_key
)) {
196 AccountState
& account
= accounts_
[account_key
];
197 if (!account
.ids
.gaia
.empty()) {
198 UpdateSignInState(account_key
, false);
199 NotifyAccountRemoved(account
);
201 accounts_
.erase(account_key
);
204 if (ContainsKey(user_info_requests_
, account_key
))
205 DeleteFetcher(user_info_requests_
[account_key
]);
208 void AccountTracker::StopTrackingAllAccounts() {
209 while (!accounts_
.empty())
210 StopTrackingAccount(accounts_
.begin()->first
);
213 void AccountTracker::StartFetchingUserInfo(const std::string account_key
) {
214 if (ContainsKey(user_info_requests_
, account_key
))
215 DeleteFetcher(user_info_requests_
[account_key
]);
217 DVLOG(1) << "StartFetching " << account_key
;
218 AccountIdFetcher
* fetcher
=
219 new AccountIdFetcher(identity_provider_
->GetTokenService(),
220 request_context_getter_
.get(),
223 user_info_requests_
[account_key
] = fetcher
;
227 void AccountTracker::OnUserInfoFetchSuccess(AccountIdFetcher
* fetcher
,
228 const std::string
& gaia_id
) {
229 const std::string
& account_key
= fetcher
->account_key();
230 DCHECK(ContainsKey(accounts_
, account_key
));
231 AccountState
& account
= accounts_
[account_key
];
233 account
.ids
.gaia
= gaia_id
;
234 NotifyAccountAdded(account
);
236 if (account
.is_signed_in
)
237 NotifySignInChanged(account
);
239 DeleteFetcher(fetcher
);
242 void AccountTracker::OnUserInfoFetchFailure(AccountIdFetcher
* fetcher
) {
243 LOG(WARNING
) << "Failed to get UserInfo for " << fetcher
->account_key();
244 std::string key
= fetcher
->account_key();
245 DeleteFetcher(fetcher
);
246 StopTrackingAccount(key
);
249 void AccountTracker::DeleteFetcher(AccountIdFetcher
* fetcher
) {
250 DVLOG(1) << "DeleteFetcher " << fetcher
->account_key();
251 const std::string
& account_key
= fetcher
->account_key();
252 DCHECK(ContainsKey(user_info_requests_
, account_key
));
253 DCHECK_EQ(fetcher
, user_info_requests_
[account_key
]);
254 user_info_requests_
.erase(account_key
);
258 AccountIdFetcher::AccountIdFetcher(
259 OAuth2TokenService
* token_service
,
260 net::URLRequestContextGetter
* request_context_getter
,
261 AccountTracker
* tracker
,
262 const std::string
& account_key
)
263 : OAuth2TokenService::Consumer("gaia_account_tracker"),
264 token_service_(token_service
),
265 request_context_getter_(request_context_getter
),
267 account_key_(account_key
) {
268 TRACE_EVENT_ASYNC_BEGIN1(
269 "identity", "AccountIdFetcher", this, "account_key", account_key
);
272 AccountIdFetcher::~AccountIdFetcher() {
273 TRACE_EVENT_ASYNC_END0("identity", "AccountIdFetcher", this);
276 void AccountIdFetcher::Start() {
277 OAuth2TokenService::ScopeSet scopes
;
278 scopes
.insert("https://www.googleapis.com/auth/userinfo.profile");
279 login_token_request_
= token_service_
->StartRequest(
280 account_key_
, scopes
, this);
283 void AccountIdFetcher::OnGetTokenSuccess(
284 const OAuth2TokenService::Request
* request
,
285 const std::string
& access_token
,
286 const base::Time
& expiration_time
) {
287 TRACE_EVENT_ASYNC_STEP_PAST0(
288 "identity", "AccountIdFetcher", this, "OnGetTokenSuccess");
289 DCHECK_EQ(request
, login_token_request_
.get());
291 gaia_oauth_client_
.reset(new gaia::GaiaOAuthClient(request_context_getter_
));
293 const int kMaxGetUserIdRetries
= 3;
294 gaia_oauth_client_
->GetUserId(access_token
, kMaxGetUserIdRetries
, this);
297 void AccountIdFetcher::OnGetTokenFailure(
298 const OAuth2TokenService::Request
* request
,
299 const GoogleServiceAuthError
& error
) {
300 TRACE_EVENT_ASYNC_STEP_PAST1("identity",
304 "google_service_auth_error",
306 LOG(ERROR
) << "OnGetTokenFailure: " << error
.ToString();
307 DCHECK_EQ(request
, login_token_request_
.get());
308 tracker_
->OnUserInfoFetchFailure(this);
311 void AccountIdFetcher::OnGetUserIdResponse(const std::string
& gaia_id
) {
312 TRACE_EVENT_ASYNC_STEP_PAST1("identity",
315 "OnGetUserIdResponse",
318 tracker_
->OnUserInfoFetchSuccess(this, gaia_id
);
321 void AccountIdFetcher::OnOAuthError() {
322 TRACE_EVENT_ASYNC_STEP_PAST0(
323 "identity", "AccountIdFetcher", this, "OnOAuthError");
324 LOG(ERROR
) << "OnOAuthError";
325 tracker_
->OnUserInfoFetchFailure(this);
328 void AccountIdFetcher::OnNetworkError(int response_code
) {
329 TRACE_EVENT_ASYNC_STEP_PAST1("identity",
335 LOG(ERROR
) << "OnNetworkError " << response_code
;
336 tracker_
->OnUserInfoFetchFailure(this);