|  | // 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 "components/gcm_driver/gcm_driver_desktop.h" | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/bind_helpers.h" | 
|  | #include "base/files/file_path.h" | 
|  | #include "base/location.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/metrics/histogram_macros.h" | 
|  | #include "base/profiler/scoped_tracker.h" | 
|  | #include "base/sequenced_task_runner.h" | 
|  | #include "base/threading/sequenced_worker_pool.h" | 
|  | #include "build/build_config.h" | 
|  | #include "components/gcm_driver/gcm_account_mapper.h" | 
|  | #include "components/gcm_driver/gcm_app_handler.h" | 
|  | #include "components/gcm_driver/gcm_channel_status_syncer.h" | 
|  | #include "components/gcm_driver/gcm_client_factory.h" | 
|  | #include "components/gcm_driver/gcm_delayed_task_controller.h" | 
|  | #include "components/gcm_driver/instance_id/instance_id_impl.h" | 
|  | #include "components/gcm_driver/system_encryptor.h" | 
|  | #include "google_apis/gcm/engine/account_mapping.h" | 
|  | #include "net/base/ip_endpoint.h" | 
|  | #include "net/url_request/url_request_context_getter.h" | 
|  |  | 
|  | #if defined(OS_CHROMEOS) | 
|  | #include "components/timers/alarm_timer_chromeos.h" | 
|  | #endif | 
|  |  | 
|  | namespace gcm { | 
|  |  | 
|  | class GCMDriverDesktop::IOWorker : public GCMClient::Delegate { | 
|  | public: | 
|  | // Called on UI thread. | 
|  | IOWorker(const scoped_refptr<base::SequencedTaskRunner>& ui_thread, | 
|  | const scoped_refptr<base::SequencedTaskRunner>& io_thread); | 
|  | virtual ~IOWorker(); | 
|  |  | 
|  | // Overridden from GCMClient::Delegate: | 
|  | // Called on IO thread. | 
|  | void OnRegisterFinished(const linked_ptr<RegistrationInfo>& registration_info, | 
|  | const std::string& registration_id, | 
|  | GCMClient::Result result) override; | 
|  | void OnUnregisterFinished( | 
|  | const linked_ptr<RegistrationInfo>& registration_info, | 
|  | GCMClient::Result result) override; | 
|  | void OnSendFinished(const std::string& app_id, | 
|  | const std::string& message_id, | 
|  | GCMClient::Result result) override; | 
|  | void OnMessageReceived(const std::string& app_id, | 
|  | const IncomingMessage& message) override; | 
|  | void OnMessagesDeleted(const std::string& app_id) override; | 
|  | void OnMessageSendError( | 
|  | const std::string& app_id, | 
|  | const GCMClient::SendErrorDetails& send_error_details) override; | 
|  | void OnSendAcknowledged(const std::string& app_id, | 
|  | const std::string& message_id) override; | 
|  | void OnGCMReady(const std::vector<AccountMapping>& account_mappings, | 
|  | const base::Time& last_token_fetch_time) override; | 
|  | void OnActivityRecorded() override; | 
|  | void OnConnected(const net::IPEndPoint& ip_endpoint) override; | 
|  | void OnDisconnected() override; | 
|  |  | 
|  | // Called on IO thread. | 
|  | void Initialize( | 
|  | std::unique_ptr<GCMClientFactory> gcm_client_factory, | 
|  | const GCMClient::ChromeBuildInfo& chrome_build_info, | 
|  | const base::FilePath& store_path, | 
|  | const scoped_refptr<net::URLRequestContextGetter>& request_context, | 
|  | const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner); | 
|  | void Start(GCMClient::StartMode start_mode, | 
|  | const base::WeakPtr<GCMDriverDesktop>& service); | 
|  | void Stop(); | 
|  | void Register(const std::string& app_id, | 
|  | const std::vector<std::string>& sender_ids); | 
|  | void Unregister(const std::string& app_id); | 
|  | void Send(const std::string& app_id, | 
|  | const std::string& receiver_id, | 
|  | const OutgoingMessage& message); | 
|  | void GetGCMStatistics(GCMDriver::ClearActivityLogs clear_logs); | 
|  | void SetGCMRecording(bool recording); | 
|  |  | 
|  | void SetAccountTokens( | 
|  | const std::vector<GCMClient::AccountTokenInfo>& account_tokens); | 
|  | void UpdateAccountMapping(const AccountMapping& account_mapping); | 
|  | void RemoveAccountMapping(const std::string& account_id); | 
|  | void SetLastTokenFetchTime(const base::Time& time); | 
|  | void WakeFromSuspendForHeartbeat(bool wake); | 
|  | void AddHeartbeatInterval(const std::string& scope, int interval_ms); | 
|  | void RemoveHeartbeatInterval(const std::string& scope); | 
|  |  | 
|  | void AddInstanceIDData(const std::string& app_id, | 
|  | const std::string& instance_id, | 
|  | const std::string& extra_data); | 
|  | void RemoveInstanceIDData(const std::string& app_id); | 
|  | void GetInstanceIDData(const std::string& app_id); | 
|  | void GetToken(const std::string& app_id, | 
|  | const std::string& authorized_entity, | 
|  | const std::string& scope, | 
|  | const std::map<std::string, std::string>& options); | 
|  | void DeleteToken(const std::string& app_id, | 
|  | const std::string& authorized_entity, | 
|  | const std::string& scope); | 
|  |  | 
|  | void RecordDecryptionFailure(const std::string& app_id, | 
|  | GCMEncryptionProvider::DecryptionResult result); | 
|  |  | 
|  | // For testing purpose. Can be called from UI thread. Use with care. | 
|  | GCMClient* gcm_client_for_testing() const { return gcm_client_.get(); } | 
|  |  | 
|  | private: | 
|  | scoped_refptr<base::SequencedTaskRunner> ui_thread_; | 
|  | scoped_refptr<base::SequencedTaskRunner> io_thread_; | 
|  |  | 
|  | base::WeakPtr<GCMDriverDesktop> service_; | 
|  |  | 
|  | std::unique_ptr<GCMClient> gcm_client_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(IOWorker); | 
|  | }; | 
|  |  | 
|  | GCMDriverDesktop::IOWorker::IOWorker( | 
|  | const scoped_refptr<base::SequencedTaskRunner>& ui_thread, | 
|  | const scoped_refptr<base::SequencedTaskRunner>& io_thread) | 
|  | : ui_thread_(ui_thread), | 
|  | io_thread_(io_thread) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  | } | 
|  |  | 
|  | GCMDriverDesktop::IOWorker::~IOWorker() { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::Initialize( | 
|  | std::unique_ptr<GCMClientFactory> gcm_client_factory, | 
|  | const GCMClient::ChromeBuildInfo& chrome_build_info, | 
|  | const base::FilePath& store_path, | 
|  | const scoped_refptr<net::URLRequestContextGetter>& request_context, | 
|  | const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) { | 
|  | // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed. | 
|  | tracked_objects::ScopedTracker tracking_profile( | 
|  | FROM_HERE_WITH_EXPLICIT_FUNCTION( | 
|  | "477117 GCMDriverDesktop::IOWorker::Initialize")); | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | gcm_client_ = gcm_client_factory->BuildInstance(); | 
|  |  | 
|  | gcm_client_->Initialize( | 
|  | chrome_build_info, store_path, blocking_task_runner, request_context, | 
|  | base::WrapUnique<Encryptor>(new SystemEncryptor), this); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::OnRegisterFinished( | 
|  | const linked_ptr<RegistrationInfo>& registration_info, | 
|  | const std::string& registration_id, | 
|  | GCMClient::Result result) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | const GCMRegistrationInfo* gcm_registration_info = | 
|  | GCMRegistrationInfo::FromRegistrationInfo(registration_info.get()); | 
|  | if (gcm_registration_info) { | 
|  | ui_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::RegisterFinished, | 
|  | service_, | 
|  | gcm_registration_info->app_id, | 
|  | registration_id, | 
|  | result)); | 
|  | } | 
|  |  | 
|  | const InstanceIDTokenInfo* instance_id_token_info = | 
|  | InstanceIDTokenInfo::FromRegistrationInfo(registration_info.get()); | 
|  | if (instance_id_token_info) { | 
|  | ui_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::GetTokenFinished, | 
|  | service_, | 
|  | instance_id_token_info->app_id, | 
|  | instance_id_token_info->authorized_entity, | 
|  | instance_id_token_info->scope, | 
|  | registration_id, | 
|  | result)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::OnUnregisterFinished( | 
|  | const linked_ptr<RegistrationInfo>& registration_info, | 
|  | GCMClient::Result result) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | const GCMRegistrationInfo* gcm_registration_info = | 
|  | GCMRegistrationInfo::FromRegistrationInfo(registration_info.get()); | 
|  | if (gcm_registration_info) { | 
|  | ui_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::RemoveEncryptionInfoAfterUnregister, | 
|  | service_, | 
|  | gcm_registration_info->app_id, | 
|  | result)); | 
|  | } | 
|  |  | 
|  | const InstanceIDTokenInfo* instance_id_token_info = | 
|  | InstanceIDTokenInfo::FromRegistrationInfo(registration_info.get()); | 
|  | if (instance_id_token_info) { | 
|  | ui_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::DeleteTokenFinished, | 
|  | service_, | 
|  | instance_id_token_info->app_id, | 
|  | instance_id_token_info->authorized_entity, | 
|  | instance_id_token_info->scope, | 
|  | result)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::OnSendFinished(const std::string& app_id, | 
|  | const std::string& message_id, | 
|  | GCMClient::Result result) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | ui_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::SendFinished, service_, app_id, message_id, | 
|  | result)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::OnMessageReceived( | 
|  | const std::string& app_id, | 
|  | const IncomingMessage& message) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | ui_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::MessageReceived, | 
|  | service_, | 
|  | app_id, | 
|  | message)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::OnMessagesDeleted(const std::string& app_id) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | ui_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::MessagesDeleted, service_, app_id)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::OnMessageSendError( | 
|  | const std::string& app_id, | 
|  | const GCMClient::SendErrorDetails& send_error_details) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | ui_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::MessageSendError, service_, app_id, | 
|  | send_error_details)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::OnSendAcknowledged( | 
|  | const std::string& app_id, | 
|  | const std::string& message_id) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | ui_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind( | 
|  | &GCMDriverDesktop::SendAcknowledged, service_, app_id, message_id)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::OnGCMReady( | 
|  | const std::vector<AccountMapping>& account_mappings, | 
|  | const base::Time& last_token_fetch_time) { | 
|  | ui_thread_->PostTask(FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::GCMClientReady, | 
|  | service_, | 
|  | account_mappings, | 
|  | last_token_fetch_time)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::OnActivityRecorded() { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  | // When an activity is recorded, get all the stats and refresh the UI of | 
|  | // gcm-internals page. | 
|  | GetGCMStatistics(GCMDriver::KEEP_LOGS); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::OnConnected( | 
|  | const net::IPEndPoint& ip_endpoint) { | 
|  | ui_thread_->PostTask(FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::OnConnected, | 
|  | service_, | 
|  | ip_endpoint)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::OnDisconnected() { | 
|  | ui_thread_->PostTask(FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::OnDisconnected, service_)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::Start( | 
|  | GCMClient::StartMode start_mode, | 
|  | const base::WeakPtr<GCMDriverDesktop>& service) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | service_ = service; | 
|  | gcm_client_->Start(start_mode); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::Stop() { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | gcm_client_->Stop(); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::Register( | 
|  | const std::string& app_id, | 
|  | const std::vector<std::string>& sender_ids) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | std::unique_ptr<GCMRegistrationInfo> gcm_info(new GCMRegistrationInfo); | 
|  | gcm_info->app_id = app_id; | 
|  | gcm_info->sender_ids = sender_ids; | 
|  | gcm_client_->Register(make_linked_ptr<RegistrationInfo>(gcm_info.release())); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::Unregister(const std::string& app_id) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | std::unique_ptr<GCMRegistrationInfo> gcm_info(new GCMRegistrationInfo); | 
|  | gcm_info->app_id = app_id; | 
|  | gcm_client_->Unregister( | 
|  | make_linked_ptr<RegistrationInfo>(gcm_info.release())); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::Send(const std::string& app_id, | 
|  | const std::string& receiver_id, | 
|  | const OutgoingMessage& message) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | gcm_client_->Send(app_id, receiver_id, message); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::GetGCMStatistics( | 
|  | ClearActivityLogs clear_logs) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  | gcm::GCMClient::GCMStatistics stats; | 
|  |  | 
|  | if (gcm_client_.get()) { | 
|  | if (clear_logs == GCMDriver::CLEAR_LOGS) | 
|  | gcm_client_->ClearActivityLogs(); | 
|  | stats = gcm_client_->GetStatistics(); | 
|  | } | 
|  |  | 
|  | ui_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::GetGCMStatisticsFinished, service_, stats)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::SetGCMRecording(bool recording) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  | gcm::GCMClient::GCMStatistics stats; | 
|  |  | 
|  | if (gcm_client_.get()) { | 
|  | gcm_client_->SetRecording(recording); | 
|  | stats = gcm_client_->GetStatistics(); | 
|  | stats.gcm_client_created = true; | 
|  | } | 
|  |  | 
|  | ui_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::GetGCMStatisticsFinished, service_, stats)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::SetAccountTokens( | 
|  | const std::vector<GCMClient::AccountTokenInfo>& account_tokens) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | if (gcm_client_.get()) | 
|  | gcm_client_->SetAccountTokens(account_tokens); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::UpdateAccountMapping( | 
|  | const AccountMapping& account_mapping) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | if (gcm_client_.get()) | 
|  | gcm_client_->UpdateAccountMapping(account_mapping); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::RemoveAccountMapping( | 
|  | const std::string& account_id) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | if (gcm_client_.get()) | 
|  | gcm_client_->RemoveAccountMapping(account_id); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::SetLastTokenFetchTime(const base::Time& time) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | if (gcm_client_.get()) | 
|  | gcm_client_->SetLastTokenFetchTime(time); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::AddInstanceIDData( | 
|  | const std::string& app_id, | 
|  | const std::string& instance_id, | 
|  | const std::string& extra_data) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | if (gcm_client_.get()) | 
|  | gcm_client_->AddInstanceIDData(app_id, instance_id, extra_data); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::RemoveInstanceIDData( | 
|  | const std::string& app_id) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | if (gcm_client_.get()) | 
|  | gcm_client_->RemoveInstanceIDData(app_id); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::GetInstanceIDData( | 
|  | const std::string& app_id) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | std::string instance_id; | 
|  | std::string extra_data; | 
|  | if (gcm_client_.get()) | 
|  | gcm_client_->GetInstanceIDData(app_id, &instance_id, &extra_data); | 
|  |  | 
|  | ui_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::GetInstanceIDDataFinished, | 
|  | service_, app_id, instance_id, extra_data)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::GetToken( | 
|  | const std::string& app_id, | 
|  | const std::string& authorized_entity, | 
|  | const std::string& scope, | 
|  | const std::map<std::string, std::string>& options) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | std::unique_ptr<InstanceIDTokenInfo> instance_id_token_info( | 
|  | new InstanceIDTokenInfo); | 
|  | instance_id_token_info->app_id = app_id; | 
|  | instance_id_token_info->authorized_entity = authorized_entity; | 
|  | instance_id_token_info->scope = scope; | 
|  | instance_id_token_info->options = options; | 
|  | gcm_client_->Register( | 
|  | make_linked_ptr<RegistrationInfo>(instance_id_token_info.release())); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::DeleteToken( | 
|  | const std::string& app_id, | 
|  | const std::string& authorized_entity, | 
|  | const std::string& scope) { | 
|  | std::unique_ptr<InstanceIDTokenInfo> instance_id_token_info( | 
|  | new InstanceIDTokenInfo); | 
|  | instance_id_token_info->app_id = app_id; | 
|  | instance_id_token_info->authorized_entity = authorized_entity; | 
|  | instance_id_token_info->scope = scope; | 
|  | gcm_client_->Unregister( | 
|  | make_linked_ptr<RegistrationInfo>(instance_id_token_info.release())); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::WakeFromSuspendForHeartbeat(bool wake) { | 
|  | #if defined(OS_CHROMEOS) | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | std::unique_ptr<base::Timer> timer; | 
|  | if (wake) | 
|  | timer.reset(new timers::SimpleAlarmTimer()); | 
|  | else | 
|  | timer.reset(new base::Timer(true, false)); | 
|  |  | 
|  | gcm_client_->UpdateHeartbeatTimer(std::move(timer)); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::AddHeartbeatInterval(const std::string& scope, | 
|  | int interval_ms) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  | gcm_client_->AddHeartbeatInterval(scope, interval_ms); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::RemoveHeartbeatInterval( | 
|  | const std::string& scope) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  | gcm_client_->RemoveHeartbeatInterval(scope); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::IOWorker::RecordDecryptionFailure( | 
|  | const std::string& app_id, | 
|  | GCMEncryptionProvider::DecryptionResult result) { | 
|  | DCHECK(io_thread_->RunsTasksOnCurrentThread()); | 
|  | gcm_client_->RecordDecryptionFailure(app_id, result); | 
|  | } | 
|  |  | 
|  | GCMDriverDesktop::GCMDriverDesktop( | 
|  | std::unique_ptr<GCMClientFactory> gcm_client_factory, | 
|  | const GCMClient::ChromeBuildInfo& chrome_build_info, | 
|  | const std::string& channel_status_request_url, | 
|  | const std::string& user_agent, | 
|  | PrefService* prefs, | 
|  | const base::FilePath& store_path, | 
|  | const scoped_refptr<net::URLRequestContextGetter>& request_context, | 
|  | const scoped_refptr<base::SequencedTaskRunner>& ui_thread, | 
|  | const scoped_refptr<base::SequencedTaskRunner>& io_thread, | 
|  | const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner) | 
|  | : GCMDriver(store_path, blocking_task_runner), | 
|  | gcm_channel_status_syncer_( | 
|  | new GCMChannelStatusSyncer(this, | 
|  | prefs, | 
|  | channel_status_request_url, | 
|  | user_agent, | 
|  | request_context)), | 
|  | signed_in_(false), | 
|  | gcm_started_(false), | 
|  | gcm_enabled_(true), | 
|  | connected_(false), | 
|  | account_mapper_(new GCMAccountMapper(this)), | 
|  | // Setting to max, to make sure it does not prompt for token reporting | 
|  | // Before reading a reasonable value from the DB, which might be never, | 
|  | // in which case the fetching will be triggered. | 
|  | last_token_fetch_time_(base::Time::Max()), | 
|  | ui_thread_(ui_thread), | 
|  | io_thread_(io_thread), | 
|  | wake_from_suspend_enabled_(false), | 
|  | weak_ptr_factory_(this) { | 
|  | gcm_enabled_ = gcm_channel_status_syncer_->gcm_enabled(); | 
|  |  | 
|  | // Create and initialize the GCMClient. Note that this does not initiate the | 
|  | // GCM check-in. | 
|  | io_worker_.reset(new IOWorker(ui_thread, io_thread)); | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::Initialize, | 
|  | base::Unretained(io_worker_.get()), | 
|  | base::Passed(&gcm_client_factory), | 
|  | chrome_build_info, | 
|  | store_path, | 
|  | request_context, | 
|  | blocking_task_runner)); | 
|  | } | 
|  |  | 
|  | GCMDriverDesktop::~GCMDriverDesktop() { | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::Shutdown() { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | Stop(); | 
|  | GCMDriver::Shutdown(); | 
|  |  | 
|  | // Dispose the syncer in order to release the reference to | 
|  | // URLRequestContextGetter that needs to be done before IOThread gets | 
|  | // deleted. | 
|  | gcm_channel_status_syncer_.reset(); | 
|  |  | 
|  | io_thread_->DeleteSoon(FROM_HERE, io_worker_.release()); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::OnSignedIn() { | 
|  | signed_in_ = true; | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::OnSignedOut() { | 
|  | signed_in_ = false; | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::AddAppHandler(const std::string& app_id, | 
|  | GCMAppHandler* handler) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  | GCMDriver::AddAppHandler(app_id, handler); | 
|  |  | 
|  | // Ensures that the GCM service is started when there is an interest. | 
|  | EnsureStarted(GCMClient::DELAYED_START); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::RemoveAppHandler(const std::string& app_id) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  | GCMDriver::RemoveAppHandler(app_id); | 
|  |  | 
|  | // Stops the GCM service when no app intends to consume it. Stop function will | 
|  | // remove the last app handler - account mapper. | 
|  | if (app_handlers().size() == 1) { | 
|  | Stop(); | 
|  | gcm_channel_status_syncer_->Stop(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::AddConnectionObserver(GCMConnectionObserver* observer) { | 
|  | connection_observer_list_.AddObserver(observer); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::RemoveConnectionObserver( | 
|  | GCMConnectionObserver* observer) { | 
|  | connection_observer_list_.RemoveObserver(observer); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::Enable() { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | if (gcm_enabled_) | 
|  | return; | 
|  | gcm_enabled_ = true; | 
|  |  | 
|  | EnsureStarted(GCMClient::DELAYED_START); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::Disable() { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | if (!gcm_enabled_) | 
|  | return; | 
|  | gcm_enabled_ = false; | 
|  |  | 
|  | Stop(); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::Stop() { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | // No need to stop GCM service if not started yet. | 
|  | if (!gcm_started_) | 
|  | return; | 
|  |  | 
|  | account_mapper_->ShutdownHandler(); | 
|  | GCMDriver::RemoveAppHandler(kGCMAccountMapperAppId); | 
|  |  | 
|  | RemoveCachedData(); | 
|  |  | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::Stop, | 
|  | base::Unretained(io_worker_.get()))); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::RegisterImpl( | 
|  | const std::string& app_id, | 
|  | const std::vector<std::string>& sender_ids) { | 
|  | // Delay the register operation until GCMClient is ready. | 
|  | if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { | 
|  | delayed_task_controller_->AddTask(base::Bind(&GCMDriverDesktop::DoRegister, | 
|  | weak_ptr_factory_.GetWeakPtr(), | 
|  | app_id, | 
|  | sender_ids)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | DoRegister(app_id, sender_ids); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::DoRegister(const std::string& app_id, | 
|  | const std::vector<std::string>& sender_ids) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  | if (!HasRegisterCallback(app_id)) { | 
|  | // The callback could have been removed when the app is uninstalled. | 
|  | return; | 
|  | } | 
|  |  | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::Register, | 
|  | base::Unretained(io_worker_.get()), | 
|  | app_id, | 
|  | sender_ids)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::UnregisterImpl(const std::string& app_id) { | 
|  | // Delay the unregister operation until GCMClient is ready. | 
|  | if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { | 
|  | delayed_task_controller_->AddTask( | 
|  | base::Bind(&GCMDriverDesktop::DoUnregister, | 
|  | weak_ptr_factory_.GetWeakPtr(), | 
|  | app_id)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | DoUnregister(app_id); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::DoUnregister(const std::string& app_id) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | // Ask the server to unregister it. There could be a small chance that the | 
|  | // unregister request fails. If this occurs, it does not bring any harm since | 
|  | // we simply reject the messages/events received from the server. | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::Unregister, | 
|  | base::Unretained(io_worker_.get()), | 
|  | app_id)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::SendImpl(const std::string& app_id, | 
|  | const std::string& receiver_id, | 
|  | const OutgoingMessage& message) { | 
|  | // Delay the send operation until all GCMClient is ready. | 
|  | if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { | 
|  | delayed_task_controller_->AddTask(base::Bind(&GCMDriverDesktop::DoSend, | 
|  | weak_ptr_factory_.GetWeakPtr(), | 
|  | app_id, | 
|  | receiver_id, | 
|  | message)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | DoSend(app_id, receiver_id, message); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::DoSend(const std::string& app_id, | 
|  | const std::string& receiver_id, | 
|  | const OutgoingMessage& message) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::Send, | 
|  | base::Unretained(io_worker_.get()), | 
|  | app_id, | 
|  | receiver_id, | 
|  | message)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::RecordDecryptionFailure( | 
|  | const std::string& app_id, | 
|  | GCMEncryptionProvider::DecryptionResult result) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::RecordDecryptionFailure, | 
|  | base::Unretained(io_worker_.get()), | 
|  | app_id, result)); | 
|  | } | 
|  |  | 
|  | GCMClient* GCMDriverDesktop::GetGCMClientForTesting() const { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  | return io_worker_ ? io_worker_->gcm_client_for_testing() : NULL; | 
|  | } | 
|  |  | 
|  | bool GCMDriverDesktop::IsStarted() const { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  | return gcm_started_; | 
|  | } | 
|  |  | 
|  | bool GCMDriverDesktop::IsConnected() const { | 
|  | return connected_; | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::GetGCMStatistics( | 
|  | const GetGCMStatisticsCallback& callback, | 
|  | ClearActivityLogs clear_logs) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  | DCHECK(!callback.is_null()); | 
|  |  | 
|  | request_gcm_statistics_callback_ = callback; | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::GetGCMStatistics, | 
|  | base::Unretained(io_worker_.get()), | 
|  | clear_logs)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::SetGCMRecording(const GetGCMStatisticsCallback& callback, | 
|  | bool recording) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | request_gcm_statistics_callback_ = callback; | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::SetGCMRecording, | 
|  | base::Unretained(io_worker_.get()), | 
|  | recording)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::UpdateAccountMapping( | 
|  | const AccountMapping& account_mapping) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::UpdateAccountMapping, | 
|  | base::Unretained(io_worker_.get()), | 
|  | account_mapping)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::RemoveAccountMapping(const std::string& account_id) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::RemoveAccountMapping, | 
|  | base::Unretained(io_worker_.get()), | 
|  | account_id)); | 
|  | } | 
|  |  | 
|  | base::Time GCMDriverDesktop::GetLastTokenFetchTime() { | 
|  | return last_token_fetch_time_; | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::SetLastTokenFetchTime(const base::Time& time) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | last_token_fetch_time_ = time; | 
|  |  | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::SetLastTokenFetchTime, | 
|  | base::Unretained(io_worker_.get()), | 
|  | time)); | 
|  | } | 
|  |  | 
|  | InstanceIDHandler* GCMDriverDesktop::GetInstanceIDHandlerInternal() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::GetToken( | 
|  | const std::string& app_id, | 
|  | const std::string& authorized_entity, | 
|  | const std::string& scope, | 
|  | const std::map<std::string, std::string>& options, | 
|  | const GetTokenCallback& callback) { | 
|  | DCHECK(!app_id.empty()); | 
|  | DCHECK(!authorized_entity.empty()); | 
|  | DCHECK(!scope.empty()); | 
|  | DCHECK(!callback.is_null()); | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | GCMClient::Result result = EnsureStarted(GCMClient::IMMEDIATE_START); | 
|  | if (result != GCMClient::SUCCESS) { | 
|  | callback.Run(std::string(), result); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // If previous GetToken operation is still in progress, bail out. | 
|  | TokenTuple tuple_key(app_id, authorized_entity, scope); | 
|  | if (get_token_callbacks_.find(tuple_key) != get_token_callbacks_.end()) { | 
|  | callback.Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING); | 
|  | return; | 
|  | } | 
|  |  | 
|  | get_token_callbacks_[tuple_key] = callback; | 
|  |  | 
|  | // Delay the GetToken operation until GCMClient is ready. | 
|  | if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { | 
|  | delayed_task_controller_->AddTask( | 
|  | base::Bind(&GCMDriverDesktop::DoGetToken, | 
|  | weak_ptr_factory_.GetWeakPtr(), | 
|  | app_id, | 
|  | authorized_entity, | 
|  | scope, | 
|  | options)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | DoGetToken(app_id, authorized_entity, scope, options); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::DoGetToken( | 
|  | const std::string& app_id, | 
|  | const std::string& authorized_entity, | 
|  | const std::string& scope, | 
|  | const std::map<std::string, std::string>& options) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | TokenTuple tuple_key(app_id, authorized_entity, scope); | 
|  | auto callback_iter = get_token_callbacks_.find(tuple_key); | 
|  | if (callback_iter == get_token_callbacks_.end()) { | 
|  | // The callback could have been removed when the app is uninstalled. | 
|  | return; | 
|  | } | 
|  |  | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::GetToken, | 
|  | base::Unretained(io_worker_.get()), | 
|  | app_id, | 
|  | authorized_entity, | 
|  | scope, | 
|  | options)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::DeleteToken(const std::string& app_id, | 
|  | const std::string& authorized_entity, | 
|  | const std::string& scope, | 
|  | const DeleteTokenCallback& callback) { | 
|  | DCHECK(!app_id.empty()); | 
|  | DCHECK(!authorized_entity.empty()); | 
|  | DCHECK(!scope.empty()); | 
|  | DCHECK(!callback.is_null()); | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | GCMClient::Result result = EnsureStarted(GCMClient::IMMEDIATE_START); | 
|  | if (result != GCMClient::SUCCESS) { | 
|  | callback.Run(result); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // If previous GetToken operation is still in progress, bail out. | 
|  | TokenTuple tuple_key(app_id, authorized_entity, scope); | 
|  | if (delete_token_callbacks_.find(tuple_key) != | 
|  | delete_token_callbacks_.end()) { | 
|  | callback.Run(GCMClient::ASYNC_OPERATION_PENDING); | 
|  | return; | 
|  | } | 
|  |  | 
|  | delete_token_callbacks_[tuple_key] = callback; | 
|  |  | 
|  | // Delay the DeleteToken operation until GCMClient is ready. | 
|  | if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { | 
|  | delayed_task_controller_->AddTask( | 
|  | base::Bind(&GCMDriverDesktop::DoDeleteToken, | 
|  | weak_ptr_factory_.GetWeakPtr(), | 
|  | app_id, | 
|  | authorized_entity, | 
|  | scope)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | DoDeleteToken(app_id, authorized_entity, scope); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::DoDeleteToken(const std::string& app_id, | 
|  | const std::string& authorized_entity, | 
|  | const std::string& scope) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::DeleteToken, | 
|  | base::Unretained(io_worker_.get()), | 
|  | app_id, | 
|  | authorized_entity, | 
|  | scope)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::AddInstanceIDData( | 
|  | const std::string& app_id, | 
|  | const std::string& instance_id, | 
|  | const std::string& extra_data) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | GCMClient::Result result = EnsureStarted(GCMClient::IMMEDIATE_START); | 
|  | if (result != GCMClient::SUCCESS) | 
|  | return; | 
|  |  | 
|  | // Delay the operation until GCMClient is ready. | 
|  | if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { | 
|  | delayed_task_controller_->AddTask( | 
|  | base::Bind(&GCMDriverDesktop::DoAddInstanceIDData, | 
|  | weak_ptr_factory_.GetWeakPtr(), | 
|  | app_id, | 
|  | instance_id, | 
|  | extra_data)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | DoAddInstanceIDData(app_id, instance_id, extra_data); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::DoAddInstanceIDData( | 
|  | const std::string& app_id, | 
|  | const std::string& instance_id, | 
|  | const std::string& extra_data) { | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::AddInstanceIDData, | 
|  | base::Unretained(io_worker_.get()), | 
|  | app_id, | 
|  | instance_id, | 
|  | extra_data)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::RemoveInstanceIDData(const std::string& app_id) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | GCMClient::Result result = EnsureStarted(GCMClient::IMMEDIATE_START); | 
|  | if (result != GCMClient::SUCCESS) | 
|  | return; | 
|  |  | 
|  | // Delay the operation until GCMClient is ready. | 
|  | if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { | 
|  | delayed_task_controller_->AddTask( | 
|  | base::Bind(&GCMDriverDesktop::DoRemoveInstanceIDData, | 
|  | weak_ptr_factory_.GetWeakPtr(), | 
|  | app_id)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | DoRemoveInstanceIDData(app_id); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::DoRemoveInstanceIDData(const std::string& app_id) { | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::RemoveInstanceIDData, | 
|  | base::Unretained(io_worker_.get()), | 
|  | app_id)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::GetInstanceIDData( | 
|  | const std::string& app_id, | 
|  | const GetInstanceIDDataCallback& callback) { | 
|  | DCHECK(!get_instance_id_data_callbacks_.count(app_id)); | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | GCMClient::Result result = EnsureStarted(GCMClient::IMMEDIATE_START); | 
|  | if (result != GCMClient::SUCCESS) { | 
|  | callback.Run(std::string(), std::string()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | get_instance_id_data_callbacks_[app_id] = callback; | 
|  |  | 
|  | // Delay the operation until GCMClient is ready. | 
|  | if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { | 
|  | delayed_task_controller_->AddTask( | 
|  | base::Bind(&GCMDriverDesktop::DoGetInstanceIDData, | 
|  | weak_ptr_factory_.GetWeakPtr(), | 
|  | app_id)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | DoGetInstanceIDData(app_id); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::DoGetInstanceIDData(const std::string& app_id) { | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::GetInstanceIDData, | 
|  | base::Unretained(io_worker_.get()), | 
|  | app_id)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::GetInstanceIDDataFinished( | 
|  | const std::string& app_id, | 
|  | const std::string& instance_id, | 
|  | const std::string& extra_data) { | 
|  | DCHECK(get_instance_id_data_callbacks_.count(app_id)); | 
|  | get_instance_id_data_callbacks_[app_id].Run(instance_id, extra_data); | 
|  | get_instance_id_data_callbacks_.erase(app_id); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::GetTokenFinished(const std::string& app_id, | 
|  | const std::string& authorized_entity, | 
|  | const std::string& scope, | 
|  | const std::string& token, | 
|  | GCMClient::Result result) { | 
|  | TokenTuple tuple_key(app_id, authorized_entity, scope); | 
|  | auto callback_iter = get_token_callbacks_.find(tuple_key); | 
|  | if (callback_iter == get_token_callbacks_.end()) { | 
|  | // The callback could have been removed when the app is uninstalled. | 
|  | return; | 
|  | } | 
|  |  | 
|  | GetTokenCallback callback = callback_iter->second; | 
|  | get_token_callbacks_.erase(callback_iter); | 
|  | callback.Run(token, result); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::DeleteTokenFinished(const std::string& app_id, | 
|  | const std::string& authorized_entity, | 
|  | const std::string& scope, | 
|  | GCMClient::Result result) { | 
|  | TokenTuple tuple_key(app_id, authorized_entity, scope); | 
|  | auto callback_iter = delete_token_callbacks_.find(tuple_key); | 
|  | if (callback_iter == delete_token_callbacks_.end()) { | 
|  | // The callback could have been removed when the app is uninstalled. | 
|  | return; | 
|  | } | 
|  |  | 
|  | DeleteTokenCallback callback = callback_iter->second; | 
|  | delete_token_callbacks_.erase(callback_iter); | 
|  | callback.Run(result); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::WakeFromSuspendForHeartbeat(bool wake) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | wake_from_suspend_enabled_ = wake; | 
|  |  | 
|  | // The GCM service has not been initialized. | 
|  | if (!delayed_task_controller_) | 
|  | return; | 
|  |  | 
|  | if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { | 
|  | // The GCM service was initialized but has not started yet. | 
|  | delayed_task_controller_->AddTask( | 
|  | base::Bind(&GCMDriverDesktop::WakeFromSuspendForHeartbeat, | 
|  | weak_ptr_factory_.GetWeakPtr(), | 
|  | wake_from_suspend_enabled_)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // The GCMClient is ready so we can go ahead and post this task to the | 
|  | // IOWorker. | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::WakeFromSuspendForHeartbeat, | 
|  | base::Unretained(io_worker_.get()), | 
|  | wake_from_suspend_enabled_)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::AddHeartbeatInterval(const std::string& scope, | 
|  | int interval_ms) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | // The GCM service has not been initialized. | 
|  | if (!delayed_task_controller_) | 
|  | return; | 
|  |  | 
|  | if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { | 
|  | // The GCM service was initialized but has not started yet. | 
|  | delayed_task_controller_->AddTask( | 
|  | base::Bind(&GCMDriverDesktop::AddHeartbeatInterval, | 
|  | weak_ptr_factory_.GetWeakPtr(), scope, interval_ms)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::AddHeartbeatInterval, | 
|  | base::Unretained(io_worker_.get()), scope, interval_ms)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::RemoveHeartbeatInterval(const std::string& scope) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | // The GCM service has not been initialized. | 
|  | if (!delayed_task_controller_) | 
|  | return; | 
|  |  | 
|  | if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { | 
|  | // The GCM service was initialized but has not started yet. | 
|  | delayed_task_controller_->AddTask( | 
|  | base::Bind(&GCMDriverDesktop::RemoveHeartbeatInterval, | 
|  | weak_ptr_factory_.GetWeakPtr(), scope)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::RemoveHeartbeatInterval, | 
|  | base::Unretained(io_worker_.get()), scope)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::SetAccountTokens( | 
|  | const std::vector<GCMClient::AccountTokenInfo>& account_tokens) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | account_mapper_->SetAccountTokens(account_tokens); | 
|  |  | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::SetAccountTokens, | 
|  | base::Unretained(io_worker_.get()), | 
|  | account_tokens)); | 
|  | } | 
|  |  | 
|  | GCMClient::Result GCMDriverDesktop::EnsureStarted( | 
|  | GCMClient::StartMode start_mode) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | if (gcm_started_) | 
|  | return GCMClient::SUCCESS; | 
|  |  | 
|  | // Have any app requested the service? | 
|  | if (app_handlers().empty()) | 
|  | return GCMClient::UNKNOWN_ERROR; | 
|  |  | 
|  | // Polling for channel status should be invoked when GCM is being requested, | 
|  | // no matter whether GCM is enabled or nor. | 
|  | gcm_channel_status_syncer_->EnsureStarted(); | 
|  |  | 
|  | if (!gcm_enabled_) | 
|  | return GCMClient::GCM_DISABLED; | 
|  |  | 
|  | if (!delayed_task_controller_) | 
|  | delayed_task_controller_.reset(new GCMDelayedTaskController); | 
|  |  | 
|  | // Note that we need to pass weak pointer again since the existing weak | 
|  | // pointer in IOWorker might have been invalidated when GCM is stopped. | 
|  | io_thread_->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&GCMDriverDesktop::IOWorker::Start, | 
|  | base::Unretained(io_worker_.get()), | 
|  | start_mode, | 
|  | weak_ptr_factory_.GetWeakPtr())); | 
|  |  | 
|  | return GCMClient::SUCCESS; | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::RemoveCachedData() { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  | // Remove all the queued tasks since they no longer make sense after | 
|  | // GCM service is stopped. | 
|  | weak_ptr_factory_.InvalidateWeakPtrs(); | 
|  |  | 
|  | gcm_started_ = false; | 
|  | delayed_task_controller_.reset(); | 
|  | ClearCallbacks(); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::MessageReceived(const std::string& app_id, | 
|  | const IncomingMessage& message) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | // Drop the event if the service has been stopped. | 
|  | if (!gcm_started_) | 
|  | return; | 
|  |  | 
|  | DispatchMessage(app_id, message); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::MessagesDeleted(const std::string& app_id) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | // Drop the event if the service has been stopped. | 
|  | if (!gcm_started_) | 
|  | return; | 
|  |  | 
|  | GetAppHandler(app_id)->OnMessagesDeleted(app_id); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::MessageSendError( | 
|  | const std::string& app_id, | 
|  | const GCMClient::SendErrorDetails& send_error_details) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | // Drop the event if the service has been stopped. | 
|  | if (!gcm_started_) | 
|  | return; | 
|  |  | 
|  | GetAppHandler(app_id)->OnSendError(app_id, send_error_details); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::SendAcknowledged(const std::string& app_id, | 
|  | const std::string& message_id) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | // Drop the event if the service has been stopped. | 
|  | if (!gcm_started_) | 
|  | return; | 
|  |  | 
|  | GetAppHandler(app_id)->OnSendAcknowledged(app_id, message_id); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::GCMClientReady( | 
|  | const std::vector<AccountMapping>& account_mappings, | 
|  | const base::Time& last_token_fetch_time) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | UMA_HISTOGRAM_BOOLEAN("GCM.UserSignedIn", signed_in_); | 
|  |  | 
|  | gcm_started_ = true; | 
|  | if (wake_from_suspend_enabled_) | 
|  | WakeFromSuspendForHeartbeat(wake_from_suspend_enabled_); | 
|  |  | 
|  | last_token_fetch_time_ = last_token_fetch_time; | 
|  |  | 
|  | GCMDriver::AddAppHandler(kGCMAccountMapperAppId, account_mapper_.get()); | 
|  | account_mapper_->Initialize(account_mappings, | 
|  | base::Bind(&GCMDriverDesktop::MessageReceived, | 
|  | weak_ptr_factory_.GetWeakPtr())); | 
|  |  | 
|  | delayed_task_controller_->SetReady(); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::OnConnected(const net::IPEndPoint& ip_endpoint) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | connected_ = true; | 
|  |  | 
|  | // Drop the event if the service has been stopped. | 
|  | if (!gcm_started_) | 
|  | return; | 
|  |  | 
|  | FOR_EACH_OBSERVER(GCMConnectionObserver, | 
|  | connection_observer_list_, | 
|  | OnConnected(ip_endpoint)); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::OnDisconnected() { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | connected_ = false; | 
|  |  | 
|  | // Drop the event if the service has been stopped. | 
|  | if (!gcm_started_) | 
|  | return; | 
|  |  | 
|  | FOR_EACH_OBSERVER( | 
|  | GCMConnectionObserver, connection_observer_list_, OnDisconnected()); | 
|  | } | 
|  |  | 
|  | void GCMDriverDesktop::GetGCMStatisticsFinished( | 
|  | const GCMClient::GCMStatistics& stats) { | 
|  | DCHECK(ui_thread_->RunsTasksOnCurrentThread()); | 
|  |  | 
|  | // request_gcm_statistics_callback_ could be null when an activity, i.e. | 
|  | // network activity, is triggered while gcm-intenals page is not open. | 
|  | if (!request_gcm_statistics_callback_.is_null()) | 
|  | request_gcm_statistics_callback_.Run(stats); | 
|  | } | 
|  |  | 
|  | bool GCMDriverDesktop::TokenTupleComparer::operator()( | 
|  | const TokenTuple& a, const TokenTuple& b) const { | 
|  | if (std::get<0>(a) < std::get<0>(b)) | 
|  | return true; | 
|  | if (std::get<0>(a) > std::get<0>(b)) | 
|  | return false; | 
|  |  | 
|  | if (std::get<1>(a) < std::get<1>(b)) | 
|  | return true; | 
|  | if (std::get<1>(a) > std::get<1>(b)) | 
|  | return false; | 
|  |  | 
|  | return std::get<2>(a) < std::get<2>(b); | 
|  | } | 
|  |  | 
|  | }  // namespace gcm |