Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / components / signin / core / browser / account_tracker_service.cc
blobf3b6eb09f39012aa78aa3d5fea03104b6a38a581
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/logging.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/prefs/scoped_user_pref_update.h"
11 #include "base/profiler/scoped_tracker.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "components/signin/core/browser/signin_manager.h"
14 #include "components/signin/core/common/signin_pref_names.h"
15 #include "google_apis/gaia/gaia_auth_util.h"
16 #include "google_apis/gaia/gaia_constants.h"
17 #include "google_apis/gaia/gaia_oauth_client.h"
18 #include "google_apis/gaia/oauth2_token_service.h"
19 #include "net/url_request/url_request_context_getter.h"
21 namespace {
23 const char kAccountKeyPath[] = "account_id";
24 const char kAccountEmailPath[] = "email";
25 const char kAccountGaiaPath[] = "gaia";
29 class AccountInfoFetcher : public OAuth2TokenService::Consumer,
30 public gaia::GaiaOAuthClient::Delegate {
31 public:
32 AccountInfoFetcher(OAuth2TokenService* token_service,
33 net::URLRequestContextGetter* request_context_getter,
34 AccountTrackerService* service,
35 const std::string& account_id);
36 ~AccountInfoFetcher() override;
38 const std::string& account_id() { return account_id_; }
40 void Start();
42 // OAuth2TokenService::Consumer implementation.
43 void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
44 const std::string& access_token,
45 const base::Time& expiration_time) override;
46 void OnGetTokenFailure(const OAuth2TokenService::Request* request,
47 const GoogleServiceAuthError& error) override;
49 // gaia::GaiaOAuthClient::Delegate implementation.
50 void OnGetUserInfoResponse(
51 scoped_ptr<base::DictionaryValue> user_info) override;
52 void OnOAuthError() override;
53 void OnNetworkError(int response_code) override;
55 private:
56 OAuth2TokenService* token_service_;
57 net::URLRequestContextGetter* request_context_getter_;
58 AccountTrackerService* service_;
59 const std::string account_id_;
61 scoped_ptr<OAuth2TokenService::Request> login_token_request_;
62 scoped_ptr<gaia::GaiaOAuthClient> gaia_oauth_client_;
65 AccountInfoFetcher::AccountInfoFetcher(
66 OAuth2TokenService* token_service,
67 net::URLRequestContextGetter* request_context_getter,
68 AccountTrackerService* service,
69 const std::string& account_id)
70 : OAuth2TokenService::Consumer("gaia_account_tracker"),
71 token_service_(token_service),
72 request_context_getter_(request_context_getter),
73 service_(service),
74 account_id_(account_id) {
75 TRACE_EVENT_ASYNC_BEGIN1(
76 "AccountTrackerService", "AccountIdFetcher", this,
77 "account_id", account_id);
80 AccountInfoFetcher::~AccountInfoFetcher() {
81 TRACE_EVENT_ASYNC_END0("AccountTrackerService", "AccountIdFetcher", this);
84 void AccountInfoFetcher::Start() {
85 OAuth2TokenService::ScopeSet scopes;
86 scopes.insert(GaiaConstants::kGoogleUserInfoEmail);
87 scopes.insert(GaiaConstants::kGoogleUserInfoProfile);
88 login_token_request_ = token_service_->StartRequest(
89 account_id_, scopes, this);
92 void AccountInfoFetcher::OnGetTokenSuccess(
93 const OAuth2TokenService::Request* request,
94 const std::string& access_token,
95 const base::Time& expiration_time) {
96 TRACE_EVENT_ASYNC_STEP_PAST0(
97 "AccountTrackerService", "AccountIdFetcher", this, "OnGetTokenSuccess");
98 DCHECK_EQ(request, login_token_request_.get());
100 gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(request_context_getter_));
102 const int kMaxRetries = 3;
103 gaia_oauth_client_->GetUserInfo(access_token, kMaxRetries, this);
106 void AccountInfoFetcher::OnGetTokenFailure(
107 const OAuth2TokenService::Request* request,
108 const GoogleServiceAuthError& error) {
109 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService",
110 "AccountIdFetcher",
111 this,
112 "OnGetTokenFailure",
113 "google_service_auth_error",
114 error.ToString());
115 LOG(ERROR) << "OnGetTokenFailure: " << error.ToString();
116 DCHECK_EQ(request, login_token_request_.get());
117 service_->OnUserInfoFetchFailure(this);
120 void AccountInfoFetcher::OnGetUserInfoResponse(
121 scoped_ptr<base::DictionaryValue> user_info) {
122 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService",
123 "AccountIdFetcher",
124 this,
125 "OnGetUserInfoResponse",
126 "account_id",
127 account_id_);
128 service_->OnUserInfoFetchSuccess(this, user_info.get());
131 void AccountInfoFetcher::OnOAuthError() {
132 TRACE_EVENT_ASYNC_STEP_PAST0(
133 "AccountTrackerService", "AccountIdFetcher", this, "OnOAuthError");
134 LOG(ERROR) << "OnOAuthError";
135 service_->OnUserInfoFetchFailure(this);
138 void AccountInfoFetcher::OnNetworkError(int response_code) {
139 TRACE_EVENT_ASYNC_STEP_PAST1("AccountTrackerService",
140 "AccountIdFetcher",
141 this,
142 "OnNetworkError",
143 "response_code",
144 response_code);
145 LOG(ERROR) << "OnNetworkError " << response_code;
146 service_->OnUserInfoFetchFailure(this);
150 const char AccountTrackerService::kAccountInfoPref[] = "account_info";
152 AccountTrackerService::AccountTrackerService()
153 : token_service_(NULL),
154 pref_service_(NULL),
155 shutdown_called_(false) {
158 AccountTrackerService::~AccountTrackerService() {
159 DCHECK(shutdown_called_);
162 void AccountTrackerService::Initialize(
163 OAuth2TokenService* token_service,
164 PrefService* pref_service,
165 net::URLRequestContextGetter* request_context_getter) {
166 DCHECK(token_service);
167 DCHECK(!token_service_);
168 DCHECK(pref_service);
169 DCHECK(!pref_service_);
170 token_service_ = token_service;
171 pref_service_ = pref_service;
172 request_context_getter_ = request_context_getter;
173 token_service_->AddObserver(this);
174 LoadFromPrefs();
175 LoadFromTokenService();
178 void AccountTrackerService::Shutdown() {
179 shutdown_called_ = true;
180 STLDeleteValues(&user_info_requests_);
181 token_service_->RemoveObserver(this);
184 void AccountTrackerService::AddObserver(Observer* observer) {
185 observer_list_.AddObserver(observer);
188 void AccountTrackerService::RemoveObserver(Observer* observer) {
189 observer_list_.RemoveObserver(observer);
192 bool AccountTrackerService::IsAllUserInfoFetched() const {
193 return user_info_requests_.empty();
196 std::vector<AccountTrackerService::AccountInfo>
197 AccountTrackerService::GetAccounts() const {
198 std::vector<AccountInfo> accounts;
200 for (std::map<std::string, AccountState>::const_iterator it =
201 accounts_.begin();
202 it != accounts_.end();
203 ++it) {
204 const AccountState& state = it->second;
205 accounts.push_back(state.info);
207 return accounts;
210 AccountTrackerService::AccountInfo AccountTrackerService::GetAccountInfo(
211 const std::string& account_id) {
212 if (ContainsKey(accounts_, account_id))
213 return accounts_[account_id].info;
215 return AccountInfo();
218 AccountTrackerService::AccountInfo
219 AccountTrackerService::FindAccountInfoByGaiaId(
220 const std::string& gaia_id) {
221 for (std::map<std::string, AccountState>::const_iterator it =
222 accounts_.begin();
223 it != accounts_.end();
224 ++it) {
225 const AccountState& state = it->second;
226 if (state.info.gaia == gaia_id)
227 return state.info;
230 return AccountInfo();
233 AccountTrackerService::AccountInfo
234 AccountTrackerService::FindAccountInfoByEmail(
235 const std::string& email) {
236 for (std::map<std::string, AccountState>::const_iterator it =
237 accounts_.begin();
238 it != accounts_.end();
239 ++it) {
240 const AccountState& state = it->second;
241 if (gaia::AreEmailsSame(state.info.email, email))
242 return state.info;
245 return AccountInfo();
248 AccountTrackerService::AccountIdMigrationState
249 AccountTrackerService::GetMigrationState() {
250 return GetMigrationState(pref_service_);
253 // static
254 AccountTrackerService::AccountIdMigrationState
255 AccountTrackerService::GetMigrationState(PrefService* pref_service) {
256 return static_cast<AccountTrackerService::AccountIdMigrationState>(
257 pref_service->GetInteger(prefs::kAccountIdMigrationState));
260 void AccountTrackerService::OnRefreshTokenAvailable(
261 const std::string& account_id) {
262 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422460 is fixed.
263 tracked_objects::ScopedTracker tracking_profile(
264 FROM_HERE_WITH_EXPLICIT_FUNCTION(
265 "422460 AccountTrackerService::OnRefreshTokenAvailable"));
267 TRACE_EVENT1("AccountTrackerService",
268 "AccountTracker::OnRefreshTokenAvailable",
269 "account_id",
270 account_id);
271 DVLOG(1) << "AVAILABLE " << account_id;
273 StartTrackingAccount(account_id);
274 AccountState& state = accounts_[account_id];
276 if (state.info.gaia.empty())
277 StartFetchingUserInfo(account_id);
280 void AccountTrackerService::OnRefreshTokenRevoked(
281 const std::string& account_id) {
282 TRACE_EVENT1("AccountTrackerService",
283 "AccountTracker::OnRefreshTokenRevoked",
284 "account_id",
285 account_id);
287 DVLOG(1) << "REVOKED " << account_id;
288 StopTrackingAccount(account_id);
291 void AccountTrackerService::NotifyAccountUpdated(const AccountState& state) {
292 DCHECK(!state.info.gaia.empty());
293 FOR_EACH_OBSERVER(
294 Observer, observer_list_, OnAccountUpdated(state.info));
297 void AccountTrackerService::NotifyAccountRemoved(const AccountState& state) {
298 DCHECK(!state.info.gaia.empty());
299 FOR_EACH_OBSERVER(
300 Observer, observer_list_, OnAccountRemoved(state.info));
303 void AccountTrackerService::StartTrackingAccount(
304 const std::string& account_id) {
305 if (!ContainsKey(accounts_, account_id)) {
306 DVLOG(1) << "StartTracking " << account_id;
307 AccountState state;
308 state.info.account_id = account_id;
309 accounts_.insert(make_pair(account_id, state));
313 void AccountTrackerService::StopTrackingAccount(const std::string& account_id) {
314 DVLOG(1) << "StopTracking " << account_id;
315 if (ContainsKey(accounts_, account_id)) {
316 AccountState& state = accounts_[account_id];
317 RemoveFromPrefs(state);
318 if (!state.info.gaia.empty())
319 NotifyAccountRemoved(state);
321 accounts_.erase(account_id);
324 if (ContainsKey(user_info_requests_, account_id))
325 DeleteFetcher(user_info_requests_[account_id]);
328 void AccountTrackerService::StartFetchingUserInfo(
329 const std::string& account_id) {
330 if (ContainsKey(user_info_requests_, account_id))
331 DeleteFetcher(user_info_requests_[account_id]);
333 DVLOG(1) << "StartFetching " << account_id;
334 AccountInfoFetcher* fetcher =
335 new AccountInfoFetcher(token_service_,
336 request_context_getter_.get(),
337 this,
338 account_id);
339 user_info_requests_[account_id] = fetcher;
340 fetcher->Start();
343 void AccountTrackerService::OnUserInfoFetchSuccess(
344 AccountInfoFetcher* fetcher,
345 const base::DictionaryValue* user_info) {
346 const std::string& account_id = fetcher->account_id();
347 DCHECK(ContainsKey(accounts_, account_id));
348 AccountState& state = accounts_[account_id];
350 std::string gaia_id;
351 std::string email;
352 if (user_info->GetString("id", &gaia_id) &&
353 user_info->GetString("email", &email)) {
354 state.info.gaia = gaia_id;
355 state.info.email = email;
357 NotifyAccountUpdated(state);
358 SaveToPrefs(state);
360 DeleteFetcher(fetcher);
363 void AccountTrackerService::OnUserInfoFetchFailure(
364 AccountInfoFetcher* fetcher) {
365 LOG(WARNING) << "Failed to get UserInfo for " << fetcher->account_id();
366 DeleteFetcher(fetcher);
367 // TODO(rogerta): figure out when to retry.
370 void AccountTrackerService::DeleteFetcher(AccountInfoFetcher* fetcher) {
371 DVLOG(1) << "DeleteFetcher " << fetcher->account_id();
372 const std::string& account_id = fetcher->account_id();
373 DCHECK(ContainsKey(user_info_requests_, account_id));
374 DCHECK_EQ(fetcher, user_info_requests_[account_id]);
375 user_info_requests_.erase(account_id);
376 delete fetcher;
379 void AccountTrackerService::LoadFromPrefs() {
380 const base::ListValue* list = pref_service_->GetList(kAccountInfoPref);
381 for (size_t i = 0; i < list->GetSize(); ++i) {
382 const base::DictionaryValue* dict;
383 if (list->GetDictionary(i, &dict)) {
384 base::string16 value;
385 if (dict->GetString(kAccountKeyPath, &value)) {
386 std::string account_id = base::UTF16ToUTF8(value);
387 StartTrackingAccount(account_id);
388 AccountState& state = accounts_[account_id];
390 if (dict->GetString(kAccountGaiaPath, &value))
391 state.info.gaia = base::UTF16ToUTF8(value);
392 if (dict->GetString(kAccountEmailPath, &value))
393 state.info.email = base::UTF16ToUTF8(value);
395 if (!state.info.gaia.empty())
396 NotifyAccountUpdated(state);
402 void AccountTrackerService::SaveToPrefs(const AccountState& state) {
403 if (!pref_service_)
404 return;
406 base::DictionaryValue* dict = NULL;
407 base::string16 account_id_16 = base::UTF8ToUTF16(state.info.account_id);
408 ListPrefUpdate update(pref_service_, kAccountInfoPref);
409 for(size_t i = 0; i < update->GetSize(); ++i, dict = NULL) {
410 if (update->GetDictionary(i, &dict)) {
411 base::string16 value;
412 if (dict->GetString(kAccountKeyPath, &value) && value == account_id_16)
413 break;
417 if (!dict) {
418 dict = new base::DictionaryValue();
419 update->Append(dict); // |update| takes ownership.
420 dict->SetString(kAccountKeyPath, account_id_16);
423 dict->SetString(kAccountEmailPath, state.info.email);
424 dict->SetString(kAccountGaiaPath, state.info.gaia);
427 void AccountTrackerService::RemoveFromPrefs(const AccountState& state) {
428 if (!pref_service_)
429 return;
431 base::string16 account_id_16 = base::UTF8ToUTF16(state.info.account_id);
432 ListPrefUpdate update(pref_service_, kAccountInfoPref);
433 for(size_t i = 0; i < update->GetSize(); ++i) {
434 base::DictionaryValue* dict = NULL;
435 if (update->GetDictionary(i, &dict)) {
436 base::string16 value;
437 if (dict->GetString(kAccountKeyPath, &value) && value == account_id_16) {
438 update->Remove(i, NULL);
439 break;
445 void AccountTrackerService::LoadFromTokenService() {
446 std::vector<std::string> accounts = token_service_->GetAccounts();
447 for (std::vector<std::string>::const_iterator it = accounts.begin();
448 it != accounts.end(); ++it) {
449 OnRefreshTokenAvailable(*it);
453 std::string AccountTrackerService::PickAccountIdForAccount(
454 const std::string& gaia,
455 const std::string& email) {
456 return PickAccountIdForAccount(pref_service_, gaia, email);
459 // static
460 std::string AccountTrackerService::PickAccountIdForAccount(
461 PrefService* pref_service,
462 const std::string& gaia,
463 const std::string& email) {
464 DCHECK(!gaia.empty());
465 DCHECK(!email.empty());
466 switch(GetMigrationState(pref_service)) {
467 case MIGRATION_NOT_STARTED:
468 case MIGRATION_IN_PROGRESS:
469 // Some tests don't use a real email address. To support these cases,
470 // don't try to canonicalize these strings.
471 return (email.find('@') == std::string::npos) ? email :
472 gaia::CanonicalizeEmail(email);
473 case MIGRATION_DONE:
474 return gaia;
475 default:
476 NOTREACHED();
477 return email;
481 void AccountTrackerService::SeedAccountInfo(const std::string& gaia,
482 const std::string& email) {
483 DVLOG(1) << "AccountTrackerService::SeedAccountInfo"
484 << " gaia_id=" << gaia
485 << " email=" << email;
487 DCHECK(!gaia.empty());
488 DCHECK(!email.empty());
489 const std::string account_id = PickAccountIdForAccount(gaia, email);
490 const bool already_exists = ContainsKey(accounts_, account_id);
491 StartTrackingAccount(account_id);
492 AccountState& state = accounts_[account_id];
493 DCHECK(!already_exists || state.info.gaia == gaia);
494 state.info.gaia = gaia;
495 state.info.email = email;
496 SaveToPrefs(state);