[GCM] Passing GCMClient::AccountTokenInfo list to GCMDriver
[chromium-blink-merge.git] / chrome / browser / services / gcm / gcm_account_tracker.cc
blobca2cda5c401b99616ef8c54a3269489ffb6510de
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 "google_apis/gaia/google_service_auth_error.h"
13 namespace gcm {
15 namespace {
16 const char kGCMGroupServerScope[] = "https://www.googleapis.com/auth/gcm";
17 const char kGCMCheckinServerScope[] =
18 "https://www.googleapis.com/auth/android_checkin";
19 const char kGCMAccountTrackerName[] = "gcm_account_tracker";
20 } // namespace
22 GCMAccountTracker::AccountInfo::AccountInfo(const std::string& email,
23 AccountState state)
24 : email(email), state(state) {
27 GCMAccountTracker::AccountInfo::~AccountInfo() {
30 GCMAccountTracker::GCMAccountTracker(
31 scoped_ptr<gaia::AccountTracker> account_tracker,
32 const UpdateAccountsCallback& callback)
33 : OAuth2TokenService::Consumer(kGCMAccountTrackerName),
34 account_tracker_(account_tracker.release()),
35 callback_(callback),
36 shutdown_called_(false) {
37 DCHECK(!callback_.is_null());
40 GCMAccountTracker::~GCMAccountTracker() {
41 DCHECK(shutdown_called_);
44 void GCMAccountTracker::Shutdown() {
45 Stop();
46 shutdown_called_ = true;
47 account_tracker_->Shutdown();
50 void GCMAccountTracker::Start() {
51 DCHECK(!shutdown_called_);
52 account_tracker_->AddObserver(this);
54 std::vector<gaia::AccountIds> accounts = account_tracker_->GetAccounts();
55 if (accounts.empty()) {
56 CompleteCollectingTokens();
57 return;
60 for (std::vector<gaia::AccountIds>::const_iterator iter = accounts.begin();
61 iter != accounts.end();
62 ++iter) {
63 if (!iter->email.empty()) {
64 account_infos_.insert(std::make_pair(
65 iter->account_key, AccountInfo(iter->email, TOKEN_NEEDED)));
69 GetAllNeededTokens();
72 void GCMAccountTracker::Stop() {
73 DCHECK(!shutdown_called_);
74 account_tracker_->RemoveObserver(this);
75 pending_token_requests_.clear();
78 void GCMAccountTracker::OnAccountAdded(const gaia::AccountIds& ids) {
79 DVLOG(1) << "Account added: " << ids.email;
80 // We listen for the account signing in, which happens after account is added.
83 void GCMAccountTracker::OnAccountRemoved(const gaia::AccountIds& ids) {
84 DVLOG(1) << "Account removed: " << ids.email;
85 // We listen for the account signing out, which happens before account is
86 // removed.
89 void GCMAccountTracker::OnAccountSignInChanged(const gaia::AccountIds& ids,
90 bool is_signed_in) {
91 if (is_signed_in)
92 OnAccountSignedIn(ids);
93 else
94 OnAccountSignedOut(ids);
97 void GCMAccountTracker::OnGetTokenSuccess(
98 const OAuth2TokenService::Request* request,
99 const std::string& access_token,
100 const base::Time& expiration_time) {
101 DCHECK(request);
102 DCHECK(!request->GetAccountId().empty());
103 DVLOG(1) << "Get token success: " << request->GetAccountId();
105 AccountInfos::iterator iter = account_infos_.find(request->GetAccountId());
106 DCHECK(iter != account_infos_.end());
107 if (iter != account_infos_.end()) {
108 DCHECK(iter->second.state == GETTING_TOKEN ||
109 iter->second.state == ACCOUNT_REMOVED);
110 // If OnAccountSignedOut(..) was called most recently, account is kept in
111 // ACCOUNT_REMOVED state.
112 if (iter->second.state == GETTING_TOKEN) {
113 iter->second.state = TOKEN_PRESENT;
114 iter->second.access_token = access_token;
118 DeleteTokenRequest(request);
119 CompleteCollectingTokens();
122 void GCMAccountTracker::OnGetTokenFailure(
123 const OAuth2TokenService::Request* request,
124 const GoogleServiceAuthError& error) {
125 DCHECK(request);
126 DCHECK(!request->GetAccountId().empty());
127 DVLOG(1) << "Get token failure: " << request->GetAccountId();
129 AccountInfos::iterator iter = account_infos_.find(request->GetAccountId());
130 DCHECK(iter != account_infos_.end());
131 if (iter != account_infos_.end()) {
132 DCHECK(iter->second.state == GETTING_TOKEN ||
133 iter->second.state == ACCOUNT_REMOVED);
134 // If OnAccountSignedOut(..) was called most recently, account is kept in
135 // ACCOUNT_REMOVED state.
136 if (iter->second.state == GETTING_TOKEN)
137 iter->second.state = TOKEN_NEEDED;
140 DeleteTokenRequest(request);
141 CompleteCollectingTokens();
144 void GCMAccountTracker::CompleteCollectingTokens() {
145 DCHECK(!callback_.is_null());
146 // Wait for gaia::AccountTracker to be done with fetching the user info, as
147 // well as all of the pending token requests from GCMAccountTracker to be done
148 // before you report the results.
149 if (!account_tracker_->IsAllUserInfoFetched() ||
150 !pending_token_requests_.empty()) {
151 return;
154 bool account_removed = false;
155 std::vector<GCMClient::AccountTokenInfo> account_tokens;
156 for (AccountInfos::iterator iter = account_infos_.begin();
157 iter != account_infos_.end();) {
158 switch (iter->second.state) {
159 case ACCOUNT_REMOVED:
160 // We only mark accounts as removed when there was an account that was
161 // explicitly signed out.
162 account_removed = true;
163 // We also stop tracking the account, now that it will be reported as
164 // removed.
165 account_infos_.erase(iter++);
166 break;
168 case TOKEN_PRESENT: {
169 GCMClient::AccountTokenInfo token_info;
170 token_info.account_id = iter->first;
171 token_info.email = iter->second.email;
172 token_info.access_token = iter->second.access_token;
173 account_tokens.push_back(token_info);
174 ++iter;
175 break;
178 case GETTING_TOKEN:
179 // This should not happen, as we are making a check that there are no
180 // pending requests above.
181 NOTREACHED();
182 ++iter;
183 break;
185 case TOKEN_NEEDED:
186 // We failed to fetch an access token for the account, but it has not
187 // been signed out (perhaps there is a network issue). We don't report
188 // it, but next time there is a sign-in change we will update its state.
189 ++iter;
190 break;
194 // Make sure that there is something to report, otherwise bail out.
195 if (!account_tokens.empty() || account_removed) {
196 DVLOG(1) << "Calling callback: " << account_tokens.size();
197 callback_.Run(account_tokens);
198 } else {
199 DVLOG(1) << "No tokens and nothing removed. Skipping callback.";
203 void GCMAccountTracker::DeleteTokenRequest(
204 const OAuth2TokenService::Request* request) {
205 ScopedVector<OAuth2TokenService::Request>::iterator iter = std::find(
206 pending_token_requests_.begin(), pending_token_requests_.end(), request);
207 if (iter != pending_token_requests_.end())
208 pending_token_requests_.erase(iter);
211 void GCMAccountTracker::GetAllNeededTokens() {
212 for (AccountInfos::iterator iter = account_infos_.begin();
213 iter != account_infos_.end();
214 ++iter) {
215 if (iter->second.state == TOKEN_NEEDED)
216 GetToken(iter);
220 void GCMAccountTracker::GetToken(AccountInfos::iterator& account_iter) {
221 DCHECK(GetTokenService());
222 DCHECK_EQ(account_iter->second.state, TOKEN_NEEDED);
224 OAuth2TokenService::ScopeSet scopes;
225 scopes.insert(kGCMGroupServerScope);
226 scopes.insert(kGCMCheckinServerScope);
227 scoped_ptr<OAuth2TokenService::Request> request =
228 GetTokenService()->StartRequest(account_iter->first, scopes, this);
230 pending_token_requests_.push_back(request.release());
231 account_iter->second.state = GETTING_TOKEN;
234 void GCMAccountTracker::OnAccountSignedIn(const gaia::AccountIds& ids) {
235 DVLOG(1) << "Account signed in: " << ids.email;
236 AccountInfos::iterator iter = account_infos_.find(ids.account_key);
237 if (iter == account_infos_.end()) {
238 DCHECK(!ids.email.empty());
239 account_infos_.insert(
240 std::make_pair(ids.account_key, AccountInfo(ids.email, TOKEN_NEEDED)));
241 } else if (iter->second.state == ACCOUNT_REMOVED) {
242 iter->second.state = TOKEN_NEEDED;
245 GetAllNeededTokens();
248 void GCMAccountTracker::OnAccountSignedOut(const gaia::AccountIds& ids) {
249 DVLOG(1) << "Account signed out: " << ids.email;
250 AccountInfos::iterator iter = account_infos_.find(ids.account_key);
251 if (iter == account_infos_.end())
252 return;
254 iter->second.access_token.clear();
255 iter->second.state = ACCOUNT_REMOVED;
256 CompleteCollectingTokens();
259 OAuth2TokenService* GCMAccountTracker::GetTokenService() {
260 DCHECK(account_tracker_->identity_provider());
261 return account_tracker_->identity_provider()->GetTokenService();
264 } // namespace gcm