account_tracker.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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.
4
5#include "google_apis/gaia/account_tracker.h"
6
7#include "base/logging.h"
8#include "base/stl_util.h"
9#include "net/url_request/url_request_context_getter.h"
10
11namespace gaia {
12
13AccountTracker::AccountTracker(
14    IdentityProvider* identity_provider,
15    net::URLRequestContextGetter* request_context_getter)
16    : identity_provider_(identity_provider),
17      request_context_getter_(request_context_getter),
18      shutdown_called_(false) {
19  identity_provider_->AddObserver(this);
20  identity_provider_->GetTokenService()->AddObserver(this);
21}
22
23AccountTracker::~AccountTracker() {
24  DCHECK(shutdown_called_);
25}
26
27void AccountTracker::Shutdown() {
28  shutdown_called_ = true;
29  STLDeleteValues(&user_info_requests_);
30  identity_provider_->GetTokenService()->RemoveObserver(this);
31  identity_provider_->RemoveObserver(this);
32}
33
34bool AccountTracker::IsAllUserInfoFetched() const {
35  return user_info_requests_.empty();
36}
37
38void AccountTracker::AddObserver(Observer* observer) {
39  observer_list_.AddObserver(observer);
40}
41
42void AccountTracker::RemoveObserver(Observer* observer) {
43  observer_list_.RemoveObserver(observer);
44}
45
46std::vector<AccountIds> AccountTracker::GetAccounts() const {
47  const std::string active_account_id =
48      identity_provider_->GetActiveAccountId();
49  std::vector<AccountIds> accounts;
50
51  for (std::map<std::string, AccountState>::const_iterator it =
52           accounts_.begin();
53       it != accounts_.end();
54       ++it) {
55    const AccountState& state = it->second;
56    bool is_visible = state.is_signed_in && !state.ids.gaia.empty();
57
58    if (it->first == active_account_id) {
59      if (is_visible)
60        accounts.insert(accounts.begin(), state.ids);
61      else
62        return std::vector<AccountIds>();
63
64    } else if (is_visible) {
65      accounts.push_back(state.ids);
66    }
67  }
68  return accounts;
69}
70
71AccountIds AccountTracker::FindAccountIdsByGaiaId(const std::string& gaia_id) {
72  for (std::map<std::string, AccountState>::const_iterator it =
73           accounts_.begin();
74       it != accounts_.end();
75       ++it) {
76    const AccountState& state = it->second;
77    if (state.ids.gaia == gaia_id) {
78      return state.ids;
79    }
80  }
81
82  return AccountIds();
83}
84
85void AccountTracker::OnRefreshTokenAvailable(const std::string& account_id) {
86  // Ignore refresh tokens if there is no active account ID at all.
87  if (identity_provider_->GetActiveAccountId().empty())
88    return;
89
90  DVLOG(1) << "AVAILABLE " << account_id;
91  UpdateSignInState(account_id, true);
92}
93
94void AccountTracker::OnRefreshTokenRevoked(const std::string& account_id) {
95  DVLOG(1) << "REVOKED " << account_id;
96  UpdateSignInState(account_id, false);
97}
98
99void AccountTracker::OnActiveAccountLogin() {
100  std::vector<std::string> accounts =
101      identity_provider_->GetTokenService()->GetAccounts();
102
103  DVLOG(1) << "LOGIN " << accounts.size() << " accounts available.";
104
105  for (std::vector<std::string>::const_iterator it = accounts.begin();
106       it != accounts.end();
107       ++it) {
108    OnRefreshTokenAvailable(*it);
109  }
110}
111
112void AccountTracker::OnActiveAccountLogout() {
113  DVLOG(1) << "LOGOUT";
114  StopTrackingAllAccounts();
115}
116
117void AccountTracker::SetAccountStateForTest(AccountIds ids, bool is_signed_in) {
118  accounts_[ids.account_key].ids = ids;
119  accounts_[ids.account_key].is_signed_in = is_signed_in;
120
121  DVLOG(1) << "SetAccountStateForTest " << ids.account_key << ":"
122           << is_signed_in;
123
124  if (VLOG_IS_ON(1)) {
125    for (std::map<std::string, AccountState>::const_iterator it =
126             accounts_.begin();
127         it != accounts_.end();
128         ++it) {
129      DVLOG(1) << it->first << ":" << it->second.is_signed_in;
130    }
131  }
132}
133
134void AccountTracker::NotifyAccountAdded(const AccountState& account) {
135  DCHECK(!account.ids.gaia.empty());
136  FOR_EACH_OBSERVER(
137      Observer, observer_list_, OnAccountAdded(account.ids));
138}
139
140void AccountTracker::NotifyAccountRemoved(const AccountState& account) {
141  DCHECK(!account.ids.gaia.empty());
142  FOR_EACH_OBSERVER(
143      Observer, observer_list_, OnAccountRemoved(account.ids));
144}
145
146void AccountTracker::NotifySignInChanged(const AccountState& account) {
147  DCHECK(!account.ids.gaia.empty());
148  FOR_EACH_OBSERVER(Observer,
149                    observer_list_,
150                    OnAccountSignInChanged(account.ids, account.is_signed_in));
151}
152
153void AccountTracker::UpdateSignInState(const std::string account_key,
154                                       bool is_signed_in) {
155  StartTrackingAccount(account_key);
156  AccountState& account = accounts_[account_key];
157  bool needs_gaia_id = account.ids.gaia.empty();
158  bool was_signed_in = account.is_signed_in;
159  account.is_signed_in = is_signed_in;
160
161  if (needs_gaia_id && is_signed_in)
162    StartFetchingUserInfo(account_key);
163
164  if (!needs_gaia_id && (was_signed_in != is_signed_in))
165    NotifySignInChanged(account);
166}
167
168void AccountTracker::StartTrackingAccount(const std::string account_key) {
169  if (!ContainsKey(accounts_, account_key)) {
170    DVLOG(1) << "StartTracking " << account_key;
171    AccountState account_state;
172    account_state.ids.account_key = account_key;
173    account_state.ids.email = account_key;
174    account_state.is_signed_in = false;
175    accounts_.insert(make_pair(account_key, account_state));
176  }
177}
178
179void AccountTracker::StopTrackingAccount(const std::string account_key) {
180  DVLOG(1) << "StopTracking " << account_key;
181  if (ContainsKey(accounts_, account_key)) {
182    AccountState& account = accounts_[account_key];
183    if (!account.ids.gaia.empty()) {
184      UpdateSignInState(account_key, false);
185      NotifyAccountRemoved(account);
186    }
187    accounts_.erase(account_key);
188  }
189
190  if (ContainsKey(user_info_requests_, account_key))
191    DeleteFetcher(user_info_requests_[account_key]);
192}
193
194void AccountTracker::StopTrackingAllAccounts() {
195  while (!accounts_.empty())
196    StopTrackingAccount(accounts_.begin()->first);
197}
198
199void AccountTracker::StartFetchingUserInfo(const std::string account_key) {
200  if (ContainsKey(user_info_requests_, account_key))
201    DeleteFetcher(user_info_requests_[account_key]);
202
203  DVLOG(1) << "StartFetching " << account_key;
204  AccountIdFetcher* fetcher =
205      new AccountIdFetcher(identity_provider_->GetTokenService(),
206                           request_context_getter_.get(),
207                           this,
208                           account_key);
209  user_info_requests_[account_key] = fetcher;
210  fetcher->Start();
211}
212
213void AccountTracker::OnUserInfoFetchSuccess(AccountIdFetcher* fetcher,
214                                            const std::string& gaia_id) {
215  const std::string& account_key = fetcher->account_key();
216  DCHECK(ContainsKey(accounts_, account_key));
217  AccountState& account = accounts_[account_key];
218
219  account.ids.gaia = gaia_id;
220  NotifyAccountAdded(account);
221
222  if (account.is_signed_in)
223    NotifySignInChanged(account);
224
225  DeleteFetcher(fetcher);
226}
227
228void AccountTracker::OnUserInfoFetchFailure(AccountIdFetcher* fetcher) {
229  LOG(WARNING) << "Failed to get UserInfo for " << fetcher->account_key();
230  std::string key = fetcher->account_key();
231  DeleteFetcher(fetcher);
232  StopTrackingAccount(key);
233}
234
235void AccountTracker::DeleteFetcher(AccountIdFetcher* fetcher) {
236  DVLOG(1) << "DeleteFetcher " << fetcher->account_key();
237  const std::string& account_key = fetcher->account_key();
238  DCHECK(ContainsKey(user_info_requests_, account_key));
239  DCHECK_EQ(fetcher, user_info_requests_[account_key]);
240  user_info_requests_.erase(account_key);
241  delete fetcher;
242}
243
244AccountIdFetcher::AccountIdFetcher(
245    OAuth2TokenService* token_service,
246    net::URLRequestContextGetter* request_context_getter,
247    AccountTracker* tracker,
248    const std::string& account_key)
249    : OAuth2TokenService::Consumer("gaia_account_tracker"),
250      token_service_(token_service),
251      request_context_getter_(request_context_getter),
252      tracker_(tracker),
253      account_key_(account_key) {
254}
255
256AccountIdFetcher::~AccountIdFetcher() {}
257
258void AccountIdFetcher::Start() {
259  login_token_request_ = token_service_->StartRequest(
260      account_key_, OAuth2TokenService::ScopeSet(), this);
261}
262
263void AccountIdFetcher::OnGetTokenSuccess(
264    const OAuth2TokenService::Request* request,
265    const std::string& access_token,
266    const base::Time& expiration_time) {
267  DCHECK_EQ(request, login_token_request_.get());
268
269  gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(request_context_getter_));
270
271  const int kMaxGetUserIdRetries = 3;
272  gaia_oauth_client_->GetUserId(access_token, kMaxGetUserIdRetries, this);
273}
274
275void AccountIdFetcher::OnGetTokenFailure(
276    const OAuth2TokenService::Request* request,
277    const GoogleServiceAuthError& error) {
278  LOG(ERROR) << "OnGetTokenFailure: " << error.ToString();
279  DCHECK_EQ(request, login_token_request_.get());
280  tracker_->OnUserInfoFetchFailure(this);
281}
282
283void AccountIdFetcher::OnGetUserIdResponse(const std::string& gaia_id) {
284  tracker_->OnUserInfoFetchSuccess(this, gaia_id);
285}
286
287void AccountIdFetcher::OnOAuthError() {
288  LOG(ERROR) << "OnOAuthError";
289  tracker_->OnUserInfoFetchFailure(this);
290}
291
292void AccountIdFetcher::OnNetworkError(int response_code) {
293  LOG(ERROR) << "OnNetworkError " << response_code;
294  tracker_->OnUserInfoFetchFailure(this);
295}
296
297}  // namespace gaia
298