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 "components/signin/core/common/signin_pref_names.h"
13 #include "google_apis/gaia/gaia_auth_util.h"
14 #include "google_apis/gaia/gaia_constants.h"
15 #include "google_apis/gaia/gaia_oauth_client.h"
16 #include "google_apis/gaia/oauth2_token_service.h"
17 #include "net/url_request/url_request_context_getter.h"
21 const char kAccountKeyPath
[] = "account_id";
22 const char kAccountEmailPath
[] = "email";
23 const char kAccountGaiaPath
[] = "gaia";
27 class AccountInfoFetcher
: public OAuth2TokenService::Consumer
,
28 public gaia::GaiaOAuthClient::Delegate
{
30 AccountInfoFetcher(OAuth2TokenService
* token_service
,
31 net::URLRequestContextGetter
* request_context_getter
,
32 AccountTrackerService
* service
,
33 const std::string
& account_id
);
34 virtual ~AccountInfoFetcher();
36 const std::string
& account_id() { return account_id_
; }
40 // OAuth2TokenService::Consumer implementation.
41 virtual void OnGetTokenSuccess(const OAuth2TokenService::Request
* request
,
42 const std::string
& access_token
,
43 const base::Time
& expiration_time
) OVERRIDE
;
44 virtual void OnGetTokenFailure(const OAuth2TokenService::Request
* request
,
45 const GoogleServiceAuthError
& error
) OVERRIDE
;
47 // gaia::GaiaOAuthClient::Delegate implementation.
48 virtual void OnGetUserInfoResponse(
49 scoped_ptr
<base::DictionaryValue
> user_info
) OVERRIDE
;
50 virtual void OnOAuthError() OVERRIDE
;
51 virtual void OnNetworkError(int response_code
) OVERRIDE
;
54 OAuth2TokenService
* token_service_
;
55 net::URLRequestContextGetter
* request_context_getter_
;
56 AccountTrackerService
* service_
;
57 const std::string account_id_
;
59 scoped_ptr
<OAuth2TokenService::Request
> login_token_request_
;
60 scoped_ptr
<gaia::GaiaOAuthClient
> gaia_oauth_client_
;
63 AccountInfoFetcher::AccountInfoFetcher(
64 OAuth2TokenService
* token_service
,
65 net::URLRequestContextGetter
* request_context_getter
,
66 AccountTrackerService
* service
,
67 const std::string
& account_id
)
68 : OAuth2TokenService::Consumer("gaia_account_tracker"),
69 token_service_(token_service
),
70 request_context_getter_(request_context_getter
),
72 account_id_(account_id
) {
73 TRACE_EVENT_ASYNC_BEGIN1(
74 "AccountTrackerService", "AccountIdFetcher", this,
75 "account_id", account_id
);
78 AccountInfoFetcher::~AccountInfoFetcher() {
79 TRACE_EVENT_ASYNC_END0("AccountTrackerService", "AccountIdFetcher", this);
82 void AccountInfoFetcher::Start() {
83 OAuth2TokenService::ScopeSet scopes
;
84 scopes
.insert(GaiaConstants::kGoogleUserInfoEmail
);
85 scopes
.insert(GaiaConstants::kGoogleUserInfoProfile
);
86 login_token_request_
= token_service_
->StartRequest(
87 account_id_
, scopes
, this);
90 void AccountInfoFetcher::OnGetTokenSuccess(
91 const OAuth2TokenService::Request
* request
,
92 const std::string
& access_token
,
93 const base::Time
& expiration_time
) {
94 TRACE_EVENT_ASYNC_STEP_PAST0(
95 "AccountTrackerService", "AccountIdFetcher", this, "OnGetTokenSuccess");
96 DCHECK_EQ(request
, login_token_request_
.get());
98 gaia_oauth_client_
.reset(new gaia::GaiaOAuthClient(request_context_getter_
));
100 const int kMaxRetries
= 3;
101 gaia_oauth_client_
->GetUserInfo(access_token
, kMaxRetries
, this);
104 void AccountInfoFetcher::OnGetTokenFailure(
105 const OAuth2TokenService::Request
* request
,
106 const GoogleServiceAuthError
& error
) {
107 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService",
111 "google_service_auth_error",
113 LOG(ERROR
) << "OnGetTokenFailure: " << error
.ToString();
114 DCHECK_EQ(request
, login_token_request_
.get());
115 service_
->OnUserInfoFetchFailure(this);
118 void AccountInfoFetcher::OnGetUserInfoResponse(
119 scoped_ptr
<base::DictionaryValue
> user_info
) {
120 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService",
123 "OnGetUserInfoResponse",
126 service_
->OnUserInfoFetchSuccess(this, user_info
.get());
129 void AccountInfoFetcher::OnOAuthError() {
130 TRACE_EVENT_ASYNC_STEP_PAST0(
131 "AccountTrackerService", "AccountIdFetcher", this, "OnOAuthError");
132 LOG(ERROR
) << "OnOAuthError";
133 service_
->OnUserInfoFetchFailure(this);
136 void AccountInfoFetcher::OnNetworkError(int response_code
) {
137 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService",
143 LOG(ERROR
) << "OnNetworkError " << response_code
;
144 service_
->OnUserInfoFetchFailure(this);
148 const char AccountTrackerService::kAccountInfoPref
[] = "account_info";
150 AccountTrackerService::AccountTrackerService()
151 : token_service_(NULL
),
153 shutdown_called_(false) {
156 AccountTrackerService::~AccountTrackerService() {
157 DCHECK(shutdown_called_
);
160 void AccountTrackerService::Initialize(
161 OAuth2TokenService
* token_service
,
162 PrefService
* pref_service
,
163 net::URLRequestContextGetter
* request_context_getter
) {
164 DCHECK(token_service
);
165 DCHECK(!token_service_
);
166 DCHECK(pref_service
);
167 DCHECK(!pref_service_
);
168 token_service_
= token_service
;
169 pref_service_
= pref_service
;
170 request_context_getter_
= request_context_getter
;
171 token_service_
->AddObserver(this);
173 LoadFromTokenService();
176 void AccountTrackerService::Shutdown() {
177 shutdown_called_
= true;
178 STLDeleteValues(&user_info_requests_
);
179 token_service_
->RemoveObserver(this);
182 void AccountTrackerService::AddObserver(Observer
* observer
) {
183 observer_list_
.AddObserver(observer
);
186 void AccountTrackerService::RemoveObserver(Observer
* observer
) {
187 observer_list_
.RemoveObserver(observer
);
190 bool AccountTrackerService::IsAllUserInfoFetched() const {
191 return user_info_requests_
.empty();
194 std::vector
<AccountTrackerService::AccountInfo
>
195 AccountTrackerService::GetAccounts() const {
196 std::vector
<AccountInfo
> accounts
;
198 for (std::map
<std::string
, AccountState
>::const_iterator it
=
200 it
!= accounts_
.end();
202 const AccountState
& state
= it
->second
;
203 accounts
.push_back(state
.info
);
208 AccountTrackerService::AccountInfo
AccountTrackerService::GetAccountInfo(
209 const std::string
& account_id
) {
210 if (ContainsKey(accounts_
, account_id
))
211 return accounts_
[account_id
].info
;
213 return AccountInfo();
216 AccountTrackerService::AccountInfo
217 AccountTrackerService::FindAccountInfoByGaiaId(
218 const std::string
& gaia_id
) {
219 for (std::map
<std::string
, AccountState
>::const_iterator it
=
221 it
!= accounts_
.end();
223 const AccountState
& state
= it
->second
;
224 if (state
.info
.gaia
== gaia_id
)
228 return AccountInfo();
231 AccountTrackerService::AccountInfo
232 AccountTrackerService::FindAccountInfoByEmail(
233 const std::string
& email
) {
234 for (std::map
<std::string
, AccountState
>::const_iterator it
=
236 it
!= accounts_
.end();
238 const AccountState
& state
= it
->second
;
239 if (gaia::AreEmailsSame(state
.info
.email
, email
))
243 return AccountInfo();
246 AccountTrackerService::AccountIdMigrationState
247 AccountTrackerService::GetMigrationState() {
248 return GetMigrationState(pref_service_
);
252 AccountTrackerService::AccountIdMigrationState
253 AccountTrackerService::GetMigrationState(PrefService
* pref_service
) {
254 return static_cast<AccountTrackerService::AccountIdMigrationState
>(
255 pref_service
->GetInteger(prefs::kAccountIdMigrationState
));
258 void AccountTrackerService::OnRefreshTokenAvailable(
259 const std::string
& account_id
) {
260 TRACE_EVENT1("AccountTrackerService",
261 "AccountTracker::OnRefreshTokenAvailable",
264 DVLOG(1) << "AVAILABLE " << account_id
;
266 StartTrackingAccount(account_id
);
267 AccountState
& state
= accounts_
[account_id
];
269 if (state
.info
.gaia
.empty())
270 StartFetchingUserInfo(account_id
);
273 void AccountTrackerService::OnRefreshTokenRevoked(
274 const std::string
& account_id
) {
275 TRACE_EVENT1("AccountTrackerService",
276 "AccountTracker::OnRefreshTokenRevoked",
280 DVLOG(1) << "REVOKED " << account_id
;
281 StopTrackingAccount(account_id
);
284 void AccountTrackerService::NotifyAccountUpdated(const AccountState
& state
) {
285 DCHECK(!state
.info
.gaia
.empty());
287 Observer
, observer_list_
, OnAccountUpdated(state
.info
));
290 void AccountTrackerService::NotifyAccountRemoved(const AccountState
& state
) {
291 DCHECK(!state
.info
.gaia
.empty());
293 Observer
, observer_list_
, OnAccountRemoved(state
.info
));
296 void AccountTrackerService::StartTrackingAccount(
297 const std::string
& account_id
) {
298 if (!ContainsKey(accounts_
, account_id
)) {
299 DVLOG(1) << "StartTracking " << account_id
;
301 state
.info
.account_id
= account_id
;
302 accounts_
.insert(make_pair(account_id
, state
));
306 void AccountTrackerService::StopTrackingAccount(const std::string
& account_id
) {
307 DVLOG(1) << "StopTracking " << account_id
;
308 if (ContainsKey(accounts_
, account_id
)) {
309 AccountState
& state
= accounts_
[account_id
];
310 RemoveFromPrefs(state
);
311 if (!state
.info
.gaia
.empty())
312 NotifyAccountRemoved(state
);
314 accounts_
.erase(account_id
);
317 if (ContainsKey(user_info_requests_
, account_id
))
318 DeleteFetcher(user_info_requests_
[account_id
]);
321 void AccountTrackerService::StartFetchingUserInfo(
322 const std::string
& account_id
) {
323 if (ContainsKey(user_info_requests_
, account_id
))
324 DeleteFetcher(user_info_requests_
[account_id
]);
326 DVLOG(1) << "StartFetching " << account_id
;
327 AccountInfoFetcher
* fetcher
=
328 new AccountInfoFetcher(token_service_
,
329 request_context_getter_
.get(),
332 user_info_requests_
[account_id
] = fetcher
;
336 void AccountTrackerService::OnUserInfoFetchSuccess(
337 AccountInfoFetcher
* fetcher
,
338 const base::DictionaryValue
* user_info
) {
339 const std::string
& account_id
= fetcher
->account_id();
340 DCHECK(ContainsKey(accounts_
, account_id
));
341 AccountState
& state
= accounts_
[account_id
];
345 if (user_info
->GetString("id", &gaia_id
) &&
346 user_info
->GetString("email", &email
)) {
347 state
.info
.gaia
= gaia_id
;
348 state
.info
.email
= email
;
350 NotifyAccountUpdated(state
);
353 DeleteFetcher(fetcher
);
356 void AccountTrackerService::OnUserInfoFetchFailure(
357 AccountInfoFetcher
* fetcher
) {
358 LOG(WARNING
) << "Failed to get UserInfo for " << fetcher
->account_id();
359 DeleteFetcher(fetcher
);
360 // TODO(rogerta): figure out when to retry.
363 void AccountTrackerService::DeleteFetcher(AccountInfoFetcher
* fetcher
) {
364 DVLOG(1) << "DeleteFetcher " << fetcher
->account_id();
365 const std::string
& account_id
= fetcher
->account_id();
366 DCHECK(ContainsKey(user_info_requests_
, account_id
));
367 DCHECK_EQ(fetcher
, user_info_requests_
[account_id
]);
368 user_info_requests_
.erase(account_id
);
372 void AccountTrackerService::LoadFromPrefs() {
373 const base::ListValue
* list
= pref_service_
->GetList(kAccountInfoPref
);
374 for (size_t i
= 0; i
< list
->GetSize(); ++i
) {
375 const base::DictionaryValue
* dict
;
376 if (list
->GetDictionary(i
, &dict
)) {
377 base::string16 value
;
378 if (dict
->GetString(kAccountKeyPath
, &value
)) {
379 std::string account_id
= base::UTF16ToUTF8(value
);
380 StartTrackingAccount(account_id
);
381 AccountState
& state
= accounts_
[account_id
];
383 if (dict
->GetString(kAccountGaiaPath
, &value
))
384 state
.info
.gaia
= base::UTF16ToUTF8(value
);
385 if (dict
->GetString(kAccountEmailPath
, &value
))
386 state
.info
.email
= base::UTF16ToUTF8(value
);
388 if (!state
.info
.gaia
.empty())
389 NotifyAccountUpdated(state
);
395 void AccountTrackerService::SaveToPrefs(const AccountState
& state
) {
399 base::DictionaryValue
* dict
= NULL
;
400 base::string16 account_id_16
= base::UTF8ToUTF16(state
.info
.account_id
);
401 ListPrefUpdate
update(pref_service_
, kAccountInfoPref
);
402 for(size_t i
= 0; i
< update
->GetSize(); ++i
, dict
= NULL
) {
403 if (update
->GetDictionary(i
, &dict
)) {
404 base::string16 value
;
405 if (dict
->GetString(kAccountKeyPath
, &value
) && value
== account_id_16
)
411 dict
= new base::DictionaryValue();
412 update
->Append(dict
); // |update| takes ownership.
413 dict
->SetString(kAccountKeyPath
, account_id_16
);
416 dict
->SetString(kAccountEmailPath
, state
.info
.email
);
417 dict
->SetString(kAccountGaiaPath
, state
.info
.gaia
);
420 void AccountTrackerService::RemoveFromPrefs(const AccountState
& state
) {
424 base::string16 account_id_16
= base::UTF8ToUTF16(state
.info
.account_id
);
425 ListPrefUpdate
update(pref_service_
, kAccountInfoPref
);
426 for(size_t i
= 0; i
< update
->GetSize(); ++i
) {
427 base::DictionaryValue
* dict
= NULL
;
428 if (update
->GetDictionary(i
, &dict
)) {
429 base::string16 value
;
430 if (dict
->GetString(kAccountKeyPath
, &value
) && value
== account_id_16
) {
431 update
->Remove(i
, NULL
);
438 void AccountTrackerService::LoadFromTokenService() {
439 std::vector
<std::string
> accounts
= token_service_
->GetAccounts();
440 for (std::vector
<std::string
>::const_iterator it
= accounts
.begin();
441 it
!= accounts
.end(); ++it
) {
442 OnRefreshTokenAvailable(*it
);