| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "google_apis/gaia/account_tracker.h" |
| |
| #include "base/logging.h" |
| #include "base/profiler/scoped_tracker.h" |
| #include "base/stl_util.h" |
| #include "base/trace_event/trace_event.h" |
| #include "net/url_request/url_request_context_getter.h" |
| |
| namespace gaia { |
| |
| AccountTracker::AccountTracker( |
| IdentityProvider* identity_provider, |
| net::URLRequestContextGetter* request_context_getter) |
| : identity_provider_(identity_provider), |
| request_context_getter_(request_context_getter), |
| shutdown_called_(false) { |
| identity_provider_->AddObserver(this); |
| identity_provider_->GetTokenService()->AddObserver(this); |
| } |
| |
| AccountTracker::~AccountTracker() { |
| DCHECK(shutdown_called_); |
| } |
| |
| void AccountTracker::Shutdown() { |
| shutdown_called_ = true; |
| STLDeleteValues(&user_info_requests_); |
| identity_provider_->GetTokenService()->RemoveObserver(this); |
| identity_provider_->RemoveObserver(this); |
| } |
| |
| bool AccountTracker::IsAllUserInfoFetched() const { |
| return user_info_requests_.empty(); |
| } |
| |
| void AccountTracker::AddObserver(Observer* observer) { |
| observer_list_.AddObserver(observer); |
| } |
| |
| void AccountTracker::RemoveObserver(Observer* observer) { |
| observer_list_.RemoveObserver(observer); |
| } |
| |
| std::vector<AccountIds> AccountTracker::GetAccounts() const { |
| const std::string active_account_id = |
| identity_provider_->GetActiveAccountId(); |
| std::vector<AccountIds> accounts; |
| |
| for (std::map<std::string, AccountState>::const_iterator it = |
| accounts_.begin(); |
| it != accounts_.end(); |
| ++it) { |
| const AccountState& state = it->second; |
| bool is_visible = state.is_signed_in && !state.ids.gaia.empty(); |
| |
| if (it->first == active_account_id) { |
| if (is_visible) |
| accounts.insert(accounts.begin(), state.ids); |
| else |
| return std::vector<AccountIds>(); |
| |
| } else if (is_visible) { |
| accounts.push_back(state.ids); |
| } |
| } |
| return accounts; |
| } |
| |
| AccountIds AccountTracker::FindAccountIdsByGaiaId(const std::string& gaia_id) { |
| for (std::map<std::string, AccountState>::const_iterator it = |
| accounts_.begin(); |
| it != accounts_.end(); |
| ++it) { |
| const AccountState& state = it->second; |
| if (state.ids.gaia == gaia_id) { |
| return state.ids; |
| } |
| } |
| |
| return AccountIds(); |
| } |
| |
| void AccountTracker::OnRefreshTokenAvailable(const std::string& account_id) { |
| // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is |
| // fixed. |
| tracked_objects::ScopedTracker tracking_profile( |
| FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| "422460 AccountTracker::OnRefreshTokenAvailable")); |
| |
| TRACE_EVENT1("identity", |
| "AccountTracker::OnRefreshTokenAvailable", |
| "account_key", |
| account_id); |
| |
| // Ignore refresh tokens if there is no active account ID at all. |
| if (identity_provider_->GetActiveAccountId().empty()) |
| return; |
| |
| DVLOG(1) << "AVAILABLE " << account_id; |
| UpdateSignInState(account_id, true); |
| } |
| |
| void AccountTracker::OnRefreshTokenRevoked(const std::string& account_id) { |
| TRACE_EVENT1("identity", |
| "AccountTracker::OnRefreshTokenRevoked", |
| "account_key", |
| account_id); |
| |
| DVLOG(1) << "REVOKED " << account_id; |
| UpdateSignInState(account_id, false); |
| } |
| |
| void AccountTracker::OnActiveAccountLogin() { |
| TRACE_EVENT0("identity", "AccountTracker::OnActiveAccountLogin"); |
| |
| std::vector<std::string> accounts = |
| identity_provider_->GetTokenService()->GetAccounts(); |
| |
| DVLOG(1) << "LOGIN " << accounts.size() << " accounts available."; |
| |
| for (std::vector<std::string>::const_iterator it = accounts.begin(); |
| it != accounts.end(); |
| ++it) { |
| OnRefreshTokenAvailable(*it); |
| } |
| } |
| |
| void AccountTracker::OnActiveAccountLogout() { |
| TRACE_EVENT0("identity", "AccountTracker::OnActiveAccountLogout"); |
| DVLOG(1) << "LOGOUT"; |
| StopTrackingAllAccounts(); |
| } |
| |
| void AccountTracker::SetAccountStateForTest(AccountIds ids, bool is_signed_in) { |
| accounts_[ids.account_key].ids = ids; |
| accounts_[ids.account_key].is_signed_in = is_signed_in; |
| |
| DVLOG(1) << "SetAccountStateForTest " << ids.account_key << ":" |
| << is_signed_in; |
| |
| if (VLOG_IS_ON(1)) { |
| for (std::map<std::string, AccountState>::const_iterator it = |
| accounts_.begin(); |
| it != accounts_.end(); |
| ++it) { |
| DVLOG(1) << it->first << ":" << it->second.is_signed_in; |
| } |
| } |
| } |
| |
| void AccountTracker::NotifyAccountAdded(const AccountState& account) { |
| DCHECK(!account.ids.gaia.empty()); |
| FOR_EACH_OBSERVER( |
| Observer, observer_list_, OnAccountAdded(account.ids)); |
| } |
| |
| void AccountTracker::NotifyAccountRemoved(const AccountState& account) { |
| DCHECK(!account.ids.gaia.empty()); |
| FOR_EACH_OBSERVER( |
| Observer, observer_list_, OnAccountRemoved(account.ids)); |
| } |
| |
| void AccountTracker::NotifySignInChanged(const AccountState& account) { |
| // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is |
| // fixed. |
| tracked_objects::ScopedTracker tracking_profile( |
| FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| "422460 AccountTracker::NotifySignInChanged")); |
| |
| DCHECK(!account.ids.gaia.empty()); |
| FOR_EACH_OBSERVER(Observer, |
| observer_list_, |
| OnAccountSignInChanged(account.ids, account.is_signed_in)); |
| } |
| |
| void AccountTracker::UpdateSignInState(const std::string& account_key, |
| bool is_signed_in) { |
| // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is |
| // fixed. |
| tracked_objects::ScopedTracker tracking_profile( |
| FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| "422460 AccountTracker::UpdateSignInState")); |
| |
| StartTrackingAccount(account_key); |
| AccountState& account = accounts_[account_key]; |
| bool needs_gaia_id = account.ids.gaia.empty(); |
| bool was_signed_in = account.is_signed_in; |
| account.is_signed_in = is_signed_in; |
| |
| if (needs_gaia_id && is_signed_in) |
| StartFetchingUserInfo(account_key); |
| |
| if (!needs_gaia_id && (was_signed_in != is_signed_in)) |
| NotifySignInChanged(account); |
| } |
| |
| void AccountTracker::StartTrackingAccount(const std::string& account_key) { |
| // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is |
| // fixed. |
| tracked_objects::ScopedTracker tracking_profile( |
| FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| "422460 AccountTracker::StartTrackingAccount")); |
| |
| if (!ContainsKey(accounts_, account_key)) { |
| DVLOG(1) << "StartTracking " << account_key; |
| AccountState account_state; |
| account_state.ids.account_key = account_key; |
| account_state.ids.email = account_key; |
| account_state.is_signed_in = false; |
| accounts_.insert(make_pair(account_key, account_state)); |
| } |
| } |
| |
| void AccountTracker::StopTrackingAccount(const std::string account_key) { |
| DVLOG(1) << "StopTracking " << account_key; |
| if (ContainsKey(accounts_, account_key)) { |
| AccountState& account = accounts_[account_key]; |
| if (!account.ids.gaia.empty()) { |
| UpdateSignInState(account_key, false); |
| NotifyAccountRemoved(account); |
| } |
| accounts_.erase(account_key); |
| } |
| |
| if (ContainsKey(user_info_requests_, account_key)) |
| DeleteFetcher(user_info_requests_[account_key]); |
| } |
| |
| void AccountTracker::StopTrackingAllAccounts() { |
| while (!accounts_.empty()) |
| StopTrackingAccount(accounts_.begin()->first); |
| } |
| |
| void AccountTracker::StartFetchingUserInfo(const std::string& account_key) { |
| // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is |
| // fixed. |
| tracked_objects::ScopedTracker tracking_profile( |
| FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| "422460 AccountTracker::StartFetchingUserInfo")); |
| |
| if (ContainsKey(user_info_requests_, account_key)) { |
| // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 |
| // is fixed. |
| tracked_objects::ScopedTracker tracking_profile1( |
| FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| "422460 AccountTracker::StartFetchingUserInfo 1")); |
| |
| DeleteFetcher(user_info_requests_[account_key]); |
| } |
| |
| DVLOG(1) << "StartFetching " << account_key; |
| // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is |
| // fixed. |
| tracked_objects::ScopedTracker tracking_profile2( |
| FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| "422460 AccountTracker::StartFetchingUserInfo 2")); |
| |
| AccountIdFetcher* fetcher = |
| new AccountIdFetcher(identity_provider_->GetTokenService(), |
| request_context_getter_.get(), |
| this, |
| account_key); |
| user_info_requests_[account_key] = fetcher; |
| |
| // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is |
| // fixed. |
| tracked_objects::ScopedTracker tracking_profile3( |
| FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| "422460 AccountTracker::StartFetchingUserInfo 3")); |
| |
| fetcher->Start(); |
| } |
| |
| void AccountTracker::OnUserInfoFetchSuccess(AccountIdFetcher* fetcher, |
| const std::string& gaia_id) { |
| const std::string& account_key = fetcher->account_key(); |
| DCHECK(ContainsKey(accounts_, account_key)); |
| AccountState& account = accounts_[account_key]; |
| |
| account.ids.gaia = gaia_id; |
| NotifyAccountAdded(account); |
| |
| if (account.is_signed_in) |
| NotifySignInChanged(account); |
| |
| DeleteFetcher(fetcher); |
| } |
| |
| void AccountTracker::OnUserInfoFetchFailure(AccountIdFetcher* fetcher) { |
| LOG(WARNING) << "Failed to get UserInfo for " << fetcher->account_key(); |
| std::string key = fetcher->account_key(); |
| DeleteFetcher(fetcher); |
| StopTrackingAccount(key); |
| } |
| |
| void AccountTracker::DeleteFetcher(AccountIdFetcher* fetcher) { |
| DVLOG(1) << "DeleteFetcher " << fetcher->account_key(); |
| const std::string& account_key = fetcher->account_key(); |
| DCHECK(ContainsKey(user_info_requests_, account_key)); |
| DCHECK_EQ(fetcher, user_info_requests_[account_key]); |
| user_info_requests_.erase(account_key); |
| delete fetcher; |
| } |
| |
| AccountIdFetcher::AccountIdFetcher( |
| OAuth2TokenService* token_service, |
| net::URLRequestContextGetter* request_context_getter, |
| AccountTracker* tracker, |
| const std::string& account_key) |
| : OAuth2TokenService::Consumer("gaia_account_tracker"), |
| token_service_(token_service), |
| request_context_getter_(request_context_getter), |
| tracker_(tracker), |
| account_key_(account_key) { |
| TRACE_EVENT_ASYNC_BEGIN1( |
| "identity", "AccountIdFetcher", this, "account_key", account_key); |
| } |
| |
| AccountIdFetcher::~AccountIdFetcher() { |
| TRACE_EVENT_ASYNC_END0("identity", "AccountIdFetcher", this); |
| } |
| |
| void AccountIdFetcher::Start() { |
| OAuth2TokenService::ScopeSet scopes; |
| scopes.insert("https://www.googleapis.com/auth/userinfo.profile"); |
| login_token_request_ = token_service_->StartRequest( |
| account_key_, scopes, this); |
| } |
| |
| void AccountIdFetcher::OnGetTokenSuccess( |
| const OAuth2TokenService::Request* request, |
| const std::string& access_token, |
| const base::Time& expiration_time) { |
| TRACE_EVENT_ASYNC_STEP_PAST0( |
| "identity", "AccountIdFetcher", this, "OnGetTokenSuccess"); |
| DCHECK_EQ(request, login_token_request_.get()); |
| |
| gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(request_context_getter_)); |
| |
| const int kMaxGetUserIdRetries = 3; |
| gaia_oauth_client_->GetUserId(access_token, kMaxGetUserIdRetries, this); |
| } |
| |
| void AccountIdFetcher::OnGetTokenFailure( |
| const OAuth2TokenService::Request* request, |
| const GoogleServiceAuthError& error) { |
| TRACE_EVENT_ASYNC_STEP_PAST1("identity", |
| "AccountIdFetcher", |
| this, |
| "OnGetTokenFailure", |
| "google_service_auth_error", |
| error.ToString()); |
| LOG(ERROR) << "OnGetTokenFailure: " << error.ToString(); |
| DCHECK_EQ(request, login_token_request_.get()); |
| tracker_->OnUserInfoFetchFailure(this); |
| } |
| |
| void AccountIdFetcher::OnGetUserIdResponse(const std::string& gaia_id) { |
| TRACE_EVENT_ASYNC_STEP_PAST1("identity", |
| "AccountIdFetcher", |
| this, |
| "OnGetUserIdResponse", |
| "gaia_id", |
| gaia_id); |
| tracker_->OnUserInfoFetchSuccess(this, gaia_id); |
| } |
| |
| void AccountIdFetcher::OnOAuthError() { |
| TRACE_EVENT_ASYNC_STEP_PAST0( |
| "identity", "AccountIdFetcher", this, "OnOAuthError"); |
| LOG(ERROR) << "OnOAuthError"; |
| tracker_->OnUserInfoFetchFailure(this); |
| } |
| |
| void AccountIdFetcher::OnNetworkError(int response_code) { |
| TRACE_EVENT_ASYNC_STEP_PAST1("identity", |
| "AccountIdFetcher", |
| this, |
| "OnNetworkError", |
| "response_code", |
| response_code); |
| LOG(ERROR) << "OnNetworkError " << response_code; |
| tracker_->OnUserInfoFetchFailure(this); |
| } |
| |
| } // namespace gaia |