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

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: Created 6 years, 2 months 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/prefs/pref_service.h"
10 #include "base/time/time.h" 11 #include "base/time/time.h"
11 #include "components/gcm_driver/gcm_driver.h" 12 #include "components/gcm_driver/gcm_driver.h"
13 #include "components/pref_registry/pref_registry_syncable.h"
12 #include "google_apis/gaia/google_service_auth_error.h" 14 #include "google_apis/gaia/google_service_auth_error.h"
13 #include "net/base/ip_endpoint.h" 15 #include "net/base/ip_endpoint.h"
14 16
15 namespace gcm { 17 namespace gcm {
16 18
17 namespace { 19 namespace {
20
21 // Scopes needed by the OAuth2 access tokens.
18 const char kGCMGroupServerScope[] = "https://www.googleapis.com/auth/gcm"; 22 const char kGCMGroupServerScope[] = "https://www.googleapis.com/auth/gcm";
19 const char kGCMCheckinServerScope[] = 23 const char kGCMCheckinServerScope[] =
20 "https://www.googleapis.com/auth/android_checkin"; 24 "https://www.googleapis.com/auth/android_checkin";
25 // Name of the GCM account tracker for the OAuth2TokenService.
21 const char kGCMAccountTrackerName[] = "gcm_account_tracker"; 26 const char kGCMAccountTrackerName[] = "gcm_account_tracker";
27 // Minimum token validity when sending to GCM groups server.
22 const int64 kMinimumTokenValidityMs = 500; 28 const int64 kMinimumTokenValidityMs = 500;
29 // Token fetching interval, when no account changes are detected.
30 const int64 kTokenFetchingIntervalMs = 12 * 60 * 60 * 1000; // 12 hours in ms.
31 // Preference name for last token fetching timestamp.
32 const char kGCMLastTokenFetchingTs[] = "gcm.last_token_fetching_ts";
33
23 } // namespace 34 } // namespace
24 35
25 GCMAccountTracker::AccountInfo::AccountInfo(const std::string& email, 36 GCMAccountTracker::AccountInfo::AccountInfo(const std::string& email,
26 AccountState state) 37 AccountState state)
27 : email(email), state(state) { 38 : email(email), state(state) {
28 } 39 }
29 40
30 GCMAccountTracker::AccountInfo::~AccountInfo() { 41 GCMAccountTracker::AccountInfo::~AccountInfo() {
31 } 42 }
32 43
44 // static
45 void GCMAccountTracker::RegisterProfilePrefs(
46 user_prefs::PrefRegistrySyncable* registry) {
47 registry->RegisterInt64Pref(
48 kGCMLastTokenFetchingTs,
Nicolas Zea 2014/10/08 00:45:29 remind me why we store this in the prefs rather th
fgorski 2014/11/06 01:16:14 Done.
49 0L,
50 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
51 }
52
33 GCMAccountTracker::GCMAccountTracker( 53 GCMAccountTracker::GCMAccountTracker(
34 scoped_ptr<gaia::AccountTracker> account_tracker, 54 scoped_ptr<gaia::AccountTracker> account_tracker,
35 GCMDriver* driver) 55 GCMDriver* driver,
56 PrefService* prefs)
36 : OAuth2TokenService::Consumer(kGCMAccountTrackerName), 57 : OAuth2TokenService::Consumer(kGCMAccountTrackerName),
37 account_tracker_(account_tracker.release()), 58 account_tracker_(account_tracker.release()),
38 driver_(driver), 59 driver_(driver),
39 shutdown_called_(false) { 60 shutdown_called_(false),
61 prefs_(prefs),
62 weak_ptr_factory_(this) {
63 int64 last_token_fetch_internal = prefs_->GetInt64(kGCMLastTokenFetchingTs);
64 last_token_fetch_time_ =
65 base::Time::FromInternalValue(last_token_fetch_internal);
40 } 66 }
41 67
42 GCMAccountTracker::~GCMAccountTracker() { 68 GCMAccountTracker::~GCMAccountTracker() {
43 DCHECK(shutdown_called_); 69 DCHECK(shutdown_called_);
44 } 70 }
45 71
46 void GCMAccountTracker::Shutdown() { 72 void GCMAccountTracker::Shutdown() {
47 shutdown_called_ = true; 73 shutdown_called_ = true;
48 driver_->RemoveConnectionObserver(this); 74 driver_->RemoveConnectionObserver(this);
49 account_tracker_->RemoveObserver(this); 75 account_tracker_->RemoveObserver(this);
50 account_tracker_->Shutdown(); 76 account_tracker_->Shutdown();
51 } 77 }
52 78
53 void GCMAccountTracker::Start() { 79 void GCMAccountTracker::Start() {
54 DCHECK(!shutdown_called_); 80 DCHECK(!shutdown_called_);
55 account_tracker_->AddObserver(this); 81 account_tracker_->AddObserver(this);
56 driver_->AddConnectionObserver(this); 82 driver_->AddConnectionObserver(this);
57 83
58 std::vector<gaia::AccountIds> accounts = account_tracker_->GetAccounts(); 84 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(); 85 for (std::vector<gaia::AccountIds>::const_iterator iter = accounts.begin();
65 iter != accounts.end(); 86 iter != accounts.end();
66 ++iter) { 87 ++iter) {
67 if (!iter->email.empty()) { 88 if (!iter->email.empty()) {
68 account_infos_.insert(std::make_pair( 89 account_infos_.insert(std::make_pair(
69 iter->account_key, AccountInfo(iter->email, TOKEN_NEEDED))); 90 iter->account_key, AccountInfo(iter->email, TOKEN_NEEDED)));
70 } 91 }
71 } 92 }
72 93
73 GetAllNeededTokens(); 94 ScheduleFetchingTokens();
95 }
96
97 void GCMAccountTracker::ScheduleFetchingTokens() {
98 if (!IsTokenReportingRequired()) {
99 DVLOG(1) << "Deferring the token fetching for: "
100 << GetTimeToNextTokenFetching().InSeconds() << " seconds.";
101 base::MessageLoop::current()->PostDelayedTask(
102 FROM_HERE,
103 base::Bind(&GCMAccountTracker::ReportTokens,
104 weak_ptr_factory_.GetWeakPtr()),
105 GetTimeToNextTokenFetching());
106 return;
107 }
108
109 DVLOG(1) << "Token fetching is due calling Complete immediately.";
110 ReportTokens();
74 } 111 }
75 112
76 void GCMAccountTracker::OnAccountAdded(const gaia::AccountIds& ids) { 113 void GCMAccountTracker::OnAccountAdded(const gaia::AccountIds& ids) {
77 DVLOG(1) << "Account added: " << ids.email; 114 DVLOG(1) << "Account added: " << ids.email;
78 // We listen for the account signing in, which happens after account is added. 115 // We listen for the account signing in, which happens after account is added.
79 } 116 }
80 117
81 void GCMAccountTracker::OnAccountRemoved(const gaia::AccountIds& ids) { 118 void GCMAccountTracker::OnAccountRemoved(const gaia::AccountIds& ids) {
82 DVLOG(1) << "Account removed: " << ids.email; 119 DVLOG(1) << "Account removed: " << ids.email;
83 // We listen for the account signing out, which happens before account is 120 // 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 145 // If OnAccountSignedOut(..) was called most recently, account is kept in
109 // ACCOUNT_REMOVED state. 146 // ACCOUNT_REMOVED state.
110 if (iter->second.state == GETTING_TOKEN) { 147 if (iter->second.state == GETTING_TOKEN) {
111 iter->second.state = TOKEN_PRESENT; 148 iter->second.state = TOKEN_PRESENT;
112 iter->second.access_token = access_token; 149 iter->second.access_token = access_token;
113 iter->second.expiration_time = expiration_time; 150 iter->second.expiration_time = expiration_time;
114 } 151 }
115 } 152 }
116 153
117 DeleteTokenRequest(request); 154 DeleteTokenRequest(request);
118 CompleteCollectingTokens(); 155 ReportTokens();
119 } 156 }
120 157
121 void GCMAccountTracker::OnGetTokenFailure( 158 void GCMAccountTracker::OnGetTokenFailure(
122 const OAuth2TokenService::Request* request, 159 const OAuth2TokenService::Request* request,
123 const GoogleServiceAuthError& error) { 160 const GoogleServiceAuthError& error) {
124 DCHECK(request); 161 DCHECK(request);
125 DCHECK(!request->GetAccountId().empty()); 162 DCHECK(!request->GetAccountId().empty());
126 DVLOG(1) << "Get token failure: " << request->GetAccountId(); 163 DVLOG(1) << "Get token failure: " << request->GetAccountId();
127 164
128 AccountInfos::iterator iter = account_infos_.find(request->GetAccountId()); 165 AccountInfos::iterator iter = account_infos_.find(request->GetAccountId());
129 DCHECK(iter != account_infos_.end()); 166 DCHECK(iter != account_infos_.end());
130 if (iter != account_infos_.end()) { 167 if (iter != account_infos_.end()) {
131 DCHECK(iter->second.state == GETTING_TOKEN || 168 DCHECK(iter->second.state == GETTING_TOKEN ||
132 iter->second.state == ACCOUNT_REMOVED); 169 iter->second.state == ACCOUNT_REMOVED);
133 // If OnAccountSignedOut(..) was called most recently, account is kept in 170 // If OnAccountSignedOut(..) was called most recently, account is kept in
134 // ACCOUNT_REMOVED state. 171 // ACCOUNT_REMOVED state.
135 if (iter->second.state == GETTING_TOKEN) 172 if (iter->second.state == GETTING_TOKEN)
136 iter->second.state = TOKEN_NEEDED; 173 iter->second.state = TOKEN_NEEDED;
137 } 174 }
138 175
139 DeleteTokenRequest(request); 176 DeleteTokenRequest(request);
140 CompleteCollectingTokens(); 177 ReportTokens();
141 } 178 }
142 179
143 void GCMAccountTracker::OnConnected(const net::IPEndPoint& ip_endpoint) { 180 void GCMAccountTracker::OnConnected(const net::IPEndPoint& ip_endpoint) {
144 if (SanitizeTokens()) 181 if (IsTokenReportingRequired())
145 GetAllNeededTokens(); 182 ReportTokens();
146 } 183 }
147 184
148 void GCMAccountTracker::OnDisconnected() { 185 void GCMAccountTracker::OnDisconnected() {
149 // We are disconnected, so no point in trying to work with tokens. 186 // We are disconnected, so no point in trying to work with tokens.
150 } 187 }
151 188
152 void GCMAccountTracker::CompleteCollectingTokens() { 189 void GCMAccountTracker::ReportTokens() {
190 SanitizeTokens();
153 // Make sure all tokens are valid. 191 // Make sure all tokens are valid.
154 if (SanitizeTokens()) { 192 if (IsTokenFetchingRequired()) {
155 GetAllNeededTokens(); 193 GetAllNeededTokens();
156 return; 194 return;
157 } 195 }
158 196
159 // Wait for gaia::AccountTracker to be done with fetching the user info, as 197 // 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 198 // well as all of the pending token requests from GCMAccountTracker to be done
161 // before you report the results. 199 // before you report the results.
162 if (!account_tracker_->IsAllUserInfoFetched() || 200 if (!account_tracker_->IsAllUserInfoFetched() ||
163 !pending_token_requests_.empty()) { 201 !pending_token_requests_.empty()) {
164 return; 202 return;
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
201 NOTREACHED(); 239 NOTREACHED();
202 ++iter; 240 ++iter;
203 break; 241 break;
204 } 242 }
205 } 243 }
206 244
207 // Make sure that there is something to report, otherwise bail out. 245 // Make sure that there is something to report, otherwise bail out.
208 if (!account_tokens.empty() || account_removed) { 246 if (!account_tokens.empty() || account_removed) {
209 DVLOG(1) << "Reporting the tokens to driver: " << account_tokens.size(); 247 DVLOG(1) << "Reporting the tokens to driver: " << account_tokens.size();
210 driver_->SetAccountTokens(account_tokens); 248 driver_->SetAccountTokens(account_tokens);
249 last_token_fetch_time_ = base::Time::Now();
250 prefs_->SetInt64(kGCMLastTokenFetchingTs,
251 last_token_fetch_time_.ToInternalValue());
252 weak_ptr_factory_.InvalidateWeakPtrs();
253 base::MessageLoop::current()->PostDelayedTask(
254 FROM_HERE,
255 base::Bind(&GCMAccountTracker::ReportTokens,
256 weak_ptr_factory_.GetWeakPtr()),
257 GetTimeToNextTokenFetching());
211 } else { 258 } else {
212 DVLOG(1) << "No tokens and nothing removed. Skipping callback."; 259 DVLOG(1) << "No tokens and nothing removed. Skipping callback.";
213 } 260 }
214 } 261 }
215 262
216 bool GCMAccountTracker::SanitizeTokens() { 263 bool GCMAccountTracker::SanitizeTokens() {
217 bool tokens_needed = false; 264 bool tokens_needed = false;
218 for (AccountInfos::iterator iter = account_infos_.begin(); 265 for (AccountInfos::iterator iter = account_infos_.begin();
219 iter != account_infos_.end(); 266 iter != account_infos_.end();
220 ++iter) { 267 ++iter) {
221 if (iter->second.state == TOKEN_PRESENT && 268 if (iter->second.state == TOKEN_PRESENT &&
222 iter->second.expiration_time < 269 iter->second.expiration_time <
223 base::Time::Now() + 270 base::Time::Now() +
224 base::TimeDelta::FromMilliseconds(kMinimumTokenValidityMs)) { 271 base::TimeDelta::FromMilliseconds(kMinimumTokenValidityMs)) {
225 iter->second.access_token.clear(); 272 iter->second.access_token.clear();
226 iter->second.state = TOKEN_NEEDED; 273 iter->second.state = TOKEN_NEEDED;
227 iter->second.expiration_time = base::Time(); 274 iter->second.expiration_time = base::Time();
228 } 275 }
229 276
230 if (iter->second.state == TOKEN_NEEDED) 277 if (iter->second.state == TOKEN_NEEDED)
231 tokens_needed = true; 278 tokens_needed = true;
232 } 279 }
233 280
234 return tokens_needed; 281 return tokens_needed;
235 } 282 }
236 283
284 bool GCMAccountTracker::IsTokenReportingRequired() {
285 if (GetTimeToNextTokenFetching() == base::TimeDelta())
286 return true;
287
288 bool reporting_required = false;
289 for (AccountInfos::iterator iter = account_infos_.begin();
290 iter != account_infos_.end();
291 ++iter) {
292 if (iter->second.state == ACCOUNT_REMOVED)
293 reporting_required = true;
294 }
295
296 return reporting_required;
297 }
298
299 bool GCMAccountTracker::IsTokenFetchingRequired() {
300 bool token_needed = false;
301 for (AccountInfos::iterator iter = account_infos_.begin();
302 iter != account_infos_.end();
303 ++iter) {
304 if (iter->second.state == TOKEN_NEEDED)
305 token_needed = true;
306 }
307
308 return token_needed;
309 }
310
311 base::TimeDelta GCMAccountTracker::GetTimeToNextTokenFetching() {
312 base::TimeDelta time_till_next_fetching =
313 last_token_fetch_time_ +
314 base::TimeDelta::FromMilliseconds(kTokenFetchingIntervalMs) -
315 base::Time::Now();
316 return time_till_next_fetching < base::TimeDelta() ? base::TimeDelta()
317 : time_till_next_fetching;
318 }
319
237 void GCMAccountTracker::DeleteTokenRequest( 320 void GCMAccountTracker::DeleteTokenRequest(
238 const OAuth2TokenService::Request* request) { 321 const OAuth2TokenService::Request* request) {
239 ScopedVector<OAuth2TokenService::Request>::iterator iter = std::find( 322 ScopedVector<OAuth2TokenService::Request>::iterator iter = std::find(
240 pending_token_requests_.begin(), pending_token_requests_.end(), request); 323 pending_token_requests_.begin(), pending_token_requests_.end(), request);
241 if (iter != pending_token_requests_.end()) 324 if (iter != pending_token_requests_.end())
242 pending_token_requests_.erase(iter); 325 pending_token_requests_.erase(iter);
243 } 326 }
244 327
245 void GCMAccountTracker::GetAllNeededTokens() { 328 void GCMAccountTracker::GetAllNeededTokens() {
246 // Only start fetching tokens if driver is running, they have a limited 329 // Only start fetching tokens if driver is running, they have a limited
247 // validity time and GCM connection is a good indication of network running. 330 // validity time and GCM connection is a good indication of network running.
331 // If the GetAllNeededTokens was called as part of periodic schedule, it may
332 // not have network. In that case the next network change will trigger token
333 // fetching.
248 if (!driver_->IsConnected()) 334 if (!driver_->IsConnected())
249 return; 335 return;
250 336
251 for (AccountInfos::iterator iter = account_infos_.begin(); 337 for (AccountInfos::iterator iter = account_infos_.begin();
252 iter != account_infos_.end(); 338 iter != account_infos_.end();
253 ++iter) { 339 ++iter) {
254 if (iter->second.state == TOKEN_NEEDED) 340 if (iter->second.state == TOKEN_NEEDED)
255 GetToken(iter); 341 GetToken(iter);
256 } 342 }
257 } 343 }
(...skipping 27 matching lines...) Expand all
285 } 371 }
286 372
287 void GCMAccountTracker::OnAccountSignedOut(const gaia::AccountIds& ids) { 373 void GCMAccountTracker::OnAccountSignedOut(const gaia::AccountIds& ids) {
288 DVLOG(1) << "Account signed out: " << ids.email; 374 DVLOG(1) << "Account signed out: " << ids.email;
289 AccountInfos::iterator iter = account_infos_.find(ids.account_key); 375 AccountInfos::iterator iter = account_infos_.find(ids.account_key);
290 if (iter == account_infos_.end()) 376 if (iter == account_infos_.end())
291 return; 377 return;
292 378
293 iter->second.access_token.clear(); 379 iter->second.access_token.clear();
294 iter->second.state = ACCOUNT_REMOVED; 380 iter->second.state = ACCOUNT_REMOVED;
295 CompleteCollectingTokens(); 381 ReportTokens();
296 } 382 }
297 383
298 OAuth2TokenService* GCMAccountTracker::GetTokenService() { 384 OAuth2TokenService* GCMAccountTracker::GetTokenService() {
299 DCHECK(account_tracker_->identity_provider()); 385 DCHECK(account_tracker_->identity_provider());
300 return account_tracker_->identity_provider()->GetTokenService(); 386 return account_tracker_->identity_provider()->GetTokenService();
301 } 387 }
302 388
303 } // namespace gcm 389 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698