forked from sanyaade-mobiledev/chromium.src
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding GCMAccountTracker to fetch tokens for GCM Checkin.
* Listening on gaia::AccountTracker for SignIn events. * Collecting GCM group tokens for all signed in accounts * Reporting email to token mapping through callback with information if any account was removed * Adding a check on gaia::AccountTracker if it is still fetching account information * Adding ability to issue token fetch errors on FakeOAuth2TokenService. R=jianli@chromium.org,rogerta@chromium.org,courage@chromium.org BUG=374969 Review URL: https://codereview.chromium.org/341273010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@280926 0039d316-1c4b-4281-b951-d872f2087c98
- Loading branch information
fgorski@chromium.org
committed
Jul 2, 2014
1 parent
9efc64b
commit 641c8cc
Showing
9 changed files
with
730 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,245 @@ | ||
// 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 "chrome/browser/services/gcm/gcm_account_tracker.h" | ||
|
||
#include <algorithm> | ||
#include <vector> | ||
|
||
#include "base/time/time.h" | ||
#include "google_apis/gaia/google_service_auth_error.h" | ||
|
||
namespace gcm { | ||
|
||
namespace { | ||
const char kGCMGroupServerScope[] = | ||
"oauth2:https://www.googleapis.com/auth/gcm"; | ||
const char kGCMAccountTrackerName[] = "gcm_account_tracker"; | ||
} // namespace | ||
|
||
GCMAccountTracker::AccountInfo::AccountInfo(const std::string& email, | ||
AccountState state) | ||
: email(email), state(state) { | ||
} | ||
|
||
GCMAccountTracker::AccountInfo::~AccountInfo() { | ||
} | ||
|
||
GCMAccountTracker::GCMAccountTracker( | ||
scoped_ptr<gaia::AccountTracker> account_tracker, | ||
const UpdateAccountsCallback& callback) | ||
: OAuth2TokenService::Consumer(kGCMAccountTrackerName), | ||
account_tracker_(account_tracker.release()), | ||
callback_(callback), | ||
shutdown_called_(false) { | ||
DCHECK(!callback_.is_null()); | ||
} | ||
|
||
GCMAccountTracker::~GCMAccountTracker() { | ||
DCHECK(shutdown_called_); | ||
} | ||
|
||
void GCMAccountTracker::Shutdown() { | ||
Stop(); | ||
shutdown_called_ = true; | ||
account_tracker_->Shutdown(); | ||
} | ||
|
||
void GCMAccountTracker::Start() { | ||
DCHECK(!shutdown_called_); | ||
account_tracker_->AddObserver(this); | ||
|
||
std::vector<gaia::AccountIds> accounts = account_tracker_->GetAccounts(); | ||
if (accounts.empty()) { | ||
CompleteCollectingTokens(); | ||
return; | ||
} | ||
|
||
for (std::vector<gaia::AccountIds>::const_iterator iter = accounts.begin(); | ||
iter != accounts.end(); | ||
++iter) { | ||
if (!iter->email.empty()) { | ||
account_infos_.insert(std::make_pair( | ||
iter->account_key, AccountInfo(iter->email, TOKEN_NEEDED))); | ||
} | ||
} | ||
|
||
GetAllNeededTokens(); | ||
} | ||
|
||
void GCMAccountTracker::Stop() { | ||
DCHECK(!shutdown_called_); | ||
account_tracker_->RemoveObserver(this); | ||
pending_token_requests_.clear(); | ||
} | ||
|
||
void GCMAccountTracker::OnAccountAdded(const gaia::AccountIds& ids) { | ||
// We listen for the account signing in, which happens after account is added. | ||
} | ||
|
||
void GCMAccountTracker::OnAccountRemoved(const gaia::AccountIds& ids) { | ||
// We listen for the account signing out, which happens before account is | ||
// removed. | ||
} | ||
|
||
void GCMAccountTracker::OnAccountSignInChanged(const gaia::AccountIds& ids, | ||
bool is_signed_in) { | ||
if (is_signed_in) | ||
OnAccountSignedIn(ids); | ||
else | ||
OnAccountSignedOut(ids); | ||
} | ||
|
||
void GCMAccountTracker::OnGetTokenSuccess( | ||
const OAuth2TokenService::Request* request, | ||
const std::string& access_token, | ||
const base::Time& expiration_time) { | ||
DCHECK(request); | ||
DCHECK(!request->GetAccountId().empty()); | ||
|
||
AccountInfos::iterator iter = account_infos_.find(request->GetAccountId()); | ||
DCHECK(iter != account_infos_.end()); | ||
if (iter != account_infos_.end()) { | ||
DCHECK(iter->second.state == GETTING_TOKEN || | ||
iter->second.state == ACCOUNT_REMOVED); | ||
// If OnAccountSignedOut(..) was called most recently, account is kept in | ||
// ACCOUNT_REMOVED state. | ||
if (iter->second.state == GETTING_TOKEN) { | ||
iter->second.state = TOKEN_PRESENT; | ||
iter->second.access_token = access_token; | ||
} | ||
} | ||
|
||
DeleteTokenRequest(request); | ||
CompleteCollectingTokens(); | ||
} | ||
|
||
void GCMAccountTracker::OnGetTokenFailure( | ||
const OAuth2TokenService::Request* request, | ||
const GoogleServiceAuthError& error) { | ||
DCHECK(request); | ||
DCHECK(!request->GetAccountId().empty()); | ||
|
||
AccountInfos::iterator iter = account_infos_.find(request->GetAccountId()); | ||
DCHECK(iter != account_infos_.end()); | ||
if (iter != account_infos_.end()) { | ||
DCHECK(iter->second.state == GETTING_TOKEN || | ||
iter->second.state == ACCOUNT_REMOVED); | ||
// If OnAccountSignedOut(..) was called most recently, account is kept in | ||
// ACCOUNT_REMOVED state. | ||
if (iter->second.state == GETTING_TOKEN) | ||
iter->second.state = TOKEN_NEEDED; | ||
} | ||
|
||
DeleteTokenRequest(request); | ||
CompleteCollectingTokens(); | ||
} | ||
|
||
void GCMAccountTracker::CompleteCollectingTokens() { | ||
DCHECK(!callback_.is_null()); | ||
// Wait for gaia::AccountTracker to be done with fetching the user info, as | ||
// well as all of the pending token requests from GCMAccountTracker to be done | ||
// before you report the results. | ||
if (!account_tracker_->IsAllUserInfoFetched() || | ||
!pending_token_requests_.empty()) { | ||
return; | ||
} | ||
|
||
bool account_removed = false; | ||
std::map<std::string, std::string> account_tokens; | ||
for (AccountInfos::iterator iter = account_infos_.begin(); | ||
iter != account_infos_.end();) { | ||
switch (iter->second.state) { | ||
case ACCOUNT_REMOVED: | ||
// We only mark accounts as removed when there was an account that was | ||
// explicitly signed out. | ||
account_removed = true; | ||
// We also stop tracking the account, now that it will be reported as | ||
// removed. | ||
account_infos_.erase(iter++); | ||
break; | ||
|
||
case TOKEN_PRESENT: | ||
account_tokens[iter->second.email] = iter->second.access_token; | ||
++iter; | ||
break; | ||
|
||
case GETTING_TOKEN: | ||
// This should not happen, as we are making a check that there are no | ||
// pending requests above. | ||
NOTREACHED(); | ||
++iter; | ||
break; | ||
|
||
case TOKEN_NEEDED: | ||
// We failed to fetch an access token for the account, but it has not | ||
// been signed out (perhaps there is a network issue). We don't report | ||
// it, but next time there is a sign-in change we will update its state. | ||
++iter; | ||
break; | ||
} | ||
} | ||
|
||
callback_.Run(account_tokens, account_removed); | ||
} | ||
|
||
void GCMAccountTracker::DeleteTokenRequest( | ||
const OAuth2TokenService::Request* request) { | ||
ScopedVector<OAuth2TokenService::Request>::iterator iter = std::find( | ||
pending_token_requests_.begin(), pending_token_requests_.end(), request); | ||
if (iter != pending_token_requests_.end()) | ||
pending_token_requests_.erase(iter); | ||
} | ||
|
||
void GCMAccountTracker::GetAllNeededTokens() { | ||
for (AccountInfos::iterator iter = account_infos_.begin(); | ||
iter != account_infos_.end(); | ||
++iter) { | ||
if (iter->second.state == TOKEN_NEEDED) | ||
GetToken(iter); | ||
} | ||
} | ||
|
||
void GCMAccountTracker::GetToken(AccountInfos::iterator& account_iter) { | ||
DCHECK(GetTokenService()); | ||
DCHECK_EQ(account_iter->second.state, TOKEN_NEEDED); | ||
|
||
OAuth2TokenService::ScopeSet scopes; | ||
scopes.insert(kGCMGroupServerScope); | ||
scoped_ptr<OAuth2TokenService::Request> request = | ||
GetTokenService()->StartRequest(account_iter->first, scopes, this); | ||
|
||
pending_token_requests_.push_back(request.release()); | ||
account_iter->second.state = GETTING_TOKEN; | ||
} | ||
|
||
void GCMAccountTracker::OnAccountSignedIn(const gaia::AccountIds& ids) { | ||
AccountInfos::iterator iter = account_infos_.find(ids.account_key); | ||
if (iter == account_infos_.end()) { | ||
DCHECK(!ids.email.empty()); | ||
account_infos_.insert( | ||
std::make_pair(ids.account_key, AccountInfo(ids.email, TOKEN_NEEDED))); | ||
} else if (iter->second.state == ACCOUNT_REMOVED) { | ||
iter->second.state = TOKEN_NEEDED; | ||
} | ||
|
||
GetAllNeededTokens(); | ||
} | ||
|
||
void GCMAccountTracker::OnAccountSignedOut(const gaia::AccountIds& ids) { | ||
AccountInfos::iterator iter = account_infos_.find(ids.account_key); | ||
if (iter == account_infos_.end()) | ||
return; | ||
|
||
iter->second.access_token.clear(); | ||
iter->second.state = ACCOUNT_REMOVED; | ||
CompleteCollectingTokens(); | ||
} | ||
|
||
OAuth2TokenService* GCMAccountTracker::GetTokenService() { | ||
DCHECK(account_tracker_->identity_provider()); | ||
return account_tracker_->identity_provider()->GetTokenService(); | ||
} | ||
|
||
} // namespace gcm |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
// 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. | ||
|
||
#ifndef CHROME_BROWSER_SERVICES_GCM_GCM_ACCOUNT_TRACKER_H_ | ||
#define CHROME_BROWSER_SERVICES_GCM_GCM_ACCOUNT_TRACKER_H_ | ||
|
||
#include <map> | ||
#include <string> | ||
|
||
#include "base/memory/scoped_vector.h" | ||
#include "google_apis/gaia/account_tracker.h" | ||
#include "google_apis/gaia/oauth2_token_service.h" | ||
|
||
namespace gcm { | ||
|
||
// Class for reporting back which accounts are signed into. It is only meant to | ||
// be used when the user is signed into sync. | ||
class GCMAccountTracker : public gaia::AccountTracker::Observer, | ||
public OAuth2TokenService::Consumer { | ||
public: | ||
// State of the account. | ||
// Allowed transitions: | ||
// TOKEN_NEEDED - account info was created. | ||
// TOKEN_NEEDED -> GETTING_TOKEN - access token was requested. | ||
// GETTING_TOKEN -> TOKEN_NEEDED - access token fetching failed. | ||
// GETTING_TOKEN -> TOKEN_PRESENT - access token fetching succeeded. | ||
// GETTING_TOKEN -> ACCOUNT_REMOVED - account was removed. | ||
// TOKEN_NEEDED -> ACCOUNT_REMOVED - account was removed. | ||
// TOKEN_PRESENT -> ACCOUNT_REMOVED - account was removed. | ||
enum AccountState { | ||
TOKEN_NEEDED, // Needs a token (AccountInfo was recently created or | ||
// token request failed). | ||
GETTING_TOKEN, // There is a pending token request. | ||
TOKEN_PRESENT, // We have a token for the account. | ||
ACCOUNT_REMOVED, // Account was removed, and we didn't report it yet. | ||
}; | ||
|
||
// Stores necessary account information and state of token fetching. | ||
struct AccountInfo { | ||
AccountInfo(const std::string& email, AccountState state); | ||
~AccountInfo(); | ||
|
||
// Email address of the tracked account. | ||
std::string email; | ||
// OAuth2 access token, when |state| is TOKEN_PRESENT. | ||
std::string access_token; | ||
// Status of the token fetching. | ||
AccountState state; | ||
}; | ||
|
||
// Callback for the GetAccountsForCheckin call. |account_tokens| maps email | ||
// addresses to OAuth2 access tokens. |account_removed| indicates whether an | ||
// account has been removed since the last time the callback was called. | ||
typedef base::Callback< | ||
void(const std::map<std::string, std::string>& account_tokens, | ||
bool account_removed)> UpdateAccountsCallback; | ||
|
||
// Creates an instance of GCMAccountTracker. |account_tracker| is used to | ||
// deliver information about the account, while |callback| will be called | ||
// once all of the accounts have been fetched a necessary OAuth2 token, as | ||
// many times as the list of accounts is stable, meaning that all accounts | ||
// are known and there is no related activity in progress for them, like | ||
// fetching OAuth2 tokens. | ||
GCMAccountTracker(scoped_ptr<gaia::AccountTracker> account_tracker, | ||
const UpdateAccountsCallback& callback); | ||
virtual ~GCMAccountTracker(); | ||
|
||
// Shuts down the tracker ensuring a proper clean up. After Shutdown() is | ||
// called Start() and Stop() should no longer be used. Must be called before | ||
// destruction. | ||
void Shutdown(); | ||
|
||
// Starts tracking accounts. | ||
void Start(); | ||
// Stops tracking accounts. Cancels all of the pending token requests. | ||
void Stop(); | ||
|
||
private: | ||
// Maps account keys to account states. Keyed by account_ids as used by | ||
// OAuth2TokenService. | ||
typedef std::map<std::string, AccountInfo> AccountInfos; | ||
|
||
// AccountTracker::Observer overrides. | ||
virtual void OnAccountAdded(const gaia::AccountIds& ids) OVERRIDE; | ||
virtual void OnAccountRemoved(const gaia::AccountIds& ids) OVERRIDE; | ||
virtual void OnAccountSignInChanged(const gaia::AccountIds& ids, | ||
bool is_signed_in) OVERRIDE; | ||
|
||
// OAuth2TokenService::Consumer overrides. | ||
virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request, | ||
const std::string& access_token, | ||
const base::Time& expiration_time) OVERRIDE; | ||
virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request, | ||
const GoogleServiceAuthError& error) OVERRIDE; | ||
|
||
// Report the list of accounts with OAuth2 tokens back using the |callback_| | ||
// function. If there are token requests in progress, do nothing. | ||
void CompleteCollectingTokens(); | ||
// Deletes a token request. Should be called from OnGetTokenSuccess(..) or | ||
// OnGetTokenFailure(..). | ||
void DeleteTokenRequest(const OAuth2TokenService::Request* request); | ||
// Checks on all known accounts, and calls GetToken(..) for those with | ||
// |state == TOKEN_NEEDED|. | ||
void GetAllNeededTokens(); | ||
// Starts fetching the OAuth2 token for the GCM group scope. | ||
void GetToken(AccountInfos::iterator& account_iter); | ||
|
||
// Handling of actual sign in and sign out for accounts. | ||
void OnAccountSignedIn(const gaia::AccountIds& ids); | ||
void OnAccountSignedOut(const gaia::AccountIds& ids); | ||
|
||
OAuth2TokenService* GetTokenService(); | ||
|
||
// Account tracker. | ||
scoped_ptr<gaia::AccountTracker> account_tracker_; | ||
|
||
// Callback to be called after all of the account and OAuth2 tokens are | ||
// collected. | ||
UpdateAccountsCallback callback_; | ||
|
||
// State of the account. | ||
AccountInfos account_infos_; | ||
|
||
// Indicates whether shutdown has been called. | ||
bool shutdown_called_; | ||
|
||
ScopedVector<OAuth2TokenService::Request> pending_token_requests_; | ||
|
||
DISALLOW_COPY_AND_ASSIGN(GCMAccountTracker); | ||
}; | ||
|
||
} // namespace gcm | ||
|
||
#endif // CHROME_BROWSER_SERVICES_GCM_GCM_ACCOUNT_TRACKER_H_ |
Oops, something went wrong.