Handle account removal correctly on all platforms.
[chromium-blink-merge.git] / components / signin / core / browser / account_tracker_service.cc
blobae4ae9b313c0bb44231543f6e4ce48f63e9a3d8c
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"
19 namespace {
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 {
29 public:
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_; }
38 void Start();
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;
53 private:
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),
71 service_(service),
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",
108 "AccountIdFetcher",
109 this,
110 "OnGetTokenFailure",
111 "google_service_auth_error",
112 error.ToString());
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",
121 "AccountIdFetcher",
122 this,
123 "OnGetUserInfoResponse",
124 "account_id",
125 account_id_);
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",
138 "AccountIdFetcher",
139 this,
140 "OnNetworkError",
141 "response_code",
142 response_code);
143 LOG(ERROR) << "OnNetworkError " << response_code;
144 service_->OnUserInfoFetchFailure(this);
148 const char AccountTrackerService::kAccountInfoPref[] = "account_info";
150 AccountTrackerService::AccountTrackerService()
151 : token_service_(NULL),
152 pref_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);
172 LoadFromPrefs();
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 =
199 accounts_.begin();
200 it != accounts_.end();
201 ++it) {
202 const AccountState& state = it->second;
203 accounts.push_back(state.info);
205 return accounts;
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 =
220 accounts_.begin();
221 it != accounts_.end();
222 ++it) {
223 const AccountState& state = it->second;
224 if (state.info.gaia == gaia_id)
225 return state.info;
228 return AccountInfo();
231 AccountTrackerService::AccountInfo
232 AccountTrackerService::FindAccountInfoByEmail(
233 const std::string& email) {
234 for (std::map<std::string, AccountState>::const_iterator it =
235 accounts_.begin();
236 it != accounts_.end();
237 ++it) {
238 const AccountState& state = it->second;
239 if (gaia::AreEmailsSame(state.info.email, email))
240 return state.info;
243 return AccountInfo();
246 AccountTrackerService::AccountIdMigrationState
247 AccountTrackerService::GetMigrationState() {
248 return GetMigrationState(pref_service_);
251 // static
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",
262 "account_id",
263 account_id);
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",
277 "account_id",
278 account_id);
280 DVLOG(1) << "REVOKED " << account_id;
281 StopTrackingAccount(account_id);
284 void AccountTrackerService::NotifyAccountUpdated(const AccountState& state) {
285 DCHECK(!state.info.gaia.empty());
286 FOR_EACH_OBSERVER(
287 Observer, observer_list_, OnAccountUpdated(state.info));
290 void AccountTrackerService::NotifyAccountRemoved(const AccountState& state) {
291 DCHECK(!state.info.gaia.empty());
292 FOR_EACH_OBSERVER(
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;
300 AccountState state;
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(),
330 this,
331 account_id);
332 user_info_requests_[account_id] = fetcher;
333 fetcher->Start();
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];
343 std::string gaia_id;
344 std::string email;
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);
351 SaveToPrefs(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);
369 delete fetcher;
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) {
396 if (!pref_service_)
397 return;
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)
406 break;
410 if (!dict) {
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) {
421 if (!pref_service_)
422 return;
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);
432 break;
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);