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 "components/signin/core/browser/account_tracker_service.h"
7 #include "base/debug/trace_event.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/prefs/scoped_user_pref_update.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "components/signin/core/browser/signin_manager.h"
12 #include "google_apis/gaia/gaia_auth_util.h"
13 #include "google_apis/gaia/gaia_constants.h"
14 #include "google_apis/gaia/gaia_oauth_client.h"
15 #include "google_apis/gaia/oauth2_token_service.h"
16 #include "net/url_request/url_request_context_getter.h"
20 const char kAccountKeyPath
[] = "account_id";
21 const char kAccountEmailPath
[] = "email";
22 const char kAccountGaiaPath
[] = "gaia";
26 class AccountInfoFetcher
: public OAuth2TokenService::Consumer
,
27 public gaia::GaiaOAuthClient::Delegate
{
29 AccountInfoFetcher(OAuth2TokenService
* token_service
,
30 net::URLRequestContextGetter
* request_context_getter
,
31 AccountTrackerService
* service
,
32 const std::string
& account_id
);
33 virtual ~AccountInfoFetcher();
35 const std::string
& account_id() { return account_id_
; }
39 // OAuth2TokenService::Consumer implementation.
40 virtual void OnGetTokenSuccess(const OAuth2TokenService::Request
* request
,
41 const std::string
& access_token
,
42 const base::Time
& expiration_time
) OVERRIDE
;
43 virtual void OnGetTokenFailure(const OAuth2TokenService::Request
* request
,
44 const GoogleServiceAuthError
& error
) OVERRIDE
;
46 // gaia::GaiaOAuthClient::Delegate implementation.
47 virtual void OnGetUserInfoResponse(
48 scoped_ptr
<base::DictionaryValue
> user_info
) OVERRIDE
;
49 virtual void OnOAuthError() OVERRIDE
;
50 virtual void OnNetworkError(int response_code
) OVERRIDE
;
53 OAuth2TokenService
* token_service_
;
54 net::URLRequestContextGetter
* request_context_getter_
;
55 AccountTrackerService
* service_
;
56 const std::string account_id_
;
58 scoped_ptr
<OAuth2TokenService::Request
> login_token_request_
;
59 scoped_ptr
<gaia::GaiaOAuthClient
> gaia_oauth_client_
;
62 AccountInfoFetcher::AccountInfoFetcher(
63 OAuth2TokenService
* token_service
,
64 net::URLRequestContextGetter
* request_context_getter
,
65 AccountTrackerService
* service
,
66 const std::string
& account_id
)
67 : OAuth2TokenService::Consumer("gaia_account_tracker"),
68 token_service_(token_service
),
69 request_context_getter_(request_context_getter
),
71 account_id_(account_id
) {
72 TRACE_EVENT_ASYNC_BEGIN1(
73 "AccountTrackerService", "AccountIdFetcher", this,
74 "account_id", account_id
);
77 AccountInfoFetcher::~AccountInfoFetcher() {
78 TRACE_EVENT_ASYNC_END0("AccountTrackerService", "AccountIdFetcher", this);
81 void AccountInfoFetcher::Start() {
82 OAuth2TokenService::ScopeSet scopes
;
83 scopes
.insert(GaiaConstants::kGoogleUserInfoEmail
);
84 scopes
.insert(GaiaConstants::kGoogleUserInfoProfile
);
85 login_token_request_
= token_service_
->StartRequest(
86 account_id_
, scopes
, this);
89 void AccountInfoFetcher::OnGetTokenSuccess(
90 const OAuth2TokenService::Request
* request
,
91 const std::string
& access_token
,
92 const base::Time
& expiration_time
) {
93 TRACE_EVENT_ASYNC_STEP_PAST0(
94 "AccountTrackerService", "AccountIdFetcher", this, "OnGetTokenSuccess");
95 DCHECK_EQ(request
, login_token_request_
.get());
97 gaia_oauth_client_
.reset(new gaia::GaiaOAuthClient(request_context_getter_
));
99 const int kMaxRetries
= 3;
100 gaia_oauth_client_
->GetUserInfo(access_token
, kMaxRetries
, this);
103 void AccountInfoFetcher::OnGetTokenFailure(
104 const OAuth2TokenService::Request
* request
,
105 const GoogleServiceAuthError
& error
) {
106 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService",
110 "google_service_auth_error",
112 LOG(ERROR
) << "OnGetTokenFailure: " << error
.ToString();
113 DCHECK_EQ(request
, login_token_request_
.get());
114 service_
->OnUserInfoFetchFailure(this);
117 void AccountInfoFetcher::OnGetUserInfoResponse(
118 scoped_ptr
<base::DictionaryValue
> user_info
) {
119 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService",
122 "OnGetUserInfoResponse",
125 service_
->OnUserInfoFetchSuccess(this, user_info
.get());
128 void AccountInfoFetcher::OnOAuthError() {
129 TRACE_EVENT_ASYNC_STEP_PAST0(
130 "AccountTrackerService", "AccountIdFetcher", this, "OnOAuthError");
131 LOG(ERROR
) << "OnOAuthError";
132 service_
->OnUserInfoFetchFailure(this);
135 void AccountInfoFetcher::OnNetworkError(int response_code
) {
136 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService",
142 LOG(ERROR
) << "OnNetworkError " << response_code
;
143 service_
->OnUserInfoFetchFailure(this);
147 const char AccountTrackerService::kAccountInfoPref
[] = "account_info";
149 AccountTrackerService::AccountTrackerService()
150 : token_service_(NULL
),
152 shutdown_called_(false) {
155 AccountTrackerService::~AccountTrackerService() {
156 DCHECK(shutdown_called_
);
159 void AccountTrackerService::Initialize(
160 OAuth2TokenService
* token_service
,
161 PrefService
* pref_service
,
162 net::URLRequestContextGetter
* request_context_getter
) {
163 DCHECK(token_service
);
164 DCHECK(!token_service_
);
165 DCHECK(pref_service
);
166 DCHECK(!pref_service_
);
167 token_service_
= token_service
;
168 pref_service_
= pref_service
;
169 request_context_getter_
= request_context_getter
;
170 token_service_
->AddObserver(this);
172 LoadFromTokenService();
175 void AccountTrackerService::Shutdown() {
176 shutdown_called_
= true;
177 STLDeleteValues(&user_info_requests_
);
178 token_service_
->RemoveObserver(this);
181 void AccountTrackerService::AddObserver(Observer
* observer
) {
182 observer_list_
.AddObserver(observer
);
185 void AccountTrackerService::RemoveObserver(Observer
* observer
) {
186 observer_list_
.RemoveObserver(observer
);
189 bool AccountTrackerService::IsAllUserInfoFetched() const {
190 return user_info_requests_
.empty();
193 std::vector
<AccountTrackerService::AccountInfo
>
194 AccountTrackerService::GetAccounts() const {
195 std::vector
<AccountInfo
> accounts
;
197 for (std::map
<std::string
, AccountState
>::const_iterator it
=
199 it
!= accounts_
.end();
201 const AccountState
& state
= it
->second
;
202 accounts
.push_back(state
.info
);
207 AccountTrackerService::AccountInfo
AccountTrackerService::GetAccountInfo(
208 const std::string
& account_id
) {
209 if (ContainsKey(accounts_
, account_id
))
210 return accounts_
[account_id
].info
;
212 return AccountInfo();
215 AccountTrackerService::AccountInfo
216 AccountTrackerService::FindAccountInfoByGaiaId(
217 const std::string
& gaia_id
) {
218 for (std::map
<std::string
, AccountState
>::const_iterator it
=
220 it
!= accounts_
.end();
222 const AccountState
& state
= it
->second
;
223 if (state
.info
.gaia
== gaia_id
)
227 return AccountInfo();
230 AccountTrackerService::AccountInfo
231 AccountTrackerService::FindAccountInfoByEmail(
232 const std::string
& email
) {
233 for (std::map
<std::string
, AccountState
>::const_iterator it
=
235 it
!= accounts_
.end();
237 const AccountState
& state
= it
->second
;
238 if (gaia::AreEmailsSame(state
.info
.email
, email
))
242 return AccountInfo();
245 void AccountTrackerService::OnRefreshTokenAvailable(
246 const std::string
& account_id
) {
247 TRACE_EVENT1("AccountTrackerService",
248 "AccountTracker::OnRefreshTokenAvailable",
251 DVLOG(1) << "AVAILABLE " << account_id
;
253 StartTrackingAccount(account_id
);
254 AccountState
& state
= accounts_
[account_id
];
256 if (state
.info
.gaia
.empty())
257 StartFetchingUserInfo(account_id
);
260 void AccountTrackerService::OnRefreshTokenRevoked(
261 const std::string
& account_id
) {
262 TRACE_EVENT1("AccountTrackerService",
263 "AccountTracker::OnRefreshTokenRevoked",
267 DVLOG(1) << "REVOKED " << account_id
;
268 StopTrackingAccount(account_id
);
271 void AccountTrackerService::NotifyAccountUpdated(const AccountState
& state
) {
272 DCHECK(!state
.info
.gaia
.empty());
274 Observer
, observer_list_
, OnAccountUpdated(state
.info
));
277 void AccountTrackerService::NotifyAccountRemoved(const AccountState
& state
) {
278 DCHECK(!state
.info
.gaia
.empty());
280 Observer
, observer_list_
, OnAccountRemoved(state
.info
));
283 void AccountTrackerService::StartTrackingAccount(
284 const std::string
& account_id
) {
285 if (!ContainsKey(accounts_
, account_id
)) {
286 DVLOG(1) << "StartTracking " << account_id
;
288 state
.info
.account_id
= account_id
;
289 accounts_
.insert(make_pair(account_id
, state
));
293 void AccountTrackerService::StopTrackingAccount(const std::string
& account_id
) {
294 DVLOG(1) << "StopTracking " << account_id
;
295 if (ContainsKey(accounts_
, account_id
)) {
296 AccountState
& state
= accounts_
[account_id
];
297 RemoveFromPrefs(state
);
298 if (!state
.info
.gaia
.empty())
299 NotifyAccountRemoved(state
);
301 accounts_
.erase(account_id
);
304 if (ContainsKey(user_info_requests_
, account_id
))
305 DeleteFetcher(user_info_requests_
[account_id
]);
308 void AccountTrackerService::StartFetchingUserInfo(
309 const std::string
& account_id
) {
310 if (ContainsKey(user_info_requests_
, account_id
))
311 DeleteFetcher(user_info_requests_
[account_id
]);
313 DVLOG(1) << "StartFetching " << account_id
;
314 AccountInfoFetcher
* fetcher
=
315 new AccountInfoFetcher(token_service_
,
316 request_context_getter_
.get(),
319 user_info_requests_
[account_id
] = fetcher
;
323 void AccountTrackerService::OnUserInfoFetchSuccess(
324 AccountInfoFetcher
* fetcher
,
325 const base::DictionaryValue
* user_info
) {
326 const std::string
& account_id
= fetcher
->account_id();
327 DCHECK(ContainsKey(accounts_
, account_id
));
328 AccountState
& state
= accounts_
[account_id
];
332 if (user_info
->GetString("id", &gaia_id
) &&
333 user_info
->GetString("email", &email
)) {
334 state
.info
.gaia
= gaia_id
;
335 state
.info
.email
= email
;
337 NotifyAccountUpdated(state
);
340 DeleteFetcher(fetcher
);
343 void AccountTrackerService::OnUserInfoFetchFailure(
344 AccountInfoFetcher
* fetcher
) {
345 LOG(WARNING
) << "Failed to get UserInfo for " << fetcher
->account_id();
346 DeleteFetcher(fetcher
);
347 // TODO(rogerta): figure out when to retry.
350 void AccountTrackerService::DeleteFetcher(AccountInfoFetcher
* fetcher
) {
351 DVLOG(1) << "DeleteFetcher " << fetcher
->account_id();
352 const std::string
& account_id
= fetcher
->account_id();
353 DCHECK(ContainsKey(user_info_requests_
, account_id
));
354 DCHECK_EQ(fetcher
, user_info_requests_
[account_id
]);
355 user_info_requests_
.erase(account_id
);
359 void AccountTrackerService::LoadFromPrefs() {
360 const base::ListValue
* list
= pref_service_
->GetList(kAccountInfoPref
);
361 for (size_t i
= 0; i
< list
->GetSize(); ++i
) {
362 const base::DictionaryValue
* dict
;
363 if (list
->GetDictionary(i
, &dict
)) {
364 base::string16 value
;
365 if (dict
->GetString(kAccountKeyPath
, &value
)) {
366 std::string account_id
= base::UTF16ToUTF8(value
);
367 StartTrackingAccount(account_id
);
368 AccountState
& state
= accounts_
[account_id
];
370 if (dict
->GetString(kAccountGaiaPath
, &value
))
371 state
.info
.gaia
= base::UTF16ToUTF8(value
);
372 if (dict
->GetString(kAccountEmailPath
, &value
))
373 state
.info
.email
= base::UTF16ToUTF8(value
);
375 if (!state
.info
.gaia
.empty())
376 NotifyAccountUpdated(state
);
382 void AccountTrackerService::SaveToPrefs(const AccountState
& state
) {
386 base::DictionaryValue
* dict
= NULL
;
387 base::string16 account_id_16
= base::UTF8ToUTF16(state
.info
.account_id
);
388 ListPrefUpdate
update(pref_service_
, kAccountInfoPref
);
389 for(size_t i
= 0; i
< update
->GetSize(); ++i
, dict
= NULL
) {
390 if (update
->GetDictionary(i
, &dict
)) {
391 base::string16 value
;
392 if (dict
->GetString(kAccountKeyPath
, &value
) && value
== account_id_16
)
398 dict
= new base::DictionaryValue();
399 update
->Append(dict
); // |update| takes ownership.
400 dict
->SetString(kAccountKeyPath
, account_id_16
);
403 dict
->SetString(kAccountEmailPath
, state
.info
.email
);
404 dict
->SetString(kAccountGaiaPath
, state
.info
.gaia
);
407 void AccountTrackerService::RemoveFromPrefs(const AccountState
& state
) {
411 base::string16 account_id_16
= base::UTF8ToUTF16(state
.info
.account_id
);
412 ListPrefUpdate
update(pref_service_
, kAccountInfoPref
);
413 for(size_t i
= 0; i
< update
->GetSize(); ++i
) {
414 base::DictionaryValue
* dict
= NULL
;
415 if (update
->GetDictionary(i
, &dict
)) {
416 base::string16 value
;
417 if (dict
->GetString(kAccountKeyPath
, &value
) && value
== account_id_16
) {
418 update
->Remove(i
, NULL
);
425 void AccountTrackerService::LoadFromTokenService() {
426 std::vector
<std::string
> accounts
= token_service_
->GetAccounts();
427 for (std::vector
<std::string
>::const_iterator it
= accounts
.begin();
428 it
!= accounts
.end(); ++it
) {
429 OnRefreshTokenAvailable(*it
);