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