Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / services / gcm / gcm_account_tracker.cc
bloba2cdba0a05ad13692b32836939b881090bfcf776
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 "chrome/browser/services/gcm/gcm_account_tracker.h"
7 #include <algorithm>
8 #include <vector>
10 #include "base/time/time.h"
11 #include "components/gcm_driver/gcm_driver.h"
12 #include "google_apis/gaia/google_service_auth_error.h"
13 #include "net/base/ip_endpoint.h"
15 namespace gcm {
17 namespace {
18 const char kGCMGroupServerScope[] = "https://www.googleapis.com/auth/gcm";
19 const char kGCMCheckinServerScope[] =
20 "https://www.googleapis.com/auth/android_checkin";
21 const char kGCMAccountTrackerName[] = "gcm_account_tracker";
22 const int64 kMinimumTokenValidityMs = 500;
23 } // namespace
25 GCMAccountTracker::AccountInfo::AccountInfo(const std::string& email,
26 AccountState state)
27 : email(email), state(state) {
30 GCMAccountTracker::AccountInfo::~AccountInfo() {
33 GCMAccountTracker::GCMAccountTracker(
34 scoped_ptr<gaia::AccountTracker> account_tracker,
35 GCMDriver* driver)
36 : OAuth2TokenService::Consumer(kGCMAccountTrackerName),
37 account_tracker_(account_tracker.release()),
38 driver_(driver),
39 shutdown_called_(false) {
42 GCMAccountTracker::~GCMAccountTracker() {
43 DCHECK(shutdown_called_);
46 void GCMAccountTracker::Shutdown() {
47 shutdown_called_ = true;
48 driver_->RemoveConnectionObserver(this);
49 account_tracker_->RemoveObserver(this);
50 account_tracker_->Shutdown();
53 void GCMAccountTracker::Start() {
54 DCHECK(!shutdown_called_);
55 account_tracker_->AddObserver(this);
56 driver_->AddConnectionObserver(this);
58 std::vector<gaia::AccountIds> accounts = account_tracker_->GetAccounts();
59 if (accounts.empty()) {
60 CompleteCollectingTokens();
61 return;
64 for (std::vector<gaia::AccountIds>::const_iterator iter = accounts.begin();
65 iter != accounts.end();
66 ++iter) {
67 if (!iter->email.empty()) {
68 account_infos_.insert(std::make_pair(
69 iter->account_key, AccountInfo(iter->email, TOKEN_NEEDED)));
73 GetAllNeededTokens();
76 void GCMAccountTracker::OnAccountAdded(const gaia::AccountIds& ids) {
77 DVLOG(1) << "Account added: " << ids.email;
78 // We listen for the account signing in, which happens after account is added.
81 void GCMAccountTracker::OnAccountRemoved(const gaia::AccountIds& ids) {
82 DVLOG(1) << "Account removed: " << ids.email;
83 // We listen for the account signing out, which happens before account is
84 // removed.
87 void GCMAccountTracker::OnAccountSignInChanged(const gaia::AccountIds& ids,
88 bool is_signed_in) {
89 if (is_signed_in)
90 OnAccountSignedIn(ids);
91 else
92 OnAccountSignedOut(ids);
95 void GCMAccountTracker::OnGetTokenSuccess(
96 const OAuth2TokenService::Request* request,
97 const std::string& access_token,
98 const base::Time& expiration_time) {
99 DCHECK(request);
100 DCHECK(!request->GetAccountId().empty());
101 DVLOG(1) << "Get token success: " << request->GetAccountId();
103 AccountInfos::iterator iter = account_infos_.find(request->GetAccountId());
104 DCHECK(iter != account_infos_.end());
105 if (iter != account_infos_.end()) {
106 DCHECK(iter->second.state == GETTING_TOKEN ||
107 iter->second.state == ACCOUNT_REMOVED);
108 // If OnAccountSignedOut(..) was called most recently, account is kept in
109 // ACCOUNT_REMOVED state.
110 if (iter->second.state == GETTING_TOKEN) {
111 iter->second.state = TOKEN_PRESENT;
112 iter->second.access_token = access_token;
113 iter->second.expiration_time = expiration_time;
117 DeleteTokenRequest(request);
118 CompleteCollectingTokens();
121 void GCMAccountTracker::OnGetTokenFailure(
122 const OAuth2TokenService::Request* request,
123 const GoogleServiceAuthError& error) {
124 DCHECK(request);
125 DCHECK(!request->GetAccountId().empty());
126 DVLOG(1) << "Get token failure: " << request->GetAccountId();
128 AccountInfos::iterator iter = account_infos_.find(request->GetAccountId());
129 DCHECK(iter != account_infos_.end());
130 if (iter != account_infos_.end()) {
131 DCHECK(iter->second.state == GETTING_TOKEN ||
132 iter->second.state == ACCOUNT_REMOVED);
133 // If OnAccountSignedOut(..) was called most recently, account is kept in
134 // ACCOUNT_REMOVED state.
135 if (iter->second.state == GETTING_TOKEN)
136 iter->second.state = TOKEN_NEEDED;
139 DeleteTokenRequest(request);
140 CompleteCollectingTokens();
143 void GCMAccountTracker::OnConnected(const net::IPEndPoint& ip_endpoint) {
144 if (SanitizeTokens())
145 GetAllNeededTokens();
148 void GCMAccountTracker::OnDisconnected() {
149 // We are disconnected, so no point in trying to work with tokens.
152 void GCMAccountTracker::CompleteCollectingTokens() {
153 // Make sure all tokens are valid.
154 if (SanitizeTokens()) {
155 GetAllNeededTokens();
156 return;
159 // Wait for gaia::AccountTracker to be done with fetching the user info, as
160 // well as all of the pending token requests from GCMAccountTracker to be done
161 // before you report the results.
162 if (!account_tracker_->IsAllUserInfoFetched() ||
163 !pending_token_requests_.empty()) {
164 return;
167 bool account_removed = false;
168 // Stop tracking the accounts, that were removed, as it will be reported to
169 // the driver.
170 for (AccountInfos::iterator iter = account_infos_.begin();
171 iter != account_infos_.end();) {
172 if (iter->second.state == ACCOUNT_REMOVED) {
173 account_removed = true;
174 account_infos_.erase(iter++);
175 } else {
176 ++iter;
180 std::vector<GCMClient::AccountTokenInfo> account_tokens;
181 for (AccountInfos::iterator iter = account_infos_.begin();
182 iter != account_infos_.end(); ++iter) {
183 if (iter->second.state == TOKEN_PRESENT) {
184 GCMClient::AccountTokenInfo token_info;
185 token_info.account_id = iter->first;
186 token_info.email = iter->second.email;
187 token_info.access_token = iter->second.access_token;
188 account_tokens.push_back(token_info);
189 } else {
190 // This should not happen, as we are making a check that there are no
191 // pending requests above, stopping tracking of removed accounts, or start
192 // fetching tokens.
193 NOTREACHED();
197 // Make sure that there is something to report, otherwise bail out.
198 if (!account_tokens.empty() || account_removed) {
199 DVLOG(1) << "Reporting the tokens to driver: " << account_tokens.size();
200 driver_->SetAccountTokens(account_tokens);
201 } else {
202 DVLOG(1) << "No tokens and nothing removed. Skipping callback.";
206 bool GCMAccountTracker::SanitizeTokens() {
207 bool tokens_needed = false;
208 for (AccountInfos::iterator iter = account_infos_.begin();
209 iter != account_infos_.end();
210 ++iter) {
211 if (iter->second.state == TOKEN_PRESENT &&
212 iter->second.expiration_time <
213 base::Time::Now() +
214 base::TimeDelta::FromMilliseconds(kMinimumTokenValidityMs)) {
215 iter->second.access_token.clear();
216 iter->second.state = TOKEN_NEEDED;
217 iter->second.expiration_time = base::Time();
220 if (iter->second.state == TOKEN_NEEDED)
221 tokens_needed = true;
224 return tokens_needed;
227 void GCMAccountTracker::DeleteTokenRequest(
228 const OAuth2TokenService::Request* request) {
229 ScopedVector<OAuth2TokenService::Request>::iterator iter = std::find(
230 pending_token_requests_.begin(), pending_token_requests_.end(), request);
231 if (iter != pending_token_requests_.end())
232 pending_token_requests_.erase(iter);
235 void GCMAccountTracker::GetAllNeededTokens() {
236 // Only start fetching tokens if driver is running, they have a limited
237 // validity time and GCM connection is a good indication of network running.
238 if (!driver_->IsConnected())
239 return;
241 for (AccountInfos::iterator iter = account_infos_.begin();
242 iter != account_infos_.end();
243 ++iter) {
244 if (iter->second.state == TOKEN_NEEDED)
245 GetToken(iter);
249 void GCMAccountTracker::GetToken(AccountInfos::iterator& account_iter) {
250 DCHECK(GetTokenService());
251 DCHECK_EQ(account_iter->second.state, TOKEN_NEEDED);
253 OAuth2TokenService::ScopeSet scopes;
254 scopes.insert(kGCMGroupServerScope);
255 scopes.insert(kGCMCheckinServerScope);
256 scoped_ptr<OAuth2TokenService::Request> request =
257 GetTokenService()->StartRequest(account_iter->first, scopes, this);
259 pending_token_requests_.push_back(request.release());
260 account_iter->second.state = GETTING_TOKEN;
263 void GCMAccountTracker::OnAccountSignedIn(const gaia::AccountIds& ids) {
264 DVLOG(1) << "Account signed in: " << ids.email;
265 AccountInfos::iterator iter = account_infos_.find(ids.account_key);
266 if (iter == account_infos_.end()) {
267 DCHECK(!ids.email.empty());
268 account_infos_.insert(
269 std::make_pair(ids.account_key, AccountInfo(ids.email, TOKEN_NEEDED)));
270 } else if (iter->second.state == ACCOUNT_REMOVED) {
271 iter->second.state = TOKEN_NEEDED;
274 GetAllNeededTokens();
277 void GCMAccountTracker::OnAccountSignedOut(const gaia::AccountIds& ids) {
278 DVLOG(1) << "Account signed out: " << ids.email;
279 AccountInfos::iterator iter = account_infos_.find(ids.account_key);
280 if (iter == account_infos_.end())
281 return;
283 iter->second.access_token.clear();
284 iter->second.state = ACCOUNT_REMOVED;
285 CompleteCollectingTokens();
288 OAuth2TokenService* GCMAccountTracker::GetTokenService() {
289 DCHECK(account_tracker_->identity_provider());
290 return account_tracker_->identity_provider()->GetTokenService();
293 } // namespace gcm