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/profiler/scoped_tracker.h"
9 #include "base/stl_util.h"
10 #include "base/trace_event/trace_event.h"
11 #include "net/url_request/url_request_context_getter.h"
15 AccountTracker::AccountTracker(
16 IdentityProvider
* identity_provider
,
17 net::URLRequestContextGetter
* request_context_getter
)
18 : identity_provider_(identity_provider
),
19 request_context_getter_(request_context_getter
),
20 shutdown_called_(false) {
21 identity_provider_
->AddObserver(this);
22 identity_provider_
->GetTokenService()->AddObserver(this);
25 AccountTracker::~AccountTracker() {
26 DCHECK(shutdown_called_
);
29 void AccountTracker::Shutdown() {
30 shutdown_called_
= true;
31 STLDeleteValues(&user_info_requests_
);
32 identity_provider_
->GetTokenService()->RemoveObserver(this);
33 identity_provider_
->RemoveObserver(this);
36 bool AccountTracker::IsAllUserInfoFetched() const {
37 return user_info_requests_
.empty();
40 void AccountTracker::AddObserver(Observer
* observer
) {
41 observer_list_
.AddObserver(observer
);
44 void AccountTracker::RemoveObserver(Observer
* observer
) {
45 observer_list_
.RemoveObserver(observer
);
48 std::vector
<AccountIds
> AccountTracker::GetAccounts() const {
49 const std::string active_account_id
=
50 identity_provider_
->GetActiveAccountId();
51 std::vector
<AccountIds
> accounts
;
53 for (std::map
<std::string
, AccountState
>::const_iterator it
=
55 it
!= accounts_
.end();
57 const AccountState
& state
= it
->second
;
58 bool is_visible
= state
.is_signed_in
&& !state
.ids
.gaia
.empty();
60 if (it
->first
== active_account_id
) {
62 accounts
.insert(accounts
.begin(), state
.ids
);
64 return std::vector
<AccountIds
>();
66 } else if (is_visible
) {
67 accounts
.push_back(state
.ids
);
73 AccountIds
AccountTracker::FindAccountIdsByGaiaId(const std::string
& gaia_id
) {
74 for (std::map
<std::string
, AccountState
>::const_iterator it
=
76 it
!= accounts_
.end();
78 const AccountState
& state
= it
->second
;
79 if (state
.ids
.gaia
== gaia_id
) {
87 void AccountTracker::OnRefreshTokenAvailable(const std::string
& account_id
) {
88 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
90 tracked_objects::ScopedTracker
tracking_profile(
91 FROM_HERE_WITH_EXPLICIT_FUNCTION(
92 "422460 AccountTracker::OnRefreshTokenAvailable"));
94 TRACE_EVENT1("identity",
95 "AccountTracker::OnRefreshTokenAvailable",
99 // Ignore refresh tokens if there is no active account ID at all.
100 if (identity_provider_
->GetActiveAccountId().empty())
103 DVLOG(1) << "AVAILABLE " << account_id
;
104 UpdateSignInState(account_id
, true);
107 void AccountTracker::OnRefreshTokenRevoked(const std::string
& account_id
) {
108 TRACE_EVENT1("identity",
109 "AccountTracker::OnRefreshTokenRevoked",
113 DVLOG(1) << "REVOKED " << account_id
;
114 UpdateSignInState(account_id
, false);
117 void AccountTracker::OnActiveAccountLogin() {
118 TRACE_EVENT0("identity", "AccountTracker::OnActiveAccountLogin");
120 std::vector
<std::string
> accounts
=
121 identity_provider_
->GetTokenService()->GetAccounts();
123 DVLOG(1) << "LOGIN " << accounts
.size() << " accounts available.";
125 for (std::vector
<std::string
>::const_iterator it
= accounts
.begin();
126 it
!= accounts
.end();
128 OnRefreshTokenAvailable(*it
);
132 void AccountTracker::OnActiveAccountLogout() {
133 TRACE_EVENT0("identity", "AccountTracker::OnActiveAccountLogout");
134 DVLOG(1) << "LOGOUT";
135 StopTrackingAllAccounts();
138 void AccountTracker::SetAccountStateForTest(AccountIds ids
, bool is_signed_in
) {
139 accounts_
[ids
.account_key
].ids
= ids
;
140 accounts_
[ids
.account_key
].is_signed_in
= is_signed_in
;
142 DVLOG(1) << "SetAccountStateForTest " << ids
.account_key
<< ":"
146 for (std::map
<std::string
, AccountState
>::const_iterator it
=
148 it
!= accounts_
.end();
150 DVLOG(1) << it
->first
<< ":" << it
->second
.is_signed_in
;
155 void AccountTracker::NotifyAccountAdded(const AccountState
& account
) {
156 DCHECK(!account
.ids
.gaia
.empty());
158 Observer
, observer_list_
, OnAccountAdded(account
.ids
));
161 void AccountTracker::NotifyAccountRemoved(const AccountState
& account
) {
162 DCHECK(!account
.ids
.gaia
.empty());
164 Observer
, observer_list_
, OnAccountRemoved(account
.ids
));
167 void AccountTracker::NotifySignInChanged(const AccountState
& account
) {
168 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
170 tracked_objects::ScopedTracker
tracking_profile(
171 FROM_HERE_WITH_EXPLICIT_FUNCTION(
172 "422460 AccountTracker::NotifySignInChanged"));
174 DCHECK(!account
.ids
.gaia
.empty());
175 FOR_EACH_OBSERVER(Observer
,
177 OnAccountSignInChanged(account
.ids
, account
.is_signed_in
));
180 void AccountTracker::UpdateSignInState(const std::string account_key
,
182 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
184 tracked_objects::ScopedTracker
tracking_profile(
185 FROM_HERE_WITH_EXPLICIT_FUNCTION(
186 "422460 AccountTracker::UpdateSignInState"));
188 StartTrackingAccount(account_key
);
189 AccountState
& account
= accounts_
[account_key
];
190 bool needs_gaia_id
= account
.ids
.gaia
.empty();
191 bool was_signed_in
= account
.is_signed_in
;
192 account
.is_signed_in
= is_signed_in
;
194 if (needs_gaia_id
&& is_signed_in
)
195 StartFetchingUserInfo(account_key
);
197 if (!needs_gaia_id
&& (was_signed_in
!= is_signed_in
))
198 NotifySignInChanged(account
);
201 void AccountTracker::StartTrackingAccount(const std::string account_key
) {
202 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
204 tracked_objects::ScopedTracker
tracking_profile(
205 FROM_HERE_WITH_EXPLICIT_FUNCTION(
206 "422460 AccountTracker::StartTrackingAccount"));
208 if (!ContainsKey(accounts_
, account_key
)) {
209 DVLOG(1) << "StartTracking " << account_key
;
210 AccountState account_state
;
211 account_state
.ids
.account_key
= account_key
;
212 account_state
.ids
.email
= account_key
;
213 account_state
.is_signed_in
= false;
214 accounts_
.insert(make_pair(account_key
, account_state
));
218 void AccountTracker::StopTrackingAccount(const std::string account_key
) {
219 DVLOG(1) << "StopTracking " << account_key
;
220 if (ContainsKey(accounts_
, account_key
)) {
221 AccountState
& account
= accounts_
[account_key
];
222 if (!account
.ids
.gaia
.empty()) {
223 UpdateSignInState(account_key
, false);
224 NotifyAccountRemoved(account
);
226 accounts_
.erase(account_key
);
229 if (ContainsKey(user_info_requests_
, account_key
))
230 DeleteFetcher(user_info_requests_
[account_key
]);
233 void AccountTracker::StopTrackingAllAccounts() {
234 while (!accounts_
.empty())
235 StopTrackingAccount(accounts_
.begin()->first
);
238 void AccountTracker::StartFetchingUserInfo(const std::string account_key
) {
239 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
241 tracked_objects::ScopedTracker
tracking_profile(
242 FROM_HERE_WITH_EXPLICIT_FUNCTION(
243 "422460 AccountTracker::StartFetchingUserInfo"));
245 if (ContainsKey(user_info_requests_
, account_key
)) {
246 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
248 tracked_objects::ScopedTracker
tracking_profile1(
249 FROM_HERE_WITH_EXPLICIT_FUNCTION(
250 "422460 AccountTracker::StartFetchingUserInfo 1"));
252 DeleteFetcher(user_info_requests_
[account_key
]);
255 DVLOG(1) << "StartFetching " << account_key
;
256 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
258 tracked_objects::ScopedTracker
tracking_profile2(
259 FROM_HERE_WITH_EXPLICIT_FUNCTION(
260 "422460 AccountTracker::StartFetchingUserInfo 2"));
262 AccountIdFetcher
* fetcher
=
263 new AccountIdFetcher(identity_provider_
->GetTokenService(),
264 request_context_getter_
.get(),
267 user_info_requests_
[account_key
] = fetcher
;
269 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
271 tracked_objects::ScopedTracker
tracking_profile3(
272 FROM_HERE_WITH_EXPLICIT_FUNCTION(
273 "422460 AccountTracker::StartFetchingUserInfo 3"));
278 void AccountTracker::OnUserInfoFetchSuccess(AccountIdFetcher
* fetcher
,
279 const std::string
& gaia_id
) {
280 const std::string
& account_key
= fetcher
->account_key();
281 DCHECK(ContainsKey(accounts_
, account_key
));
282 AccountState
& account
= accounts_
[account_key
];
284 account
.ids
.gaia
= gaia_id
;
285 NotifyAccountAdded(account
);
287 if (account
.is_signed_in
)
288 NotifySignInChanged(account
);
290 DeleteFetcher(fetcher
);
293 void AccountTracker::OnUserInfoFetchFailure(AccountIdFetcher
* fetcher
) {
294 LOG(WARNING
) << "Failed to get UserInfo for " << fetcher
->account_key();
295 std::string key
= fetcher
->account_key();
296 DeleteFetcher(fetcher
);
297 StopTrackingAccount(key
);
300 void AccountTracker::DeleteFetcher(AccountIdFetcher
* fetcher
) {
301 DVLOG(1) << "DeleteFetcher " << fetcher
->account_key();
302 const std::string
& account_key
= fetcher
->account_key();
303 DCHECK(ContainsKey(user_info_requests_
, account_key
));
304 DCHECK_EQ(fetcher
, user_info_requests_
[account_key
]);
305 user_info_requests_
.erase(account_key
);
309 AccountIdFetcher::AccountIdFetcher(
310 OAuth2TokenService
* token_service
,
311 net::URLRequestContextGetter
* request_context_getter
,
312 AccountTracker
* tracker
,
313 const std::string
& account_key
)
314 : OAuth2TokenService::Consumer("gaia_account_tracker"),
315 token_service_(token_service
),
316 request_context_getter_(request_context_getter
),
318 account_key_(account_key
) {
319 TRACE_EVENT_ASYNC_BEGIN1(
320 "identity", "AccountIdFetcher", this, "account_key", account_key
);
323 AccountIdFetcher::~AccountIdFetcher() {
324 TRACE_EVENT_ASYNC_END0("identity", "AccountIdFetcher", this);
327 void AccountIdFetcher::Start() {
328 OAuth2TokenService::ScopeSet scopes
;
329 scopes
.insert("https://www.googleapis.com/auth/userinfo.profile");
330 login_token_request_
= token_service_
->StartRequest(
331 account_key_
, scopes
, this);
334 void AccountIdFetcher::OnGetTokenSuccess(
335 const OAuth2TokenService::Request
* request
,
336 const std::string
& access_token
,
337 const base::Time
& expiration_time
) {
338 TRACE_EVENT_ASYNC_STEP_PAST0(
339 "identity", "AccountIdFetcher", this, "OnGetTokenSuccess");
340 DCHECK_EQ(request
, login_token_request_
.get());
342 gaia_oauth_client_
.reset(new gaia::GaiaOAuthClient(request_context_getter_
));
344 const int kMaxGetUserIdRetries
= 3;
345 gaia_oauth_client_
->GetUserId(access_token
, kMaxGetUserIdRetries
, this);
348 void AccountIdFetcher::OnGetTokenFailure(
349 const OAuth2TokenService::Request
* request
,
350 const GoogleServiceAuthError
& error
) {
351 TRACE_EVENT_ASYNC_STEP_PAST1("identity",
355 "google_service_auth_error",
357 LOG(ERROR
) << "OnGetTokenFailure: " << error
.ToString();
358 DCHECK_EQ(request
, login_token_request_
.get());
359 tracker_
->OnUserInfoFetchFailure(this);
362 void AccountIdFetcher::OnGetUserIdResponse(const std::string
& gaia_id
) {
363 TRACE_EVENT_ASYNC_STEP_PAST1("identity",
366 "OnGetUserIdResponse",
369 tracker_
->OnUserInfoFetchSuccess(this, gaia_id
);
372 void AccountIdFetcher::OnOAuthError() {
373 TRACE_EVENT_ASYNC_STEP_PAST0(
374 "identity", "AccountIdFetcher", this, "OnOAuthError");
375 LOG(ERROR
) << "OnOAuthError";
376 tracker_
->OnUserInfoFetchFailure(this);
379 void AccountIdFetcher::OnNetworkError(int response_code
) {
380 TRACE_EVENT_ASYNC_STEP_PAST1("identity",
386 LOG(ERROR
) << "OnNetworkError " << response_code
;
387 tracker_
->OnUserInfoFetchFailure(this);