Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(360)

Side by Side Diff: chrome/browser/services/gcm/gcm_account_tracker.cc

Issue 631343002: [GCM] Fetching OAuth2 tokens periodically in account tracker (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@mapper-in-driver
Patch Set: Adding tests and removing pref related code from tests Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/services/gcm/gcm_account_tracker.h" 5 #include "chrome/browser/services/gcm/gcm_account_tracker.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/time/time.h" 10 #include "base/time/time.h"
11 #include "components/gcm_driver/gcm_driver.h" 11 #include "components/gcm_driver/gcm_driver.h"
12 #include "google_apis/gaia/google_service_auth_error.h" 12 #include "google_apis/gaia/google_service_auth_error.h"
13 #include "net/base/ip_endpoint.h" 13 #include "net/base/ip_endpoint.h"
14 14
15 namespace gcm { 15 namespace gcm {
16 16
17 namespace { 17 namespace {
18
19 // Scopes needed by the OAuth2 access tokens.
18 const char kGCMGroupServerScope[] = "https://www.googleapis.com/auth/gcm"; 20 const char kGCMGroupServerScope[] = "https://www.googleapis.com/auth/gcm";
19 const char kGCMCheckinServerScope[] = 21 const char kGCMCheckinServerScope[] =
20 "https://www.googleapis.com/auth/android_checkin"; 22 "https://www.googleapis.com/auth/android_checkin";
23 // Name of the GCM account tracker for the OAuth2TokenService.
21 const char kGCMAccountTrackerName[] = "gcm_account_tracker"; 24 const char kGCMAccountTrackerName[] = "gcm_account_tracker";
25 // Minimum token validity when sending to GCM groups server.
22 const int64 kMinimumTokenValidityMs = 500; 26 const int64 kMinimumTokenValidityMs = 500;
27 // Token fetching interval, when no account changes are detected.
28 const int64 kTokenFetchingIntervalMs = 12 * 60 * 60 * 1000; // 12 hours in ms.
29
23 } // namespace 30 } // namespace
24 31
25 GCMAccountTracker::AccountInfo::AccountInfo(const std::string& email, 32 GCMAccountTracker::AccountInfo::AccountInfo(const std::string& email,
26 AccountState state) 33 AccountState state)
27 : email(email), state(state) { 34 : email(email), state(state) {
28 } 35 }
29 36
30 GCMAccountTracker::AccountInfo::~AccountInfo() { 37 GCMAccountTracker::AccountInfo::~AccountInfo() {
31 } 38 }
32 39
33 GCMAccountTracker::GCMAccountTracker( 40 GCMAccountTracker::GCMAccountTracker(
34 scoped_ptr<gaia::AccountTracker> account_tracker, 41 scoped_ptr<gaia::AccountTracker> account_tracker,
35 GCMDriver* driver) 42 GCMDriver* driver)
36 : OAuth2TokenService::Consumer(kGCMAccountTrackerName), 43 : OAuth2TokenService::Consumer(kGCMAccountTrackerName),
37 account_tracker_(account_tracker.release()), 44 account_tracker_(account_tracker.release()),
38 driver_(driver), 45 driver_(driver),
39 shutdown_called_(false) { 46 shutdown_called_(false),
47 weak_ptr_factory_(this) {
40 } 48 }
41 49
42 GCMAccountTracker::~GCMAccountTracker() { 50 GCMAccountTracker::~GCMAccountTracker() {
43 DCHECK(shutdown_called_); 51 DCHECK(shutdown_called_);
44 } 52 }
45 53
46 void GCMAccountTracker::Shutdown() { 54 void GCMAccountTracker::Shutdown() {
47 shutdown_called_ = true; 55 shutdown_called_ = true;
48 driver_->RemoveConnectionObserver(this); 56 driver_->RemoveConnectionObserver(this);
49 account_tracker_->RemoveObserver(this); 57 account_tracker_->RemoveObserver(this);
50 account_tracker_->Shutdown(); 58 account_tracker_->Shutdown();
51 } 59 }
52 60
53 void GCMAccountTracker::Start() { 61 void GCMAccountTracker::Start() {
54 DCHECK(!shutdown_called_); 62 DCHECK(!shutdown_called_);
55 account_tracker_->AddObserver(this); 63 account_tracker_->AddObserver(this);
56 driver_->AddConnectionObserver(this); 64 driver_->AddConnectionObserver(this);
57 65
58 std::vector<gaia::AccountIds> accounts = account_tracker_->GetAccounts(); 66 std::vector<gaia::AccountIds> accounts = account_tracker_->GetAccounts();
59 if (accounts.empty()) {
60 CompleteCollectingTokens();
61 return;
62 }
63
64 for (std::vector<gaia::AccountIds>::const_iterator iter = accounts.begin(); 67 for (std::vector<gaia::AccountIds>::const_iterator iter = accounts.begin();
65 iter != accounts.end(); 68 iter != accounts.end();
66 ++iter) { 69 ++iter) {
67 if (!iter->email.empty()) { 70 if (!iter->email.empty()) {
68 account_infos_.insert(std::make_pair( 71 account_infos_.insert(std::make_pair(
69 iter->account_key, AccountInfo(iter->email, TOKEN_NEEDED))); 72 iter->account_key, AccountInfo(iter->email, TOKEN_NEEDED)));
70 } 73 }
71 } 74 }
72 75
73 GetAllNeededTokens(); 76 ScheduleFetchingTokens();
77 }
78
79 void GCMAccountTracker::ScheduleFetchingTokens() {
80 if (!IsTokenReportingRequired()) {
81 DVLOG(1) << "Deferring the token fetching for: "
82 << GetTimeToNextTokenFetching().InSeconds() << " seconds.";
83 base::MessageLoop::current()->PostDelayedTask(
84 FROM_HERE,
85 base::Bind(&GCMAccountTracker::ReportTokens,
86 weak_ptr_factory_.GetWeakPtr()),
87 GetTimeToNextTokenFetching());
88 return;
89 }
90
91 DVLOG(1) << "Token fetching is due calling Complete immediately.";
92 ReportTokens();
74 } 93 }
75 94
76 void GCMAccountTracker::OnAccountAdded(const gaia::AccountIds& ids) { 95 void GCMAccountTracker::OnAccountAdded(const gaia::AccountIds& ids) {
77 DVLOG(1) << "Account added: " << ids.email; 96 DVLOG(1) << "Account added: " << ids.email;
78 // We listen for the account signing in, which happens after account is added. 97 // We listen for the account signing in, which happens after account is added.
79 } 98 }
80 99
81 void GCMAccountTracker::OnAccountRemoved(const gaia::AccountIds& ids) { 100 void GCMAccountTracker::OnAccountRemoved(const gaia::AccountIds& ids) {
82 DVLOG(1) << "Account removed: " << ids.email; 101 DVLOG(1) << "Account removed: " << ids.email;
83 // We listen for the account signing out, which happens before account is 102 // We listen for the account signing out, which happens before account is
(...skipping 24 matching lines...) Expand all
108 // If OnAccountSignedOut(..) was called most recently, account is kept in 127 // If OnAccountSignedOut(..) was called most recently, account is kept in
109 // ACCOUNT_REMOVED state. 128 // ACCOUNT_REMOVED state.
110 if (iter->second.state == GETTING_TOKEN) { 129 if (iter->second.state == GETTING_TOKEN) {
111 iter->second.state = TOKEN_PRESENT; 130 iter->second.state = TOKEN_PRESENT;
112 iter->second.access_token = access_token; 131 iter->second.access_token = access_token;
113 iter->second.expiration_time = expiration_time; 132 iter->second.expiration_time = expiration_time;
114 } 133 }
115 } 134 }
116 135
117 DeleteTokenRequest(request); 136 DeleteTokenRequest(request);
118 CompleteCollectingTokens(); 137 ReportTokens();
119 } 138 }
120 139
121 void GCMAccountTracker::OnGetTokenFailure( 140 void GCMAccountTracker::OnGetTokenFailure(
122 const OAuth2TokenService::Request* request, 141 const OAuth2TokenService::Request* request,
123 const GoogleServiceAuthError& error) { 142 const GoogleServiceAuthError& error) {
124 DCHECK(request); 143 DCHECK(request);
125 DCHECK(!request->GetAccountId().empty()); 144 DCHECK(!request->GetAccountId().empty());
126 DVLOG(1) << "Get token failure: " << request->GetAccountId(); 145 DVLOG(1) << "Get token failure: " << request->GetAccountId();
127 146
128 AccountInfos::iterator iter = account_infos_.find(request->GetAccountId()); 147 AccountInfos::iterator iter = account_infos_.find(request->GetAccountId());
129 DCHECK(iter != account_infos_.end()); 148 DCHECK(iter != account_infos_.end());
130 if (iter != account_infos_.end()) { 149 if (iter != account_infos_.end()) {
131 DCHECK(iter->second.state == GETTING_TOKEN || 150 DCHECK(iter->second.state == GETTING_TOKEN ||
132 iter->second.state == ACCOUNT_REMOVED); 151 iter->second.state == ACCOUNT_REMOVED);
133 // If OnAccountSignedOut(..) was called most recently, account is kept in 152 // If OnAccountSignedOut(..) was called most recently, account is kept in
134 // ACCOUNT_REMOVED state. 153 // ACCOUNT_REMOVED state.
135 if (iter->second.state == GETTING_TOKEN) 154 if (iter->second.state == GETTING_TOKEN)
136 iter->second.state = TOKEN_NEEDED; 155 iter->second.state = TOKEN_NEEDED;
137 } 156 }
138 157
139 DeleteTokenRequest(request); 158 DeleteTokenRequest(request);
140 CompleteCollectingTokens(); 159 ReportTokens();
141 } 160 }
142 161
143 void GCMAccountTracker::OnConnected(const net::IPEndPoint& ip_endpoint) { 162 void GCMAccountTracker::OnConnected(const net::IPEndPoint& ip_endpoint) {
144 if (SanitizeTokens()) 163 if (IsTokenReportingRequired())
145 GetAllNeededTokens(); 164 ReportTokens();
146 } 165 }
147 166
148 void GCMAccountTracker::OnDisconnected() { 167 void GCMAccountTracker::OnDisconnected() {
149 // We are disconnected, so no point in trying to work with tokens. 168 // We are disconnected, so no point in trying to work with tokens.
150 } 169 }
151 170
152 void GCMAccountTracker::CompleteCollectingTokens() { 171 void GCMAccountTracker::ReportTokens() {
172 SanitizeTokens();
Nicolas Zea 2014/11/06 21:43:17 Why do we ignore the return value now and use IsTo
fgorski 2014/11/07 01:42:12 I think that was the comment we discussed with one
153 // Make sure all tokens are valid. 173 // Make sure all tokens are valid.
154 if (SanitizeTokens()) { 174 if (IsTokenFetchingRequired()) {
155 GetAllNeededTokens(); 175 GetAllNeededTokens();
156 return; 176 return;
157 } 177 }
158 178
159 // Wait for gaia::AccountTracker to be done with fetching the user info, as 179 // 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 180 // well as all of the pending token requests from GCMAccountTracker to be done
161 // before you report the results. 181 // before you report the results.
162 if (!account_tracker_->IsAllUserInfoFetched() || 182 if (!account_tracker_->IsAllUserInfoFetched() ||
163 !pending_token_requests_.empty()) { 183 !pending_token_requests_.empty()) {
164 return; 184 return;
(...skipping 26 matching lines...) Expand all
191 // pending requests above, stopping tracking of removed accounts, or start 211 // pending requests above, stopping tracking of removed accounts, or start
192 // fetching tokens. 212 // fetching tokens.
193 NOTREACHED(); 213 NOTREACHED();
194 } 214 }
195 } 215 }
196 216
197 // Make sure that there is something to report, otherwise bail out. 217 // Make sure that there is something to report, otherwise bail out.
198 if (!account_tokens.empty() || account_removed) { 218 if (!account_tokens.empty() || account_removed) {
199 DVLOG(1) << "Reporting the tokens to driver: " << account_tokens.size(); 219 DVLOG(1) << "Reporting the tokens to driver: " << account_tokens.size();
200 driver_->SetAccountTokens(account_tokens); 220 driver_->SetAccountTokens(account_tokens);
221 driver_->SetLastTokenFetchTime(base::Time::Now());
222 weak_ptr_factory_.InvalidateWeakPtrs();
223 base::MessageLoop::current()->PostDelayedTask(
224 FROM_HERE,
225 base::Bind(&GCMAccountTracker::ReportTokens,
226 weak_ptr_factory_.GetWeakPtr()),
227 GetTimeToNextTokenFetching());
201 } else { 228 } else {
202 DVLOG(1) << "No tokens and nothing removed. Skipping callback."; 229 DVLOG(1) << "No tokens and nothing removed. Skipping callback.";
203 } 230 }
204 } 231 }
205 232
206 bool GCMAccountTracker::SanitizeTokens() { 233 bool GCMAccountTracker::SanitizeTokens() {
207 bool tokens_needed = false; 234 bool tokens_needed = false;
208 for (AccountInfos::iterator iter = account_infos_.begin(); 235 for (AccountInfos::iterator iter = account_infos_.begin();
209 iter != account_infos_.end(); 236 iter != account_infos_.end();
210 ++iter) { 237 ++iter) {
211 if (iter->second.state == TOKEN_PRESENT && 238 if (iter->second.state == TOKEN_PRESENT &&
212 iter->second.expiration_time < 239 iter->second.expiration_time <
213 base::Time::Now() + 240 base::Time::Now() +
214 base::TimeDelta::FromMilliseconds(kMinimumTokenValidityMs)) { 241 base::TimeDelta::FromMilliseconds(kMinimumTokenValidityMs)) {
215 iter->second.access_token.clear(); 242 iter->second.access_token.clear();
216 iter->second.state = TOKEN_NEEDED; 243 iter->second.state = TOKEN_NEEDED;
217 iter->second.expiration_time = base::Time(); 244 iter->second.expiration_time = base::Time();
218 } 245 }
219 246
220 if (iter->second.state == TOKEN_NEEDED) 247 if (iter->second.state == TOKEN_NEEDED)
221 tokens_needed = true; 248 tokens_needed = true;
222 } 249 }
223 250
224 return tokens_needed; 251 return tokens_needed;
225 } 252 }
226 253
254 bool GCMAccountTracker::IsTokenReportingRequired() const {
255 if (GetTimeToNextTokenFetching() == base::TimeDelta())
256 return true;
257
258 bool reporting_required = false;
259 for (AccountInfos::const_iterator iter = account_infos_.begin();
260 iter != account_infos_.end();
261 ++iter) {
262 if (iter->second.state == ACCOUNT_REMOVED)
263 reporting_required = true;
264 }
265
266 return reporting_required;
267 }
268
269 bool GCMAccountTracker::IsTokenFetchingRequired() const {
270 bool token_needed = false;
271 for (AccountInfos::const_iterator iter = account_infos_.begin();
272 iter != account_infos_.end();
273 ++iter) {
274 if (iter->second.state == TOKEN_NEEDED)
275 token_needed = true;
276 }
277
278 return token_needed;
279 }
280
281 base::TimeDelta GCMAccountTracker::GetTimeToNextTokenFetching() const {
282 base::TimeDelta time_till_next_fetching =
283 driver_->GetLastTokenFetchTime() +
284 base::TimeDelta::FromMilliseconds(kTokenFetchingIntervalMs) -
285 base::Time::Now();
286 return time_till_next_fetching < base::TimeDelta() ? base::TimeDelta()
287 : time_till_next_fetching;
288 }
289
227 void GCMAccountTracker::DeleteTokenRequest( 290 void GCMAccountTracker::DeleteTokenRequest(
228 const OAuth2TokenService::Request* request) { 291 const OAuth2TokenService::Request* request) {
229 ScopedVector<OAuth2TokenService::Request>::iterator iter = std::find( 292 ScopedVector<OAuth2TokenService::Request>::iterator iter = std::find(
230 pending_token_requests_.begin(), pending_token_requests_.end(), request); 293 pending_token_requests_.begin(), pending_token_requests_.end(), request);
231 if (iter != pending_token_requests_.end()) 294 if (iter != pending_token_requests_.end())
232 pending_token_requests_.erase(iter); 295 pending_token_requests_.erase(iter);
233 } 296 }
234 297
235 void GCMAccountTracker::GetAllNeededTokens() { 298 void GCMAccountTracker::GetAllNeededTokens() {
236 // Only start fetching tokens if driver is running, they have a limited 299 // 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. 300 // validity time and GCM connection is a good indication of network running.
301 // If the GetAllNeededTokens was called as part of periodic schedule, it may
302 // not have network. In that case the next network change will trigger token
303 // fetching.
Nicolas Zea 2014/11/06 21:43:17 What code is listening to the network change?
fgorski 2014/11/07 01:42:12 Lines 162:169 above.
238 if (!driver_->IsConnected()) 304 if (!driver_->IsConnected())
239 return; 305 return;
240 306
241 for (AccountInfos::iterator iter = account_infos_.begin(); 307 for (AccountInfos::iterator iter = account_infos_.begin();
242 iter != account_infos_.end(); 308 iter != account_infos_.end();
243 ++iter) { 309 ++iter) {
244 if (iter->second.state == TOKEN_NEEDED) 310 if (iter->second.state == TOKEN_NEEDED)
245 GetToken(iter); 311 GetToken(iter);
246 } 312 }
247 } 313 }
(...skipping 27 matching lines...) Expand all
275 } 341 }
276 342
277 void GCMAccountTracker::OnAccountSignedOut(const gaia::AccountIds& ids) { 343 void GCMAccountTracker::OnAccountSignedOut(const gaia::AccountIds& ids) {
278 DVLOG(1) << "Account signed out: " << ids.email; 344 DVLOG(1) << "Account signed out: " << ids.email;
279 AccountInfos::iterator iter = account_infos_.find(ids.account_key); 345 AccountInfos::iterator iter = account_infos_.find(ids.account_key);
280 if (iter == account_infos_.end()) 346 if (iter == account_infos_.end())
281 return; 347 return;
282 348
283 iter->second.access_token.clear(); 349 iter->second.access_token.clear();
284 iter->second.state = ACCOUNT_REMOVED; 350 iter->second.state = ACCOUNT_REMOVED;
285 CompleteCollectingTokens(); 351 ReportTokens();
286 } 352 }
287 353
288 OAuth2TokenService* GCMAccountTracker::GetTokenService() { 354 OAuth2TokenService* GCMAccountTracker::GetTokenService() {
289 DCHECK(account_tracker_->identity_provider()); 355 DCHECK(account_tracker_->identity_provider());
290 return account_tracker_->identity_provider()->GetTokenService(); 356 return account_tracker_->identity_provider()->GetTokenService();
291 } 357 }
292 358
293 } // namespace gcm 359 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698