// 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 "base/bind.h" #include "base/location.h" #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" #include "components/gcm_driver/gcm_driver.h" #include "components/invalidation/gcm_invalidation_bridge.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" #include "components/signin/core/browser/signin_manager.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/identity_provider.h" namespace invalidation { namespace { // For 3rd party developers SenderId should come from application dashboard when // server side application is registered with Google. Android invalidations use // legacy format where gmail account can be specificed. Below value is copied // from Android. const char kInvalidationsSenderId[] = "ipc.invalidation@gmail.com"; // In Android world AppId is provided by operating system and should // match package name and hash of application. In desktop world these values // are arbitrary and not verified/enforced by registration service (yet). const char kInvalidationsAppId[] = "com.google.chrome.invalidations"; // Cacheinvalidation specific gcm message keys. const char kContentKey[] = "content"; const char kEchoTokenKey[] = "echo-token"; } // namespace // Core should be very simple class that implements GCMNetwrokChannelDelegate // and passes all calls to GCMInvalidationBridge. All calls should be serialized // through GCMInvalidationBridge to avoid race conditions. class GCMInvalidationBridge::Core : public syncer::GCMNetworkChannelDelegate, public base::NonThreadSafe { public: Core(base::WeakPtr bridge, scoped_refptr ui_thread_task_runner); virtual ~Core(); // syncer::GCMNetworkChannelDelegate implementation. virtual void Initialize() OVERRIDE; virtual void RequestToken(RequestTokenCallback callback) OVERRIDE; virtual void InvalidateToken(const std::string& token) OVERRIDE; virtual void Register(RegisterCallback callback) OVERRIDE; virtual void SetMessageReceiver(MessageCallback callback) OVERRIDE; void RequestTokenFinished(RequestTokenCallback callback, const GoogleServiceAuthError& error, const std::string& token); void RegisterFinished(RegisterCallback callback, const std::string& registration_id, gcm::GCMClient::Result result); void OnIncomingMessage(const std::string& message, const std::string& echo_token); private: base::WeakPtr bridge_; scoped_refptr ui_thread_task_runner_; MessageCallback message_callback_; base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(Core); }; GCMInvalidationBridge::Core::Core( base::WeakPtr bridge, scoped_refptr ui_thread_task_runner) : bridge_(bridge), ui_thread_task_runner_(ui_thread_task_runner), weak_factory_(this) { // Core is created on UI thread but all calls happen on IO thread. DetachFromThread(); } GCMInvalidationBridge::Core::~Core() {} void GCMInvalidationBridge::Core::Initialize() { DCHECK(CalledOnValidThread()); // Pass core WeapPtr and TaskRunner to GCMInvalidationBridge for it to be able // to post back. ui_thread_task_runner_->PostTask( FROM_HERE, base::Bind(&GCMInvalidationBridge::CoreInitializationDone, bridge_, weak_factory_.GetWeakPtr(), base::ThreadTaskRunnerHandle::Get())); } void GCMInvalidationBridge::Core::RequestToken(RequestTokenCallback callback) { DCHECK(CalledOnValidThread()); ui_thread_task_runner_->PostTask( FROM_HERE, base::Bind(&GCMInvalidationBridge::RequestToken, bridge_, callback)); } void GCMInvalidationBridge::Core::InvalidateToken(const std::string& token) { DCHECK(CalledOnValidThread()); ui_thread_task_runner_->PostTask( FROM_HERE, base::Bind(&GCMInvalidationBridge::InvalidateToken, bridge_, token)); } void GCMInvalidationBridge::Core::Register(RegisterCallback callback) { DCHECK(CalledOnValidThread()); ui_thread_task_runner_->PostTask( FROM_HERE, base::Bind(&GCMInvalidationBridge::Register, bridge_, callback)); } void GCMInvalidationBridge::Core::SetMessageReceiver(MessageCallback callback) { message_callback_ = callback; ui_thread_task_runner_->PostTask( FROM_HERE, base::Bind(&GCMInvalidationBridge::SubscribeForIncomingMessages, bridge_)); } void GCMInvalidationBridge::Core::RequestTokenFinished( RequestTokenCallback callback, const GoogleServiceAuthError& error, const std::string& token) { DCHECK(CalledOnValidThread()); callback.Run(error, token); } void GCMInvalidationBridge::Core::RegisterFinished( RegisterCallback callback, const std::string& registration_id, gcm::GCMClient::Result result) { DCHECK(CalledOnValidThread()); callback.Run(registration_id, result); } void GCMInvalidationBridge::Core::OnIncomingMessage( const std::string& message, const std::string& echo_token) { DCHECK(!message_callback_.is_null()); message_callback_.Run(message, echo_token); } GCMInvalidationBridge::GCMInvalidationBridge( gcm::GCMDriver* gcm_driver, IdentityProvider* identity_provider) : OAuth2TokenService::Consumer("gcm_network_channel"), gcm_driver_(gcm_driver), identity_provider_(identity_provider), subscribed_for_incoming_messages_(false), weak_factory_(this) {} GCMInvalidationBridge::~GCMInvalidationBridge() { if (subscribed_for_incoming_messages_) gcm_driver_->RemoveAppHandler(kInvalidationsAppId); } scoped_ptr GCMInvalidationBridge::CreateDelegate() { DCHECK(CalledOnValidThread()); scoped_ptr core(new Core( weak_factory_.GetWeakPtr(), base::ThreadTaskRunnerHandle::Get())); return core.Pass(); } void GCMInvalidationBridge::CoreInitializationDone( base::WeakPtr core, scoped_refptr core_thread_task_runner) { DCHECK(CalledOnValidThread()); core_ = core; core_thread_task_runner_ = core_thread_task_runner; } void GCMInvalidationBridge::RequestToken( syncer::GCMNetworkChannelDelegate::RequestTokenCallback callback) { DCHECK(CalledOnValidThread()); if (access_token_request_ != NULL) { // Report previous request as cancelled. GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED); std::string access_token; core_thread_task_runner_->PostTask( FROM_HERE, base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished, core_, request_token_callback_, error, access_token)); } request_token_callback_ = callback; OAuth2TokenService::ScopeSet scopes; scopes.insert(GaiaConstants::kChromeSyncOAuth2Scope); access_token_request_ = identity_provider_->GetTokenService()->StartRequest( identity_provider_->GetActiveAccountId(), scopes, this); } void GCMInvalidationBridge::OnGetTokenSuccess( const OAuth2TokenService::Request* request, const std::string& access_token, const base::Time& expiration_time) { DCHECK(CalledOnValidThread()); DCHECK_EQ(access_token_request_, request); core_thread_task_runner_->PostTask( FROM_HERE, base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished, core_, request_token_callback_, GoogleServiceAuthError::AuthErrorNone(), access_token)); request_token_callback_.Reset(); access_token_request_.reset(); } void GCMInvalidationBridge::OnGetTokenFailure( const OAuth2TokenService::Request* request, const GoogleServiceAuthError& error) { DCHECK(CalledOnValidThread()); DCHECK_EQ(access_token_request_, request); core_thread_task_runner_->PostTask( FROM_HERE, base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished, core_, request_token_callback_, error, std::string())); request_token_callback_.Reset(); access_token_request_.reset(); } void GCMInvalidationBridge::InvalidateToken(const std::string& token) { DCHECK(CalledOnValidThread()); OAuth2TokenService::ScopeSet scopes; scopes.insert(GaiaConstants::kChromeSyncOAuth2Scope); identity_provider_->GetTokenService()->InvalidateToken( identity_provider_->GetActiveAccountId(), scopes, token); } void GCMInvalidationBridge::Register( syncer::GCMNetworkChannelDelegate::RegisterCallback callback) { DCHECK(CalledOnValidThread()); // No-op if GCMClient is disabled. if (gcm_driver_ == NULL) return; std::vector sender_ids; sender_ids.push_back(kInvalidationsSenderId); gcm_driver_->Register(kInvalidationsAppId, sender_ids, base::Bind(&GCMInvalidationBridge::RegisterFinished, weak_factory_.GetWeakPtr(), callback)); } void GCMInvalidationBridge::RegisterFinished( syncer::GCMNetworkChannelDelegate::RegisterCallback callback, const std::string& registration_id, gcm::GCMClient::Result result) { DCHECK(CalledOnValidThread()); core_thread_task_runner_->PostTask( FROM_HERE, base::Bind(&GCMInvalidationBridge::Core::RegisterFinished, core_, callback, registration_id, result)); } void GCMInvalidationBridge::SubscribeForIncomingMessages() { // No-op if GCMClient is disabled. if (gcm_driver_ == NULL) return; DCHECK(!subscribed_for_incoming_messages_); gcm_driver_->AddAppHandler(kInvalidationsAppId, this); subscribed_for_incoming_messages_ = true; } void GCMInvalidationBridge::ShutdownHandler() { // Nothing to do. } void GCMInvalidationBridge::OnMessage( const std::string& app_id, const gcm::GCMClient::IncomingMessage& message) { gcm::GCMClient::MessageData::const_iterator it; std::string content; std::string echo_token; it = message.data.find(kContentKey); if (it != message.data.end()) content = it->second; it = message.data.find(kEchoTokenKey); if (it != message.data.end()) echo_token = it->second; core_thread_task_runner_->PostTask( FROM_HERE, base::Bind(&GCMInvalidationBridge::Core::OnIncomingMessage, core_, content, echo_token)); } void GCMInvalidationBridge::OnMessagesDeleted(const std::string& app_id) { // Cacheinvalidation doesn't use long lived non-collapsable messages with GCM. // Android implementation of cacheinvalidation doesn't handle MessagesDeleted // callback so this should be no-op in desktop version as well. } void GCMInvalidationBridge::OnSendError( const std::string& app_id, const gcm::GCMClient::SendErrorDetails& send_error_details) { // cacheinvalidation doesn't send messages over GCM. NOTREACHED(); } } // namespace invalidation